使用無頭 (headless) Oracle Content Management 在 Swift 打造部落格

簡介

使用 Swift 和 SwiftUI 的 iOS 開發環境是一項功能強大的工具,可用來建置使用來自 Oracle Content Management 內容的應用程式。有了正確的內容模型,您可以快速建立構成典型部落格 App 的使用者介面。

本教學課程將運用 Oracle Content Management 作為無頭 (headless) CMS 及其軟體開發套件 (SDK) 進行內容傳遞,在 Swift 中建置簡單的 iOS 部落格應用系統。此 iOS 範例位於 GitHub

教學課程包含三個步驟:

  1. 準備 Oracle Content Management
  2. 以 Xcode 建立部落格應用程式
  3. 執行應用程式

必要條件

繼續本教學課程之前,建議您先閱讀下列資訊:

若要依循此教學課程,您需要:

我們正在打造什麼

我們的部落格應用程式包含三個不同的頁面,可讓訪客瀏覽編排到主題的部落格文章。

此動畫影像顯示從部落格範例應用程式瀏覽,從主題開始瀏覽至文章清單,最後詳細檢視部落格文章。

第一頁是首頁,包含品牌建立 (公司名稱和標誌)、部分連結,以及部落格主題清單。

第二頁是文章清單頁面,顯示屬於該主題之每個部落格文章的預覽。

最後,文章頁面會呈現最終部落格文章,包括部落格作者的相關資訊。

若要繼續,您必須擁有 Oracle Content Management 的有效訂閱,並且以「內容管理員」角色登入。

步驟 1:準備 Oracle Content Management

如果您尚未擁有 Oracle Content Management 執行處理,請參閱快速入門,瞭解如何註冊 Oracle Cloud、佈建 Oracle Content Management 執行處理,以及將 Oracle Content Management 設定為無頭 CMS。

對於此教學課程,您需要以兩種方式建立內容模型。有可下載的資產套件可供使用,將內容類型與相關內容填入您的空白儲存庫,或者您可以建立自己的內容模型與內容。

若要準備 Oracle Content Management:

  1. 建立通道和資產儲存區域
  2. 使用下列兩種方法之一來建立內容模型

建立通道和資產儲存區域

您必須先在 Oracle Content Management 中建立通道和資產儲存區域,才能發布內容。

在 Oracle Content Management 中建立通道和資產儲存區域:

  1. 以管理員身分登入 Oracle Content Management Web 介面。

  2. 從左側導覽功能表中選擇內容,然後從頁面標頭的選取清單中選擇發布管道

    此圖像顯示「內容」頁面標頭的下拉式功能表中選取的「發布通道」選項。

  3. 在右上角,按一下建立 (Create) 以建立新通道。請為此教學課程命名通道 'OCEGettingStartedChannel',並讓存取保持公用。按一下儲存以建立管道。

    此圖像顯示發布通道定義面板,通道名稱欄位中包含 'OCEGettingStartedChannel'。

  4. 在左側導覽功能表中選擇內容,然後從頁面表頭的選取清單中選擇儲存庫

    此圖像顯示「內容」頁面標頭的下拉式功能表中選取的「儲存區域」選項。

  5. 在右上角,按一下建立以建立新的資產儲存庫。請為此教學課程命名資產儲存區域 'OCEGettingStartedRepository'。

    此圖像顯示儲存區域定義面板,儲存庫名稱欄位中有 'OCEGettingStartedRepository'。

  6. 發布通道欄位中,選取 OCEGettingStartedChannel 通道以向 Oracle Content Management 表示 OCEGettingStartedRepository 儲存區域中的內容可發布至 OCEGettingStartedChannel 通道。完成後按一下儲存

    此圖像顯示「發布通道」欄位中包含 」OCEGettingStartedChannel' 的儲存區域定義面板。

建立內容模型

下一步是建立內容模型。您可以使用兩個方法中的任一個:

匯入 Oracle Content Management 範例資產套件

您可以下載預先設定的 Oracle Content Management 範例資產套件,其中包含此教學課程的所有必要內容類型和資產。您也可以選擇建立自己的內容模型,而非下載範例資產套件。

