Panduan iOS SDK

SDK seluler Contact Center AI Platform (CCAI Platform) untuk sistem operasi Apple iOS memberikan kemampuan untuk menyematkan pengalaman seluler CCAI Platform dalam aplikasi seluler iOS.

Persyaratan

SDK seluler iOS memiliki persyaratan berikut:

  • iOS 12.0+

Mengambil kredensial perusahaan

  1. Login ke portal Contact Center AI Platform (CCAI Platform) menggunakan kredensial Admin.

  2. Buka Setelan > Setelan Developer.

  3. Di bagian Company Key dan Secret Code, catat Company Key dan Company Secret Code.

Memulai

Berikut adalah panduan tentang cara memulai penggunaan SDK seluler iOS CCAI Platform.

Penginstalan

Untuk memulai, Anda perlu menginstal iOS SDK.

Download aplikasi contoh

  1. Download Aplikasi Contoh iOS.

  2. Buka folder dan instal dependensi menggunakan CocoaPods:

    $ pod install --project-directory=ExampleApp
    
  3. Untuk mengonfigurasi setelan project dengan cepat, jalankan skrip shell:

    $ ./setup.sh
    

    Atau, Anda dapat mengedit setelan project secara manual dengan mengikuti langkah-langkah berikut:

    1. Buka ExampleApp.xcworkspace.

    2. Ganti nilai UJETCompanyKey dan UJETCompanySecret di Info.plist dengan nilai Kunci Perusahaan dan Kode Rahasia Perusahaan dari halaman Setelan > Setelan Developer di portal Platform CCAI.

    3. Ganti nilai UJETSubdomain di Info.plist dengan subdomain di URL untuk portal CCAI Platform Anda. Subdomain langsung mendahului .ujet.com di URL—misalnya, your-subdomain di https://your-subdomain.ujet.com/settings/developer-setting.

Mengintegrasikan ke project Anda

Integrasi iOS SDK dengan aplikasi Anda bergantung pada lingkungan pengembangan Anda.

Pengelola paket Swift

  1. Tambahkan Swift Package for iOS SDK.

  2. Di setelan build Anda, masukkan -ObjC di Other Linker Flags.

  3. Mulai rilis Xcode terbaru (saat ini 13.2), ada masalah umum dengan penggunaan framework biner yang didistribusikan menggunakan Swift Package Manager. Solusi sementara untuk masalah ini adalah menambahkan Fase Skrip Pengoperasian ke Tahapan Build project Xcode Anda. Fase Skrip yang Dijalankan ini harus dilakukan setelah fase build Embed Frameworks. Fase Run Script baru ini harus berisi kode berikut:

    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
  1. Tambahkan baris berikut ke Podfile:

    pod 'UJET', :podspec =>
    'https://sdk.ujet.co/ios/x.y.z/ujet.podspec' #specific version
    x.y.z
    
  2. Jalankan pod install. Jika iOS SDK telah diintegrasikan sebelumnya, jalankan pod update CCAI Platform.

Carthage

Google Cloud merekomendasikan penggunaan pengelola dependensi atau integrasi manual karena dependensi Platform CCAI tidak mendukung Carthage. Untuk melakukannya, tambahkan baris berikut:

binary "https://sdk.ujet.co/ios/UJETKit.json

binary "https://sdk.ujet.co/ios/UJETFoundationKit.json

binary https://raw.githubusercontent.com/twilio/twilio-voice-ios/Releases/twilio-voice-ios.json

Integrasi manual

Hal ini tidak didukung: https://github.com/twilio/conversations-ios/issues/12.

biner https://raw.githubusercontent.com/twilio/conversations-ios/master/twilio-convo-ios.json

  1. Jalankan carthage bootstrap --use-xcframeworks (atau carthage update --use-xcframeworks (jika Anda memperbarui dependensi).

  2. Download UJETKit.xcframework, UJETFoundationKit.xcframework, UJETChatRedKit.xcframework, UJETChatBlueKit.xcframework, UJETTwilioCallKit.xcframework, dan semua dependensi TwilioVoice.xcframework dan TwilioConversationsClient.xcframework.

  3. Tambahkan UJETKit.xcframework ke target Anda dengan menariknya ke bagian Frameworks, Libraries, and Embedded Content.

  4. Ulangi Langkah 2 dan 3 pada semua dependensi dari Langkah 1.

  5. Di Build Settings, tetapkan -ObjC ke Other Linker Flags.

  6. Tambahkan libc++.tbd sebagai dependensi di bagian Linked Frameworks target.

Jika Anda akan membuat SDK secara manual dengan project contoh, gunakan langkah-langkah di bagian berikut.

Bangun SDK secara manual dengan project contoh

Ikuti langkah-langkah berikut secara berurutan:

  1. Download semua framework termasuk UJETKit.xcframework dan dependensi lainnya.

  2. Buat folder CCAI Platform di root project dan ekstrak semua framework.

  3. Pilih target dan build Objc-Manual atau Swift-Manual.

Framework impor

Bagian berikut memberikan petunjuk tentang cara mengimpor framework.

Project Objective-C

@import UJETKit;

Swift Project

swiftimport
UJETimport UJETKit

Lakukan inisialisasi SDK

Lakukan inisialisasi CCAI Platform dengan UJET_COMPANY_KEY dan UJET_SUBDOMAIN.

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;
}

Anda dapat mengubah level log dari verbose menjadi error. Level log default adalah UjetLogLevelInfo.

[UJET.setLogLevel:UjetLogLevelVerbose];

Autentikasi pengguna akhir

Akses iOS SDK melalui aplikasi iOS.

Untuk memastikan pengguna akhir diautentikasi, kami memperkenalkan mekanisme penandatanganan JWT.

SDK iOS akan meminta untuk menandatangani payload saat autentikasi diperlukan. Jika penandatanganan berhasil, aplikasi akan menukar JWT yang ditandatangani dengan token autentikasi pengguna akhir. Blok keberhasilan atau kegagalan harus dipanggil sebelum delegasi ditampilkan.

