Contact Center AI 平台 (CCAI 平台) 行動 SDK 適用於 Apple iOS 作業系統,可將 Contact Center AI 平台行動體驗嵌入 iOS 行動應用程式。
需求條件
iOS 行動 SDK 須符合下列規定:
- iOS 12.0 以上版本
擷取公司憑證
使用管理員憑證登入 Contact Center AI 平台 (CCAI Platform) 入口網站。
依序前往 「設定」 > 「開發人員設定」。
在「Company Key」(公司金鑰) 和「Secret Code」(密鑰) 下方,記下公司金鑰和公司密鑰。
開始使用
以下指南說明如何開始使用 CCAI Platform iOS 行動 SDK。
安裝
如要開始使用,請先安裝 iOS SDK。
下載範例應用程式
下載 iOS 範例應用程式。
前往資料夾,並使用 CocoaPods 安裝依附元件:
$ pod install --project-directory=ExampleApp如要快速調整專案設定,請執行殼層指令碼:
$ ./setup.sh或者,您也可以按照下列步驟手動編輯專案設定:
開啟
ExampleApp.xcworkspace。在
Info.plist中,將UJETCompanyKey和UJETCompanySecret值替換為 CCAI Platform 入口網站「Settings」>「Developer Settings」頁面中的「Company Key」和「Company Secret Code」值 。將
Info.plist中的UJETSubdomain值替換為 CCAI 平台入口網站網址中的子網域。子網域會直接位於網址中的.ujet.com之前,例如https://your-subdomain.ujet.com/settings/developer-setting中的your-subdomain。
整合至專案
iOS SDK 與應用程式的整合方式取決於開發環境。
Swift 套件管理工具
從 GitHub 新增 iOS SDK 的 Swift 套件。
在建構設定中,將 -ObjC 放在「Other Linker Flags」上。
Xcode 13.2 有一個已知問題,會影響使用 Swift Package Manager 發布的二進位架構。目前解決這個問題的方法,是在 Xcode 專案的建構階段中加入執行指令碼階段。這個「Run Script Phase」應位於「Embed Frameworks」建構階段之後。這個新的 Run Script Phase 應包含下列程式碼:
find "${CODESIGNING_FOLDER_PATH}" -name '*.framework' -print0 | while read -d $'0' framework do codesign --force --deep --sign "${EXPANDED_CODE_SIGN_IDENTITY}" --preserve-metadata=identifier,entitlements --timestamp=none "${framework}" done
CocoaPods
在 Podfile 中新增下列指令列:
pod 'UJET', :podspec => 'https://sdk.ujet.co/ios/x.y.z/ujet.podspec' #specific version x.y.z執行 pod install。如果先前已整合 iOS SDK,請改為執行 pod update CCAI Platform。
迦太基
Google Cloud 建議使用依附元件管理工具或手動整合,因為 CCAI 平台依附元件不支援 Carthage。
如要使用其他依附元件管理工具,請按照下列步驟操作:
新增以下幾行內容:
binary "https://sdk.ujet.co/ios/UJETKit.json" binary "https://sdk.ujet.co/ios/UJETFoundationKit.json" binary "https://sdk.ujet.co/ios/UJETCallRedKit.json" binary "https://sdk.ujet.co/ios/UJETChatRedKit.json" # optional # binary "https://sdk.ujet.co/ios/UJETChatBlueKit.json" binary "https://raw.githubusercontent.com/twilio/twilio-voice-ios/Releases/twilio-voice-ios.json" # this is not supported: https://github.com/twilio/conversations-ios/issues/12 # binary "https://raw.githubusercontent.com/twilio/conversations-ios/master/twilio-convo-ios.json"執行
carthage bootstrap --use-xcframeworks(如果要更新依附元件,則執行carthage update --use-xcframeworks)。
手動整合
下載
UJETKit.xcframework、UJETFoundationKit.xcframework、UJETChatRedKit.xcframework、UJETChatBlueKit.xcframework、UJETTwilioCallKit.xcframework,以及所有依附元件TwilioVoice.xcframework和TwilioConversationsClient.xcframework。將 UJETKit.xcframework 拖曳至「Frameworks, Libraries, and Embedded Content」部分,新增至目標。
對步驟 1 中的所有依附元件重複步驟 2 和 3。
在「Build Settings」中,將
-ObjC設為Other Linker Flags。在目標的
Linked Frameworks部分中,將libc++.tbd新增為依附元件。
如要使用範例專案手動建構 SDK,請按照下一節的步驟操作。
使用範例專案手動建構 SDK
請依序按照下列步驟操作:
下載所有架構,包括
UJETKit.xcframework和其他依附元件。在專案根目錄建立 CCAI Platform 資料夾,並擷取所有架構。
選取
Objc-Manual或Swift-Manual目標並建構。
匯入架構
以下各節將說明如何匯入架構。
Objective-C 專案
@import UJETKit;
Swift 專案
swiftimport
UJETimport UJETKit
初始化 SDK
使用 UJET_COMPANY_KEY 和 UJET_SUBDOMAIN 初始化 CCAI 平台。
In application:didFinishLaunchingWithOptions: method:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Initialize CCAI Platform
[UJET.initialize:UJET_COMPANY_KEY subdomain:UJET_SUBDOMAIN delegate:self];
// YOUR CODE
return YES;
}
您可以將記錄層級從 verbose 變更為 error。預設記錄層級為 UjetLogLevelInfo。
[UJET.setLogLevel:UjetLogLevelVerbose];
使用者驗證
透過 iOS 應用程式存取 iOS SDK。
為確保使用者通過驗證,我們將推出 JWT 簽署機制。
需要驗證時,iOS SDK 會要求簽署酬載。如果簽署成功,應用程式會將已簽署的 JWT 換成使用者驗證權杖。必須先呼叫成功或失敗區塊,委派才會傳回。
如果是匿名使用者 (ID = nil),應用程式會為使用者建立 UUID。如果使用者之後透過 ID 通過驗證,應用程式會嘗試根據 UUID 合併這兩位使用者。
在範例專案的 UJETObject.h 中:
@import UJETKit;
@interface UJETObject : NSObject <UJETDelegate>
實作 signPayload:payloadType:success:failure:委派方法。
- (void)signPayload:(NSDictionary *)payload payloadType:(UjetPayloadType)payloadType success:(void (^)(NSString *))success failure:(void (^)(NSError *))failure {
if (payloadType == UjetPayloadAuthToken) {
[self signAuthTokenInLocal:payload success:success failure:failure];
}
}
- (void)signAuthTokenInLocal:(NSDictionary *)payload success:(void (^)(NSString *))success failure:(void (^)(NSError *))failure {
NSMutableDictionary *payloadData = [payload mutableCopy];
NSDictionary *userData = [[NSUserDefaults standardUserDefaults] objectForKey:@"user-data"];
[payloadData addEntriesFromDictionary:userData];
payloadData[@"iat"] = [NSNumber numberWithDouble:[[NSDate date] timeIntervalSince1970]]; // required
payloadData[@"exp"] = [NSNumber numberWithDouble:([[NSDate date] timeIntervalSince1970] + 600)]; // required
NSString *signedToken = [self encodeJWT:payloadData];
if (signedToken.length > 0) {
success(signedToken);
} else {
NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: @"Failed to sign token" };
NSError *error = [NSError errorWithDomain:@"ExampleApp" code:0 userInfo:userInfo];
failure(error);
}
}
- (NSString *)encodeJWT:(NSDictionary *)payload {
id<JWTAlgorithm> algorithm = [JWTAlgorithmHSBase algorithm384];
NSString *secret = NSBundle.mainBundle.infoDictionary[@"UJETCompanySecret"];
return [JWTBuilder encodePayload:payload].secret().algorithm(algorithm).encode;
}
強烈建議您從應用程式伺服器簽署酬載,而非在用戶端簽署。
這個範例使用本機簽署進行測試。請參閱 UJETObject.m 檔案中的 signDataInRemote:
success: failure:。
詳情請參閱「SDK 使用者驗證」。
設定推播通知
CCAI Platform 會傳送推播通知,要求執行驗證和拍照等智慧動作,以及回報來電。如果即時通訊佇列使用「簽到」功能 (需要 iOS SDK 2.16.0 以上版本),CCAI Platform 會傳送推播通知,提示等待中的消費者返回即時通訊,並確認他們已準備就緒。請參閱「Check In for Mobile SDK」。CCAI 平台需要將兩種不同類型的憑證 (VoIP 和 APN) 儲存在 CCAI 平台管理員入口網站中。
設定憑證
CCAI 平台需要將兩種不同類型的 Apple 推播憑證儲存在 CCAI 平台管理員入口網站:
Apple 推播通知服務 (APNs) SSL 憑證:用於標準遠端推播通知。
VoIP 服務憑證:用於來電通知和特定通話相關智慧動作。
這兩種憑證都是在 Apple 開發人員入口網站建立,轉換為 PEM 格式後,再上傳至 CCAI 平台管理員入口網站。
建立 APNs SSL 憑證
請按照 Apple 官方指南「建立與 APN 的憑證連線」中的後續步驟,建立 APN SSL 憑證。
登入 developer.apple.com,然後前往「Certificates, identifiers & profiles」。
按一下「+」按鈕,建立新的憑證。
在「Services」下方,選取「Apple 推播通知 service SSL (Sandbox & production)」。
選擇與應用程式軟體包 ID 相符的應用程式 ID,然後按一下「Continue」(繼續)。
上傳 Mac 鑰匙圈存取權產生的憑證簽署要求 (CSR):
依序開啟「Keychain access」>「Certificate assistant」>「Request a certificate from a certificate authority」。
輸入電子郵件地址,將「CA email address」(CA 電子郵件地址) 留空,選取「Saved to disk」(儲存至磁碟),然後按一下「Continue」(繼續)。
依序點選「繼續」和「下載產生的
.cer檔案」。按兩下
.cer檔案,將其安裝到鑰匙圈中。在「鑰匙圈存取」中,找到已安裝的憑證,按一下滑鼠右鍵,然後選取「匯出…」,將其儲存為
.p12檔案。系統會提示您設定匯出密碼。使用
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 服務憑證」。
登入 developer.apple.com,然後前往「Certificates, Identifiers & Profiles」(憑證、ID 與設定檔)。
按一下「+」按鈕,建立新的憑證。
在「Services」下方,選取「VoIP services certificate」。
選擇與應用程式軟體包 ID 相符的應用程式 ID,然後按一下「Continue」(繼續)。
上傳您在上一節產生的憑證簽署要求 (CSR),或建立新的 CSR。
依序點選「繼續」和「下載產生的
.cer檔案」。按兩下
.cer檔案,將其安裝到鑰匙圈中。在「Keychain access」中,找出已安裝的 VoIP Services 憑證,按一下滑鼠右鍵,然後選取「Export…」,將其儲存為
.p12檔案。使用
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 平台管理員入口網站
登入 CCAI Platform 管理員入口網站。
依序前往「設定」>「開發人員設定」>「行動應用程式」。
上傳 APNs PEM 憑證。
上傳 VoIP PEM 憑證。
啟用推播通知
選取目標,然後開啟「Capabilities」分頁。
開啟「推播通知」切換鈕。
測試推播通知
以下各節將說明如何測試推播通知。
推播通知偵錯部分
在管理員入口網站中,依序前往「Settings」 >「Developer Settings」。在這個頁面中,找到「推播通知偵錯」部分:

複製裝置權杖並貼到右側文字區域,然後選取正確的行動應用程式。
取得裝置權杖
裝置權杖字串範例如下:
7db0bc0044c8a203ed87cdab86a597a2c43bf16d82dae70e8d560e88253364b7
推播通知通常是在符合 UIApplicationDelegate 或 PKPushRegistryDelegate 通訊協定的類別中設定。在某個時間點,您可以使用裝置權杖。您可以先列印出來,再傳遞給 iOS SDK。如要取得裝置權杖,請使用程式碼片段。
Swift
func tokenFromData(data: Data) -> String {
return data.map { String(format: "%02x", $0) }.joined()
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
print("apns token: ", tokenFromData(data: deviceToken))
...
}
func pushRegistry(_ registry: PKPushRegistry, didUpdate credentials: PKPushCredentials, for type: PKPushType) {
print("voip token: ", tokenFromData(data: credentials.token))
...
}
Obj-C
- (NSString *)tokenFromData:(NSData *)data {
const char *d = data.bytes;
NSMutableString *token = [NSMutableString string];
for (NSUInteger i = 0; i < data.length; i++) {
[token appendFormat:@"%02.2hhX", d[i]];
}
return [[token copy] lowercaseString];
}
- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(PKPushType)type {
NSLog(@"voip token: %@", [self tokenFromData:credentials.token]);
...
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
NSLog(@"apns token: %@", [self tokenFromData:deviceToken]);
}
結果
輸入憑證 PEM 檔案和裝置權杖後,請按一下按鈕。
如果測試推播通知成功傳送,結果會顯示「Push notification successfully configured」訊息。
推播通知不保證一定會送達,具體取決於裝置的網路連線。
專案設定
以下各節將概略說明設定專案所需的變更。
功能
在目標設定中,開啟下列功能:
推播通知
背景模式 (勾選這些項目)
音訊和 AirPlay
網路通訊協定語音
Info.plist
為保護使用者隱私,凡是在 iOS 10.0 以上版本連結,且會存取裝置麥克風、相片庫和相機的 iOS 應用程式,都必須宣告這項意圖。請在應用程式的 Info.plist 檔案中加入下列索引鍵和字串值,並為這個索引鍵提供用途字串。如果應用程式嘗試存取裝置的麥克風、相片庫和相機,但沒有對應的目的字串,應用程式就會結束。
NSMicrophoneUsageDescription:允許存取麥克風,以便與支援或疑難排解團隊通話,以及傳送與產品諮詢相關的有聲影片。
NSCameraUsageDescription:允許存取相機,讓客戶拍攝及傳送與客戶服務查詢相關的相片。
NSPhotoLibraryUsageDescription:允許存取權,讓客戶傳送與客戶服務查詢相關的相片。
NSFaceIDUsageDescription:允許使用 Face ID 進行驗證。
啟動 iOS SDK
在要啟動 iOS SDK 的位置,加入下列程式碼:
[UJET startWithOptions:nil];
您也可以使用這個鍵,透過直接存取點從選單中的特定位置啟動 iOS SDK:
UJETStartOptions *option = [[UJETStartOptions alloc] initWithMenuKey:@"MENU_KEY"];
[UJET startWithOptions:option];
如要建立menuKey,請建立直接存取點 (DAP)。下列步驟說明如何建立 DAP:
使用管理員憑證登入 CCAI 平台入口網站。
依序前往「設定」>「待播清單」。
從選單結構中選取任何佇列。
選取「建立直接存取點」
在文字表單中輸入金鑰。
按一下 [儲存]。
如果使用者資料已更新,請清除本機快取
我們會將驗證權杖快取至 Keychain,以便重複使用,並減少從主機應用程式簽署酬載的要求頻率。SDK 會使用該權杖,直到權杖過期或透過 clearUserData 呼叫撤銷為止。每當使用者相關資料變更或更新 (例如登出事件),主機應用程式就會負責撤銷這個快取。
[UJET clearUserData];
啟動 Contact Center AI 平台前,請先檢查是否有現有工作階段
開始工作階段前,請先確認目前沒有工作階段。如果 userId 有所變更,這項操作尤其重要。
[UJET getStatus];
如果已有工作階段,應提示使用者繼續工作階段或取消操作:
if ([UJET getStatus] != UjetStatusNone) {
// Display alert to cancel login or resume existing session
}
自訂
UJETGlobalTheme.h 列出了多個 SDK 主題選項。
在 [UJET initialize] 後方設定主題,例如:
UJETGlobalTheme *theme = [UJETGlobalTheme new];
theme.font = [UIFont fontWithName:@"OpenSans" size: 16.0f];
theme.lightFont = [UIFont fontWithName:@"OpenSans-Light" size: 16.0f];
theme.boldFont = [UIFont fontWithName:@"OpenSans-Bold" size: 16.0f];
theme.tintColor = [UIColor colorWithRed:0.243 green:0.663 blue:0.965 alpha:1.00];
[Ujet setGlobalTheme:theme];
公司名稱擷取自「管理員入口網站」 > 「設定」 > 「支援中心詳細資料」 > 「顯示名稱」。
您可以設定標誌圖片,而非公司名稱,如下所示:
theme.companyImage = [UIImage imageNamed:@"logo"];
如果圖片過大,系統會調整大小,使其符合區域。
字串
您也可以覆寫值來自訂字串。舉例來說,請在 Localizable.strings 中加入以下鍵/值:
"ujet_greeting_title" = "Title";
"ujet_greeting_description" = "Description";
ujet.strings 檔案會列出可自訂的字串。
深色模式
您可以指定深色模式所需的色調,提高字型可讀性。
@property (nonatomic, strong) UIColor \*tintColorForDarkMode;
如未設定屬性,深色模式會使用 UJETGlobalTheme.tintColor。如果應用程式支援深色模式,建議設定這項屬性。請參閱下列 Apple 文章,瞭解如何為深色模式選擇合適的色調:
即時通訊主題
如要自訂即時通訊畫面,您可以使用 JSON 字串或各個主題類別。
如需參考,請查看範例應用程式,並取消註解 customizeChatTheme 方法。
func customizeChatTheme() throws {
guard let file = Bundle.main.path(forResource: "chat-theme-custom", ofType: "json") else { return }
let json = try String.init(contentsOfFile: file, encoding: .utf8)
let chatTheme = UJETChatTheme.init(jsonString: json)
let quickReplyTheme = UJETChatQuickReplyButtonTheme()
quickReplyTheme.style = .individual
quickReplyTheme.alignment = .right
quickReplyTheme.backgroundColor = UJETColorRef(assetName: "white_color")
quickReplyTheme.backgroundColorForHighlightedState = UJETColorRef(assetName: "quick_reply_color")
quickReplyTheme.textColor = UJETColorRef(assetName: "quick_reply_color")
quickReplyTheme.textColorForHighlightedState = UJETColorRef(assetName: "white_color")
let fontTheme = UJETFontTheme()
fontTheme.family = "Arial Rounded MT Bold"
fontTheme.size = 14
quickReplyTheme.font = fontTheme
chatTheme?.quickReplyButtonTheme = quickReplyTheme
let globalTheme = UJETGlobalTheme()
globalTheme.chatTheme = chatTheme
globalTheme.defaultAgentImage = UIImage(named: "agent_avatar_image")
globalTheme.font = UIFont(name: "Arial Rounded MT Bold", size: 14)
UJET.setGlobalTheme(globalTheme)
}
內容資訊卡主題
您可以自訂內容資訊卡和即時通訊。
您可以使用 JSON 檔案 (請參閱 content_card 屬性) 或 UJETChatContentCardTheme 類別來執行這項操作。
func customizeChatTheme() throws {
guard let file = Bundle.main.path(forResource: "chat-theme-custom", ofType: "json") else { return }
let json = try String.init(contentsOfFile: file, encoding: .utf8)
let chatTheme = UJETChatTheme.init(jsonString: json)
let contentCardTheme = UJETChatContentCardTheme()
contentCardTheme.backgroundColor = UJETColorRef(assetName: "agent_message_background_color")
contentCardTheme.cornerRadius = 16
let contentCardFontTheme = UJETFontTheme()
contentCardFontTheme.family = "Arial Rounded MT Bold"
contentCardFontTheme.size = 18
contentCardTheme.font = contentCardFontTheme
let contentCardBorder = UJETBorderTheme()
contentCardBorder.width = 1
contentCardBorder.color = UJETColorRef(assetName: "agent_message_border_color")
contentCardTheme.border = contentCardBorder
let contentCardFontTheme = UJETFontTheme()
contentCardFontTheme.family = "Arial Rounded MT Bold"
contentCardFontTheme.size = 18
contentCardTheme.font = contentCardFontTheme
// The font family is inherited from the contentCardFontTheme
let subtitle = UJETFontTheme()
subtitle.size = 12
contentCardTheme.subtitle = subtitle
// The font family is inherited from the contentCardFontTheme
let bodyFont = UJETFontTheme()
bodyFont.size = 10
contentCardTheme.body = bodyFont
theme.chatTheme?.contentCard = contentCardTheme
let globalTheme = UJETGlobalTheme()
globalTheme.chatTheme = chatTheme
globalTheme.defaultAgentImage = UIImage(named: "agent_avatar_image")
globalTheme.font = UIFont(name: "Arial Rounded MT Bold", size: 14)
UJET.setGlobalTheme(globalTheme)
}
表單資訊卡主題
您可以自訂表單資訊卡和即時通訊。方法是使用 JSON 檔案 (請參閱 form_card property) 或 UJETChatFormCardTheme 類別。
func customizeChatTheme() throws {
guard let file = Bundle.main.path(forResource: "chat-theme-custom", ofType: "json") else { return }
let json = try String.init(contentsOfFile: file, encoding: .utf8)
let chatTheme = UJETChatTheme.init(jsonString: json)
let formCardTheme = UJETChatFormCardTheme()
formCardTheme.backgroundColor = UJETColorRef(assetName: "agent_message_background_color")
formCardTheme.cornerRadius = 16
let formCardFontTheme = UJETFontTheme()
formCardFontTheme.family = "Arial Rounded MT Bold"
formCardFontTheme.size = 18
formCardTheme.font = formCardFontTheme
let formCardBorder = UJETBorderTheme()
formCardBorder.width = 1
formCardBorder.color = UJETColorRef(assetName: "agent_message_border_color")
formCardTheme.border = formCardBorder
let titleFontTheme = UJETFontTheme()
titleFontTheme.family = "Arial Rounded MT Bold"
titleFontTheme.size = 18
formCardTheme.title = titleFontTheme
// The font family is inherited from the formCardFontTheme
let subtitleFontTheme = UJETFontTheme()
subtitleFontTheme.size = 12
formCardTheme.subtitle = subtitleFontTheme
chatTheme?.formCard = formCardTheme
let globalTheme = UJETGlobalTheme()
globalTheme.chatTheme = chatTheme
globalTheme.defaultAgentImage = UIImage(named: "agent_avatar_image")
globalTheme.font = UIFont(name: "Arial Rounded MT Bold", size: 14)
UJET.setGlobalTheme(globalTheme)
}
網頁表單設定
如要設定網頁表單功能,請實作 UJETDelegate 通訊協定的 ujetWebFormDidReceive 方法。這個方法會接收事件 (FormMessageReceivedEvent 字典) 做為參數,其中包含表單相關資訊。事件 (FormMessageReceivedEvent) 字典包含下列 JSON 結構:
{
"type": "form_message_received",
"smart_action_id": 1,
"external_form_id": "external_foobar"
"signature": "4868a7e1dcb5..."
}
如要處理事件,請按照下列步驟操作:
從事件字典 (
smart_action_id、external_form_id和signature) 擷取相關資訊。產生表單 URI 和表單資料的簽章。
使用
completion closure將表單資料以FormDataEvent字典的形式傳遞至 SDK。如果在產生 URI/簽章時發生任何錯誤,請使用
callback.onError()搭配Error叫用回呼。
傳遞至 SDK 的字典 (FormDataEvent) 應採用下列結構:
{
"type": "form_data",
"signature": "4868a7e1dcb5...",
"data": {
"smart_action_id":1,
"external_form_id": "form_id",
"uri":"foobar"
}
}
簽章 (HMAC-SHA:256) 必須使用 data 產生,並以共用密鑰簽署。資料的物件鍵必須先依字母順序排列,才能產生簽章,且應將相同的 data 傳送至 SDK。
課後轉移
您可以自訂工作階段結束後的虛擬服務專員,以及自訂即時通訊。您可以使用 JSON 檔案 (請參閱 post_session 屬性) 或 UJETChatPostSessionVaTheme 類別來完成這項操作。框線寬度只能是 0 或 1,如果不想區分工作階段後的虛擬服務專員體驗,可以將 containerColor 設為白色,並將框線設為 0。
func customizeChatTheme() throws {
guard let file = Bundle.main.path(forResource: "chat-theme-custom", ofType: "json") else { return }
let json = try String.init(contentsOfFile: file, encoding: .utf8)
let chatTheme = UJETChatTheme.init(jsonString: json)
let postSessionVaTheme = UJETChatPostSessionVaTheme()
postSessionVaTheme.containerColor = UJETColorRef(assetName: "white_color")
let postSessionVaBorder = UJETBorderTheme()
postSessionVaBorder.width = 0
postSessionVaBorder.color = UJETColorRef(assetName: "white_color")
containerColor.border = postSessionVaBorder
chatTheme?.postSessionVaTheme = postSessionVaTheme
let globalTheme = UJETGlobalTheme()
globalTheme.chatTheme = chatTheme
UJET.setGlobalTheme(globalTheme)
}
即時通訊動作選單
您可以自訂即時通訊動作選單和即時通訊。您可以使用 JSON 檔案 (請參閱 form_card 屬性) 或 UJETChatActionMenuTheme 類別來完成這項操作。
func customizeChatTheme() throws {
guard let file = Bundle.main.path(forResource: "chat-theme-custom", ofType: "json") else { return }
let json = try String.init(contentsOfFile: file, encoding: .utf8)
let chatTheme = UJETChatTheme.init(jsonString: json)
let actionMenuTheme = UJETChatActionMenuTheme()
let photoLibraryIcon = UJETChatUserInputIconTheme()
photoLibraryIcon.visible = true
photoLibraryIcon.image = UJETImageRef(assetName: "library_button_asset")
let cameraIcon = UJETChatUserInputIconTheme()
cameraIcon.visible = true
cameraIcon.image = UJETImageRef(assetName: "camera_button_asset")
let cobrowseIcon = UJETChatUserInputIconTheme()
cobrowseIcon.visible = true
cobrowseIcon.image = UJETImageRef(assetName: "cobrowse_button_asset")
actionMenuTheme.libraryIcon = photoLibraryIcon
actionMenuTheme.cameraIcon = cameraIcon
actionMenuTheme.cobrowseIcon = cobrowseIcon
chatTheme?.actionMenu = actionMenuTheme
let globalTheme = UJETGlobalTheme()
globalTheme.chatTheme = chatTheme
UJET.setGlobalTheme(globalTheme)
}
其他外觀
你也可以自訂其他外觀,例如字型大小和背景顏色。
theme.supportTitleLabelFontSize = 30;
theme.supportDescriptionLabelFontSize = 20;
theme.supportPickerViewFontSize = 30;
theme.staticFontSizeInSupportPickerView = YES;
theme.backgroundColor = UIColor.darkGrayColor;
theme.backgroundColorForDarkMode = UIColor.lightGrayColor;
CallKit
在 iOS 10.0 以上版本中,所有通話都會啟用 CallKit。
CallKit 會在通話畫面中顯示應用程式內來電,並在手機的通話記錄中顯示通話。
如要從通話記錄啟動新的 CCAI Platform 支援工作階段,請在 AppDelegate.m 中加入下列區塊:
AppDelegate.m:
- (BOOL)application:(UIApplication *)app continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray * _Nullable))restorationHandler {
if ([userActivity.activityType isEqualToString:@"INStartAudioCallIntent"]) {
// Open app from Call history
[UJET startWithOptions:nil];
}
return YES;
}
CallKit 允許在裝置鎖定時,於螢幕鎖定畫面上顯示 40x40 的圖示。在 Xcassets 中放置名為「icon-call-kit」的圖片。
設定 SDK
您可以在啟動 SDK 前設定多個選項。
詳情請參閱 UJETGlobalOptions 類別。
UJETGlobalOptions *options = [UJETGlobalOptions new];
options.fallbackPhoneNumber = @"+18001112222";
options.preferredLanguage = @"en";
[UJET setGlobalOptions:options];
顯示或隱藏「下載轉錄稿」按鈕
您可以設定 SDK,在即時通訊選項選單和即時通訊後畫面中,顯示或隱藏下載轉錄稿按鈕。
下列程式碼說明如何設定下載轉錄稿按鈕:
typedef NS_OPTIONS(NSUInteger, UJETChatDownloadTranscriptVisibilityOptions) {
UJETChatDownloadTranscriptVisibilityOptionsShowAll = 0,
UJETChatDownloadTranscriptVisibilityOptionsHideFromOptionsMenu = 1 << 0,
UJETChatDownloadTranscriptVisibilityOptionsHideFromPostChatScreen = 1 << 1,
UJETChatDownloadTranscriptVisibilityOptionsHideAll = UJETChatDownloadTranscriptVisibilityOptionsHideFromOptionsMenu | UJETChatDownloadTranscriptVisibilityOptionsHideFromPostChatScreen
};
@property (nonatomic, assign) UJETChatDownloadTranscriptVisibilityOptions transcriptVisibilityOptions;
PSTN 備用
我們在以下幾種情況下提供 PSTN 備用方案:
行動網路已離線。
無法連上應用程式後端。
無法使用 VoIP
網路狀況不佳,無法連線。詳情請參閱 UJETGlobalOptions.pstnFallbackSensitivity 屬性。
連線失敗,原因可能是防火牆設定或供應商問題。
建議您在 UJETGlobalOptions.fallbackPhoneNumber 中設定公司的 IVR 號碼。建議格式為「+」加上國家/地區代碼和電話號碼,例如 +18001112222。
PSTN 備用方案敏感度
您可以調整檢查網路狀況的敏感度等級,以便備用 PSTN。
@property (nonatomic, assign) float pstnFallbackSensitivity;
值必須介於 0.0 至 1.0 之間。如果設為 1,通話一律會透過 PSTN 連線,而非 VoIP。如果值為 0,延遲時間上限和頻寬下限分別為 10000 毫秒和 10 KB/秒。舉例來說,如果值為 0.5,代表最低延遲時間和頻寬分別為 5000 毫秒和 15 KB/秒。
如要設定這個值,請按照下列步驟操作:
以管理員身分登入 CCAI Platform 入口網站。
依序前往「設定」>「開發人員設定」>「行動應用程式」。
找到「備用電話號碼門檻」部分。預設值為 0.85。
指定新的門檻值。
按一下「儲存」。
關閉全域層級的推播通知
您可以在全域層級關閉推播通知。將下列屬性設為 false,即可略過所有推播通知依附元件,並防止推播通知傳送給使用者:
@property (nonatomic, assign) BOOL allowsPushNotifications;
忽略深色模式
您可以使用這項屬性,在 CCAI Platform SDK 中忽略深色模式:
@property (nonatomic, assign) BOOL ignoreDarkMode;
隱藏狀態列
您可以使用這項屬性控制狀態列的顯示設定:
@property (nonatomic, assign) BOOL hideStatusBar;
根據預設,hideStatusBar 會設為 false 和 visible。
略過顧客滿意度問卷調查
您可以新增按鈕,讓使用者略過 CSAT 問卷調查。以下程式碼範例說明如何新增按鈕:
let options = UJETGlobalOptions()
options.skipCsat = true
自訂活動指標
您可以將自己的載入器動畫 (在 UIView 內) 新增至 SDK,並覆寫預設的 UIActivityIndicatorView。從 UJETDelegate 實作 ujet_activityIndicator 方法,並傳回自訂檢視畫面。
public func ujet_activityIndicator() -> UIView! {
let loader = UIView.init()
let animation = CABasicAnimation()
loader.backgroundColor = .blue
loader.layer.cornerRadius = 15
animation.timingFunction = CAMediaTimingFunction.init(name: CAMediaTimingFunctionName.easeOut)
animation.keyPath = "transform.scale"
animation.duration = 1.0
animation.fromValue = 0.0
animation.toValue = 1.0
animation.repeatCount = Float.infinity
animation.isRemovedOnCompletion = false
loader.layer.add(animation, forKey: "Load")
return loader
}
如果您已在應用程式的 Info.plist 中將 UIUserInterfaceStyle 設為 Light,完全停用深色模式,則可忽略這項屬性。
偏好語言
CCAI Platform SDK 會按照下列優先順序,判斷偏好的語言。
在應用程式啟動畫面中選取的語言。
預設語言選自
UJETGlobalOptions。您可以使用preferredLanguage屬性設定預設語言。如要查看支援的語言代碼,請參閱UJETGlobalOptions.h檔案。如果應用程式支援,系統會使用裝置中選取的裝置語言 (依序前往「設定」>「一般」 >「語言與地區」)。
如果應用程式不支援裝置語言,但支援最接近的母語方言,則會使用最接近的裝置語言方言。舉例來說,如果使用者在裝置中選取古巴西班牙文做為語言,而應用程式不支援古巴西班牙文,但支援上層方言西班牙文,則系統會使用西班牙文。
如果應用程式不支援裝置語言,則會使用英文。
設定外部轉移連結圖示
如要自訂外部導向連結管道中的圖示,請將圖示上傳至應用程式的素材資源目錄,並確保在管理員入口網站中建立外部導向連結時,使用相同的圖示名稱。建立外部導向連結的路徑為「設定」>「即時通訊」>「外部導向連結」>「查看連結」>「新增導向連結」。如果管理員入口網站中的圖示名稱與上傳至應用程式的圖示不符,SDK 會使用預設圖示。如要瞭解如何在素材資源目錄中新增圖片,請參閱這個連結。



