Découvrez les fonctionnalités découplées d’Adobe Experience Manager (AEM) grâce aux exemples d’applications. Cette application iOS explique comment interroger du contenu à l’aide des API GraphQL d’AEM en utilisant les requêtes persistantes.
Afficher le code source sur GitHub
Les outils suivants doivent être installés localement :
L’application iOS fonctionne avec les options de déploiement AEM suivantes. Tous les déploiements nécessitent que la version v2.0.0 ou supérieure du site WKND soit installée.
L’application iOS est conçue pour se connecter à un environnement de publication AEM. Cependant, elle peut récupérer du contenu depuis l’instance de création AEM, si l’authentification est fournie dans la configuration de l’application iOS.
Clonez le référentiel adobe/aem-guides-wknd-graphql
:
$ git clone git@github.com:adobe/aem-guides-wknd-graphql.git
Lancez Xcode et ouvrez le dossier ios-app
.
Modifiez le fichier Config.xcconfig
et mettez à jour AEM_SCHEME
et AEM_HOST
pour correspondre à votre service de publication AEM cible.
// 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
Si vous vous connectez au service de création AEM, ajoutez AEM_AUTH_TYPE
et les propriétés d’authentification nécessaires à Config.xcconfig
.
Authentification de base
Les éléments AEM_USERNAME
et AEM_PASSWORD
authentifient une personne utilisatrice locale d’AEM ayant accès au contenu GraphQL de WKND.
AEM_AUTH_TYPE = basic
AEM_USERNAME = admin
AEM_PASSWORD = admin
Authentification par jeton
Le AEM_TOKEN
est un jeton d’accès qui s’authentifie auprès d’un utilisateur ou d’une utilisatrice d’AEM ayant accès au contenu GraphQL de WKND.
AEM_AUTH_TYPE = token
AEM_TOKEN = abcd...0123
Créer l’application à l’aide de Xcode et la déployer sur un simulateur iOS
Une liste des Adventures du site WKND s’affiche dans l’application. Si vous en sélectionnez une, vous ouvrez les détails de l’Adventure. Dans l’affichage en liste des Adventures, balayez du haut de l’écran vers le bas pour actualiser les données d’AEM.
Vous trouverez ci-dessous un résumé de la création de l’application iOS, de sa connexion à AEM Headless pour récupérer du contenu à l’aide de requêtes persistantes GraphQL et de la manière dont ces données sont présentées. Le code complet se trouve sur GitHub.
En suivant les bonnes pratiques d’AEM Headless, l’application iOS utilise les requêtes persistantes GraphQL d’AEM pour interroger les données d’Adventure. L’application utilise deux requêtes persistantes :
wknd/adventures-all
, qui renvoie toutes les Adventures dans AEM avec un ensemble abrégé de propriétés. Cette requête persistante génère la liste des Adventures de la vue initiale.# Retrieves a list of all adventures
{
adventureList {
items {
_path
slug
title
price
tripLength
primaryImage {
... on ImageRef {
_path
mimeType
width
height
}
}
}
}
}
wknd/adventure-by-slug
, qui renvoie une seule Adventure par slug
(propriété personnalisée qui identifie de manière unique une Adventure) avec un ensemble complet de propriétés. Cette requête persistante alimente les vues détaillées de l’Adventure.# 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
}
}
}
}
Les requêtes persistantes d’AEM sont exécutées via HTTP GET. Par conséquent, les bibliothèques GraphQL courantes qui utilisent HTTP POST, comme Apollo, ne peuvent pas être utilisées. Créez plutôt une classe personnalisée qui exécute les requêtes HTTP GET persistantes vers AEM.
AEM/Aem.swift
instancie la classe Aem
utilisée pour toutes les interactions avec AEM Headless. Le modèle est le suivant :
Chaque requête persistante a une fonction publique correspondante (par exemple, getAdventures(..)
ou getAdventureBySlug(..)
) que les vues de l’application iOS appellent pour obtenir des données de l’Adventure.
La fonction publique appelle une fonction privée makeRequest(..)
qui appelle une requête HTTP GET asynchrone vers AEM Headless et renvoie les données JSON.
Chaque fonction publique décode ensuite les données JSON et effectue toutes les vérifications ou transformations requises, avant de renvoyer les données de l’adventure à la vue.
AEM/Models.swift
, qui correspondent aux objets JSON renvoyés par 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 préfère mapper les objets JSON aux modèles de données avec un type.
L’élément src/AEM/Models.swift
définit les structures et classes Swift décodables qui correspondent aux réponses JSON d’AEM renvoyées par les réponses JSON d’AEM.
SwiftUI est utilisé pour les différentes vues de l’application. Apple fournit un tutoriel de prise en main permettant de créer des listes et une navigation avec SwiftUI.
WKNDAdventuresApp.swift
Entrée de l’application. Inclut AdventureListView
dont le gestionnaire d’événements .onAppear
est utilisé pour récupérer toutes les données des Adventures via aem.getAdventures()
. L’objet aem
partagé est initialisé ici et exposé à d’autres vues sous la forme d’un EnvironmentObject.
Views/AdventureListView.swift
Affiche une liste d’Adventures (en fonction des données de aem.getAdventures()
) et affiche un élément de liste pour chaque Adventure à l’aide de AdventureListItemView
.
Views/AdventureListItemView.swift
Affiche chaque élément de la liste des Adventures (Views/AdventureListView.swift
).
Views/AdventureDetailView.swift
Affiche les détails d’une Adventure, notamment le titre, la description, le Price, le type d’Activity et l’image principale. Cette vue interroge AEM pour obtenir les détails complets de l’Adventure à l’aide de aem.getAdventureBySlug(slug: slug)
, où le paramètre slug
est transmis en fonction de la ligne de liste sélectionnée.
Les images référencées par les fragments de contenu des Adventures sont diffusées par AEM. Cette application iOS utilise le champ de chemin d’accès _path
dans la réponse GraphQL et ajoute un préfixe à AEM_SCHEME
et AEM_HOST
pour créer une URL complète.
Si vous vous connectez à des ressources protégées sur AEM qui nécessitent une autorisation, les informations d’identification doivent également être ajoutées aux requêtes d’images.
SDWebImageSwiftUI et SDWebImage sont utilisés pour charger les images distantes d’AEM qui renseignent l’image de l’Adventure sur les vues AdventureListItemView
et AdventureDetailView
.
La classe aem
(dans AEM/Aem.swift
) facilite l’utilisation des images AEM de deux manières :
aem.imageUrl(path: String)
est utilisé dans les vues pour ajouter le schéma et l’hôte d’AEM au chemin d’accès de l’image, créant ainsi une URL complète.
// 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
L’élément convenience init(..)
dans Aem
définit les en-têtes d’autorisation HTTP sur la requête HTTP d’image, en fonction de la configuration des applications 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")
}
Une approche similaire peut être utilisée avec l’élément AsyncImage natif sur SwiftUI. AsyncImage
est pris en charge sur iOS 15.0+.