Untuk pengguna anonim (identifier = nil), aplikasi akan membuat UUID untuk pengguna. Jika di lain waktu pengguna diautentikasi dengan ID, aplikasi akan mencoba menggabungkan kedua pengguna berdasarkan UUID.

Di UJETObject.h dari contoh project:

@import UJETKit;

@interface UJETObject : NSObject <UJETDelegate>

Implementasikan signPayload: payloadType: success: failure: delegate method.

- (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;
}

Sebaiknya tanda tangani payload dari server aplikasi Anda, bukan di klien.

Contoh ini menggunakan penandatanganan lokal untuk tujuan pengujian. Lihat signDataInRemote: success: failure: dalam file UJETObject.m.

Untuk mengetahui informasi selengkapnya, lihat Autentikasi pengguna akhir SDK.

Menyiapkan notifikasi push

Aplikasi mengirimkan notifikasi push untuk meminta Smart Action seperti verifikasi dan foto, serta melaporkan panggilan masuk. Aplikasi memerlukan dua jenis sertifikat yang berbeda (VoIP dan APNs) untuk disimpan di Admin Portal.

Menyiapkan sertifikat layanan VoIP

Dokumentasi referensi tersedia untuk notifikasi push VoIP Apple.

  1. Buat dan download sertifikat VoIP dari situs developer Apple.

  2. Klik dua kali sertifikat untuk menambahkannya ke Keychain.

  3. Mulai aplikasi Keychain Access di Mac Anda.

  4. Pilih kategori Sertifikat Saya di sidebar sebelah kiri.

  5. Klik kanan VoIP Services: your.app.id certificate.

  6. Di menu pop-up, pilih Ekspor.

  7. Simpan sebagai cert.p12 tanpa melindunginya dengan sandi dengan mengosongkan sandi.

  8. Jalankan perintah berikut di terminal.

    openssl s_client -connect gateway.push.apple.com:2195 -cert cert.pem -debug -showcert
    
  9. Bagian atas cert.pem adalah sertifikat dan bagian bawah adalah kunci pribadi.

  10. Periksa apakah sertifikat Anda berfungsi dengan server push notification Apple.

    openssl s_client -connect gateway.push.apple.com:2195 -cert cert.pem -debug -showcerts
    

    Jika berhasil, perintah akan menampilkan:

    ---
    New, TLSv1/SSLv3, Cipher is AES256-SHA
    Server public key is 2048 bit
    Secure Renegotiation IS supported
    Compression: NONE
    Expansion: NONE
    SSL-Session:
        Protocol  : TLSv1
        Cipher    : AES256-SHA
        Session-ID:
        Session-ID-ctx:
        Master-Key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
        Key-Arg   : None
        Start Time: 1475785489
        Timeout   : 300 (sec)
        Verify return code: 0 (ok)
    ---
    
  11. Login ke portal Platform CCAI dengan kredensial administrator, lalu buka Settings > Developer Settings > Mobile App.

  12. Isi sertifikat di bagian 'VoIP Services Certificate', lalu simpan. Pastikan untuk menyertakan batas (-----BEGIN----- dan -----END-----) untuk sertifikat dan kunci pribadi.

  13. Centang kotak Sandbox jika Anda menjalankan aplikasi dengan profil penyediaan pengembangan seperti saat men-debug di Xcode. Jika aplikasi Anda diarsipkan untuk Ad hoc atau App store dan menggunakan profil penyediaan distribusi, hapus centang kotak Sandbox.

Menyiapkan SSL layanan push notification Apple

Proses untuk hal ini mirip dengan proses untuk sertifikat layanan VOIP. Dalam kasus ini, sertifikat SSL layanan push notification Apple (Sandbox & Production) digunakan. Anda dapat membaca dokumentasi server notifikasi jarak jauh Apple untuk mendapatkan panduan tentang cara membuat sertifikat.

Mengintegrasikan notifikasi push

Di AppDelegate.m:

@import PushKit;

@interface AppDelegate() <PKPushRegistryDelegate>
In application:didFinishLaunchingWithOptions: method:
// Initialize CCAI Platform
[UJET] initialize:UJET_COMPANY_KEY subdomain:UJET_SUBDOMAIN delegate:self];

//  Register for VoIP notifications on launch.
PKPushRegistry *voipRegistry = [[PKPushRegistry alloc] initWithQueue: dispatch_get_main_queue()];
voipRegistry.delegate = self;
voipRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP];

Tambahkan metode delegasi berikut dalam menerapkan file protokol UIApplicationDelegate:

Cetak token perangkat Anda untuk menguji notifikasi push.

// PKPushRegistryDelegate

- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type {
  [UJET updatePushToken:credentials.token type:UjetPushTypeVoIP];
}

- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type withCompletionHandler:(void (^)(void))completion {
  if (payload.dictionaryPayload[@"ujet"]) {
    [UjetreceivedNotification:payload.dictionaryPayload completion:completion];
  } else {
    completion();
  }
}

// UIApplicationDelegate

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
  [UjetupdatePushToken:deviceToken type:UjetPushTypeAPN];
}

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
  if (userInfo[@"ujet"]) {
    [UJET receivedNotification:userInfo completion:nil];
  }
}

// UserNotificationsDelegate overrides [UIApplicationDelegate didReceiveRemoteNotification:]

- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler {
    NSDictionary *userInfo = notification.request.content.userInfo;

    if (userInfo[@"ujet"] != nil) {
        [UJET receivedNotification:userInfo completion:nil];
    }
}

- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler {
    NSDictionary *userInfo = response.notification.request.content.userInfo;

    if (userInfo[@"ujet"] != nil) {
        [UJET receivedNotification:userInfo completion:nil];
    }
}

Aktifkan notifikasi push

  1. Pilih target Anda dan buka tab Kemampuan.

  2. Aktifkan tombol Push Notifications.

Menguji notifikasi push

Bagian berikut memberikan panduan tentang cara menguji notifikasi push.

Bagian debug notifikasi push

Di portal administrator, buka Setelan > Setelan Developer. Di halaman ini, temukan bagian berjudul Push Notification Debug:

Salin dan tempel token perangkat di area teks kanan, lalu pilih Aplikasi Seluler yang benar.

Mendapatkan token perangkat

