iOS 專用的無頭行動裝置 SDK:開始使用

本文說明如何開始使用 iOS 專用的無頭行動版 SDK。

透過 iOS 版無頭行動 SDK,您可以直接在內建的 iOS 體驗中加入 CCAI 平台支援,不必使用預先建構的 CCAI 平台 UI。

您不必顯示 CCAI 平台品牌的 Widget,而是:

  • 將 CCAI iOS SDK 整合至應用程式。

  • 自行建構畫面、導覽和視覺設計。

  • 使用 SDK 執行下列操作:

    • 開始及管理即時通訊和語音工作階段。

    • 提供電子郵件、預約通話和轉移選項。

    • 支援智慧動作、附件和會後流程 (客戶滿意度調查、問卷調查、虛擬服務專員)。

CCAI 平台會處理客服中心邏輯 (路徑、佇列、管道、設定、報表)。您擁有完整的應用程式體驗 (使用者介面、流程和品牌)。

如果您偏好使用預先建構的可設定 UI,而非自行建構,請使用標準行動 SDK。

需求條件

以下是整合 iOS 專用無頭行動 SDK 的必要條件。

iOS 平台需求條件

  • 最低 iOS 版本:iOS 16.6 以上版本

  • 語言:Swift

  • IDE:應用程式支援的最新穩定版 Xcode

  • 架構:目前 Xcode 工具鍊支援的標準 iOS 架構。

應用程式權限

由於內建應用程式會處理智慧動作的 UI (拍攝相片、錄製影片或驗證身分),因此您必須聲明 Apple 的強制性隱私權。

在 Info.plist 中新增下列鍵,並說明應用程式如何使用資料:

  • NSCameraUsageDescription:使用者必須具備這項權限,才能拍攝及傳送相片/影片,或執行服務專員要求的智慧動作。

  • NSMicrophoneUsageDescription:即時語音通話和錄製有聲影片時必須使用。

  • NSPhotoLibraryUsageDescription:使用者必須取得這項權限,才能上傳現有的相片或影片。

  • NSFaceIDUsageDescription:如果您支援生物辨識驗證智慧動作,則必須提供這項資訊。

CCAI 平台執行個體需求條件

您必須存取 CCAI 平台執行個體的「開發人員設定」,才能取得下列詳細資料:

  • 公司金鑰

  • 公司密碼

  • 主機網址 - CCAI 平台的主機名稱 (例如 your_subdomain.ccaiplatform.com)

這些值可用於:

  • 公司金鑰 + 主機網址:在 iOS 應用程式中初始化 SDK。

  • 公司密碼:在後端簽署用於驗證使用者的 JSON Web Token (JWT)。

推播通知

如要撥打電話及使用特定智慧動作,您需要:

  • 設定 Apple 推播通知服務 (APNs),以便傳送標準推播通知。

  • VoIP 服務憑證,可支援來電和特定通話相關通知。

您將學會以下內容:

  • 在 Apple 開發人員入口網站中設定 APNs 和 VoIP 憑證。

  • 在 CCAI Platform 管理員入口網站中,依序前往「設定」>「開發人員設定」>「行動應用程式」,上傳轉換後的憑證。

  • 在應用程式中註冊遠端通知和 VoIP 推送通知。

  • 將適當的裝置權杖轉送至 SDK。

  • 在「目標」>「簽署和功能」中,啟用下列項目:

    • 推播通知功能。

    • 背景模式功能,並勾選下列項目:

      • 網際網路通訊協定語音 - 這是 VoIP 推播通知 (來電、使用 PSTN 的智慧動作) 的必要項目。

      • 音訊、AirPlay 和子母畫面 - 應用程式在背景執行時,必須使用這項功能才能繼續進行語音通話。

      • 遠端通知 - 這是標準 APNs 推播傳送功能的必要條件。

無頭 iOS SDK 如何整合至應用程式

iOS 專用的無頭行動裝置 SDK 可視為三層:

CCAI 平台和 SDK

