Le applicazioni di esempio sono un ottimo modo per esplorare le funzionalità headless di Adobe Experience Manager (AEM). Questa applicazione iOS illustra come eseguire query sul contenuto utilizzando AEM API GraphQL utilizzando query persistenti.
Visualizza la codice sorgente su GitHub
È necessario installare localmente i seguenti strumenti:
L’applicazione iOS funziona con le seguenti opzioni di distribuzione AEM. Tutte le implementazioni richiedono l’ Sito WKND v2.0.0+ da installare.
L’applicazione iOS è progettata per connettersi a un Pubblicazione AEM Tuttavia, può generare contenuto da AEM Author se nella configurazione dell’applicazione iOS viene fornita l’autenticazione.
Clona il adobe/aem-guides-wknd-graphql
archivio:
$ git clone git@github.com:adobe/aem-guides-wknd-graphql.git
Launch Xcode e apri la cartella ios-app
Modificare il file Config.xcconfig
file e aggiornamento AEM_SCHEME
e AEM_HOST
per corrispondere al servizio AEM Publish di destinazione.
// The http/https protocol scheme used to access the AEM_HOST
AEM_SCHEME = http
// Target hostname for AEM environment, do not include http:// or https://
AEM_HOST = localhost:4503
Se ti connetti ad AEM Author, aggiungi la AEM_AUTH_TYPE
e supporto delle proprietà di autenticazione per Config.xcconfig
.
Autenticazione di base
La AEM_USERNAME
e AEM_PASSWORD
autentica un utente AEM locale con accesso al contenuto WKND GraphQL.
AEM_AUTH_TYPE = basic
AEM_USERNAME = admin
AEM_PASSWORD = admin
Autenticazione token
La AEM_TOKEN
è un token di accesso che esegue l’autenticazione a un utente AEM con accesso al contenuto WKND GraphQL.
AEM_AUTH_TYPE = token
AEM_TOKEN = abcd...0123
Crea l’applicazione utilizzando Xcode e distribuisci l’app al simulatore iOS
Nell’applicazione deve essere visualizzato un elenco delle avventure del sito WKND. Selezionando un’avventura si aprono i dettagli dell’avventura. Nella vista elenco Avventures, seleziona per aggiornare i dati da AEM.
Di seguito è riportato un riepilogo di come viene creata l'applicazione iOS, di come si connette a AEM Headless per recuperare il contenuto utilizzando le query persistenti GraphQL e di come tali dati vengono presentati. Il codice completo è disponibile all'indirizzo GitHub.
Seguendo AEM best practice headless, l’applicazione iOS utilizza query persistenti AEM GraphQL per eseguire query sui dati di avventura. L'applicazione utilizza due query persistenti:
wknd/adventures-all
query persistente, che restituisce tutte le avventure in AEM con un set abbreviato di proprietà. Questa query persistente guida l'elenco di avventura della visualizzazione iniziale.# Retrieves a list of all adventures
{
adventureList {
items {
_path
slug
title
price
tripLength
primaryImage {
... on ImageRef {
_path
mimeType
width
height
}
}
}
}
}
wknd/adventure-by-slug
query persistente, che restituisce una singola avventura per slug
(proprietà personalizzata che identifica in modo univoco un’avventura) con un set completo di proprietà. Questa query persistente potenzia le visualizzazioni dei dettagli dell’avventura.# Retrieves an adventure Content Fragment based on it's slug
# Example query variables:
# {"slug": "bali-surf-camp"}
# Technically returns an adventure list but since the the slug
# property is set to be unique in the CF Model, only a single CF is expected
query($slug: String!) {
adventureList(filter: {
slug: {
_expressions: [ { value: $slug } ]
}
}) {
items {
_path
title
slug
activity
adventureType
price
tripLength
groupSize
difficulty
price
primaryImage {
... on ImageRef {
_path
mimeType
width
height
}
}
description {
json
plaintext
}
itinerary {
json
plaintext
}
}
_references {
...on AdventureModel {
_path
slug
title
price
__typename
}
}
}
}
AEM le query persistenti vengono eseguite su HTTP GET, pertanto non è possibile utilizzare le librerie GraphQL comuni che utilizzano HTTP POST, come Apollo. Al contrario, crea una classe personalizzata che esegua le richieste HTTP di query persistenti a AEM.
AEM/Aem.swift
crea un'istanza del Aem
Classe utilizzata per tutte le interazioni con AEM Headless. Il pattern è:
getAdventures(..)
o getAdventureBySlug(..)
) le visualizzazioni dell’applicazione iOS richiamano per ottenere i dati relativi all’avventura.makeRequest(..)
che richiama una richiesta HTTP GET asincrona a AEM Headless e restituisce i dati JSON.AEM/Models.swift
, che viene mappata agli oggetti JSON che hanno restituito il mio AEM Headless. /// # 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(completion: @escaping ([Adventure]) -> ()) {
// Create the HTTP request object representing the persisted query to get all adventures
let request = makeRequest(persistedQueryName: "wknd-shared/adventures-all")
// Wait fo the HTTP request to return
URLSession.shared.dataTask(with: request) { (data, response, error) in
// Error check as needed
if ((error) != nil) {
print("Unable to connect to AEM GraphQL endpoint")
completion([])
}
if (!data!.isEmpty) {
// Decode the JSON data into Swift objects
let adventures = try! JSONDecoder().decode(Adventures.self, from: data!)
DispatchQueue.main.async {
// Return the array of Adventure objects
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
}
...
iOS preferisce la mappatura di oggetti JSON a modelli di dati tipizzati.
La src/AEM/Models.swift
definisce il decodificabile Le strutture e le classi Swift da mappare alle risposte JSON AEM restituite dalle risposte JSON AEM.
SwiftUI viene utilizzato per le varie visualizzazioni nell’applicazione. Apple fornisce un tutorial introduttivo per creazione di elenchi e navigazione con SwiftUI.
WKNDAdventuresApp.swift
L'iscrizione della domanda e comprende AdventureListView
di cui .onAppear
il gestore eventi viene utilizzato per recuperare tutti i dati relativi alle avventure tramite aem.getAdventures()
. Il aem
oggetto inizializzato qui ed esposto ad altre viste come EnvironmentObject.
Views/AdventureListView.swift
Visualizza un elenco di avventure (in base ai dati di aem.getAdventures()
) e visualizza una voce di elenco per ogni avventura che utilizza AdventureListItemView
.
Views/AdventureListItemView.swift
Visualizza ogni elemento nell'elenco delle avventure (Views/AdventureListView.swift
).
Views/AdventureDetailView.swift
Visualizza i dettagli di un’avventura, inclusi il titolo, la descrizione, il prezzo, il tipo di attività e l’immagine primaria. Questa visualizzazione richiede AEM per dettagli completi sull'avventura utilizzando aem.getAdventureBySlug(slug: slug)
, dove slug
viene passato in base alla riga dell’elenco di selezione.
Le immagini a cui fa riferimento Adventure Content Fragments sono servite da AEM. Questa app iOS utilizza il percorso _path
nella risposta GraphQL e prefissa il AEM_SCHEME
e AEM_HOST
per creare un URL completo.
Se ci si connette a risorse protette su AEM che richiede un’autorizzazione, è necessario aggiungere le credenziali anche alle richieste di immagini.
SDWebImageSwiftUI e SDWebImage vengono utilizzati per caricare le immagini remote da AEM che popolano l'immagine Adventure sul AdventureListItemView
e AdventureDetailView
visualizzazioni.
La aem
(in AEM/Aem.swift
) facilita l'utilizzo di immagini AEM in due modi:
aem.imageUrl(path: String)
viene utilizzato nelle visualizzazioni per anteporre lo schema di AEM e ospitare il percorso dell'immagine, creando un URL completo.
// adventure.image() => /content/dam/path/to/an/image.png
let imageUrl = aem.imageUrl(path: adventure.image())
// imageUrl => http://localhost:4503/content/dam/path/to/an/image.png
La convenience init(..)
in Aem
imposta le intestazioni di autorizzazione HTTP sulla richiesta HTTP immagine, in base alla configurazione delle applicazioni iOS.
/// 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")
}
/// 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")
}
Un approccio simile può essere utilizzato con l’interfaccia nativa SwiftUI AsyncImage. AsyncImage
è supportato da iOS 15.0+.