Contoh string token perangkat terlihat seperti ini:

7db0bc0044c8a203ed87cdab86a597a2c43bf16d82dae70e8d560e88253364b7

Notifikasi push biasanya disetel di class yang sesuai dengan protokol UIApplicationDelegate atau PKPushRegistryDelegate. Pada titik tertentu, token perangkat tersedia untuk Anda. Anda dapat mencetaknya sebelum meneruskannya ke iOS SDK. Untuk mendapatkan token perangkat, gunakan cuplikan kode.

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]);
}
Hasil

Setelah Anda memasukkan file PEM sertifikat dan token perangkat, klik tombol.

Hasilnya akan menampilkan pesan Notifikasi push berhasil dikonfigurasi jika notifikasi push pengujian berhasil dikirim.

Notifikasi push tidak dijamin 100% akan terkirim, bergantung pada koneksi jaringan perangkat.

Konfigurasi project

Bagian berikut menguraikan perubahan yang diperlukan untuk mengonfigurasi project.

Kemampuan

Di setelan target, aktifkan kemampuan berikut:

  • Notifikasi Push

  • Mode Latar Belakang (periksa item ini)

  • Audio dan AirPlay

  • Voice over IP

Info.plist

Untuk melindungi privasi pengguna, aplikasi iOS apa pun yang ditautkan pada atau setelah iOS 10.0 yang mengakses mikrofon, galeri foto, dan kamera perangkat, harus menyatakan niat untuk melakukannya. Sertakan kunci berikut dengan nilai string dalam file Info.plist aplikasi Anda dan berikan string tujuan untuk kunci ini. Jika aplikasi Anda mencoba mengakses mikrofon, library foto, dan kamera perangkat tanpa string tujuan yang sesuai, aplikasi akan keluar.

  • NSMicrophoneUsageDescription: Mengizinkan akses ke mikrofon untuk melakukan panggilan dan berbicara dengan tim dukungan atau tim pemecahan masalah, serta untuk mengirim video dengan suara terkait pertanyaan produk.

  • NSCameraUsageDescription: Mengizinkan akses ke kamera agar pelanggan dapat mengambil dan mengirim foto yang terkait dengan pertanyaan dukungan pelanggan mereka.

  • NSPhotoLibraryUsageDescription: Mengizinkan pelanggan mengirim foto yang terkait dengan pertanyaan dukungan pelanggan mereka.

  • NSFaceIDUsageDescription: Mengizinkan akses ke verifikasi menggunakan Face ID.

Mulai iOS SDK

Tambahkan baris berikut di tempat Anda ingin memulai iOS SDK:

[UJET startWithOptions:nil];

Anda juga dapat memulai iOS SDK dari titik tertentu di menu dengan kunci ini menggunakan Titik Akses Langsung:

UJETStartOptions *option = [[UJETStartOptions alloc] initWithMenuKey:@"MENU_KEY"];
[UJET startWithOptions:option];

menuKey dapat dibuat dengan membuat Titik Akses Langsung (DAP). Langkah-langkah berikut memberikan petunjuk tentang cara membuat DAP:

  1. Login ke portal CCAI Platform dengan kredensial administrator.

  2. Buka Setelan > Antrean.

  3. Pilih antrean apa pun dari struktur menu.

  4. Pilih Buat titik akses langsung

  5. Masukkan kunci di formulir teks.

  6. Klik Simpan.

Hapus cache dari lokal jika data pengguna telah diperbarui

Kami menyimpan token autentikasi dalam Keychain untuk digunakan kembali dan membuat permintaan tanda tangan payload dari aplikasi host menjadi lebih jarang. SDK akan menggunakannya hingga habis masa berlaku atau dicabut melalui panggilan clearUserData. Aplikasi host bertanggung jawab untuk mencabut cache ini setiap kali data terkait pengguna telah berubah atau diperbarui, seperti peristiwa logout.

[UJET clearUserData];

Memeriksa sesi yang ada sebelum memulai Contact Center AI Platform

Sebelum memulai sesi, periksa untuk melihat apakah tidak ada sesi saat ini. Hal ini sangat penting jika userId telah berubah.

[UJET getStatus];

Jika ada sesi yang sudah ada, kita harus meminta pengguna untuk melanjutkan sesi atau membatalkan tindakan:

if ([UJET getStatus] != UjetStatusNone) {
  // Display alert to cancel login or resume existing session
}

Sesuaikan

Ada beberapa opsi tema SDK yang tercantum di UJETGlobalTheme.h.

Tetapkan tema Anda setelah [UJET initialize]—misalnya:

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];

Nama perusahaan diambil dari Admin Portal > Setelan > Detail Pusat Dukungan > Nama Tampilan.

Anda dapat menyetel gambar logo, bukan nama perusahaan, seperti ini:

theme.companyImage = [UIImage imageNamed:@"logo"];

Ukuran gambar akan diubah agar sesuai dengan area jika terlalu besar.

String

Anda juga dapat menyesuaikan string dengan mengganti nilainya. Misalnya, masukkan pasangan kunci - nilai ini di Localizable.strings Anda:

"ujet_greeting_title" = "Title";

"ujet_greeting_description" = "Description";

String yang dapat disesuaikan yang tersedia tercantum dalam file ujet.strings.

Mode gelap

Anda dapat menentukan warna yang diinginkan untuk mode gelap agar font lebih mudah dibaca.

@property (nonatomic, strong) UIColor \*tintColorForDarkMode;

Jika Anda tidak menyetel properti, UJETGlobalTheme.tintColor akan digunakan untuk mode gelap. Sebaiknya setel properti ini jika aplikasi Anda mendukung mode gelap. Lihat artikel Apple berikut tentang cara memilih warna tint yang tepat untuk mode gelap:

Tema chat

Untuk menyesuaikan layar chat, Anda memiliki opsi untuk menggunakan string JSON atau setiap class tema.

Sebagai referensi, lihat aplikasi contoh dan hapus komentar metode customizeChatTheme ini.

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

Contoh tema chat yang disesuaikan ditampilkan.

Tema kartu konten