SDK 會與 CCAI Platform 平台安全地互動,並提供下列功能:

  • 使用 JSON Web Token (JWT) 進行驗證。

  • 佇列和管道路徑中繼資料 (即時通訊、語音、預約通話、電子郵件、外部轉移連結)。

  • 即時管理工作階段狀態。

  • 智慧動作、檔案附件和螢幕分享的安全傳輸管道。

  • 轉移邏輯和工作階段後階層評估。

您的 iOS 應用程式 (UI 和邏輯由您處理)

你可以全面掌控視覺和導覽體驗:

  • 進入點:例如「說明」分頁或「與我們聯絡」按鈕。

  • 選單:顯示佇列的方式 (例如「帳單」、「技術支援」)。

  • 通話期間的 UI:自訂即時通訊泡泡、通話畫面和控制項。

  • 應用程式的用途:呼叫 SDK API 來啟動及結束工作階段、監聽 SDK 事件,以及驅動檢視畫面控制器導覽。

設定 CCAI 平台管理員入口網站

CCAI Platform 管理員會設定參與規則 (營業時間、可用管道、等待時間門檻和問卷調查)。SDK 會讀取這項設定,並以原始資料和事件的形式向應用程式公開。您決定如何在畫面上繪製該設定。

擷取公司憑證

整合 SDK 前,請先從 CCAI Platform 執行個體取得憑證:

  1. 使用管理員帳戶登入 CCAI Platform 管理員入口。

  2. 依序前往「設定」>「開發人員設定」。

  3. 在「公司金鑰和密碼」下方,複製:

    • 公司金鑰

    • 公司密碼

  4. 記下主機網址:CCAI 平台的主機名稱 (例如 your_subdomain.ccaiplatform.com)。

您將使用:

  • 在 iOS 應用程式中:公司金鑰 + 主機網址。

  • 在後端伺服器中:用於簽署 JWT 的公司密碼:

    • 驗證使用者身分。

    • CCAI Platform 使用的選用自訂資料或內容。

在 iOS 應用程式中新增 SDK

無頭 SDK 採用模組化架構建構而成,您會與應用程式所需的特定功能模組 (例如 CCAIChat 或 CCAIScreenShare) 一併安裝核心 CCAIKit。

如要在應用程式中整合 iOS SDK,請按照下列步驟操作。

  1. 請將以下內容新增到您的 Package.swift 檔案中:

    dependencies: [
       .package(url: "https://github.com/UJET/ccai-ios-sdk.git", from: "3.3.1")
    ],
    targets: [
       .target(
           name: "YourTargetName",
           dependencies: [
               .product(name: "CCAIKit", package: "CCAIKit")
           ]
       )
    ]
    
  2. 在 AppDelegate 檔案中匯入 CCAIKit:

    import CCAIKit
    
  3. 如果專案是 SwiftUI,請在 App 結構體中宣告 @UIApplicationDelegateAdaptor:

    @main
    struct YourAppName: App {
       @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
       // ...
    }
    

初始化無頭 SDK

在應用程式啟動期間初始化 SDK。由於 SDK 採用模組化架構,您必須先初始化核心 CCAI 系統,然後註冊特定服務模組 (例如即時通訊或螢幕分享)。

基本初始化

SDK 會使用 InitOptions 結構體,將所有必要設定綁在一起:

import CCAIKit
import CCAIChat       // If using Chat
import CCAIScreenShare // If using Screen Share