您可以從 Oracle Content Management 範例資產套件上傳本教學課程中使用的內容複本。這可讓您對內容類型進行實驗並修改內容。若要匯入 Oracle Content Management 範例資產套件,請下載資產套件封存 OCESamplesAssetPack.zip,並將其擷取至您選擇的目錄:

  1. 從 Oracle Content Management 下載頁面下載 Oracle Content Management 範例資產套件 (OCESamplesAssetPack.zip)。將下載的壓縮檔解壓縮至電腦上的位置。擷取之後,此位置將會包含一個名為 OCEGettingStarted_data.zip 的檔案。

  2. 以管理員身分登入 Oracle Content Management Web 介面。

  3. 在左側導覽功能表中選擇內容,然後從頁面表頭的選取清單中選擇儲存庫。現在選取 OCEGettingStartedRepository ,然後按一下頂端動作列中的匯入內容按鈕。

    此圖像顯示「儲存區域」頁面,其中已選取 OCEGettingStartedRepository 項目。

  4. 將本機電腦中的 OCEGettingStarted_data.zip 上傳至文件資料夾。

    此圖像顯示 OCEGettingStarted_data.zip 檔案的上傳確認畫面。

  5. 上傳後,請選取 OCEGettingStarted_data.zip ,然後按一下確定,將內容匯入資產儲存庫。

    此圖像顯示選取的 OCEGettingStarted_data.zip 檔案,並啟用「確定」按鈕。

  6. 順利匯入內容之後,請瀏覽至資產頁面,然後開啟 OCEGettingStartedRepository 儲存庫。您會看到所有相關的影像和內容項目現在都已新增至資產儲存庫。

    此圖像顯示 OCEGettingStartedRepository 儲存區域,其中包含剛匯入的所有資產。

  7. 按一下左上方的全選,然後按一下發布,將所有匯入的資產新增至稍早建立的發布通道 OCEGettingStartedChannel。

    此圖像顯示 OCEGettingStartedRepository 儲存區域,其中已選取所有資產,並顯示動作列中的「發布」選項。

  8. 發布之前,您需要驗證所有資產。首先,將 OCEGettingStartedChannel 新增為選取的通道,然後按一下 [ 驗證 ] 按鈕。

    此圖像顯示「驗證結果」頁面,其中 OCEGettingStartedChannel 管道已新增至「管道」欄位、所有要驗證的資產,以及啟用「驗證」按鈕。

  9. 資產經過驗證之後,您可以按一下右上角的發布按鈕,將所有資產發布至選取的管道。

    此圖像顯示「驗證結果」頁面,其中 OCEGettingStartedChannel 管道已新增至「管道」欄位、所有已驗證的資產,以及已啟用「發布」按鈕。

完成之後,您可以在資產頁面上查看所有已發布的資產。(您可以透過資產名稱上方的圖示來說明。)

此圖像顯示「資產」頁面,所有資產都已發佈。

匯入 Oracle Content Management 範例資產套件之後,您可以開始在 Xcode 中建立部落格

建立您自己的內容模型

您也可以建立自己的內容模型,而非匯入 Oracle Content Management 範例資產套件

本教學課程使用名為 'OCEGettingStartedHomePage' 的內容類型來建立部落格的首頁。此首頁包含品牌 (公司名稱和標誌)、一些連結 URL,以及應包含在頁面中的部落格主題清單。

此圖像顯示 Cafe Supremo 示範網站的首頁。

若要建立內容模型的內容類型:

  1. 以管理員身分登入 Oracle Content Management Web 介面。
  2. 從左側導覽功能表中選擇內容,然後從頁面標頭的選取清單中選擇資產類型
  3. 按一下右上角的建立 (Create)
  4. 選擇建立內容類型 (不是數位資產類型)。針對所有必要的內容類型重複此步驟。

此圖像顯示 Oracle Content Management Web 介面中的「建立資產類型」對話方塊。

我們會建立四種內容類型,每種類型都有其專屬的欄位集合:

第一個內容類型 OCEGettingStartedHomePage 應具有下列欄位:

顯示名稱 欄位類型 必要 機器名稱
公司名稱 單值文字欄位 X company_name
公司商標 單值文字欄位 X company_logo
主題 多重值參考欄位 X topics - 主題
聯絡人 URL 單值文字欄位 X contact_url
關於 URL 單值文字欄位 X about_url