Anda dapat menambahkan penyesuaian untuk kartu konten bersama dengan penyesuaian chat. Anda dapat melakukannya menggunakan file json (lihat properti content_card) atau dengan menggunakan class 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)
}

Contoh kartu konten ditampilkan.

Tema kartu formulir

Anda dapat menambahkan penyesuaian untuk kartu formulir bersama dengan penyesuaian chat. Lakukan ini dengan menggunakan file json (lihat form_card property) atau dengan menggunakan class 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)
}

Konfigurasi formulir web

Untuk mengonfigurasi kemampuan formulir web, terapkan metode ujetWebFormDidReceive protokol UJETDelegate. Metode ini menerima peristiwa (kamus FormMessageReceivedEvent) sebagai parameter, yang berisi informasi terkait formulir. Kamus peristiwa (FormMessageReceivedEvent) mencakup struktur JSON berikut:

  {
    "type": "form_message_received",
    "smart_action_id": 1,
    "external_form_id": "external_foobar"
    "signature": "4868a7e1dcb5..."
  }

Untuk menangani peristiwa, lakukan hal berikut:

  1. Ekstrak informasi yang relevan dari kamus peristiwa (smart_action_id, external_form_id, dan signature).

  2. Buat URI formulir dan tanda tangan untuk data formulir.

  3. Teruskan data formulir ke SDK sebagai kamus FormDataEvent menggunakan completion closure.

  4. Jika terjadi error selama pembuatan URI/tanda tangan, panggil callback menggunakan callback.onError() dengan Error.

Kamus (FormDataEvent) yang diteruskan ke SDK harus memiliki struktur berikut:

 {
    "type": "form_data",
    "signature": "4868a7e1dcb5...",
    "data": {
       "smart_action_id":1,
       "external_form_id": "form_id",
       "uri":"foobar"
    }
  }

Tanda tangan (HMAC-SHA:256) harus dibuat menggunakan data, ditandatangani dengan kunci rahasia bersama. Kunci objek data harus diurutkan secara alfabet sebelum menghasilkan tanda tangan dan data yang sama harus dikirim ke SDK.

Transfer setelah sesi

Anda dapat menambahkan penyesuaian untuk va pasca-sesi bersama dengan penyesuaian chat. Hal ini dapat dilakukan menggunakan file JSON (lihat properti post_session) atau menggunakan class UJETChatPostSessionVaTheme. Lebar batas hanya dapat berupa 0 atau 1, dan jika Anda tidak ingin membedakan pengalaman VA pasca-sesi, Anda dapat menyetel containerColor putih dan batas ke 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)
}

Menu tindakan chat

Anda dapat menambahkan penyesuaian untuk menu tindakan chat bersama dengan penyesuaian chat. Hal ini dapat dilakukan dengan menggunakan file JSON (lihat properti form_card) atau dengan menggunakan class 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)
}

Penampilan lainnya

Anda dapat menyesuaikan tampilan lain seperti ukuran font dan warna latar belakang.

theme.supportTitleLabelFontSize = 30;
theme.supportDescriptionLabelFontSize = 20;
theme.supportPickerViewFontSize = 30;
theme.staticFontSizeInSupportPickerView = YES;

theme.backgroundColor = UIColor.darkGrayColor;
theme.backgroundColorForDarkMode = UIColor.lightGrayColor;

CallKit

Di iOS 10.0 dan yang lebih baru, CallKit diaktifkan untuk semua panggilan.

Dengan CallKit, panggilan masuk dalam aplikasi ditampilkan dengan layar panggilan dan panggilan ditampilkan di histori panggilan ponsel.

Untuk memulai sesi dukungan CCAI Platform baru dari histori panggilan, tambahkan blok berikut ke AppDelegate.m Anda:

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 memungkinkan ikon 40x40 ditampilkan di layar kunci saat menerima panggilan saat perangkat terkunci. Tempatkan gambar di Xcassets Anda yang bernama 'icon-call-kit'.

Mengonfigurasi SDK

Anda dapat menyetel beberapa opsi sebelum memulai SDK.

Lihat class UJETGlobalOptions untuk mengetahui detailnya.

UJETGlobalOptions *options = [UJETGlobalOptions new];
options.fallbackPhoneNumber = @"+18001112222";
options.preferredLanguage = @"en";

[UJET setGlobalOptions:options];

Menampilkan atau menyembunyikan tombol download transkrip

Anda dapat mengonfigurasi SDK untuk menampilkan atau menyembunyikan tombol download transkrip di menu opsi chat dan di layar pasca-chat.

Kode berikut menunjukkan cara mengonfigurasi tombol download transkrip:

typedef NS_OPTIONS(NSUInteger, UJETChatDownloadTranscriptVisibilityOptions) {
    UJETChatDownloadTranscriptVisibilityOptionsShowAll = 0,
    UJETChatDownloadTranscriptVisibilityOptionsHideFromOptionsMenu = 1 << 0,
    UJETChatDownloadTranscriptVisibilityOptionsHideFromPostChatScreen = 1 << 1,
    UJETChatDownloadTranscriptVisibilityOptionsHideAll = UJETChatDownloadTranscriptVisibilityOptionsHideFromOptionsMenu | UJETChatDownloadTranscriptVisibilityOptionsHideFromPostChatScreen
};

@property (nonatomic, assign) UJETChatDownloadTranscriptVisibilityOptions transcriptVisibilityOptions;

Pengganti PSTN

Kami menyediakan penggantian PSTN untuk beberapa situasi:

  • Jaringan seluler sedang offline.

  • Backend aplikasi tidak dapat dijangkau.

  • VoIP tidak tersedia

    • Kondisi jaringan tidak cukup baik untuk terhubung. Lihat properti UJETGlobalOptions.pstnFallbackSensitivity untuk mengetahui detailnya.

    • Terjadi kegagalan selama koneksi karena konfigurasi firewall atau masalah penyedia.

Sebaiknya tetapkan nomor IVR perusahaan Anda di UJETGlobalOptions.fallbackPhoneNumber. Format yang direkomendasikan adalah + diikuti dengan kode negara dan nomor telepon. Misalnya. +18001112222.

Sensitivitas Pengalihan PSTN