class AppDelegate: NSObject, UIApplicationDelegate, CCAIDelegate {

    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {

        // 1. Configure initialization options
        let options = InitOptions(
            key: "YOUR_COMPANY_KEY",
            urlHost: "your_subdomain.ccaiplatform.com",
            delegate: self,
            languageCode: "en",       // ISO 639 code (defaults to "en")
            cacheAuthToken: true       // Whether to cache the auth token (defaults to true)
        )

        // 2. Initialize the core SDK
        do {
            try CCAI.shared.initialize(options: options)
        } catch {
            print("Failed to initialize CCAI: \(error.localizedDescription)")
            return true
        }

        // 3. Initialize feature modules
        // Chat minimal (no options required)
        CCAI.shared.initializeChat()

        // Chat with optional configuration
        // let chatOptions = ChatOptions(
        //     delegate: webFormDelegate,           // Implement to intercept and render custom web forms within your own UI
        //     downloadTranscriptVisibility: .hideInPostChat, // Hide the post-chat download affordance
        //     greeting: "Hello! How can I help you?"
        // )
        // CCAI.shared.initializeChat(chatOptions)

        // Screen Share (optional)
        // let screenShareOptions = ScreenShareOptions(
        //     key: "YOUR_SCREEN_SHARE_KEY",
        //     domain: "your_domain.com"
        // )
        // CCAI.shared.initializeScreenShare(screenShareOptions)

        return true
    }

    // ... CCAIDelegate methods (Authentication) go here ...
}

InitOptions 設定參考資料

public struct InitOptions {
    /// The company key used for authentication
    public let key: String

    /// The host URL for CCAI platform (for example, "my-unique-instance.uc1.ccaiplatform.com")
    public let urlHost: String

    /// The preferred language code for localization (defaults to "en")
    public var languageCode: String

    /// The delegate that receives SDK events and callbacks
    public weak var delegate: CCAIDelegate?

    /// Whether to cache the authentication token (defaults to true)
    public let cacheAuthToken: Bool
}

可用的 SDK 服務

初始化後,您就可以透過 CCAI.shared 單例存取各種服務:

let authService = CCAI.shared.authService
let companyService = CCAI.shared.companyService
let queueMenuService = CCAI.shared.queueMenuService
let optionsService = CCAI.shared.optionsService
let languageService = CCAI.shared.languageService
let pushNotificationService = CCAI.shared.pushNotificationService
let endUserService = CCAI.shared.endUserService
let chatService = CCAI.shared.chatService           // Only available after initializeChat()
let screenShareService = CCAI.shared.screenShareService // Only available after initializeScreenShare()
let rateService = CCAI.shared.rateService            // For CSAT ratings and survey submission
let smartActionService = CCAI.shared.smartActionService // Dedicated service for smart action lifecycle

擷取公司設定

初始化後,請使用 companyService 擷取公司層級的設定。這項功能有助於填入語言選擇器、顯示公司名稱,或在使用者進入佇列前讀取支援聯絡資訊。

do {
    let company = try await CCAI.shared.companyService?.get()
    print("Company: \(company?.displayName ?? "Unknown")")
} catch {
    print("Failed to get company info: \(error)")
}

驗證使用者身分

Headless Mobile SDK 會使用 JSON Web Token (JWT) 驗證使用者,並安全地將情境資訊傳遞至服務專員的顧客關係管理系統。

運作方式

SDK 會使用簡化的兩步驟非同步驗證流程:

  1. SDK 判斷需要驗證使用者。

  2. 它會呼叫您的 CCAIDelegate 方法 (ccaiShouldAuthenticate()),這是非同步函式。

  3. 應用程式會使用公司密碼,在後端伺服器上遠端簽署 JWT。

  4. 接著,應用程式會將簽署的 JWT 傳遞至 CCAI.shared.authService?.authenticate(jwt),以交換驗證權杖。

  5. 系統會將驗證權杖傳回 SDK,完成連線。

實作 CCAIDelegate 進行驗證

應用程式必須實作 CCAIDelegate 協定。SDK 需要驗證時,會呼叫單一非同步委派方法。

CCAIDelegate 通訊協定

public protocol CCAIDelegate: AnyObject {
    /// Authenticates the end user and returns an auth token
    func ccaiShouldAuthenticate() async -> String?
}

導入範例 (Swift)

class AppDelegate: CCAIDelegate {
    func ccaiShouldAuthenticate() async -> String? {
        // 1. Sign JWT remotely on your backend server
        guard let jwt = await signJWTRemotely() else { return nil }

        // 2. Authenticate JWT using authService to get an auth token
        return try? await CCAI.shared.authService?.authenticate(jwt)
    }
}