將自訂資料傳送至客戶關係管理系統
您可以將自訂資料傳送至 CRM 票證。
傳送自訂資料的方法有兩種:
安全方法:使用 JWT 預先定義資料簽署。
不安全的方法:使用純 JSON 預先定義資料 (不建議)。
使用安全的方法傳送自訂資料
您必須實作簽署方法。首先,您可以將自訂資料放在用戶端,並傳送至伺服器進行簽署。您可以在伺服器上,透過定義的表單新增其他資料,並使用 company.secret 簽署,然後透過 JWT 傳回。
- (void)signPayload:(NSDictionary *)payload payloadType:(UjetPayloadType)payloadType success:(void (^)(NSString *))success failure:(void (^)(NSError *))failure
{
if (payloadType == UjetPayloadCustomData) {
// sign custom data using UJET_COMPANY_SECRET on your server.
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration];
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] init];
mutableRequest.URL = [NSURL URLWithString:@"https://your.company.com/api/ujet/sign/custom_data"];
mutableRequest.HTTPMethod = @"POST";
NSError *error;
// Make client's custom data
UJETCustomData *customData = [[UJETCustomData alloc] init];
[customData set:@"name" label:@"Name" stringValue:@"USER_NAME"];
[customData set:@"os_version" label:@"OS Version" stringValue:[[UIDevice currentDevice] systemVersion]];
[customData set:@"model" label:@"Model number" numberValue:[NSNumber numberWithInteger:1234]];
[customData set:@"temperature" label:@"Temperature" numberValue:[NSNumber numberWithFloat:70.5]];
[customData set:@"purchase_date" label:@"Purchase Date" dateValue:[NSDate date]];
[customData set:@"dashboard_url" label:@"Dashboard" urlValue:[NSURL URLWithString:@"http://internal.dashboard.com/1234"]];
NSDictionary *data = @{@"custom_data": [customData getData]};
mutableRequest.HTTPBody = [NSJSONSerialization dataWithJSONObject:data options:0 error:&error];
NSURLSessionDataTask *task = [session dataTaskWithRequest:mutableRequest completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if(error) {
failure(error);
}
else {
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
success(json[@"jwt"]);
}
}];
[task resume];
}
}
使用不安全的方法傳送自訂資料
我們不建議採用這種方法,因為這會產生潛在安全漏洞,可能導致應用程式遭受中間人攻擊。如果您選擇使用這個方法,我們不負責可能發生的安全性漏洞和潛在損害。建議您使用先前所述的安全方法,在應用程式中傳送自訂資料。或者,您也可以直接使用 UJETCustomData 執行個體啟動 iOS SDK。在這種情況下,UJETPayloadCustomData 的 signPayload 委派應呼叫 success(nil);。
- (void)signPayload:(NSDictionary *)payload payloadType:(UjetPayloadType)payloadType success:(void (^)(NSString *))success failure:(void (^)(NSError *))failure {
if (payloadType == UjetPayloadCustomData) {
success(nil);
}
}
UJETStartOptions *options = [UJETStartOptions new];
options.unsignedCustomData = customData;
[UJET startWithOptions:options];
使用未簽署的自訂資料傳送外部即時通訊轉錄稿
如果 SDK 是以未簽署的自訂資料啟動,您可以傳送即時通訊轉錄稿給 SDK,方法是呼叫 setExternalChatTransfer: 或 setExternalChatTransferWithDictionary: 方法,分別使用 NSString 或 NSDictionary 設定 JSON 資料。
UJETCustomData *customData = [UJETCustomData new];
[customData setExternalChatTransfer:jsonString];
UJETStartOptions *options = [UJETStartOptions new];
options.unsignedCustomData = customData;
[UJET startWithOptions:options];
JSON 格式:
greeting_override:字串agent:字典name:字串avatar:字串 [服務專員顯示圖片的網址,選用]
transcript:陣列sender:字串 [end_user或agent]timestamp:字串 [例如2021-03-15 12:00:00Z]content:陣列type:字串 [text、media之一]text:字串 [文字類型必填]media:字典 [媒體類型必填]type:字串 [image 或 video]url:字串 [指向媒體檔案的公開網址]
JSON 範例:
{
"greeting_override": "Please hold while we connect you with a human agent.",
"agent": {
"name": "Name",
"avatar": "avatar url"
},
"transcript": [
{
"sender": "agent",
"timestamp": "2021-03-15 12:00:15Z",
"content": [
{
"type": "text",
"text": "**Suggestions shown:**\n\n* Help with batch or delivery\n* Help with metrics or order feedback\n* Help with Instant Cashout"
}
]
},
{
"sender": "end_user",
"timestamp": "2021-03-15 12:00:16Z",
"content": [
{
"type": "text",
"text": "Help with batch or delivery"
}
]
}
]
}
您可以在文字類型上使用 Markdown。支援的語法包括斜體、粗體、項目符號清單、超連結和底線 (--text--)。
自訂資料範例
本節提供自訂資料範例。
JSON 編碼為 JWT
JSON 檔案應驗證 JWT。自訂資料物件則是 custom_data 鍵的值。
{
"iat" : 1537399656,
"exp" : 1537400256,
"custom_data" : {
"location" : {
"label" : "Location",
"value" : "1000 Stockton St, San Francisco, CA, United States",
"type" : "string"
},
"dashboard_url" : {
"label" : "Dashboard URL",
"value" : "http://(company_name)/dashboard/device_user_ID",
"type" : "url"
},
"contact_date" : {
"label" : "Contact Date",
"value" : 1537399655992,
"type" : "date"
},
"membership_number" : {
"label" : "Membership Number",
"value" : 62303,
"type" : "number"
},
"model" : {
"label" : "Model",
"value" : "iPhone",
"type" : "string"
},
"os_version" : {
"label" : "OS Version",
"value" : "12.0",
"type" : "string"
},
"last_transaction_id" : {
"label" : "Last Transaction ID",
"value" : "243324DE-01A1-4F71-BABC-3572B77AC487",
"type" : "string"
},
"battery" : {
"label" : "Battery",
"value" : "-100%",
"type" : "string"
},
"bluetooth" : {
"label" : "Bluetooth",
"value" : "Bluetooth not supported",
"type" : "string"
},
"wifi" : {
"label" : "Wi-Fi",
"value" : "Wi-Fi not connected",
"type" : "string"
},
"ssn" : {
"invisible_to_agent" : true,
"label" : "Social Security Number",
"value" : "102-186-1837",
"type" : "string"
}
}
}
每項資料都類似 JSON 物件格式,且應包含鍵、值、類型和標籤。
鍵是資料的專屬 ID。標籤是客戶關係管理頁面上的顯示名稱。類型是值的類型。
字串
- JSON 字串
數字
- integer、float
日期
- 13 位數的 UTC Unix 時間戳記格式。(包含毫秒)
網址
- HTTP 網址格式
客戶關係管理範例
以下是客戶關係管理 (CRM) 的範例。
位置
使用 CoreLocation 架構。詳情請參閱 AppDelegate.m。
裝置 OS 版本
[customData set:@"os_version" label:@"OS Version" stringValue:[[UIDevice currentDevice] systemVersion]];
禁止顯示自訂資料
您可以搭配自訂資料物件使用 invisible_to_agent 屬性,防止已簽署或未簽署的自訂資料顯示在代理程式轉接器中。在上述範例中,由於 ssn 物件包含 "invisible_to_agent" : true,因此代理程式轉接器不會顯示使用者的身分證字號。
在自訂資料物件中加入 "invisible_to_agent" : true 屬性時,預期會發生下列行為:
- 自訂資料會包含在工作階段中繼資料檔案中。
- 自訂資料不會納入客戶關係管理記錄。
詳情請參閱「在代理程式轉接器中查看工作階段資料」。
保留資料屬性
工作階段開始時,您可以將保留的資料屬性,以已簽署自訂資料的形式傳送至 Contact Center AI Platform (CCAI Platform)。詳情請參閱「傳送預留資料屬性」。
以下是自訂資料中保留資料屬性的範例:
{
"custom_data": {
"reserved_verified_customer": {
"label": "Verified Customer",
"value": false,
"type": "boolean"
},
"reserved_bad_actor": {
"label": "Bad Actor",
"value": false,
"type": "boolean"
},
"reserved_repeat_customer": {
"label": "Repeat Customer",
"value": false,
"type": "boolean"
}
}
}
將每個值設為 true 或 false。
reserved_verified_customer:如果您認為這位使用者是合法客戶,請輸入true。reserved_bad_actor:如果您認為這位使用者可能是惡意人士,請使用true。reserved_repeat_customer:如果您判定這位使用者先前曾與客服中心聯絡,請設為true。
自訂 Flow
本節說明如何自訂流程。
中斷 CCAI 平台與主機應用程式事件的連線
// CCAI Platform is connected
...
// An event has come
[UJET disconnect:^{
// Handle an event
}];
延後 CCAI 平台來電或即時通訊
導入委派方法來處理傳入事件:
- (BOOL)shouldConnectUjetIncoming:(NSString *)identifier forType:(UjetIncomingType)type {
if (weDoingSomething) {
// save identifier and type
return NO; // postpone
} else {
return YES;
}
}
連結延期活動
[UJET connect:identifier forType:UjetIncomingTypeCall];
設定深層連結
這樣一來,無論使用者是否安裝應用程式,服務專員都能在 PSTN 通話中透過簡訊使用智慧動作。
在 CCAI Platform 入口網站中,依序前往 「Settings」 >「Operation Management」 >「Enable Send SMS to Download App」。
設定通用連結或自訂網址配置後,您可以使用網頁 (例如 https://your-company.com/support) 設定應用程式網址。你可以選擇任一方式。
實作委派方法來處理深層連結
通用連結和自訂網址分別類似於 https://your-company.com/support?call_id=x&nonce=y 和 your-company://support?call_id=x&nonce=y。在管理入口網站的「應用程式網址」下方,輸入不含查詢參數的連結。例如,如果使用自訂網址配置,請輸入 your-company://support。
在委派方法中,請務必只在通用連結或自訂網址中的網址路徑和參數專用於 CCAI Platform 時,才呼叫 [UJET start]。
- (BOOL)application:(UIApplication *)app continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray * _Nullable))restorationHandler {
...
if ([NSUserActivityTypeBrowsingWeb isEqualToString:userActivity.activityType]) {
NSURL *url = userActivity.webpageURL;
NSArray *availableSchema = @[
@"your-company", // custom URL scheme
@"https" // universal link
];
NSArray *availableHostAndPath = @[
@"ujet", // custom URL scheme
@"your-comany.com/ujet" // universal link
];
if (![availableSchema containsObject:url.scheme]) {
return NO;
}
NSString *hostAndPath = [NSString stringWithFormat:@"%@%@", url.host, url.path];
if (![availableHostAndPath containsObject:hostAndPath]) {
return NO;
}
// your-company://ujet?call_id={call_id}&nonce={nonce}
// https://your-company.com/ujet?call_id={call_id}&nonce={nonce}
NSURLComponents *urlComponents = [NSURLComponents componentsWithURL:url
resolvingAgainstBaseURL:NO];
NSArray *queryItems = urlComponents.queryItems;
NSString *callId = [self valueForKey:@"call_id" fromQueryItems:queryItems];
// validate call ID
if (![self isValidCallId:callId]) {
return NO;
}
NSString *nonce = [self valueForKey:@"nonce" fromQueryItems:queryItems];
UJETStartOptions *options = [[UJETStartOptions alloc] initWithCallId:callId nonce:nonce];
[UJET startWithOptions:options];
}
...
}
如果應用程式採用 UIWindowSceneDelegate,請加入以下程式碼片段:
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
//if app is called with universal Link and started from cold
if connectionOptions.urlContexts.first != nil {
self.scene(scene, openURLContexts: connectionOptions.urlContexts)
}
guard let _ = (scene as? UIWindowScene) else { return }
}
func scene(_ scene: UIScene, willContinueUserActivityWithType userActivityType: String) {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let _ = appDelegate.application(UIApplication.shared,
continue: NSUserActivity(activityType: userActivityType)) { _ in
}
}
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
guard let url = URLContexts.first?.url else {
return
}
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let _ = appDelegate.application(UIApplication.shared,
open: url,
options: [:])
}
}
詳情請參閱 UJETObject+DeepLink 檔案中的程式碼範例。
觀測 CCAI 平台事件
我們會透過 NSNotificationCenter.defaultCenter 發布下列事件。您可以監聽這些事件,並根據用途自訂流程,例如自訂鍵盤版面配置。
UJETEventEmailDidClick
- 待播清單選單資料
UJETEventEmailDidSubmit
待播清單選單資料
has_attachment: (NSNumber) @YES, @NO
UJETEventSessionViewDidAppear
type: @"call", @"chat"
timestamp:(NSString) ISO 8601
UJETEventSessionViewDidDisappear
type: @"call", @"chat"
timestamp:(NSString) ISO 8601
UJETEventSessionDidCreate
- 工作階段資料
UJETEventSessionDidEnd
工作階段資料
agent_name:(NSString) 如果服務專員未加入,則為空值
duration:(NSNumber) 僅限通話
ended_by: (NSString)
type=call: @"agent", @"end_user"
type=chat: @"agent", @"end_user", @"timeout", @"dismissed"
UJETEventSdkDidTerminate
UJETEventPostSessionOptInDidSelected
- opt_in_selected: (NSString) @"Yes", @"No"
事件資料
中繼資料
application: @"iOS"app_id:(NSString) 軟體包 IDapp_version:(NSString)company:(NSString) 子網域device_model:(NSString)device_version:(NSString)sdk_version:(NSString)timestamp:(NSString) ISO 8601
佇列選單資料
中繼資料
menu_id:NSStringmenu_key: NSString, nullablemenu_name:NSStringmenu_path: NSString
工作階段資料
中繼資料
session_id:NSStringtype:@"call"、@"chat"end_user_identifier:NSString
設定分享螢幕畫面
如要使用分享螢幕畫面功能,請整合 UJETCobrowseKit.xcframework。
CocoaPods
將這個子規格新增至應用程式目標:
ruby
target 'MyApp' do
pod 'UJET'
pod 'UJET/Cobrowse'
end
設定與 Carthage 分享螢幕畫面
在 Cartfile 中加入以下這行:
binary "https://sdk.ujet.co/ios/UJETKit.json"
SwiftPM
選取 UJET 和 UJETCobrowse 產品,並新增至應用程式目標。
並將 UJETGlobalOptions.cobrowseKey 的值設為貴公司的共同瀏覽金鑰。
swift
let options = UJETGlobalOptions()
options.cobrowseKey = cobrowseKey
UJET.setGlobalOptions(options)
分享整個裝置螢幕畫面 (選用)
分享整個裝置螢幕畫面,可讓客服專員查看您應用程式以外的畫面。如果客服專員需要查看系統設定狀態,或需要瞭解使用者在多個應用程式之間的瀏覽情形,這項功能通常很有幫助。如果不需要這項功能,可以略過本節。
自訂分享螢幕畫面同意聲明對話方塊
如要自訂分享螢幕畫面同意聲明對話方塊,您需要在供應商類別中實作 UJETCobrowseAlertProvider 協定。在這個實作中,請透過各自的通訊協定方法,傳回自訂 UIViewController 或任何其他繼承 UIViewController 的物件。UIViewController 應有兩個按鈕,一個用於接受,另一個用於拒絕。
取得同意聲明後,請呼叫閉包 consentStatus,將同意聲明傳遞至 SDK。「cobrowseFullDeviceRequestAlert」的UIViewController。委派代表應包含含有標題的 RPSystemBroadcastPickerView (請參閱下方程式碼範例),並應有另一個拒絕按鈕。在使用者點選拒絕按鈕時,叫用關閉的閉包。
class CobrowseAlertProvider: NSObject, UJETCobrowseAlertProvider {
func cobrowseSessionInitializationAlert(consentStatus: @escaping (Bool) -> Void) -> UIViewController? {
let customAlertViewController = CustomAlertViewController()
customAlertViewController.consentStatus = consentStatus
return customAlertViewController
}
func cobrowseSessionRequestAlert(consentStatus: @escaping (Bool) -> Void) -> UIViewController? {
// Same as cobrowseSessionInitializationAlert
}
func cobrowseRemoteRequestAlert(consentStatus: @escaping (Bool) -> Void) -> UIViewController? {
// Same as cobrowseSessionInitializationAlert
}
func cobrowseFullDeviceRequestAlert(dismissed: @escaping () -> Void) -> UIViewController? {
let customAlertViewController = CustomFullDeviceAlertViewController()
cobrowseSessionAlertViewController.dismissed = dismissed
return customAlertViewController
}
func cobrowseSessionEndAlert(consentStatus: @escaping (Bool) -> Void) -> UIViewController? {
// Same as cobrowseSessionInitializationAlert
}
}
自訂檢視控制器應有閉包,將同意聲明狀態傳遞至 SDK。
class CustomAlertViewController: UIViewController {
var consentStatus: ((Bool) -> Void)?
@IBAction func allowButtonClicked(_ sender: Any) {
dismiss(animated: true) {[weak self] in
self?.consentStatus?(true)
}
}
@IBAction func denyButtonClicked(_ sender: Any) {
dismiss(animated: true) {[weak self] in
self?.consentStatus?(false)
}
}
}
全裝置請求快訊的自訂檢視區塊控制器應有 RPSystemBroadcastPickerView,以及將 dismiss 狀態傳遞至 SDK 的閉包。
class CustomFullDeviceAlertViewController: UIViewController {
var broadcastPickerView: RPSystemBroadcastPickerView!
var dismissed: (() -> Void)?
override func viewDidLoad() {
super.viewDidLoad()
let frame = CGRect(x: x, y: y, width: 50, height: 50) // Set your own value
broadcastPickerView = RPSystemBroadcastPickerView(frame: frame)
broadcastPickerView.preferredExtension = Bundle.main.object(forInfoDictionaryKey: "CBIOBroadcastExtension") as? String // Should have this value as it is
view.addSubview(broadcastPickerView)
}
@IBAction func denyButtonClicked(_ sender: Any) {
dismiss(animated: true) {[weak self] in
self?.dismissed?()
}
}
}
請務必透過下列 API 將這個供應商傳遞至 SDK:
let provider = CobrowseAlertProvider()
UJET.setCobrowseAlertProvider(provider)
廣播擴充功能
這項功能需要新增廣播擴充功能。
開啟 Xcode 專案。
依序前往「File」 > 「Target」。
選擇「直播上傳擴充功能」。
輸入目標名稱。
取消勾選「Include UI Extension」核取方塊。
建立目標,並記下其軟體包 ID。
將廣播擴充功能的目標 SDK 變更為 iOS 12.0 以上版本。
整合 SDK
CocoaPods{:在擴充功能目標中加入下列子規格:
target 'MyApp' do
pod 'UJET'
pod 'UJET/Cobrowse'
end
target 'MyAppExtension' do
pod 'UJET/CobrowseExtension'
end
如果使用 SwiftPM,請選取 UJETCobrowseExtension 產品,並新增至擴充功能目標。
設定鑰匙圈共用
您的應用程式和先前建立的應用程式擴充功能,需要透過 iOS 鑰匙圈共用一些密鑰。他們會使用自己的 Keychain 群組,因此與應用程式 Keychain 的其餘部分隔離。
在 Xcode 中,選取應用程式目標和擴充功能目標。為 io.cobrowse 鑰匙圈群組新增鑰匙圈共用授權。
將軟體包 ID 新增至 plist
取得先前建立的擴充功能軟體包 ID,並將這個項目新增至應用程式的 Info.plist。
xml
<key>CBIOBroadcastExtension</key>
<string>your.app.extension.bundle.ID.here</string>
導入擴充功能
Xcode 會將 SampleHandler.m 和 SampleHandler.h (或 SampleHandler.swift) 檔案新增為您先前建立的目標的一部分。將檔案內容替換為下列內容:
Swift:
選取 UJETCobrowseExtension 產品並新增至擴充功能目標:
import CobrowseIOAppExtension
class SampleHandler: CobrowseIOReplayKitExtension {
}
ObjC:
objc// SampleHandler.h
@import CobrowseIOAppExtension;
@interface SampleHandler : CobrowseIOReplayKitExtension
@end// SampleHandler.m
#import "SampleHandler.h"
@implementation SampleHandler
@end
打造並執行您的應用程式
現在可以建構及執行應用程式。完整的裝置功能僅適用於實體裝置,無法在 iOS 模擬器中運作。
盡量減少 SDK
在即時通訊工作階段或通話期間,可以將 Contact Center AI 平台 SDK 最小化。當您想在收到 SDK 事件 (例如點擊內容資訊卡) 後,將使用者導回應用程式,這項功能就非常實用。如要縮小 SDK 並引導使用者返回應用程式,可以使用:
UJET.minimize(nil)
// Or if you want to take some action once the SDK has been minimized:
UJET.minimize {
// Add the code you want to run once the SDK has been minimized here
}
備用
您可以使用 didHandleUjetError 函式,做為非預期錯誤的備用方案。如果您不使用這個函式,或函式傳回 false,iOS SDK 會處理錯誤。
下表列出 didHandleUjetError 函式監聽的錯誤:
| 錯誤類型 | 錯誤代碼 | 說明 |
|---|---|---|
networkError |
1 | 無法連上網路。 注意:在即時通訊或通話工作階段或費率畫面中,如果網路無法使用,系統不會觸發這項錯誤。 |
authenticationError |
100 | 驗證期間發生未預期的錯誤。 |
authenticationJwtError |
101 | JWT 驗證期間發生未預期的錯誤,例如剖析錯誤。 |
voipConnectionError |
1000 | 無法與 VoIP 供應商建立連線。VoIP SDK 回呼會處理這項作業。 |
voipLibraryNotFound |
1001 | 系統預期透過 VoIP 供應商連線通話,但找不到供應商。如果整合錯誤的 SDK,或未將 VoIP 供應商程式庫新增至依附元件,就可能發生這種情況。 |
chatLibraryNotFound |
1100 | 系統找不到即時通訊程式庫時,就會發生這種情況。如果整合錯誤的 SDK,或未將 Twilio 即時通訊程式庫新增至依附元件,就可能發生這種情況。 |
以下程式碼範例說明如何使用 didHandleUjetError 函式:
public func didHandleUjetError(_ errorCode: Int32) -> Bool {
guard let ujetError = UjetErrorCode(rawValue: Int(errorCode)) else {
return false // Let the SDK handle unknown integer codes.
}
switch ujetError {
case .networkError:
// Example for if you have a custom UI for network errors. You can
// handle the error and prevent the SDK from showing its own alert.
showCustomNetworkAlert() // Your custom UI for this type of error.
return true
case .authenticationError, .voipConnectionError:
// For all other errors, use the default SDK behavior.
return false
@unknown default:
// Let the SDK handle future errors.
return false
}
}
疑難排解
本節提供常見問題的疑難排解資訊。
應用程式提交遭拒
提交的應用程式在中國境內包含 CallKit 架構,因此遭到拒絕。
如果 Apple 因這個原因拒絕您的應用程式,請留下註解,因為系統設計會在中國地區停用 CallKit 架構,以進行 VoIP 通話。這項異動自 SDK 0.31.1 版起生效。
SDK 過大
SDK 太大,難以在 GitHub 上追蹤
在這篇文章中,他們提供兩種選擇。建議使用 Git LFS。
如果您未使用 Bitcode,則可選擇從二進位檔中移除 Bitcode。在 UJETKit.xcframework 資料夾下執行這項指令。
xcrun bitcode_strip -r UJET -o UJET
dyld:未載入程式庫錯誤
依序前往「Target」 >「Build Settings」>「Linking」,在「Runpath Search Paths」中新增 @executable_path/Frameworks。
在 iTunes Connect 提交應用程式
由於啟用了網路語音通訊協定背景模式,Apple 可能會在審查過程中詢問以下問題:
使用者可以在您的應用程式中接聽 VoIP 通話嗎?
回答「Yes」。
啟動 SDK 時無法使用快訊通知
請確認下列事項:
請使用實體裝置,而非模擬器。
啟用推播通知和「Background Modes」>「Voice over IP capability」。
如果上述方法無效,請嘗試使用發行佈建設定檔 (Ad-hoc 或 Apple Store) 建構。
對測試應用程式測試推播通知
準備 VoIP 憑證和裝置權杖。
在 CCAI Platform 入口網站中,依序前往 「Settings」(設定) >「Developer Settings」(開發人員設定) 選單,然後參閱「Push Notification Debug」(推播通知偵錯) 部分。
如果您已設定 APNS 憑證,就不必再次輸入憑證。
輸入憑證 (選用),勾選是否為沙箱 (選用),然後輸入測試應用程式推播通知裝置權杖。
發起新對話的時間超過 30 秒
請確認您是否正在回應自訂資料的委派方法。您應在收到要求時傳回有效的自訂資料,或在成功區塊中傳回 nil。
請參考以下程式碼片段的設定範例:
public func signPayload(_ payload: [AnyHashable: Any]?, payloadType: UjetPayloadType, success: (String?) -> Void, failure: (Error?) -> Void)
{
if payloadType == .customData {
success(nil)
}
}