iOS應用程式

[AEM Headlessas a Cloud Service]{class="badge informative"}

範例應用程式是探索Adobe Experience Manager (AEM)無周邊功能的絕佳方式。 此iOS應用程式示範了如何使用AEM的GraphQL API透過持續性查詢來查詢內容。

使用AEM Headless的 iOS SwiftUI應用程式

在GitHub🔗上檢視原始程式碼

先決條件 prerequisites

下列工具應在本機安裝:

AEM需求

iOS應用程式可與下列AEM部署選項搭配使用。 所有部署都需要安裝WKND網站v3.0.0+

iOS應用程式設計來連線至​ AEM Publish ​環境,不過,如果iOS應用程式的設定中有提供驗證,則可以從AEM Author取得內容。

使用方式

  1. 複製adobe/aem-guides-wknd-graphql存放庫:

    code language-shell
    $ git clone git@github.com:adobe/aem-guides-wknd-graphql.git
    
  2. 開啟Xcode並開啟資料夾ios-app

  3. 修改檔案Config.xcconfig並更新AEM_SCHEMEAEM_HOST,以符合您的目標AEM Publish服務。

    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
    

    如果連線到AEM Author,請將AEM_AUTH_TYPE和支援的驗證屬性新增到Config.xcconfig

    基本驗證

    AEM_USERNAMEAEM_PASSWORD驗證本機AEM使用者是否有權存取WKND GraphQL內容。

    code language-plain
    AEM_AUTH_TYPE = basic
    AEM_USERNAME = admin
    AEM_PASSWORD = admin
    

    權杖驗證

    AEM_TOKEN存取Token,其可驗證給有權存取WKND GraphQL內容的AEM使用者。

    code language-plain
    AEM_AUTH_TYPE = token
    AEM_TOKEN = abcd...0123
    
  4. 使用Xcode建立應用程式,並將應用程式部署到iOS模擬器

  5. WKND網站中的冒險清單應顯示在應用程式上。 選取冒險會開啟冒險詳細資料。 在冒險清單檢視上,提取以從AEM重新整理資料。

程式碼

以下摘要說明如何建立iOS應用程式、其如何連線至AEM Headless以使用GraphQL持續查詢來擷取內容,以及資料如何呈現。 您可以在GitHub上找到完整程式碼。

持久查詢

依照AEM Headless最佳實務,iOS應用程式會使用AEM GraphQL持續性查詢來查詢冒險資料。 應用程式使用兩個持續查詢:

  • wknd/adventures-all持續查詢,這會傳回AEM中所有具有刪節屬性集的冒險。 此持續查詢會驅動初始檢視的冒險清單。
# 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持續查詢,會傳回slug的單一冒險(唯一識別冒險的自訂屬性)和完整屬性集。 此持續性查詢可為冒險詳細資料檢視提供支援。
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
      }
    }
  }
}

執行GraphQL持久查詢

AEM的持續查詢會透過HTTPGET執行,因此使用HTTPPOST的常見GraphQL程式庫(例如Apollo)無法使用。 請改為建立自訂類別,對AEM執行持續查詢HTTPGET請求。

AEM/Aem.swift會具現化所有與AEM Headless互動時所使用的Aem類別。 模式為:

  1. 每個持續查詢都有對應的公用函式(例如 getAdventures(..)getAdventureBySlug(..)) iOS應用程式的檢視叫用以取得冒險資料。

  2. 公用函式會呼叫私人函式makeRequest(..),該函式會對AEM Headless叫用非同步HTTPGET要求,並傳回JSON資料。

  3. 接著,每個公用函式都會解碼JSON資料,並執行任何必要的檢查或轉換,然後將冒險資料傳回檢視。

    • AEM的GraphQL JSON資料會使用AEM/Models.swift中定義的結構/類別來解碼,這些結構/類別對應到傳回我的AEM Headless的JSON物件。
    /// # 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
    }

    ...

GraphQL回應資料模型

iOS偏好將JSON物件對應至輸入的資料模型。

src/AEM/Models.swift定義了可解碼的 Swift結構和類別,這些結構和類別對應到AEM JSON回應所傳回的AEM JSON回應。

檢視

SwiftUI用於應用程式中的各種檢視。 Apple提供使用SwiftUI建立清單和導覽的快速入門教學課程。

  • WKNDAdventuresApp.swift

    應用程式的專案,並包含其.onAppear事件處理常式用來透過aem.getAdventures()擷取所有冒險資料的AdventureListView。 已在此初始化共用aem物件,並以EnvironmentObject的形式公開給其他檢視。

  • Views/AdventureListView.swift

    顯示冒險清單(根據aem.getAdventures()的資料),並使用AdventureListItemView顯示每個冒險的清單專案。

  • Views/AdventureListItemView.swift

    顯示Adventures清單(Views/AdventureListView.swift)中的每個專案。

  • Views/AdventureDetailView.swift

    顯示冒險的詳細資訊,包括標題、說明、價格、活動型別和主要影像。 此檢視會使用aem.getAdventureBySlug(slug: slug)查詢AEM的完整冒險詳細資料,其中slug引數是根據選取清單列傳入。

遠端影像

冒險內容片段參考的影像由AEM提供。 此iOS應用程式在GraphQL回應中使用路徑_dynamicUrl欄位,並在AEM_SCHEMEAEM_HOST加上前置詞,以建立完整限定的URL。 如果針對AE SDK開發,_dynamicUrl會傳回null,因此對於開發,會退回影像的_path欄位。

如果連線到AEM上需要授權的受保護資源,則也必須將憑證新增到影像請求。

SDWebImageSwiftUISDWebImage是用來從AEM載入遠端影像,這些影像會在AdventureListItemViewAdventureDetailView檢視中填入Adventure影像。

aem類別(在AEM/Aem.swift中)以兩種方式方便使用AEM影像:

  1. aem.imageUrl(path: String)用於檢視中,以將AEM配置加在影像的路徑前面,並建立完整限定的URL。

    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
    
  2. Aem中的convenience init(..)會根據iOS應用程式設定,在影像HTTP要求上設定HTTP授權標頭。

    • 如果已設定​ 基本驗證,則會將基本驗證附加到所有影像要求。
    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")
    }
    
    • 如果已設定​ 權杖驗證,則權杖驗證會附加至所有影像要求。
    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")
    }
    
    • 如果未設定​ 驗證,則不會將驗證附加到影像要求。

類似的處理方式可用於SwiftUI-native AsyncImage。 iOS 15.0+支援AsyncImage

其他資源

recommendation-more-help
e25b6834-e87f-4ff3-ba56-4cd16cdfdec4