使用无头 Oracle Content Management 在 Swift 中构建博客

简介

利用 Swift 和 SwiftUI 的 iOS 开发环境可以成为构建使用 Oracle Content Management 内容的应用程序的一个强大工具。借助合适的内容模型,您可以快速构建构成典型博客应用程序的用户界面。

在本教程中,我们将利用 Oracle Content Management 作为无头 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. 在右上角,单击创建以创建新通道。为此教程命名通道 ‘ 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)。将下载的 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. 单击右上角的创建
  4. 选择创建内容类型(不是数字资产类型)。对所有必需的内容类型重复此操作。

此图显示 Oracle Content Management Web 界面中的“创建资产类型”对话框。

我们将创建四种内容类型,每种类型都有自己的一组字段:

第一个内容类型 OCEGettingStartedHomePage 应具有以下字段:

显示名称 字段类型 要求 计算机名
公司名称 单值文本字段 X company_name
公司标识 单值文本字段 X company_logo
主题 多值参考字段 X 主题
联系人 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 主题

OCEGettingStartedArticle 内容类型应如下所示:

此图显示内容类型“OCEGettingStartedArticlePage”的定义。它包括以下数据字段:“发布日期”、“作者”、“图像”、“图像标题”、“文章内容”和“主题”。

创建内容类型后,您可以将这些内容类型添加到之前创建的资料档案库 OCEGettingStartedRepository:

  1. 作为管理员登录 Oracle Content Management Web 界面。
  2. 导航到 OCEGettingStartedRepository
  3. 编辑资料档案库,然后在资产类型下指定所有四个新创建的内容类型。单击 Save 按钮以保存更改。

此图显示 Oracle Content Management 中的“编辑资料档案库”页,其中包含与 OCEGettingStartedRepository 资料档案库关联的四个新创建的内容类型。

将内容类型添加到资料档案库后,可以在资产页上打开 OCEGettingStartedRepository 资料档案库,并开始为所有内容类型创建内容项。

此图显示 Oracle Content Management Web 界面的“资产”页上的内容项,左侧包含集合、渠道、语言、类型、内容项选择和状态选项。

步骤 2:在 Xcode 中构建博客应用程序

要在 iOS 应用程序中使用 Oracle Content Management 内容,我们可以使用 iOS 博客示例,该示例在 GitHub 上作为开源资料档案库提供。

注:请记住,使用 iOS 示例是可选的,在本教程中使用它可帮助您快速入门。您还可以构建自己的 iOS 应用程序。

在 Swift 中构建博客:

  1. 复制示例资源库
  2. 配置 iOS 应用程序
  3. 使用内容 SDK 获取内容

克隆示例资源库

iOS 博客示例在 GitHub 上作为开源系统信息库提供。

首先需要将示例从 GitHub 克隆到本地计算机:

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

克隆示例后,打开 Xcode 项目文件 BlogDemo.xcodeproj。

在 Xcode 中打开示例项目时,它将自动提取 content-management-swift 的相关项,即实施 Oracle Content Delivery SDK 的 Swift 程序包。

此应用程序没有其他第三方依赖项,因此不需要其他手动安装。但是,在运行应用程序之前,需要进行一些其他配置。

配置 iOS 应用程序

在此 iOS 博客示例中,您需要配置一些信息,以便 Oracle Content Management Content SDK 使用正确的渠道标记定位正确的实例 URL。每次应用程序从 Oracle Content Management 实例请求数据时,都会使用这些值。

打开文件 credentials.json 并更改两个键 - 值对以反映实例 URL 和与发布通道关联的通道令牌。此教程的渠道为 OCEGettingStartedChannel。

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

使用内容 SDK 获取内容

Oracle Content Management 提供 Swift 程序包 (content-management-swift),由 OracleContentCore 和 OracleContentDelivery 库组成,可帮助搜索和使用应用程序中的内容。软件包托管在 GitHub 上。

在 Oracle Content Management 文档库中详细了解适用于 Swift 的内容 SDK:

您可以利用这些内容 SDK 库来提取内容,以便我们可以在 iOS 应用程序中呈现。

注册应用程序

要请求数据,您需要向库提供一些必需的信息,这些信息称为应用程序入职。您可以分配特定信息片段,以便正确形成请求并指向正确的实例 URL。

在此演示中,当应用程序首次启动时,会将 Onboarding.urlProvider 分配给您自己的类 MyURLProvider 的实例。这将建立内容库用于每个请求的实例 URL 和通道标记。

虽然严格不需要指定 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 实施仅包含控制台的“打印”。在生产环境中,日志记录器可以利用通用日志记录、核心数据或您选择的任何技术。

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

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

使用 Oracle Content 库请求数据

注:此演示的所有网络请求代码都可以在 BlogNetworking.swift 中找到。

我们现在可以利用内容 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() 提交的。

注:由于我们的请求对象以“列表”开头,因此调用动词将以“fetchNext”开头。

“List”请求将返回数据,指示返回的记录数、是否存在更多数据以及结果集合(在“items”属性中)。

函数从“items”属性中获取第一个值并返回该值。这是代表博客的资产,其中包含徽标 ID、公司名称、关于 URL 和联系人 URL 以及主题列表。

有了标识,我们可以发出第二个下载徽标的请求:

/// 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 中,我们通过请求名为“缩略图”的定制字段来获取缩略图信息。因为自定义字段可以是许多不同类型之一,所以我们需要明确指定此字段是资产类型。成功找到资产后,我们可以获取其标识符。

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
}

对于检索到的每篇文章,我们需要确定要下载的图像。我们会获取“图像”字段中引用的资产的标识符,并使用该标识符提交下载其缩略图的请求。

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 帮助中心的 Oracle Content Management 示例页上找到更多类似这样的示例。