這是您的 OCEGettingStartedHomePage 內容類型定義外觀:

此圖像顯示內容類型 'OCEGettingStartedHomePage' 的定義。其中包含下列資料欄位:公司名稱、公司標誌、主題、聯絡人 URL 及關於 URL。

第二個內容類型 OCEGettingStartedTopic 應具有下列欄位:

顯示名稱 欄位類型 必要 機器名稱
縮圖 單一值影像欄位 X 縮圖

這是 OCEGettingStartedTopic 內容類型的外觀:

此圖像顯示內容類型 'OCEGettingStartedTopic' 的定義。它包含此資料欄位:縮圖。

第三個內容類型 OCEGettingStartedAuthor 應具有下列欄位:

顯示名稱 欄位類型 必要 機器名稱
大頭貼 單一值影像欄位 X 大頭貼

這是 OCEGettingStartedAuthor 內容類型的外觀:

此圖像顯示內容類型 'OCEGettingStartedAuthor' 的定義。它包含此資料欄位:大頭貼。

第四種與最終內容類型 OCEGettingStartedArticle 應具有下列欄位:

顯示名稱 欄位類型 必要 機器名稱
發布日期 單一值日期欄位 X published_name
作者 單一值參考欄位 X 撰寫
影像 單一值影像欄位 X 影像
圖像標題 單值文字欄位 X image_caption
文章內容 單一值大型文字欄位 X article_content
主題 單一值參考欄位 X topic - 主題

這是 OCEGettingStartedArticle 內容類型的外觀:

此圖像顯示內容類型 'OCEGettingStartedArticlePage' 的定義。其中包含下列資料欄位:發布日期、作者、影像、影像標題、文章內容及主題。

建立內容類型之後,您可以將這些內容類型新增至先前建立的儲存庫,OCEGettingStartedRepository:

  1. 以管理員身分登入 Oracle Content Management Web 介面。
  2. 瀏覽至 OCEGettingStartedRepository
  3. 編輯儲存區域,然後在資產類型底下指定這四個新建立的內容類型。按一下儲存按鈕以儲存變更。

此圖像顯示 Oracle Content Management 中的「編輯儲存區域」頁面,其中有四個新建立的內容類型與 OCEGettingStartedRepository 儲存區域關聯。

將內容類型新增至儲存庫後,您可以在資產頁面上開啟 OCEGettingStartedRepository 儲存庫,然後開始為所有內容類型建立內容項目。

此圖像顯示 Oracle Content Management Web 介面之「資產」頁面上的內容項目,其中包含集合、通道、語言、類型、內容項目選擇以及狀態的左側選項。

步驟 2:以 Xcode 建立部落格應用程式

若要在 iOS 應用程式中使用 Oracle Content Management 內容,您可以使用 iOS 部落格範例,此範例可作為 GitHub 上的開源儲存庫。

注意:請記住,使用 iOS 範例是選擇性的,在此教學課程中使用它可讓您快速開始。您也可以建置您自己的 iOS 應用程式。

若要在 Swift 建置部落格:

  1. 複製範例儲存區域
  2. 設定 iOS 應用程式
  3. 使用 Content SDK 擷取內容

複製範例儲存區域

iOS 部落格範例可作為 GitHub 上的開放原始碼儲存庫。

您必須先將範例從 GitHub 複製到本機電腦:

git clone https://github.com/oracle-samples/oce-ios-blog-sample.git

複製範例之後,請開啟 Xcode 專案檔 BlogDemo.xcodeproj。

以 Xcode 開啟範例專案時,它會自動提取 content-management-swift 的相依性,Swift 套裝程式會實行 Oracle Content Delivery SDK。

此應用程式沒有其他協力廠商相依性,因此不需要進行其他手動安裝。不過,在執行應用程式之前,還需要設定其他組態。

設定 iOS 應用程式

在此 iOS 部落格範例中,您需要設定一些資訊,讓 Oracle Content Management Content SDK 能夠使用正確的通道記號來鎖定正確的執行處理 URL。每次您的應用程式要求來自 Oracle Content Management 執行處理的資料時都會使用這些值。