完整範例,包含 JWT 簽署,可供參考和測試

class AuthController: CCAIDelegate {
    func ccaiShouldAuthenticate() async -> String? {
        var jwt = JWT(claims: claims)

        // First, sign JWT with company secret (do this on your backend in production)
        guard let secret = self.companySecret,
              let key = secret.data(using: .utf8) else { return nil }
        let signer = JWTSigner.hs384(key: key)
        guard let encodedJWT = try? jwt.sign(using: signer) else { return nil }

        // Then authenticate JWT using authService to get an auth token
        return try? await CCAI.shared.authService?.authenticate(encodedJWT)
    }
}

將自訂資料傳遞至 CRM

如要將情境資料傳遞給代理程式 (例如使用者的目前裝置 OS、位置或帳戶層級),後端必須先將 custom_data 物件插入 JWT 酬載,再進行簽署。

每筆自訂資料都必須格式化為 JSON 物件,內含 label (代理程式看到的內容)、value 和 type。

支援的資料類型

  • string:標準文字 (例如「iPhone 14 Pro」)。

  • number:整數或浮點數 (例如 1234 或 99.99)。

  • date:13 位數的 UTC Unix 時間戳記,包含毫秒 (例如 1537399655992)。

  • url:標準 HTTP/HTTPS 網址格式。

  • boolean:標準的 true 或 false 值。

保留的 CRM 金鑰

CCAI Platform 支援特定保留鍵,可觸發平台內建行為,例如將使用者標示為 VIP,或向服務專員發出惡意行為人警告。這些值必須格式化為布林類型,且只有在酬載是使用安全的 JWT 方法簽署時,系統才會接受。

  • reserved_verified_customer:指出客戶是否已通過內部系統驗證。

  • reserved_bad_actor:向服務專員回報使用者可能是垃圾內容或詐欺帳戶。

  • reserved_repeat_customer:指出這位顧客最近是否經常聯絡支援團隊。

含有保留鍵的酬載範例

{
  "iat": 1537399656,
  "exp": 1537400256,
  "custom_data": {
    "reserved_verified_customer": {
      "label": "Verified Customer",
      "value": true,
      "type": "boolean"
    },
    "reserved_bad_actor": {
      "label": "Bad Actor",
      "value": false,
      "type": "boolean"
    },
    "reserved_repeat_customer": {
      "label": "Repeat Customer",
      "value": false,
      "type": "boolean"
    }
  }
}

管理驗證權杖

SDK 也提供手動更新或清除快取驗證權杖的方法:

// Set a new auth token
CCAI.shared.authService?.updateAuthToken("new_auth_token")

// Clear the current token (for example, on user logout)
CCAI.shared.authService?.updateAuthToken(nil)

自訂資料 JWT 酬載結構定義

後端建構最終簽署酬載時,必須嚴格遵守這個結構定義。請注意強制性 iat (發行時間) 和 exp (到期時間) 時間戳記。

{
  "iat": 1537399656,
  "exp": 1537400256,
  "custom_data": {
    "os_version": {
      "label": "OS Version",
      "value": "16.4",
      "type": "string"
    },
    "membership_tier": {
      "label": "Membership Tier",
      "value": "Platinum",
      "type": "string"
    },
    "ssn_last_four": {
      "label": "SSN",
      "value": "1234",
      "type": "string",
      "invisible_to_agent": true
    },
    "reserved_verified_customer": {
      "label": "Verified Customer",
      "value": true,
      "type": "boolean"
    }
  }
}

啟用推播通知

SDK 會使用推播通知執行下列操作:

  • 來電。

  • 特定智慧操作和通話相關事件。

  • 在應用程式於背景執行時維持狀態。

設定憑證