Anda dapat menyesuaikan tingkat sensitivitas pemeriksaan kondisi jaringan untuk penggantian PSTN.

@property (nonatomic, assign) float pstnFallbackSensitivity;

Nilai harus dalam rentang 0,0 hingga 1,0. Jika disetel ke 1, panggilan akan selalu terhubung melalui PSTN, bukan VoIP. Latensi maksimum dan nilai minimum bandwidth masing-masing adalah 10000 md dan 10 KB/dtk untuk nilai 0. Misalnya, nilai 0, 5 berarti latensi dan bandwidth minimum masing-masing adalah 5.000 md dan 15 KB/dtk.

Nilai ini dapat dikonfigurasi dengan mengikuti langkah-langkah berikut:

  1. Login ke portal CCAI Platform sebagai administrator.

  2. Buka Setelan > Setelan Developer > Aplikasi Seluler.

  3. Cari bagian Nilai minimum nomor telepon cadangan. Nilai defaultnya adalah 0,85.

  4. Tentukan nilai batas baru.

  5. Klik Save.

Menonaktifkan notifikasi push di tingkat global

Anda dapat menonaktifkan notifikasi push di tingkat global. Menetapkan properti berikut ke false akan melewati semua dependensi notifikasi push dan mencegah notifikasi push menjangkau pengguna akhir:

@property (nonatomic, assign) BOOL allowsPushNotifications;

Mengabaikan mode gelap

Anda dapat mengabaikan mode gelap di CCAI Platform SDK secara khusus dengan properti ini:

@property (nonatomic, assign) BOOL ignoreDarkMode;

Menyembunyikan Status Bar

Anda dapat mengontrol visibilitas status bar dengan properti ini:

  @property (nonatomic, assign) BOOL hideStatusBar;

Secara default, hideStatusBar disetel ke false dan visible.

Melewati survei CSAT

Anda dapat menambahkan tombol yang memungkinkan pengguna melewati survei CSAT. Contoh kode berikut menunjukkan cara menambahkan tombol:

let options = UJETGlobalOptions()
options.skipCsat = true

Menyesuaikan indikator aktivitas

Anda dapat menambahkan animasi pemuat Anda sendiri (di dalam UIView) ke SDK dan mengganti UIActivityIndicatorView default. Terapkan metode ujet_activityIndicator dari UJETDelegate dan tampilkan tampilan yang disesuaikan.

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
}

Jika Anda telah menyetel UIUserInterfaceStyle sebagai Light di Info.plist aplikasi untuk menonaktifkan mode gelap sepenuhnya, Anda dapat mengabaikan properti ini.

Bahasa pilihan

CCAI Platform SDK akan menggunakan urutan prioritas berikut untuk menentukan bahasa pilihan.

  1. Bahasa yang dipilih dari layar pembuka dalam aplikasi.

  2. Bahasa default dipilih dari UJETGlobalOptions. Anda dapat menyetel bahasa default dengan properti preferredLanguage. Kode bahasa yang didukung dapat ditemukan dalam file UJETGlobalOptions.h.

  3. Bahasa perangkat yang dipilih di perangkat (dari Setelan > Umum > Bahasa & Wilayah) akan digunakan, jika didukung oleh aplikasi.

  4. Dialek yang paling dekat dengan bahasa perangkat akan digunakan jika aplikasi tidak mendukung bahasa perangkat, tetapi mendukung dialek induk terdekatnya. Misalnya, jika pengguna memilih bahasa Spanyol Kuba sebagai bahasa di perangkat dan aplikasi tidak mendukung bahasa Spanyol Kuba, tetapi mendukung bahasa Spanyol induk, maka bahasa Spanyol akan digunakan.

  5. Bahasa Inggris akan digunakan jika bahasa perangkat tidak didukung oleh aplikasi.

Mengonfigurasi ikon link pengalihan eksternal

Anda dapat menyesuaikan ikon di saluran link pengalihan eksternal dengan mengupload ikon ke katalog aset aplikasi dan pastikan untuk menggunakan nama ikon yang sama saat membuat link pengalihan eksternal di Setelan > Chat > Link Pengalihan Eksternal > Lihat link > Tambahkan Link Pengalihan di Portal Admin. Jika nama ikon di Admin Portal tidak cocok dengan ikon yang diupload ke aplikasi, SDK akan menggunakan ikon default. Anda dapat melihat link ini untuk mengetahui cara menambahkan gambar di katalog aset.

Pengganti

Anda dapat menggunakan fungsi didHandleUjetError untuk penggantian error yang tidak terduga. Jika Anda tidak menggunakan fungsi ini atau fungsi ini menampilkan false, iOS SDK akan menangani error.

Tabel berikut menunjukkan error yang dipantau oleh fungsi didHandleUjetError:

Jenis error Kode error Deskripsi
networkError 1 Jaringan tidak tersedia.

Catatan: Error ini tidak dipicu saat jaringan tidak tersedia selama sesi chat atau panggilan atau layar pemberian rating.

authenticationError 100 Terjadi error tidak terduga saat autentikasi.
authenticationJwtError 101 Terjadi error yang tidak terduga selama validasi JWT—misalnya, error penguraian.
voipConnectionError 1000 Gagal membuat koneksi ke penyedia VoIP. Callback VoIP SDK menangani hal ini.
voipLibraryNotFound 1001 Sistem mengharapkan panggilan terhubung melalui penyedia VoIP, tetapi tidak dapat menemukannya. Hal ini dapat terjadi jika Anda mengintegrasikan SDK yang salah atau tidak menambahkan library penyedia VoIP ke dependensi Anda.
chatLibraryNotFound 1.100 Terjadi saat sistem tidak dapat menemukan library chat. Hal ini dapat terjadi saat Anda mengintegrasikan SDK yang salah atau tidak menambahkan library chat Twilio ke dependensi Anda.

Contoh kode berikut menunjukkan cara menggunakan fungsi 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
    }
}

Mengirim data kustom ke CRM Anda

Anda dapat mengirim data kustom ke tiket CRM.

Ada dua metode untuk mengirim data kustom:

  1. Metode aman: penandatanganan data yang telah ditentukan sebelumnya dengan JWT.

  2. Metode tidak aman: data yang telah ditentukan dengan JSON biasa (Tidak direkomendasikan).