開啟 credentials.json 檔案並變更這兩個索引鍵 - 值組,以反映您的執行處理 URL 和與發布通道關聯的通道記號。本教學課程的頻道為 OCEGettingStartedChannel。

{
    "url": "https://samples.mycontentdemo.com",
    "channelToken": "47c9fb78774d4485bc7090bf7b955632"
}

使用 Content SDK 擷取內容

Oracle Content Management 提供由 OracleContentCore 和 OracleContentDelivery 程式庫組成的 Swift 套裝軟體 (content-management-swift),可協助您尋找與使用應用程式中的內容。套裝軟體是在 GitHub 上代管。

進一步瞭解 Oracle Content Management 文件庫中 Swift 的 Content SDK:

您可以利用這些 Content SDK 程式庫擷取內容,讓我們可以在 iOS 應用程式中轉譯。

將應用程式上線

若要要求資料,您必須提供一些必要資訊給程式庫,這稱為將應用程式上線。您可以指定特定的資訊片段,以便正確地格式化要求並指向正確的執行處理 URL。

在這個示範中,當應用程式首次啟動時,Onboarding.urlProvider 會被指派為您自己的類別 MyURLProvider 的執行處理。這會建立內容庫針對每個提出之要求所使用的執行處理 URL 和通道記號。

指定 URL 提供者並非絕對必要 (因為 URL 和記號資訊可以根據每個要求加以指定),但指派這些提供者會減少每個呼叫網站所需的代碼數量。

@main
struct BlogDemo: App {
    init() {
        // ... 

        // The sample code expects the URL and channel token to be provided by ``OracleContentCore.Onboarding``
        // Assign your ``OracleContentCore.URLProvider`` implementation to the ``OracleContentCore.Onboarding.urlProvider`` property
        Onboarding.urlProvider = MyURLProvider()
        
        // ...
        
    }
}

MyURLProvider 實行會從 credentials.json 讀取資料,以取得 URL 和通道記號。

{
    "url": "https://samples.mycontentdemo.com",
    "channelToken": "47c9fb78774d4485bc7090bf7b955632"
}

每次 Oracle 內容庫需要建立要求時,都會擷取下列特性:

/// This function provides the URL to be used for each OracleContentCore request
///
/// Services which implement ``OracleContentCore.ImplementsOverrides`` may provide a different URL and
/// authorization headers (if required) on a call-by-call basis
public var url: () -> URL? = {
  return URL(string: MyURLProvider.credentials.url)
}

程式庫每次需要建立要求時,也會擷取傳遞權杖:

/// This function provides the delivery channel token to be used for each OracleContentCore request
///
/// Services which implement ``OracleContentCore.ImplementsChannelToken`` may override this value
/// on a call-by-call basis
public var deliveryChannelToken: () -> String? = {
  
  return MyURLProvider.credentials.channelToken
}

此外,指派給 Onboarding.logger 可讓您定義自己的記錄日誌實行。基於這個示範,MyLogger 實行 只包含 "printing" 到主控台。在生產環境中,記錄器可以使用通用記錄日誌、核心資料或您選擇的任何技術。

@main
struct BlogDemo: App {
    init() {
        // ... 

         Onboarding.logger = MyLogger()
        
        // ...
        
    }
}

使用 Oracle Content Library 要求資料

備註:您可以在 BlogNetworking.swift 中找到此示範的所有網路要求代碼。

我們現在可以運用 Content SDK 擷取內容,讓我們在 iOS 應用程式中轉譯。

BlogNetworking.swift 檔案包含用來取得應用程式資料的所有程式碼。與每個頁面相關聯的模型物件會呼叫各種方法,以擷取 Oracle Content Management 資料。

首頁資料

我們應用程式的首頁是 BlogDemoMain.swift,一個含有相關模型物件的 SwiftUI 檔案。模型物件可重新用於維護頁面狀態,並發出啟動資料擷取處理所需的函數呼叫。當收到資料時,頁面狀態會變更,而 SwiftUI 將會相應地重新整理 UI。

一開始,主頁面會要求兩項不同的資料:

  1. 首先,我們查詢代表部落格首頁的內容項目。
  2. 然後下載內容項目所參考的標誌。

