헤드리스 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 웹 인터페이스에 관리자로 로그인합니다.

  2. 왼쪽 탐색 메뉴에서 콘텐츠를 선택한 다음 페이지 헤더의 선택 목록에서 채널 게시를 선택합니다.

    이 이미지는 콘텐츠 페이지 헤더의 드롭다운 메뉴에서 선택된 게시 채널 옵션을 보여줍니다.

  3. 오른쪽 맨 위에서 생성을 눌러 새 채널을 생성합니다. 이 자습서의 목적을 위해 채널 이름을 'OCEGettingStartedChannel'로 지정하고 액세스 권한을 공용으로 유지합니다. 저장을 눌러 채널을 생성합니다.

    이 이미지는 채널 이름 필드에 'OCEGettingStartedChannel'가 있는 게시 채널 정의 패널을 보여줍니다.

  4. 왼쪽 탐색 메뉴에서 콘텐츠를 선택한 다음 페이지 헤더의 선택 목록에서 저장소를 선택합니다.

    이 이미지는 [콘텐츠] 페이지 머리글의 드롭다운 메뉴에서 선택된 [저장소] 옵션을 보여줍니다.

  5. 오른쪽 맨 위에서 생성을 눌러 새 자산 저장소를 생성합니다. 이 자습서의 목적을 위해 자산 저장소의 이름을 'OCEGettingStartedRepository'로 지정합니다.

    이 이미지는 저장소 이름 필드에 'OCEGettingStartedRepository'가 있는 저장소 정의 패널을 보여줍니다.

  6. 게시 채널 필드에서 OCEGettingStartedRepository 저장소의 콘텐츠를 OCEGettingStartedChannel 채널에 게시할 수 있음을 Oracle Content Management에 나타낼 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 웹 인터페이스에 관리자로 로그인합니다.

  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 및 페이지에 포함되어야 하는 블로그 주제 목록으로 구성됩니다.

이 이미지는 카페 수프리모 데모 사이트의 홈 페이지를 보여줍니다.

콘텐츠 모델에 대한 콘텐츠 유형을 생성하려면 다음을 수행합니다.

  1. Oracle Content Management 웹 인터페이스에 관리자로 로그인합니다.
  2. 왼쪽 탐색 메뉴에서 콘텐츠를 선택한 다음 페이지 헤더의 선택 목록에서 자산 유형을 선택합니다.
  3. 오른쪽 상단 모서리에 있는 생성을 누릅니다.
  4. 콘텐츠 유형(디지털 자산 유형이 아님)을 생성하도록 선택합니다. 모든 필수 콘텐츠 유형에 대해 이 작업을 반복합니다.

이 이미지는 Oracle Content Management 웹 인터페이스의 자산 유형 생성 대화상자를 보여줍니다.

각각 고유한 필드 세트를 사용하여 네 가지 콘텐츠 유형을 생성합니다.

첫번째 컨텐츠 유형 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 웹 인터페이스에 관리자로 로그인합니다.
  2. OCEGettingStartedRepository로 이동합니다.
  3. 저장소를 편집하고 자산 유형에서 4개의 새로 생성된 콘텐츠 유형을 모두 지정합니다. Save 버튼을 눌러 변경 사항을 저장합니다.

이 이미지는 Oracle Content Management의 [저장소 편집] 페이지에서 OCEGettingStartedRepository 저장소와 연관된 새로 생성된 콘텐츠 유형 4개를 보여줍니다.

저장소에 콘텐츠 유형을 추가한 후 자산 페이지에서 OCEGettingStartedRepository 저장소를 열고 모든 콘텐츠 유형에 대한 콘텐츠 항목 생성을 시작할 수 있습니다.

이 이미지는 Oracle Content Management 웹 인터페이스의 자산 페이지에 있는 콘텐츠 항목을 표시하며, 왼쪽에는 모음, 채널, 언어, 유형, 콘텐츠 항목 선택 및 상태에 대한 옵션이 있습니다.

2단계: Xcode에서 블로그 애플리케이션 구축

iOS 애플리케이션에서 Oracle Content Management 콘텐츠를 사용하려면 GitHub에서 오픈 소스 저장소로 제공되는 iOS 블로그 샘플을 사용할 수 있습니다.

참고: iOS 샘플 사용은 선택 사항이며 이 자습서에서 이 샘플을 사용하여 빠르게 시작할 수 있습니다. 자체 iOS 응용 프로그램을 빌드할 수도 있습니다.

Swift에서 블로그를 구축하려면:

  1. 샘플 저장소 복제
  2. iOS 애플리케이션 구성
  3. Content SDK를 사용하여 컨텐트 패치(fetch)

샘플 저장소 복제