Menggunakan metode aman untuk mengirim data kustom

Anda harus menerapkan metode penandatanganan. Pertama, Anda dapat menempatkan data kustom di sisi klien, dan mengirimkannya ke server untuk ditandatangani. Di server, Anda dapat menambahkan data tambahan dengan formulir yang ditentukan dan menandatanganinya dengan company.secret Anda, lalu menampilkannya dengan 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];
    }
}

Menggunakan metode yang tidak aman untuk mengirim data kustom

Metode ini tidak direkomendasikan karena berpotensi menimbulkan kerentanan yang dapat membuka aplikasi Anda terhadap serangan man-in-the-middle. Jika Anda memilih untuk menggunakan metode ini, kami tidak bertanggung jawab atas eksposur keamanan dan potensi kerusakan yang mungkin terjadi. Sebaiknya gunakan metode aman yang dijelaskan sebelumnya untuk mengirim data kustom di aplikasi Anda. Atau, Anda dapat memulai iOS SDK dengan instance UJETCustomData. Dalam hal ini, delegasi signPayload untuk UJETPayloadCustomData hanya boleh memanggil 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];

Menggunakan data kustom yang tidak bertanda untuk mengirim transkrip chat eksternal

Anda dapat mengirim transkrip chat ke SDK saat dimulai dengan data kustom yang tidak bertanda tangan dengan memanggil metode setExternalChatTransfer: atau setExternalChatTransferWithDictionary: untuk menetapkan data JSON dengan NSString atau NSDictionary.

UJETCustomData *customData = [UJETCustomData new];
[customData setExternalChatTransfer:jsonString];

UJETStartOptions *options = [UJETStartOptions new];
options.unsignedCustomData = customData;

[UJET startWithOptions:options];

Format JSON:

  • greeting_override: string

  • agen: kamus

    • name: string

    • avatar: string [URL avatar agen, opsional]

  • transkrip: array

    • pengirim: string ["end_user" atau "agent"]

    • timestamp: string [yaitu "2021-03-15 12.00.00Z"]

    • content: array

      • type: string [salah satu dari text, media]

      • text: string [wajib untuk jenis teks]

      • media: dictionary [wajib untuk jenis media]

        • type: string [salah satu dari image, video]

        • url: string [URL publik yang mengarah ke file media]

Contoh 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"
        }
      ]
    }
  ]
}

Anda dapat menggunakan Markdown pada jenis teks. Sintaksis yang didukung mencakup miring, tebal, daftar berbutir, hyperlink, dan garis bawah (--text--).

Contoh data kustom { :#example-of-custom-data }

JSON dienkode ke JWT

File JSON harus memvalidasi JWT. Objek data kustom adalah nilai kunci 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"
    }
  }
}

Setiap data mirip dengan format objek JSON dan harus berisi kunci, nilai, jenis, dan label.

Kunci adalah ID unik untuk data. label adalah nama tampilan di halaman CRM type adalah jenis nilai.

  • string

    • String JSON
  • angka

    • integer, float
  • tanggal

    • Format stempel waktu Unix UTC dengan 13 digit. (berisi milidetik)
  • URL

    • Format URL HTTP
Contoh CRM

Lokasi

Gunakan framework CoreLocation. Untuk mengetahui detail selengkapnya, lihat AppDelegate.m.

Versi OS Perangkat
[customData set:@"os_version" label:@"OS Version" stringValue:[[UIDevice currentDevice] systemVersion]];

Mencegah tampilan data kustom

Anda dapat menggunakan properti invisible_to_agent dengan objek data kustom untuk mencegah data kustom bertanda atau tidak bertanda ditampilkan di adaptor agen. Dalam contoh sebelumnya, nomor jaminan sosial pengguna akhir tidak ditampilkan di adaptor agen karena "invisible_to_agent" : true disertakan dalam objek ssn.

Jika Anda menyertakan properti "invisible_to_agent" : true dengan objek data kustom, Anda dapat mengharapkan perilaku berikut:

  • Data kustom disertakan dalam file metadata sesi.
  • Data kustom tidak disertakan dalam catatan CRM.

Untuk mengetahui informasi selengkapnya, lihat Melihat data sesi di adaptor agen.

Properti data yang dicadangkan

Anda dapat mengirim properti data yang dicadangkan ke Contact Center AI Platform (CCAI Platform) sebagai data kustom bertanda tangan saat sesi dimulai. Untuk mengetahui informasi selengkapnya, lihat Mengirim properti data yang dicadangkan.

Berikut adalah contoh properti data yang dicadangkan dalam data kustom:

  {
    "custom_data": {
      "reserved_verified_customer": {
        "label": "Verified Customer",
        "value": "VERIFIED_CUSTOMER_BOOLEAN": ,
        "type": "boolean"
      },
      "reserved_bad_actor": {
        "label": "Bad Actor",
        "value": "VERIFIED_BAD_ACTOR_BOOLEAN": ,
        "type": "boolean"
      },
      "reserved_repeat_customer": {
        "label": "Repeat Customer",
        "value": "REPEAT_CUSTOMER_BOOLEAN": ,
        "type": "boolean"
      }
    }
  }
  

Ganti kode berikut:

  • VERIFIED_CUSTOMER_BOOLEAN: Benar (True) jika Anda menganggap pengguna akhir ini sebagai pelanggan yang sah.
  • VERIFIED_BAD_ACTOR_BOOLEAN: Benar jika Anda menganggap pengguna akhir ini berpotensi menjadi pihak tidak bertanggung jawab.
  • REPEAT_CUSTOMER_BOOLEAN: Benar (True) jika Anda telah menentukan bahwa pengguna akhir ini telah menghubungi pusat kontak Anda sebelumnya.

Menyesuaikan Alur

Memutus koneksi Platform CCAI untuk menangani peristiwa aplikasi Host

// CCAI Platform is connected
...
// An event has come
[UJET disconnect:^{
  // Handle an event
}];

Menunda panggilan atau chat masuk CCAI Platform

