aplicativo iOS
[AEM Headless as a Cloud Service]{class="badge informative"}
Aplicativos de exemplo são uma ótima maneira de explorar as capacidades headless do Adobe Experience Manager (AEM). Este aplicativo do iOS demonstra como consultar conteúdo usando as APIs do GraphQL AEM usando consultas persistentes.
Exibir o código-fonte no GitHub
Pré-requisitos prerequisites
As seguintes ferramentas devem ser instaladas localmente:
Requisitos do AEM
O aplicativo iOS funciona com as seguintes opções de implantação do AEM. Todas as implantações exigem que o WKND Site v3.0.0+ esteja instalado.
- AEM as a Cloud Service
- Configuração local usando o SDK do AEM Cloud Service
O aplicativo do iOS foi projetado para se conectar a um ambiente AEM do Publish AEM. No entanto, ele poderá obter conteúdo do autor do se a autenticação for fornecida na configuração do aplicativo do iOS.
Como usar
-
Clonar o repositório
adobe/aem-guides-wknd-graphql
:code language-shell $ git clone git@github.com:adobe/aem-guides-wknd-graphql.git
-
Abrir Xcode e abrir a pasta
ios-app
-
Modifique o arquivo
Config.xcconfig
e atualizeAEM_SCHEME
eAEM_HOST
para corresponder ao serviço AEM Publish de destino.code language-plain // The http/https protocol scheme used to access the AEM_HOST AEM_SCHEME = https // Target hostname for AEM environment, do not include http:// or https:// AEM_HOST = publish-p123-e456.adobeaemcloud.com
Se estiver se conectando ao Autor do AEM, adicione o
AEM_AUTH_TYPE
e as propriedades de autenticação de suporte aoConfig.xcconfig
.Autenticação básica
O
AEM_USERNAME
e oAEM_PASSWORD
autenticam um usuário AEM local com acesso ao conteúdo do WKND GraphQL.code language-plain AEM_AUTH_TYPE = basic AEM_USERNAME = admin AEM_PASSWORD = admin
Autenticação do token
O
AEM_TOKEN
é um token de acesso que se autentica para um usuário AEM com acesso ao conteúdo do WKND GraphQL.code language-plain AEM_AUTH_TYPE = token AEM_TOKEN = abcd...0123
-
Crie o aplicativo usando o Xcode e implante o aplicativo no simulador do iOS
-
Uma lista de aventuras do site WKND deve ser exibida no aplicativo. Selecionar uma aventura abre os detalhes da aventura. Na exibição da lista de aventuras, puxe para atualizar os dados do AEM.
O código
Abaixo está um resumo de como o aplicativo iOS é criado, como ele se conecta ao AEM Headless para recuperar conteúdo usando consultas persistentes do GraphQL e como esses dados são apresentados. O código completo pode ser encontrado no GitHub.
Consultas persistentes
Seguindo as práticas recomendadas do AEM Headless, o aplicativo iOS usa consultas persistentes do AEM GraphQL para consultar dados de aventura. O aplicativo usa duas consultas persistentes:
wknd/adventures-all
consulta persistente, que retorna todas as aventuras no AEM com um conjunto abreviado de propriedades. Essa consulta persistente direciona a lista de aventura da visualização inicial.
# Retrieves a list of all Adventures
#
# Optional query variables:
# - { "offset": 10 }
# - { "limit": 5 }
# - {
# "imageFormat": "JPG",
# "imageWidth": 1600,
# "imageQuality": 90
# }
query ($offset: Int, $limit: Int, $sort: String, $imageFormat: AssetTransformFormat=JPG, $imageWidth: Int=1200, $imageQuality: Int=80) {
adventureList(
offset: $offset
limit: $limit
sort: $sort
_assetTransform: {
format: $imageFormat
width: $imageWidth
quality: $imageQuality
preferWebp: true
}) {
items {
_path
slug
title
activity
price
tripLength
primaryImage {
... on ImageRef {
_path
_dynamicUrl
}
}
}
}
}
wknd/adventure-by-slug
consulta persistente, que retorna uma única aventura porslug
(uma propriedade personalizada que identifica exclusivamente uma aventura) com um conjunto completo de propriedades. Essa consulta persistente possibilita as exibições de detalhes de aventura.
query ($slug: String!, $imageFormat:AssetTransformFormat=JPG, $imageSeoName: String, $imageWidth: Int=1200, $imageQuality: Int=80) {
adventureList(
filter: {slug: {_expressions: [{value: $slug}]}}
_assetTransform: {
format: $imageFormat
seoName: $imageSeoName
width: $imageWidth
quality: $imageQuality
preferWebp: true
}) {
items {
_path
title
slug
activity
adventureType
price
tripLength
groupSize
difficulty
price
primaryImage {
... on ImageRef {
_path
_dynamicUrl
}
}
description {
json
plaintext
html
}
itinerary {
json
plaintext
html
}
}
_references {
... on AdventureModel {
_path
slug
title
price
__typename
}
}
}
}
Executar consulta persistente do GraphQL
As consultas persistentes do AEM são executadas por HTTP GET e, portanto, bibliotecas GraphQL comuns que usam POST HTTP, como Apollo, não podem ser usadas. Em vez disso, crie uma classe personalizada que execute a consulta persistente de solicitações HTTP GET para AEM.
AEM/Aem.swift
instancia a classe Aem
usada para todas as interações com AEM Headless. O padrão é:
-
Cada consulta persistente tem uma função pública correspondente (por exemplo,
getAdventures(..)
ougetAdventureBySlug(..)
) as visualizações do aplicativo iOS invocam para obter dados de aventura. -
A função pública chama uma função privada
makeRequest(..)
que invoca uma solicitação HTTP GET assíncrona para AEM Headless e retorna os dados JSON. -
Cada função pública então decodifica os dados JSON e executa todas as verificações ou transformações necessárias antes de retornar os dados do Adventure para a exibição.
- Os dados JSON do GraphQL do AEM são decodificados usando as structs/classes definidas em
AEM/Models.swift
, que mapeiam para os objetos JSON retornados por meu AEM Headless.
- Os dados JSON do GraphQL do AEM são decodificados usando as structs/classes definidas em
/// # getAdventures(..)
/// Returns all WKND adventures using the `wknd-shared/adventures-all` persisted query.
/// For this func call to work, the `wknd-shared/adventures-all` query must be deployed to the AEM environment/service specified by the host.
///
/// Since HTTP requests are async, the completion syntax is used.
func getAdventures(params: [String:String], completion: @escaping ([Adventure]) -> ()) {
let request = makeRequest(persistedQueryName: "wknd-shared/adventures-all", params: params)
URLSession.shared.dataTask(with: request) { (data, response, error) in
if ((error) != nil) {
print("Unable to connect to AEM GraphQL endpoint")
completion([])
} else if (!data!.isEmpty) {
let adventures = try! JSONDecoder().decode(Adventures.self, from: data!)
DispatchQueue.main.async {
completion(adventures.data.adventureList.items)
}
}
}.resume();
}
...
/// #makeRequest(..)
/// Generic method for constructing and executing AEM GraphQL persisted queries
private func makeRequest(persistedQueryName: String, params: [String: String] = [:]) -> URLRequest {
// Encode optional parameters as required by AEM
let persistedQueryParams = params.map { (param) -> String in
encode(string: ";\(param.key)=\(param.value)")
}.joined(separator: "")
// Construct the AEM GraphQL persisted query URL, including optional query params
let url: String = "\(self.scheme)://\(self.host)/graphql/execute.json/" + persistedQueryName + persistedQueryParams;
var request = URLRequest(url: URL(string: url)!);
// Add authentication to the AEM GraphQL persisted query requests as defined by the iOS application's configuration
request = addAuthHeaders(request: request)
return request
}
...
Modelos de dados de resposta do GraphQL
O iOS prefere mapear objetos JSON a modelos de dados digitados.
O src/AEM/Models.swift
define as estruturas e classes Swift decodificáveis que mapeiam as respostas JSON do AEM retornadas pelas respostas JSON do AEM.
Exibições
A SwiftUI é usada para as várias exibições no aplicativo. O Apple fornece um tutorial de introdução para criação de listas e navegação com SwiftUI.
-
WKNDAdventuresApp.swift
A entrada do aplicativo e inclui
AdventureListView
cujo manipulador de eventos.onAppear
é usado para buscar todos os dados de aventuras viaaem.getAdventures()
. O objetoaem
compartilhado é inicializado aqui e exposto a outras exibições como um EnvironmentObject. -
Views/AdventureListView.swift
Exibe uma lista de aventuras (com base nos dados de
aem.getAdventures()
) e exibe um item de lista para cada aventura usando oAdventureListItemView
. -
Views/AdventureListItemView.swift
Exibe cada item na lista de aventuras (
Views/AdventureListView.swift
). -
Views/AdventureDetailView.swift
Exibe os detalhes de uma aventura, incluindo o título, a descrição, o preço, o tipo de atividade e a imagem principal. Esta exibição consulta o AEM para obter detalhes completos da aventura usando
aem.getAdventureBySlug(slug: slug)
, no qual o parâmetroslug
é passado com base na linha da lista selecionada.
Imagens remotas
Imagens referenciadas por Fragmentos de conteúdo de aventura são servidas pelo AEM. Este aplicativo iOS usa o campo de caminho _dynamicUrl
na resposta do GraphQL e adiciona os prefixos AEM_SCHEME
e AEM_HOST
para criar uma URL totalmente qualificada. Se estiver desenvolvendo no SDK do AEM, _dynamicUrl
retornará nulo, portanto, para fallback de desenvolvimento para o campo _path
da imagem.
Se a conexão com recursos protegidos no AEM exigir autorização, credenciais também deverão ser adicionadas às solicitações de imagem.
A SDWebImageSwiftUI e a SDWebImage são usadas para carregar as imagens remotas do AEM que populam a imagem Adventure nas exibições AdventureListItemView
e AdventureDetailView
.
A classe aem
(em AEM/Aem.swift
) facilita o uso de imagens AEM de duas maneiras:
-
aem.imageUrl(path: String)
é usado nas exibições para anexar o esquema e o host do AEM ao caminho da imagem, criando uma URL totalmente qualificada.code language-swift // adventure.image() => /adobe/dynamicmedia/deliver/dm-aid--741ed388-d5f8-4797-8095-10c896dc9f1d/example.jpg?quality=80&preferwebp=true let imageUrl = aem.imageUrl(path: adventure.image()) // imageUrl => https://publish-p123-e456.adobeaemcloud.com/adobe/dynamicmedia/deliver/dm-aid--741ed388-d5f8-4797-8095-10c896dc9f1d/example.jpg?quality=80&preferwebp=true
-
O
convenience init(..)
emAem
definiu cabeçalhos de Autorização HTTP na solicitação de imagem HTTP, com base na configuração de aplicativos iOS.- Se a autenticação básica estiver configurada, a autenticação básica será anexada a todas as solicitações de imagem.
code language-swift /// AEM/Aem.swift /// /// # Basic authentication init /// Used when authenticating to AEM using local accounts (basic auth) convenience init(scheme: String, host: String, username: String, password: String) { ... // Add basic auth headers to all Image requests, as they are (likely) protected as well SDWebImageDownloader.shared.setValue("Basic \(encodeBasicAuth(username: username, password: password))", forHTTPHeaderField: "Authorization") }
- Se a autenticação de token estiver configurada, a autenticação de token será anexada a todas as solicitações de imagem.
code language-swift /// AEM/Aem.swift /// /// # Token authentication init /// Used when authenticating to AEM using token authentication (Dev Token or access token generated from Service Credentials) convenience init(scheme: String, host: String, token: String) { ... // Add token auth headers to all Image requests, as they are (likely) protected as well SDWebImageDownloader.shared.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization") }
- Se nenhuma autenticação estiver configurada, nenhuma autenticação será anexada às solicitações de imagem.
Uma abordagem semelhante pode ser usada com a AsyncImage nativa de SwiftUI. Há suporte para AsyncImage
no iOS 15.0+.