iOS 블로그 샘플은 GitHub에서 오픈 소스 저장소로 사용할 수 있습니다.

먼저 GitHub의 샘플을 로컬 컴퓨터로 복제해야 합니다.

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

샘플을 복제한 후에는 Xcode 프로젝트 파일 BlogDemo.xcodeproj를 엽니다.

Xcode에서 샘플 프로젝트를 열면 Oracle Content Delivery SDK를 구현하는 Swift 패키지인 content-management-swift에 대한 종속성이 자동으로 풀링됩니다.

이 애플리케이션에 대한 다른 타사 종속성이 없으므로 다른 수동 설치가 필요하지 않습니다. 그러나 응용 프로그램을 실행하기 전에 몇 가지 추가 구성이 필요합니다.

iOS 애플리케이션 구성

이 iOS 블로그 샘플에서는 Oracle Content Management Content SDK가 올바른 채널 토큰의 올바른 인스턴스 URL을 대상으로 지정할 수 있도록 몇 가지 정보를 구성해야 합니다. 해당 값은 애플리케이션이 Oracle Content Management 인스턴스에서 데이터를 요청할 때마다 사용됩니다.

credentials.json 파일을 열고 두 키-값 쌍을 모두 변경하여 게시 채널과 연관된 인스턴스 URL 및 채널 토큰을 반영합니다. 이 자습서의 채널은 OCEGettingStartedChannel입니다.

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

Content SDK를 사용하여 컨텐트 패치(fetch)

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 제공자를 지정하는 것은 반드시 필요하지는 않지만 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 데이터를 검색하기 위해 다양한 메소드를 호출합니다.

홈페이지 데이터

애플리케이션의 홈 페이지는 연관된 모델 객체가 있는 SwiftUI 파일인 BlogDemoMain.swift입니다. 모델 객체는 페이지 상태를 유지 관리하고 데이터 검색 프로세스를 시작하는 데 필요한 함수 호출을 실행하는 데 사용할 수 있습니다. 데이터가 수신되면 페이지의 상태가 변경되고 이에 따라 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에 대한 당사의 요청은 invocation verb fetchNextAsync()를 사용하여 제출됩니다.

주: 요청 객체가 "list"로 시작하므로 호출 동사는 "fetchNext"로 시작됩니다.

"List" 요청은 반환된 레코드 수, 데이터 존재 여부 및 결과 모음("items" 속성)을 나타내는 데이터를 반환합니다.

이 함수는 "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

이미지를 가져오는 네트워킹 코드는 jpg 형식으로 이름이 'Medium'인 변환을 검색하는 "downloadRendition" 요청 객체를 생성합니다. 요청 제출에 사용되는 호출 동사는 "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를 보여줍니다. 그것은 '방법'의 제목이 포함되어 있습니다, containg 라테와 커피 컵의 썸네일 이미지와 아름다운 라테 예술을 만들고 오른쪽 컵을 부어 학습으로 주제를 정의하는 설명.

문서 목록 데이터

토픽을 누르면 선택한 토픽에 지정된 문서 목록이 포함된 새 페이지로 이동합니다.

문서 페이지 작성은 항목 리스트 작성에 사용되는 프로세스와 매우 유사합니다. 항목 식별자를 사용할 경우 다음을 수행해야 합니다.

  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")!)
    }
}

이제 각 문서의 미리보기를 표시할 수 있습니다.

이 이미지는 문서의 미리보기를 보여줍니다. 'Create Beautiful Latte Art'라는 제목이 들어 있습니다. 기사가 출판되었을 때를 나타내는 형식이 지정된 날짜인 'Create Beautiful Latte Art'라는 제목이 들어 있습니다. 커피 컵의 썸네일 이미지와 함께 라테에 관한 설명 텍스트입니다.

문서 데이터

문서 미리보기를 누르면 샘플 애플리케이션의 최종 페이지, 즉 문서 자체가 표시됩니다. 이전과 마찬가지로 여러 데이터 요청을 수행해야 합니다.

  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에 배포할 수 있습니다.

결론

이 자습서에서는 GitHub에서 찾을 수 있는 iOS 블로그 응용 프로그램 사이트를 만들었습니다. 이 애플리케이션은 Oracle Content Management를 헤드리스 CMS로 사용합니다. 블로그 자습서에 대해 게시된 콘텐츠 채널로 Oracle Content Management를 설정하고 구성한 후 애플리케이션을 실행하여 필요한 콘텐츠를 인출했습니다.

Swift에 대한 자세한 내용은 Swift 웹 사이트를 참조하십시오.

설명서에서 중요한 Oracle Content Management 개념에 대해 알아보십시오.

Oracle Content Management 샘플 페이지의 Oracle Help Center에서 이와 같은 더 많은 샘플을 찾을 수 있습니다.