Menerapkan metode delegasi untuk menangani peristiwa masuk
- (BOOL)shouldConnectUjetIncoming:(NSString *)identifier forType:(UjetIncomingType)type {
  if (weDoingSomething) {
    // save identifier and type
    return NO; // postpone
  } else {
    return YES;
  }
}
Menghubungkan acara yang ditunda
[UJET connect:identifier forType:UjetIncomingTypeCall];

Menyiapkan Deep link

Hal ini memungkinkan agen dalam panggilan PSTN menggunakan tindakan smart melalui SMS, baik saat pengguna akhir memiliki atau tidak memiliki aplikasi.

Buka Setelan > Pengelolaan Operasi > Aktifkan Kirim SMS untuk Mendownload Aplikasi di portal Platform CCAI.

Anda dapat menyetel URL Aplikasi dengan halaman web Anda (yaitu, https://your-company.com/support) setelah mengonfigurasi Universal Link atau skema URL kustom. Anda dapat memilih salah satu cara.

Menerapkan metode delegasi untuk menangani deep link

Link universal dan URL kustom terlihat seperti https://your-company.com/support?call_id=x&nonce=y dan your-company://support?call_id=x&nonce=y. Masukkan salah satu link Anda tanpa parameter kueri di bagian URL Aplikasi di Admin Portal. Misalnya, masukkan your-company://support jika menggunakan skema URL kustom.

Dalam metode delegasi, pastikan untuk hanya memanggil [UJET start] saat jalur dan parameter URL di link universal atau URL kustom khusus untuk Platform CCAI.

- (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];
  }
  ...
}

Jika aplikasi Anda mengadopsi UIWindowSceneDelegate, tambahkan cuplikan kode ini:

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: [:])
    }
}

Untuk mengetahui informasi selengkapnya, lihat kode contoh dari file UJETObject+DeepLink.

Mengamati peristiwa CCAI Platform

Kami memposting peristiwa berikut melalui NSNotificationCenter.defaultCenter. Anda dapat mendengarkannya dan menyesuaikan alur Anda bergantung pada kasus penggunaan, misalnya, tata letak keyboard kustom.

  • UJETEventEmailDidClick

    • Data Menu Antrean
  • UJETEventEmailDidSubmit

    • Data Menu Antrean

    • has_attachment: (NSNumber) @YES, @NO

  • UJETEventSessionViewDidAppear

    • type: @"call", @"chat"

    • timestamp: (NSString) ISO 8601

  • UJETEventSessionViewDidDisappear

    • type: @"call", @"chat"

    • timestamp: (NSString) ISO 8601

  • UJETEventSessionDidCreate

    • Data Sesi
  • UJETEventSessionDidEnd

    • Data Sesi

    • agent_name: (NSString) null jika agen tidak bergabung

    • durasi: (NSNumber) hanya untuk panggilan

    • ended_by: (NSString)

      • type=call: @"agent", @"end_user"

      • type=chat: @"agent", @"end_user", @"timeout", @"dismissed"

  • UJETEventSdkDidTerminate

  • UJETEventPostSessionOptInDidSelected

    • opt_in_selected: (NSString) @"Yes", @"No"

Data Peristiwa

Metadata
  • aplikasi: @"iOS"

  • app_id: (NSString) ID paket

  • app_version: (NSString)

  • perusahaan: (NSString) subdomain

  • device_model: (NSString)

  • device_version: (NSString)

  • sdk_version: (NSString)

  • timestamp: (NSString) ISO 8601

Data Menu Antrean
  • Metadata

  • menu_id: NSString

  • menu_key: NSString, nullable

  • menu_name: NSString

  • menu_path : NSString

Data Sesi
  • Data Menu Antrean

  • session_id: NSString

  • type: @"call", @"chat"

  • end_user_identifier: NSString

Menyiapkan Berbagi Layar

Jika Anda ingin menggunakan fitur Berbagi Layar, integrasikan UJETCobrowseKit.xcframework.

CocoaPods: Tambahkan subspec berikut ke target aplikasi Anda.

    ruby
target 'MyApp' do
  pod 'UJET'
  pod 'UJET/Cobrowse'
end

Carthage: Tambahkan baris berikut di Cartfile:

binary "https://sdk.ujet.co/ios/UJETKit.json"

SwiftPM: Pilih produk UJET dan UJETCobrowse, lalu tambahkan ke target aplikasi Anda.

Lalu, tetapkan properti UJETGlobalOptions.cobrowseKey.

swift
let options = UJETGlobal
Options()options.cobrowseKey = cobrowseKey

UJET.setGlobalOptions(options)

Berbagi Layar perangkat penuh (opsional)

Berbagi layar perangkat penuh memungkinkan agen dukungan Anda melihat layar dari aplikasi di luar aplikasi Anda sendiri. Hal ini sering kali berguna jika agen dukungan perlu memeriksa status setelan sistem, atau perlu melihat pengguna bernavigasi di antara beberapa aplikasi. Jika tidak menginginkan fitur ini, Anda dapat melewati bagian ini.

Untuk menyesuaikan dialog izin Berbagi Layar, Anda perlu menerapkan protokol UJETCobrowseAlertProvider di class penyedia. Dalam implementasi ini, tampilkan UIViewController kustom atau objek lain yang diwarisi UIViewController melalui metode protokol masing-masing. UIViewController harus memiliki dua tombol, satu untuk menerima, dan satu untuk menolak.

Setelah mendapatkan izin, teruskan ke SDK kami dengan memanggil penutupan consentStatus. UIViewController dari cobrowseFullDeviceRequestAlert. Delegasi harus berisi RPSystemBroadcastPickerView dengan judul (lihat di contoh kode di bawah) dan harus memiliki tombol tolak lainnya. Panggil penutupan yang ditutup saat tombol tolak diklik.

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
    }
}

Pengontrol tampilan kustom harus memiliki penutupan untuk meneruskan status izin ke 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)
        }
    }
}

Pengontrol tampilan kustom untuk pemberitahuan permintaan perangkat penuh harus memiliki RPSystemBroadcastPickerView dan penutupan untuk meneruskan status dismiss ke 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?()
        }
    }
}