CCAI 平台需要兩種不同類型的 Apple 推送憑證,才能儲存在 CCAI 平台管理員入口網站:

  • Apple 推播通知服務 (APNs) SSL 憑證 - 用於標準遠端推播通知。

  • VoIP 服務憑證 - 用於來電通知和特定通話相關智慧動作。

這兩項憑證都是在 Apple 開發人員入口網站中建立,並轉換為 PEM 格式,然後上傳至 CCAI 平台管理員入口網站。

建立 APNs SSL 憑證

請按照 Apple 官方「建立與 APNs 的憑證型連線」指南中的後續步驟,建立 APNs SSL 憑證。

  1. 登入 developer.apple.com,然後前往「Certificates, Identifiers & Profiles」。

  2. 選取「+」按鈕,建立新憑證。

  3. 在「服務」下方,選取「Apple 推播通知服務 SSL (Sandbox 和 Production)」。

  4. 選擇與應用程式套件 ID 相符的應用程式 ID,然後選取「繼續」。

  5. 上傳從 Mac 上的「鑰匙圈存取」產生的憑證簽署要求 (CSR):

    • 依序開啟「鑰匙圈存取」>「憑證輔助程式」>「向憑證授權單位要求憑證…」

    • 輸入電子郵件地址,將 CA 電子郵件地址留空,選取「儲存到磁碟」,然後選取「繼續」。

  6. 選取「繼續」,然後「下載」生成的 .cer 檔案。

  7. 按兩下 .cer 檔案,將其安裝到「鑰匙圈存取」。

  8. 在「鑰匙圈存取」中找到已安裝的憑證,按一下滑鼠右鍵,然後選取「匯出…」,將憑證儲存為 .p12 檔案。系統會提示你設定匯出密碼。

  9. 使用 openssl 將 .p12 檔案轉換為 PEM 格式:

    # Extract the certificate
    openssl pkcs12 -in apns_certificate.p12 -out apns_cert.pem -clcerts -nokeys
    
    # Extract the private key
    openssl pkcs12 -in apns_certificate.p12 -out apns_key.pem -nocerts -nodes
    
    # (Optional) Combine into a single PEM file
    cat apns_cert.pem apns_key.pem > apns_combined.pem
    

建立 VoIP 服務憑證

透過 VoIP 服務憑證,應用程式就能透過 Apple 的 PushKit 架構接收高優先順序推播通知,從而從終止狀態喚醒應用程式,處理來電。如需最新官方步驟,請參閱 Apple 開發人員說明文件中的「建立 VoIP 服務憑證」。

  1. 登入 developer.apple.com,然後前往「Certificates, Identifiers & Profiles」。

  2. 選取「+」按鈕,建立新憑證。

  3. 在「服務」下方,選取「VoIP 服務憑證」。

  4. 選擇與應用程式套件 ID 相符的應用程式 ID,然後選取「繼續」。

  5. 上傳您為 APNs SSL 憑證產生的憑證簽署要求 (CSR) (或建立新的 CSR)。

  6. 選取「繼續」,然後「下載」生成的 .cer 檔案。

  7. 按兩下 .cer 檔案,將其安裝到「鑰匙圈存取」。

  8. 在「鑰匙圈存取」中找出已安裝的 VoIP 服務憑證,按一下滑鼠右鍵,然後選取「匯出…」,將憑證儲存為 .p12 檔案。

  9. 使用 openssl 將 .p12 檔案轉換為 PEM 格式:

    # Extract the certificate
    openssl pkcs12 -in voip_certificate.p12 -out voip_cert.pem -clcerts -nokeys
    
    # Extract the private key
    openssl pkcs12 -in voip_certificate.p12 -out voip_key.pem -nocerts -nodes
    
    # (Optional) Combine into a single PEM file
    cat voip_cert.pem voip_key.pem > voip_combined.pem
    

將憑證上傳至 CCAI 平台管理員入口網站

  1. 登入 CCAI Platform 管理員入口網站。

  2. 依序前往「設定」>「開發人員設定」>「行動應用程式」。

  3. 上傳 APNs PEM 憑證。

  4. 上傳 VoIP PEM 憑證。