開啟 BlogNetworking.swift 並尋找執行初始要求的 fetchHomePage() 函數。

/// Retrieve the content item which represents the home page of the blog
/// - returns: Asset
public func fetchHomePage() async throws -> Asset {
    let typeNode = QueryNode.equal(field: "type", value: "OCEGettingStartedHomePage")
    let nameNode = QueryNode.equal(field: "name", value: "HomePage")
    let q = QueryBuilder(node: typeNode).and(nameNode)
    
    let result = try await DeliveryAPI
        .listAssets()
        .query(q)
        .fields(.all)
        .limit(1)
        .fetchNextAsync()
        .items
        .first
       
    guard let foundResult = result else {
        throw BlogNetworkingError.homePageNotFound
    }
    
    return foundResult
}

此函數會使用 Swift 並行來要求資料。請注意,所有要求物件 (或服務) 皆已命名空間至 DeliveryAPI。在此範例中,我們的要求物件為 listAssets()

依照要求物件是一系列的產生器元件,讓我們能夠微調要求。我們已在此處指定一些查詢詳細資訊,要求擷取所有欄位,並只將回應資料限制在單一物件。

我們對 Oracle Content Management 的要求是使用呼叫動詞 fetchNextAsync() 來提交。

注意: 由於我們的要求物件以 "list" 為開頭,因此呼叫動詞將以 "fetchNext" 為開頭。

「清單」要求會傳回資料,指出傳回多少記錄、是否有更多資料,以及結果集合 (在「項目」特性中)。

我們的函數會從 "items" 特性抓取第一個值並傳回該值。這是代表我們部落格的資產,包含標誌 ID、公司名稱、關於和聯絡人 URL,以及主題清單。

只要備妥標誌 ID,我們就可以發出第二個下載標誌要求:

/// Download the logo for display on the home page
/// - parameter logoId: The identifier of the logo to download
/// - returns: BlogImageState 
public func fetchLogo(logoId: String?) async throws -> BlogImageState {
    
    do {
        guard let logoID = logoId else { 
          throw BlogNetworkingError.missingLogoId 
        }
        
        let result = try await DeliveryAPI
            .downloadNative(identifier: logoID)
            .downloadAsync(progress: nil)
        
        guard let image = UIImage(contentsOfFile: result.result.path) else {
            throw OracleContentError.couldNotCreateImageFromURL(URL(string: result.result.path))
        }
        
        return .image(image)
        
    } catch {
        return .error(error)
    }

}

我們的要求和調用在此功能中比較簡單。我們使用 downloadNative(identifier: logoID) 建立要求物件,然後使用呼叫動詞 downloadAsync 提交該物件。

下載物件後,我們會將資料轉換成 UIImage,並將其以 BlogImageState 列舉形式傳回影像本身作為關聯值。

注意:此範例使用 BlogImageState 列舉,因為我們允許我們代表影像的各種狀態,而不需要使用選擇性值。這樣便於在 SwiftUI 中攝取。

主題資料

當我們的模型要求首頁資料時,它會執行如下所示的呼叫:

self.home = try await self.networking.fetchHomePage()   

成功擷取首頁後,我們需要尋找代表我們部落格主題的資產價值集合。為此,我們要求名為「主題」的欄位,其類型為資產陣列。

self.topics = try self.home.customField("topics") as [Asset]

BlogDemoMain.swift 會重複此主題集合,並表示每個主題為方格檢視中的元素。該 UI 表示法定義於 Topic.swift 及其關聯的模型物件 TopicModel.swift 中。

為了顯示個別主題的相關資訊,我們必須執行兩個任務:

  1. 我們需要擷取每個主題的詳細資訊。
  2. 我們必須下載每個主題所參考的影像。

在 TopicModel.swift 中,我們撥打網路代碼以取得完整資產:

let fullAsset = try await self.networking.readAsset(assetId: self.topic.identifier)

取得主題的詳細資訊是一個簡單程序。請使用指定的識別碼建構讀取要求,然後擷取資料。