Jangan lupa untuk meneruskan penyedia ini ke SDK kami melalui API berikut:

let provider = CobrowseAlertProvider()
UJET.setCobrowseAlertProvider(provider)

Ekstensi Siaran

Fitur ini memerlukan penambahan Ekstensi Siaran.

  1. Buka project Xcode Anda.

  2. Buka File > Target.

  3. Pilih Broadcast Upload Extension.

  4. Masukkan Nama untuk target.

  5. Hapus centang pada Sertakan Ekstensi UI.

  6. Buat target, dengan mencatat ID paket-nya.

  7. Ubah target SDK Ekstensi Siaran Anda ke iOS 12.0 atau yang lebih tinggi.

Mengintegrasikan SDK

CocoaPods: Tambahkan subspec berikut ke target ekstensi Anda:

target 'MyApp' do
  pod 'UJET'
  pod 'UJET/Cobrowse'
end
target 'MyAppExtension' do
  pod 'UJET/CobrowseExtension'
end

Jika Anda menggunakan SwiftPM, pilih produk UJETCobrowseExtension dan tambahkan ke target ekstensi.

Menyiapkan berbagi keychain

Aplikasi dan ekstensi aplikasi yang Anda buat sebelumnya harus berbagi beberapa rahasia melalui Keychain iOS. Mereka melakukannya menggunakan grup Keychain mereka sendiri sehingga mereka diisolasi dari Keychain aplikasi Anda lainnya.

Di target aplikasi dan target ekstensi, tambahkan hak Berbagi Keychain untuk grup keychain io.cobrowse.

Tambahkan ID paket ke plist Anda

Ambil ID paket ekstensi yang Anda buat sebelumnya dan tambahkan entri berikut di Info.plist aplikasi Anda (Catatan: bukan di Info.plist ekstensi), ganti ID paket berikut dengan ID Anda sendiri:

xml
<key>CBIOBroadcastExtension</key>
<string>your.app.extension.bundle.ID.here</string>

Menerapkan ekstensi

Xcode akan menambahkan file SampleHandler.m dan SampleHandler.h (atau SampleHander.swift) sebagai bagian dari target yang Anda buat sebelumnya. Ganti konten file dengan kode berikut:

Swift: Pilih produk UJETCobrowseExtension dan tambahkan ke target ekstensi Anda:

import CobrowseIOAppExtension
class SampleHandler: CobrowseIOReplayKitExtension {
}

ObjC

objc// SampleHandler.h
@import CobrowseIOAppExtension;
@interface SampleHandler : CobrowseIOReplayKitExtension
@end// SampleHandler.m
#import "SampleHandler.h"
@implementation SampleHandler
@end

Buat dan jalankan aplikasi Anda

Anda kini siap membangun dan menjalankan aplikasi. Kemampuan perangkat penuh hanya tersedia di perangkat fisik, dan tidak akan berfungsi di Simulator iOS.

Minimalkan SDK

SDK Contact Center AI Platform dapat diminimalkan saat sesi chat atau panggilan sedang berlangsung. Hal ini dapat berguna saat Anda ingin mengarahkan pengguna kembali ke aplikasi Anda setelah peristiwa SDK diterima, seperti kartu konten yang diklik. Untuk meminimalkan SDK dan mengarahkan pengguna kembali ke aplikasi Anda, Anda dapat menggunakan:

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
}

Pemecahan masalah

Penolakan pengiriman aplikasi

Pengiriman aplikasi ditolak karena menyertakan framework CallKit di wilayah China.

Jika aplikasi Anda ditolak oleh Apple karena alasan ini, cukup tinggalkan komentar karena sistem dirancang untuk menonaktifkan framework CallKit untuk wilayah China pada panggilan VoIP. Perubahan ini berlaku mulai versi SDK 0.31.1.

Ukuran SDK terlalu besar

Jika ukuran SDK terlalu besar dan sulit dilacak di GitHub

Dalam artikel ini, mereka menawarkan dua pilihan. Sebaiknya gunakan Git LFS.

Jika Anda tidak menggunakan Bitcode, maka menghapus bitcode dari biner dapat menjadi opsi lain. Jalankan perintah ini di folder UJETKit.xcframework.

xcrun bitcode_strip -r UJET -o UJET

dyld: Error Library tidak dimuat

Tambahkan @executable_path/Frameworks di Runpath Search Paths dari Target > Build Settings > Linking.

Pengiriman aplikasi di iTunes Connect

Apple mungkin mengajukan pertanyaan berikut selama proses peninjauan karena mode latar belakang Voice over IP diaktifkan:

Dapatkah pengguna menerima panggilan VoIP di aplikasi Anda?

Jawab Ya untuk pertanyaan tersebut.

Notifikasi Pemberitahuan tidak tersedia saat memulai SDK

Periksa hal-hal berikut:

  • Gunakan perangkat sebenarnya, bukan simulator.

  • Aktifkan Notifikasi push dan Background Modes > Voice over IP capability.

Jika hal tersebut tidak membantu, coba buat dengan profil penyediaan distribusi (Ad-hoc atau Apple Store).

Menguji notifikasi push terhadap aplikasi pengujian Anda

Siapkan sertifikat VoIP dan token perangkat perangkat Anda.

Di portal Platform CCAI, lihat bagian Debug Notifikasi Push di menu Setelan > Setelan Developer.

Jika sudah menyetel sertifikat untuk APNS, Anda tidak perlu memasukkan sertifikat lagi.

Masukkan sertifikat Anda (opsional) dan periksa apakah sandbox atau tidak (opsional) dan masukkan token perangkat push notification aplikasi pengujian Anda.

Memulai percakapan baru memerlukan waktu lebih dari 30 detik

Periksa apakah Anda merespons metode delegasi data kustom. Anda harus menampilkan data kustom yang valid saat diminta atau hanya menampilkan nil di blok keberhasilan.

Gunakan cuplikan kode ini sebagai contoh konfigurasi:

public func signPayload(_ payload: [AnyHashable: Any]?, payloadType: UjetPayloadType, success: (String?) -> Void, failure: (Error?) -> Void)
{
    if payloadType == .customData {
        success(nil)
    }
}