要求推播通知權限

SDK 提供便利方法,可要求推送通知授權 (僅限 iOS):

let options: UNAuthorizationOptions = [.alert, .sound]
try await CCAI.shared.pushNotificationService?.registerForPushNotifications(
    options: options
) { granted in
    if granted {
        print("Push notifications granted")
    } else {
        print("Push notifications denied")
    }
}

註冊遠端通知

在 AppDelegate 中實作推播通知委派。SDK 提供 pushNotificationService,可轉送權杖:

import CCAIKit

class AppDelegate: NSObject, UIApplicationDelegate {
    func application(
        _ application: UIApplication,
        didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
    ) {
        CCAI.shared.pushNotificationService?.updatePushToken(
            data: deviceToken,
            type: .apns
        )
    }

    func application(
        _ application: UIApplication,
        didFailToRegisterForRemoteNotificationsWithError error: Error
    ) {
        CCAI.shared.pushNotificationService?.updatePushToken(
            data: nil,
            type: .apns
        )
    }
}

請務必在 Xcode 的「Target」>「Signing & capabilities」分頁中,啟用「Capabilities」>「Push notifications」和「Background modes」,以便接收遠端通知。

處理傳入的推播通知

收到推送有效負載時,請轉送至 SDK 進行處理:

func application(
    _ application: UIApplication,
    didReceiveRemoteNotification userInfo: [AnyHashable: Any],
    fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
) {
    // Let the SDK determine if this push relates to an incoming call,
    // session update, or another internal event
    CCAI.shared.pushNotificationService?.handlePushNotification(userInfo)
    completionHandler(.newData)
}

SDK 會判斷推送通知是否與來電、工作階段更新或其他內部事件相關,並據此處理。

註冊 VoIP 推播通知

對於來電和某些以 PSTN 為基礎的智慧動作,除了標準 APNs 權杖,SDK 也需要 VoIP 推送權杖。VoIP 推播通知的優先順序較高,可從終止狀態喚醒應用程式。

在 AppDelegate 的 didFinishLaunchingWithOptions 中匯入 PushKit 並建立 PKPushRegistry

import PushKit

class AppDelegate: NSObject, UIApplicationDelegate, PKPushRegistryDelegate {

    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {

        // ... SDK initialization ...

        // Register for VoIP push notifications
        let voipRegistry = PKPushRegistry(queue: DispatchQueue.main)
        voipRegistry.delegate = self
        voipRegistry.desiredPushTypes = [.voIP]

        return true
    }
}

導入 PKPushRegistryDelegate 方法,將 VoIP 權杖和酬載轉送至 SDK

// MARK: - PKPushRegistryDelegate

func pushRegistry(
    _ registry: PKPushRegistry,
    didUpdate credentials: PKPushCredentials,
    for type: PKPushType
) {
    // Forward the VoIP token to the SDK
    CCAI.shared.pushNotificationService?.updatePushToken(
        data: credentials.token,
        type: .voip
    )
}

func pushRegistry(
    _ registry: PKPushRegistry,
    didInvalidatePushTokenFor type: PKPushType
) {
    // Clear the VoIP token
    CCAI.shared.pushNotificationService?.updatePushToken(
        data: nil,
        type: .voip
    )
}

func pushRegistry(
    _ registry: PKPushRegistry,
    didReceiveIncomingPushWith payload: PKPushPayload,
    for type: PKPushType,
    completion: @escaping () -> Void
) {
    guard type == .voIP else {
        completion()
        return
    }

    // Forward the VoIP payload to the SDK using the `handleVoIPPush`
    // extension on `CCAI` (defined in the `CCAICall` module). The SDK
    // takes ownership of the call lifecycle; invoke PushKit's completion
    // handler in the trailing closure after the SDK has accepted the
    // payload.
    CCAI.shared.handleVoIPPush(payload: payload.dictionaryPayload) { completion() }
}