/// Obtain detailed information about an Asset
/// - parameter assetId: The identifier of the asset to read
/// - returns: Asset
public func readAsset(assetId: String) async throws -> Asset {
    let result = try await DeliveryAPI
        .readAsset(assetId: assetId)
        .fetchAsync()
    
    return result
}

注意:所有讀取要求都會傳回單一物件,並使用以 "fetch" 開頭的呼叫動詞來送出。

我們擷取了關於主題的詳細資訊之後,就必須擷取定義縮圖的資料,然後下載該縮圖。

在 TopicModel.swift 中,我們會要求名稱為 "thumbnail" 的自訂欄位來取得縮圖資訊。由於自訂欄位可以是許多不同類型之一,因此必須明確指定此欄位為資產類型。順利找到資產之後,我們就可以抓取其識別碼。

imageIdentifier = (try fullAsset.customField("thumbnail") as Asset).identifier

使用可用的識別碼,TopicModel.swift 現在可以要求網路代碼擷取縮圖影像的媒體轉譯。

let imageState = await self.networking.downloadMediumRendition(identifier: imageIdentifier)
return imageState

取得影像的網路代碼會建立一個 "downloadRendition" 要求物件,以 jpg 格式擷取名稱為 'Medium' 的轉譯。用來提交要求的呼叫動詞是 "downloadAsync"。

下載物件後,我們會將資料轉換成 UIImage,並將其以 BlogImageState 列舉形式傳回影像本身作為關聯值。

/// Downloads the "Medium" rendition of an asset and returns the value as a `BlogImageState`
/// Note that any error while downloading the image will result in a placeholder image
public func downloadMediumRendition(identifier: String) async -> BlogImageState {
    
    do {
        let result = try await DeliveryAPI
                                .downloadRendition(identifier: identifier,
                                                   renditionName: "Medium",
                                                   format: "jpg")
                                .downloadAsync(progress: nil)
        
        guard let uiImage = UIImage(contentsOfFile: result.result.path()) else {
            throw OracleContentError.couldNotCreateImageFromURL(result.result)
        }
        
        return .image(uiImage)
        
    } catch {
        return .image(UIImage(systemName: "questionmark.circle")!)
    }

}

現在,我們有詳細的主題資訊和縮圖影像,SwiftUI 可以建構主題的視覺呈現。

此圖像顯示代表主題的 UI。它包含了標題為「如何」,這是咖啡杯的縮圖影像,裡面有具臨時潛藏的咖啡杯,以及將主題定義為學習建立美麗潛藏藝術的描述,並只靠右杯來灌溉。

文章列表資料

點選主題,將使用者導覽至包含指定給所選主題的文章清單的新頁面。

建立文章頁面與用來建立主題清單的程序非常相似。針對主題識別碼,我們需要:

  1. 擷取該主題的文章清單,以及
  2. 針對每篇文章,擷取其「影像」欄位中所含資產的縮圖轉譯。

擷取資產清單時,我們的要求物件為 .listAssets()

缺少任何其他建構器元件,這會從 credentials.json 中指定的發布通道擷取所有已發布的資產。不過,我們希望只擷取類型為 OCEGettingStartedArticle 且主題特性符合傳入主題識別碼的資產。

/// Obtain the collection of articles for a given topic. Limited to a maximum of 50 articles for demo purposes.
/// - parameter topic: Asset
/// - returns: [Asset] representing the articles for the specified topic
public func fetchArticles(for topic: Asset) async throws -> [Asset] {

    let typeNode = QueryNode.equal(field: "type", value: "OCEGettingStartedArticle")
    let fieldsTopicNode = QueryNode.equal(field: "fields.topic", value: topic.identifier)
    let fullQuery = QueryBuilder(node: typeNode).and(fieldsTopicNode)
    
    let result = try await DeliveryAPI
        .listAssets()
        .query(fullQuery)
        .order(.field("fields.published_date", .desc))
        .limit(50)
        .fetchNextAsync()
    
    return result.items
}

針對每一個擷取的文章,我們需要決定要下載哪些影像。我們會取得「影像」欄位中參照的資產 ID,並用來提交要求以下載其縮圖。

let identifier = (try article.customField("image") as Asset).identifier
let image = await self.networking.downloadThumbnail(identifier: identifier, fileGroup: article.fileGroup)

要求下載的網路代碼如下所示:

/// Downloads the thumbnail rendition of an asset and returns the values as a ``BlogImageState``
/// Note that any error while downloading the image will result in a placeholder image
/// - parameter identifier: The identifier of the asset
/// - parameter fileGroup: The file group of the asset - used to differentiate thumbnails for digital assets, videos and "advanced videos"
public func downloadThumbnail(identifier: String, fileGroup: String) async -> BlogImageState {
    do {
        let result = try await DeliveryAPI
            .downloadThumbnail(identifier: identifier, fileGroup: fileGroup)
            .downloadAsync(progress: nil)
        
        guard let uiImage = UIImage(contentsOfFile: result.result.path()) else {
           throw OracleContentError.couldNotCreateImageFromURL(result.result)
        }
        
        return .image(uiImage)
    } catch {
        Onboarding.logError(error.localizedDescription)
        return .image(UIImage(systemName: "questionmark.circle")!)
    }
}

我們現在可以預覽每篇文章 :

此圖像顯示文章的預覽。它包含了「創造美麗拉丁藝術」的標題,這個格式化日期指出文章發布時間、咖啡杯的縮圖影像,以及一些關於文章的描述性文字 -「在一些練習下,您可以用蒸牛奶建立設計。

文章資料

點選文章預覽會導致範例應用程式的最終頁面顯示,亦即文章本身。之前必須執行幾個資料要求:

  1. 根據所選文章的識別碼,我們需要擷取其詳細資訊。
  2. 下載作者的大頭照。
  3. 下載要顯示的文章影像。
@MainActor
func fetchArticle() async throws {

    self.article = try await self.networking.readArticle(assetId: article.identifier)
    
    let author: Asset = try self.article.customField("author")
    let authorAvatar: Asset = try author.customField("avatar")

    Task {
        self.avatar = await self.networking.downloadNative(identifier: authorAvatar.identifier)
    }
    
    Task {
        let hero: Asset = try self.article.customField("image_16x9")
        self.heroImage = await self.networking.downloadNative(identifier: hero.identifier)
    }
}

閱讀資產的詳細資訊,過程相當簡單:

/// Obtain detailed information about an Asset
/// - parameter assetId: The identifier of the asset to read
/// - returns: Asset
public func readAsset(assetId: String) async throws -> Asset {
    let result = try await DeliveryAPI
        .readAsset(assetId: assetId)
        .fetchAsync()
    
    return result
}

下載大頭貼和文章影像皆遵循用來取得其他影像的一般樣式:

/// Downloads the native rendition of an asset and returns the values as a ``BlogImageState``
/// Note that any error while downloading the image will result in a placeholder image
public func downloadNative(identifier: String) async -> BlogImageState {
    do {
        let result = try await DeliveryAPI
            .downloadNative(identifier: identifier)
            .downloadAsync(progress: nil)
        
        guard let uiImage = UIImage(contentsOfFile: result.result.path()) else {
           throw OracleContentError.couldNotCreateImageFromURL(result.result)
        }
        
        return .image(uiImage)
    } catch {
        Onboarding.logError(error.localizedDescription)
        return .image(UIImage(systemName: "questionmark.circle")!)
    }
    
}

結果是完整格式的部落格文章:

此圖片顯示完整的部落格文章 。其中包含作者的相關資訊 (包括大頭貼影像)、用來代表文章的影像,以及文章的 HTML 格式文字。

步驟 3:執行您的應用程式

應用程式完成後,您可以在模擬器中執行該應用程式,或將其部署至任何執行 iOS 或 iPadOS 16.0 或更新版本的 iPhone 或 iPad。

結論

在本教學課程中,我們建立了 iOS 部落格應用程式網站,您可以在 GitHub 找到此網站。此應用程式使用 Oracle Content Management 作為無頭 CMS。使用部落格教學課程的發布內容通道設定及設定 Oracle Content Management 之後,我們會執行應用程式來擷取必要的內容。

如需 Swift 的詳細資訊,請前往 Swift 網站

瞭解文件中重要的 Oracle Content Management 概念。

您可以在 Oracle Help Center 的 Oracle Content Management 範例頁面上找到更多範例。