L'SDK mobile Contact Center AI Platform (CCAI Platform) per il sistema operativo Apple iOS offre la possibilità di incorporare l'esperienza mobile di CCAI Platform nelle applicazioni mobile iOS.
Requisiti
L'SDK mobile per iOS presenta i seguenti requisiti:
- iOS 12.0 o versioni successive
Recuperare le credenziali aziendali
Accedi al portale Contact Center AI Platform (CCAI Platform) utilizzando le credenziali amministratore.
Vai a Impostazioni > Impostazioni sviluppatore.
In Chiave azienda e Codice segreto, prendi nota della chiave azienda e del codice segreto azienda.
Per iniziare
Di seguito è riportata una guida su come iniziare a utilizzare l'SDK mobile iOS di CCAI Platform.
Installazione
Per iniziare, devi installare l'SDK per iOS.
Scarica l'app di esempio
Scarica l'app di esempio per iOS.
Vai alla cartella e installa le dipendenze utilizzando CocoaPods:
$ pod install --project-directory=ExampleAppPer configurare rapidamente le impostazioni del progetto, esegui uno script shell:
$ ./setup.shIn alternativa, puoi modificare manualmente le impostazioni del progetto seguendo questi passaggi:
Apri
ExampleApp.xcworkspace.Sostituisci i valori
UJETCompanyKeyeUJETCompanySecretinInfo.plistcon i valori di Chiave aziendale e Codice segreto aziendale della pagina Impostazioni > Impostazioni sviluppatore nel portale della piattaforma CCAI.Sostituisci il valore
UJETSubdomaininInfo.plistcon il sottodominio nell'URL del portale della piattaforma CCAI. Il sottodominio precede direttamente.ujet.comnell'URL, ad esempioyour-subdomaininhttps://your-subdomain.ujet.com/settings/developer-setting.
Integrare nel progetto
L'integrazione dell'SDK per iOS con la tua applicazione dipende dal tuo ambiente di sviluppo.
Swift Package Manager
Aggiungi lo Swift Package per l'SDK iOS.
Nelle impostazioni di build, inserisci -ObjC in Other Linker Flags (Altri flag del linker).
A partire dall'ultima release di Xcode (attualmente la 13.2), è presente un problema noto con l'utilizzo di framework binari distribuiti tramite Swift Package Manager. La soluzione alternativa corrente a questo problema consiste nell'aggiungere una fase di esecuzione script alle fasi di build del progetto Xcode. Questa fase di esecuzione dello script deve essere successiva alla fase di build Incorpora framework. Questa nuova fase Run Script deve contenere il seguente codice:
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
Aggiungi la seguente riga al Podfile:
pod 'UJET', :podspec => 'https://sdk.ujet.co/ios/x.y.z/ujet.podspec' #specific version x.y.zEsegui l'installazione del pod. Se l'SDK per iOS è già stato integrato, esegui pod update CCAI Platform.
Cartagine
Google Cloud consiglia di utilizzare un gestore delle dipendenze o l'integrazione manuale perché una dipendenza della piattaforma CCAI non supporta Carthage. Per farlo, aggiungi le seguenti righe:
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
Integrazione manuale
Questo non è supportato: https://github.com/twilio/conversations-ios/issues/12.
binary https://raw.githubusercontent.com/twilio/conversations-ios/master/twilio-convo-ios.json
Esegui
carthage bootstrap --use-xcframeworks(ocarthage update --use-xcframeworks(se stai aggiornando le dipendenze).Scarica
UJETKit.xcframework,UJETFoundationKit.xcframework,UJETChatRedKit.xcframework,UJETChatBlueKit.xcframework,UJETTwilioCallKit.xcframeworke tutte le dipendenzeTwilioVoice.xcframeworkeTwilioConversationsClient.xcframework.Aggiungi UJETKit.xcframework al tuo target trascinandolo nella sezione Frameworks, Libraries, and Embedded Content (Framework, librerie e contenuti incorporati).
Ripeti i passaggi 2 e 3 per tutte le dipendenze del passaggio 1.
Nelle impostazioni di build, imposta
-ObjCsuOther Linker Flags.Aggiungi
libc++.tbdcome dipendenza nella sezioneLinked Frameworksdella destinazione.
Se vuoi creare l'SDK manualmente con il progetto di esempio, segui i passaggi della sezione successiva.
Compila manualmente l'SDK con il progetto di esempio
Segui questi passaggi nell'ordine:
Scarica tutti i framework, inclusi
UJETKit.xcframeworke altre dipendenze.Crea la cartella CCAI Platform nella radice del progetto ed estrai tutti i framework.
Seleziona la build e il target
Objc-ManualoSwift-Manual.
Framework di importazione
Le sezioni seguenti forniscono istruzioni su come importare il framework.
Progetto Objective-C
@import UJETKit;
Swift Project
swiftimport
UJETimport UJETKit
Inizializza l'SDK
Inizializza CCAI Platform con UJET_COMPANY_KEY e 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;
}
Puoi modificare il livello di log da verbose a error. Il livello di log predefinito è
UjetLogLevelInfo.
[UJET.setLogLevel:UjetLogLevelVerbose];
Autenticazione degli utenti finali
Accedi all'SDK iOS tramite l'app per iOS.
Per assicurarci che l'utente finale sia autenticato, stiamo introducendo il meccanismo di firma JWT.
L'SDK per iOS chiederà di firmare il payload quando è necessaria l'autenticazione. Se la firma va a buon fine, l'applicazione scambia il JWT firmato con il token di autenticazione dell'utente finale. Il blocco di successo o errore deve essere chiamato prima che il delegato restituisca un valore.
Per l'utente anonimo (identificatore = nil), l'applicazione creerà un UUID per l'utente. Se in un secondo momento l'utente viene autenticato con un identificatore, l'applicazione tenterà di unire i due utenti in base all'UUID.
In UJETObject.h dal progetto di esempio:
@import UJETKit;
@interface UJETObject : NSObject <UJETDelegate>
Implementa 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;
}
Ti consigliamo vivamente di firmare il payload dal server delle applicazioni, non nel client.
Questo esempio utilizza la firma locale a scopo di test. Vedi
signDataInRemote: success: failure: nel file UJETObject.m.
Per maggiori informazioni, vedi Autenticazione utente finale dell'SDK.
Configurare le notifiche push
L'applicazione invia notifiche push per richiedere azioni intelligenti come verifica e foto, nonché per segnalare una chiamata in arrivo. L'applicazione richiede il salvataggio di due diversi tipi di certificati (VoIP e APN) nel portale amministratore.
Prepara il certificato dei servizi VoIP
La documentazione di riferimento è disponibile per la notifica push VoIP di Apple.
Crea e scarica il certificato VoIP dal sito per sviluppatori Apple.
Fai doppio clic sul certificato per aggiungerlo al portachiavi.
Avvia l'applicazione Accesso Portachiavi sul Mac.
Scegli la categoria I miei certificati nella barra laterale a sinistra.
Fai clic con il tasto destro del mouse su Servizi VoIP: il certificato your.app.id.
Nel menu popup, scegli Esporta.
Salvalo come cert.p12 senza proteggerlo con una password lasciando il campo password vuoto.
Esegui questo comando nel terminale.
openssl s_client -connect gateway.push.apple.com:2195 -cert cert.pem -debug -showcertLa parte superiore di cert.pem è il certificato e la parte inferiore è la chiave privata.
Verifica che il certificato funzioni con il server di notifiche push di Apple.
openssl s_client -connect gateway.push.apple.com:2195 -cert cert.pem -debug -showcertsIn caso di esito positivo, dovrebbe restituire:
--- 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) ---Accedi al portale della piattaforma CCAI con le credenziali di amministratore e vai a Impostazioni > Impostazioni sviluppatore > App mobile.
Compila il certificato nella sezione "Certificato dei servizi VoIP" e salva. Assicurati di includere i delimitatori (
-----BEGIN-----e-----END-----) per il certificato e la chiave privata.Seleziona la casella di controllo Sandbox se esegui un'app con un profilo di provisioning di sviluppo, ad esempio il debug in Xcode. Se la tua app è archiviata per Ad hoc o App Store e utilizza un profilo di provisioning di distribuzione, allora deseleziona la casella di controllo Sandbox.
Prepara SSL per il servizio di notifiche push di Apple
La procedura è simile a quella per i certificati di servizio VoIP. In questo caso viene utilizzato il certificato SSL del servizio di notifiche push di Apple (sandbox e produzione). Per indicazioni su come creare il certificato, consulta la documentazione del server di notifiche remote di Apple.
Integrazione delle notifiche push
In 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];
Aggiungi i seguenti metodi delegati nell'implementazione del file di protocollo UIApplicationDelegate:
Stampa il token del dispositivo per testare le notifiche 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];
}
}
Attivare le notifiche push
Seleziona il target e apri la scheda Funzionalità.
Attiva l'opzione Notifiche push.
Testare le notifiche push
Le sezioni seguenti forniscono indicazioni su come testare le notifiche push.
Sezione di debug delle notifiche push
Nel portale amministratore, vai a Impostazioni > Impostazioni sviluppatore. In questa pagina, trova la sezione intitolata Debug notifiche push:

Copia e incolla il token del dispositivo nell'area di testo a destra e seleziona l'app mobile corretta.
Recuperare il token del dispositivo
Una stringa di token dispositivo di esempio ha il seguente aspetto:
7db0bc0044c8a203ed87cdab86a597a2c43bf16d82dae70e8d560e88253364b7
Le notifiche push vengono in genere impostate nella classe conforme al protocollo UIApplicationDelegate o PKPushRegistryDelegate. A un certo punto, il token del dispositivo è disponibile. Puoi stamparlo prima di passarlo all'SDK per iOS. Per ottenere il token del dispositivo, utilizza lo snippet di codice.
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]);
}
Risultato
Dopo aver inserito il file PEM del certificato e il token del dispositivo, fai clic sul pulsante.
Se la notifica push di test è stata inviata correttamente, il risultato mostrerà il messaggio Notifica push configurata correttamente.
La notifica push non è garantita al 100%, a seconda della connessione di rete del dispositivo.
Configurazioni del progetto
Le sezioni seguenti descrivono le modifiche necessarie per configurare il progetto.
Funzionalità
Nelle impostazioni del target, attiva le seguenti funzionalità:
Notifiche push
Modalità background (controlla questi elementi)
Audio e AirPlay
Voice over IP
Info.plist
Per proteggere la privacy degli utenti, qualsiasi app per iOS collegata a partire da iOS 10.0 che accede a uno qualsiasi dei microfoni, della libreria di foto e della fotocamera del dispositivo deve dichiarare l'intenzione di farlo. Includi le seguenti chiavi con un valore stringa nel file Info.plist della tua app e fornisci una stringa di scopo per questa chiave. Se la tua app tenta di accedere a uno qualsiasi dei microfoni, alla raccolta foto e alla fotocamera del dispositivo senza una stringa di scopo corrispondente, l'app viene chiusa.
NSMicrophoneUsageDescription: Consente l'accesso al microfono per chiamare e parlare con i team di assistenza o risoluzione dei problemi e per inviare video con audio correlato a richieste di informazioni sui prodotti.
NSCameraUsageDescription: consente l'accesso alla fotocamera per consentire al cliente di scattare e inviare foto relative alla sua richiesta di assistenza clienti.
NSPhotoLibraryUsageDescription: consente al cliente di inviare foto relative alla sua richiesta di assistenza clienti.
NSFaceIDUsageDescription: consente l'accesso alla verifica tramite Face ID.
Avvia l'SDK iOS
Aggiungi la seguente riga nel punto in cui vuoi avviare l'SDK per iOS:
[UJET startWithOptions:nil];
Puoi anche avviare l'SDK per iOS da un punto specifico del menu con questo tasto utilizzando un punto di accesso diretto:
UJETStartOptions *option = [[UJETStartOptions alloc] initWithMenuKey:@"MENU_KEY"];
[UJET startWithOptions:option];
Il menuKey può essere creato creando un punto di accesso diretto (DAP). I
passaggi seguenti forniscono indicazioni su come creare un DAP:
Accedi al portale della piattaforma CCAI con le credenziali di amministratore.
Vai a Impostazioni > Coda.
Seleziona una coda dalla struttura del menu.
Seleziona Crea punto di accesso diretto.
Inserisci la chiave nel modulo di testo.
Fai clic su Salva.

Cancella la cache locale se i dati utente sono stati aggiornati
Memorizziamo nella cache il token di autenticazione nel portachiavi per riutilizzarlo e ridurre la frequenza delle richieste di firma del payload dall'app host. L'SDK lo utilizzerà fino alla scadenza o alla revoca tramite la chiamata clearUserData. L'app host è responsabile della revoca di questa cache ogni volta che i dati relativi all'utente sono stati modificati o aggiornati, ad esempio un evento di disconnessione.
[UJET clearUserData];
Verifica la presenza di una sessione esistente prima di avviare Contact Center AI Platform
Prima di iniziare una sessione, controlla che non ce ne sia una in corso. Ciò è particolarmente importante quando l'ID utente è cambiato.
[UJET getStatus];
Se esiste una sessione, dobbiamo chiedere all'utente di riprenderla o annullare l'azione:
if ([UJET getStatus] != UjetStatusNone) {
// Display alert to cancel login or resume existing session
}
Personalizza
In UJETGlobalTheme.h sono elencate diverse opzioni per il tema dell'SDK.
Imposta il tema dopo [UJET initialize], ad esempio:
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];

Il nome dell'azienda viene recuperato da Admin Portal > Impostazioni > Dettagli del Centro assistenza > Nome visualizzato.
Puoi impostare l'immagine del logo anziché il nome dell'azienda nel seguente modo:
theme.companyImage = [UIImage imageNamed:@"logo"];

Se l'immagine è troppo grande, verrà ridimensionata per adattarsi all'area.
Stringhe
Puoi anche personalizzare le stringhe sostituendo il valore. Ad esempio, inserisci questa coppia chiave-valore nel file Localizable.strings:
"ujet_greeting_title" = "Title";
"ujet_greeting_description" = "Description";

Le stringhe personalizzabili disponibili sono elencate nel file ujet.strings.
Modalità Buio
Puoi specificare una tonalità del colore che preferisci per la modalità Buio per migliorare la leggibilità dei caratteri.
@property (nonatomic, strong) UIColor \*tintColorForDarkMode;
Se non imposti la proprietà, per la modalità buio verrà utilizzato UJETGlobalTheme.tintColor. Ti consigliamo di impostare questa proprietà se la tua app supporta la modalità Buio. Consulta i seguenti articoli Apple su come scegliere il colore di tinta giusto per la modalità Buio:
Tema della chat
Per personalizzare la schermata della chat, puoi utilizzare una stringa JSON o ogni classe di temi.
Per riferimento, consulta l'app di esempio e rimuovi il commento dal metodo 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)
}

Tema delle schede dei contenuti
Puoi aggiungere la personalizzazione per le schede dei contenuti insieme alla personalizzazione della chat.
Puoi farlo utilizzando il file JSON (vedi la proprietà content_card) o la classe 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)
}

Tema della scheda del modulo
Puoi aggiungere la personalizzazione per le schede dei moduli insieme alla personalizzazione della chat. Per farlo, utilizza il file JSON (vedi form_card property) o la classe 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)
}
Configurazione del modulo web
Per configurare la funzionalità del modulo web, implementa il metodo ujetWebFormDidReceive del protocollo UJETDelegate. Questo metodo riceve un evento (un dizionario
FormMessageReceivedEvent) come parametro, contenente informazioni
relative al modulo. Il dizionario degli eventi (FormMessageReceivedEvent) include la seguente struttura JSON:
{
"type": "form_message_received",
"smart_action_id": 1,
"external_form_id": "external_foobar"
"signature": "4868a7e1dcb5..."
}
Per gestire l'evento:
Estrai le informazioni pertinenti dal dizionario degli eventi (
smart_action_id,external_form_idesignature).Genera un URI del modulo e una firma per i dati del modulo.
Trasferisci i dati del modulo all'SDK come dizionario
FormDataEventutilizzandocompletion closure.Se si verifica un errore durante la generazione dell'URI/della firma, richiama il callback utilizzando
callback.onError()conError.
Il dizionario (FormDataEvent) passato all'SDK deve avere la seguente
struttura:
{
"type": "form_data",
"signature": "4868a7e1dcb5...",
"data": {
"smart_action_id":1,
"external_form_id": "form_id",
"uri":"foobar"
}
}
La firma (HMAC-SHA:256) deve essere generata utilizzando data e firmata con la chiave segreta condivisa. Le chiavi oggetto dei dati devono essere ordinate alfabeticamente prima
di generare le firme e lo stesso data deve essere inviato all'SDK.
Trasferimento post-sessione
Puoi aggiungere la personalizzazione per la sessione post insieme alla personalizzazione della chat.
Puoi farlo utilizzando il file JSON (vedi la proprietà post_session) o la classe UJETChatPostSessionVaTheme. La larghezza del bordo può
essere solo 0 o 1 e se non vuoi differenziare l'esperienza VA post-sessione,
puoi impostare containerColor bianco e il bordo su 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 delle azioni di chat
Puoi aggiungere la personalizzazione per il menu delle azioni della chat insieme alla personalizzazione della chat. Puoi farlo utilizzando il file JSON (vedi la proprietà
form_card) o la classe 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)
}
Altre apparizioni
Puoi personalizzare altri aspetti, come la dimensione del carattere e il colore dello sfondo.
theme.supportTitleLabelFontSize = 30;
theme.supportDescriptionLabelFontSize = 20;
theme.supportPickerViewFontSize = 30;
theme.staticFontSizeInSupportPickerView = YES;
theme.backgroundColor = UIColor.darkGrayColor;
theme.backgroundColorForDarkMode = UIColor.lightGrayColor;

CallKit
Su iOS 10.0 e versioni successive, CallKit è attivo per tutte le chiamate.
Con CallKit, viene visualizzata una chiamata in arrivo nell'app con la schermata della chiamata e la chiamata viene visualizzata nella cronologia chiamate del telefono.
Per avviare una nuova sessione di assistenza per CCAI Platform dalla cronologia chiamate, aggiungi il seguente blocco al file 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 consente di visualizzare un'icona 40x40 sulla schermata di blocco quando si riceve una chiamata mentre il dispositivo è bloccato. Inserisci un'immagine in Xcassets denominata "icon-call-kit".
Configura l'SDK
Puoi impostare diverse opzioni prima di avviare l'SDK.
Per maggiori dettagli, consulta la classe UJETGlobalOptions.
UJETGlobalOptions *options = [UJETGlobalOptions new];
options.fallbackPhoneNumber = @"+18001112222";
options.preferredLanguage = @"en";
[UJET setGlobalOptions:options];
Mostrare o nascondere il pulsante per scaricare la trascrizione
Puoi configurare l'SDK per mostrare o nascondere il pulsante di download della trascrizione nel menu delle opzioni della chat e nella schermata post-chat.
Il seguente codice mostra come configurare il pulsante per scaricare la trascrizione:
typedef NS_OPTIONS(NSUInteger, UJETChatDownloadTranscriptVisibilityOptions) {
UJETChatDownloadTranscriptVisibilityOptionsShowAll = 0,
UJETChatDownloadTranscriptVisibilityOptionsHideFromOptionsMenu = 1 << 0,
UJETChatDownloadTranscriptVisibilityOptionsHideFromPostChatScreen = 1 << 1,
UJETChatDownloadTranscriptVisibilityOptionsHideAll = UJETChatDownloadTranscriptVisibilityOptionsHideFromOptionsMenu | UJETChatDownloadTranscriptVisibilityOptionsHideFromPostChatScreen
};
@property (nonatomic, assign) UJETChatDownloadTranscriptVisibilityOptions transcriptVisibilityOptions;
Fallback PSTN
Forniamo il fallback PSTN per diverse situazioni:
La rete mobile è offline.
Il backend dell'applicazione non è raggiungibile.
Il VoIP non è disponibile
Le condizioni della rete non sono sufficienti per la connessione. Per informazioni dettagliate, consulta la proprietà UJETGlobalOptions.pstnFallbackSensitivity.
Si è verificato un errore durante la connessione a causa della configurazione del firewall o di un problema con il provider.
Ti consigliamo di impostare il numero IVR della tua azienda in UJETGlobalOptions.fallbackPhoneNumber. Il formato consigliato è + seguito da codice paese e numero di telefono. Ad es. +18001112222.
Sensibilità al fallback RTG
Puoi regolare il livello di sensibilità del controllo delle condizioni di rete per il fallback PSTN.
@property (nonatomic, assign) float pstnFallbackSensitivity;
Il valore deve essere compreso tra 0,0 e 1,0. Se impostato su 1, la chiamata si connetterà sempre tramite la rete PSTN anziché VoIP. La latenza massima e la soglia di larghezza di banda minima sono rispettivamente 10.000 ms e 10 KB/s per il valore 0. Ad esempio, un valore di 0,5 indica una latenza e una larghezza di banda minime di 5000 ms e 15 KB/s, rispettivamente.
Questo valore può essere configurato seguendo questi passaggi:
Accedi al portale della piattaforma CCAI come amministratore.
Vai a Impostazioni > Impostazioni sviluppatore > App mobile.
Individua la sezione Soglia del numero di telefono di riserva. Il valore predefinito è 0,85.
Specifica il nuovo valore di soglia.
Fai clic su Salva.
Disattivare le notifiche push a livello globale
Puoi disattivare le notifiche push a livello globale. L'impostazione della seguente
proprietà su false ignora tutte le dipendenze delle notifiche push e impedisce
che le notifiche push raggiungano gli utenti finali:
@property (nonatomic, assign) BOOL allowsPushNotifications;
Ignora la modalità Buio
Puoi ignorare la modalità Buio nell'SDK della piattaforma CCAI in modo specifico con questa proprietà:
@property (nonatomic, assign) BOOL ignoreDarkMode;
Nascondi barra di stato
Puoi controllare la visibilità della barra di stato con questa proprietà:
@property (nonatomic, assign) BOOL hideStatusBar;
Per impostazione predefinita, hideStatusBar è impostato su false e visible.
Ignorare il sondaggio CSAT
Puoi aggiungere un pulsante che consente all'utente di saltare il sondaggio CSAT. Il seguente esempio di codice mostra come aggiungere il pulsante:
let options = UJETGlobalOptions()
options.skipCsat = true
Personalizzare l'indicatore di attività
Puoi aggiungere la tua animazione di caricamento (all'interno di un UIView) all'SDK e
ignorare il UIActivityIndicatorView predefinito. Implementa il metodo
ujet_activityIndicator da UJETDelegate e restituisci la
visualizzazione personalizzata.
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
}
Se hai già impostato UIUserInterfaceStyle su Light nel file Info.plist della tua app per disattivare completamente la modalità Buio, puoi ignorare questa proprietà.
Lingua preferita
L'SDK della piattaforma CCAI utilizzerà il seguente ordine di priorità per determinare la lingua preferita.
Lingua selezionata dalla schermata iniziale all'interno dell'app.
Lingua predefinita selezionata da
UJETGlobalOptions. Puoi impostare la lingua predefinita con la proprietàpreferredLanguage. I codici lingua supportati sono disponibili nel fileUJETGlobalOptions.h.Se supportata dall'app, verrà utilizzata la lingua del dispositivo selezionata nel dispositivo (da Impostazioni > Generali > Lingua e regione).
Quando l'applicazione non supporta la lingua del dispositivo, ma supporta il dialetto principale più vicino, verrà utilizzato quest'ultimo. Ad esempio, se l'utente ha selezionato lo spagnolo di Cuba come lingua nel dispositivo e l'app non supporta lo spagnolo di Cuba, ma supporta il dialetto principale spagnolo, verrà utilizzata la lingua spagnola.
Se la lingua del dispositivo non è supportata dall'app, verrà utilizzato l'inglese.
Configurare le icone dei link di deviazione esterni
Puoi personalizzare l'icona nel canale del link di deviazione esterno caricando l'icona nel catalogo delle risorse della tua app e assicurandoti di utilizzare lo stesso nome dell'icona durante la creazione del link di deviazione esterno in Impostazioni > Chat > Link di deviazione esterni > Visualizza link > Aggiungi link di deviazione nel portale di amministrazione. Se il nome dell'icona nel portale di amministrazione non corrisponde all'icona caricata nell'app, l'SDK utilizzerà l'icona predefinita. Puoi consultare questo link su come aggiungere immagini al catalogo degli asset.



Fallback
Puoi utilizzare la funzione didHandleUjetError per il fallback di errori imprevisti. Se non utilizzi questa funzione o se restituisce false, l'SDK per iOS
gestisce l'errore.
La tabella seguente mostra gli errori che la funzione didHandleUjetError
ascolta:
| Tipo di errore | Codice di errore | Descrizione |
|---|---|---|
networkError |
1 | La rete non è disponibile. Nota: questo errore non viene attivato quando la rete non è disponibile durante una sessione di chat o chiamata o una schermata di valutazione. |
authenticationError |
100 | Si è verificato un errore imprevisto durante l'autenticazione. |
authenticationJwtError |
101 | Si è verificato un errore imprevisto durante la convalida del JWT, ad esempio un errore di analisi. |
voipConnectionError |
1000 | Impossibile stabilire una connessione con il provider VoIP. Un callback dell'SDK VoIP gestisce questa operazione. |
voipLibraryNotFound |
1001 | Il sistema prevede che una chiamata si connetta tramite un provider VoIP, ma non riesce a trovarne uno. Ciò può verificarsi se integri l'SDK errato o non aggiungi una libreria del provider VoIP alle dipendenze. |
chatLibraryNotFound |
1100 | Si verifica quando il sistema non riesce a trovare la libreria della chat. Ciò può accadere quando integri l'SDK sbagliato o non hai aggiunto una libreria di chat Twilio alle dipendenze. |
Il seguente esempio di codice mostra come utilizzare la funzione 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
}
}
Inviare dati personalizzati al tuo CRM
Puoi inviare dati personalizzati al ticket CRM.
Esistono due metodi per inviare dati personalizzati:
Metodo sicuro: firma dei dati predefinita con JWT.
Metodo non sicuro: dati predefiniti con JSON semplice (non consigliato).
Utilizzo del metodo sicuro per inviare dati personalizzati
Devi implementare il metodo di firma. Innanzitutto, puoi inserire i tuoi dati personalizzati sul lato client e inviarli al server per firmarli. Sul tuo server puoi aggiungere dati aggiuntivi tramite un modulo definito e firmare con company.secret e restituirli tramite 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];
}
}
Utilizzo di un metodo non sicuro per inviare dati personalizzati
Questo metodo non è consigliato perché crea una potenziale vulnerabilità che potrebbe esporre la tua applicazione a un attacco man-in-the-middle. Se scegli di utilizzare
questo metodo, non siamo responsabili dell'esposizione alla sicurezza e dei potenziali
danni che potrebbero verificarsi. Ti invitiamo a utilizzare il metodo sicuro descritto in precedenza
per inviare dati personalizzati nella tua applicazione. In alternativa, puoi avviare l'SDK
per iOS con l'istanza UJETCustomData. In questo caso, il delegato signPayload per
UJETPayloadCustomData deve semplicemente chiamare 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];
Utilizzo di dati personalizzati non firmati per inviare la trascrizione della chat esterna
Puoi inviare la trascrizione della chat all'SDK quando viene avviato con dati personalizzati non firmati chiamando il metodo setExternalChatTransfer: o setExternalChatTransferWithDictionary: per impostare i dati JSON con NSString o NSDictionary, rispettivamente.
UJETCustomData *customData = [UJETCustomData new];
[customData setExternalChatTransfer:jsonString];
UJETStartOptions *options = [UJETStartOptions new];
options.unsignedCustomData = customData;
[UJET startWithOptions:options];
Formato JSON:
greeting_override: string
agente: dizionario
name: stringa
avatar: stringa [URL dell'avatar dell'agente, facoltativo]
transcript: array
sender: string ["end_user" o "agent"]
timestamp: stringa [ad es. "2021-03-15 12:00:00Z"]
content: array
type: stringa [uno tra text, media]
text: stringa [obbligatorio per il tipo di testo]
media: dictionary [obbligatorio per il tipo di media]
type: stringa [uno tra image, video]
url: stringa [URL pubblico che rimanda al file multimediale]
Esempio 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"
}
]
}
]
}
Puoi utilizzare Markdown per il tipo di testo. La sintassi supportata include corsivo, grassetto,
elenco puntato, link ipertestuale e sottolineatura (--text--).
Esempio di dati personalizzati { :#example-of-custom-data }
JSON codificato in JWT
Il file JSON deve convalidare il JWT. L'oggetto dei dati personalizzati è il valore della chiave 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"
}
}
}
Ogni dato è simile al formato dell'oggetto JSON e deve contenere la chiave, il valore, il tipo e l'etichetta.
La chiave è l'identificatore univoco dei dati. L'etichetta è il nome visualizzato nella pagina della CRM Il tipo è il tipo di valore.
string
- Stringa JSON
numero
- numero intero, float
data
- Formato timestamp Unix UTC con 13 cifre. (contiene millisecondi)
URL
- Formato dell'URL HTTP
Esempio di CRM

Località
Utilizza il framework CoreLocation. Per maggiori dettagli, consulta AppDelegate.m.
Versione del sistema operativo del dispositivo
[customData set:@"os_version" label:@"OS Version" stringValue:[[UIDevice currentDevice] systemVersion]];
Impedire la visualizzazione di dati personalizzati
Puoi utilizzare la proprietà invisible_to_agent con un oggetto dati personalizzato per
impedire la visualizzazione di dati personalizzati firmati o non firmati nell'adattatore
dell'agente. Nell'esempio precedente, il numero di previdenza sociale dell'utente finale non viene
mostrato nell'adattatore agente perché "invisible_to_agent" : true è incluso
nell'oggetto ssn.
Quando includi la proprietà "invisible_to_agent" : true con un oggetto dati personalizzato,
puoi aspettarti il seguente comportamento:
- I dati personalizzati sono inclusi nel file di metadati della sessione.
- I dati personalizzati non sono inclusi nei record CRM.
Per saperne di più, consulta Visualizzare i dati delle sessioni nell'adattatore dell'agente.
Proprietà dei dati riservate
Puoi inviare proprietà dei dati riservate a Contact Center AI Platform (CCAI Platform) come dati personalizzati firmati all'inizio di una sessione. Per saperne di più, consulta Inviare proprietà dei dati riservati.
Di seguito è riportato un esempio di proprietà dei dati riservate nei dati personalizzati:
{
"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"
}
}
}
Sostituisci quanto segue:
VERIFIED_CUSTOMER_BOOLEAN: True se consideri questo utente finale un cliente legittimo.VERIFIED_BAD_ACTOR_BOOLEAN: True se ritieni che questo utente finale possa essere un malintenzionato.REPEAT_CUSTOMER_BOOLEAN: True se hai stabilito che questo utente finale ha contattato il tuo contact center in precedenza.
Personalizzare Flow
Scollega CCAI Platform per la gestione degli eventi dell'app host
// CCAI Platform is connected
...
// An event has come
[UJET disconnect:^{
// Handle an event
}];
Posticipare la chiamata o la chat in arrivo di CCAI Platform
Implementa un metodo delegato per la gestione degli eventi in entrata
- (BOOL)shouldConnectUjetIncoming:(NSString *)identifier forType:(UjetIncomingType)type {
if (weDoingSomething) {
// save identifier and type
return NO; // postpone
} else {
return YES;
}
}
Collegare l'evento posticipato
[UJET connect:identifier forType:UjetIncomingTypeCall];
Configurare i link diretti
In questo modo, gli agenti che effettuano chiamate PSTN possono utilizzare le azioni intelligenti tramite SMS sia quando un utente finale ha o non ha l'app.
Vai a Impostazioni > Gestione operazioni > Attiva Invio SMS per scaricare l'app nel portale della piattaforma CCAI.
Puoi impostare l'URL dell'app con la tua pagina web (ad es.https://your-company.com/support) dopo aver configurato il link universale o lo schema URL personalizzato. Puoi selezionare una delle due opzioni.
Implementa il metodo del delegato per gestire i deep link
Il link universale e l'URL personalizzato hanno l'aspetto di
https://your-company.com/support?call_id=x&nonce=y e
your-company://support?call_id=x&nonce=y rispettivamente. Inserisci uno dei tuoi link
senzaparametri di ricercay in URL app nel portale amministratore. Ad esempio, inserisci
your-company://support se utilizzi uno schema URL personalizzato.
Nel metodo delegato, assicurati di chiamare [UJET start] solo quando i parametri e i percorsi URL nel link universale o nell'URL personalizzato sono specifici per la piattaforma 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];
}
...
}
Se la tua app adotta UIWindowSceneDelegate, aggiungi questo snippet di codice:
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: [:])
}
}
Per ulteriori informazioni, consulta i codici di esempio del file UJETObject+DeepLink.
Osserva l'evento della piattaforma CCAI
Pubblichiamo i seguenti eventi tramite NSNotificationCenter.defaultCenter. Puoi ascoltarli e personalizzare il flusso in base al tuo caso d'uso, ad esempio il layout della tastiera personalizzato.
UJETEventEmailDidClick
- Dati del menu Coda
UJETEventEmailDidSubmit
Dati del menu Coda
has_attachment: (NSNumber) @YES, @NO
UJETEventSessionViewDidAppear
type: @"call", @"chat"
timestamp: (NSString) ISO 8601
UJETEventSessionViewDidDisappear
type: @"call", @"chat"
timestamp: (NSString) ISO 8601
UJETEventSessionDidCreate
- Dati della sessione
UJETEventSessionDidEnd
Dati della sessione
agent_name: (NSString) null se l'agente non ha partecipato
duration: (NSNumber) solo per la chiamata
ended_by: (NSString)
type=call: @"agent", @"end_user"
type=chat: @"agent", @"end_user", @"timeout", @"dismissed"
UJETEventSdkDidTerminate
UJETEventPostSessionOptInDidSelected
- opt_in_selected: (NSString) @"Yes", @"No"
Dati sugli eventi
Metadati
application: @"iOS"
app_id: (NSString) bundle identifier
app_version: (NSString)
company: (NSString) subdomain
device_model: (NSString)
device_version: (NSString)
sdk_version: (NSString)
timestamp: (NSString) ISO 8601
Queue Menu Data
Metadati
menu_id: NSString
menu_key: NSString, nullable
menu_name: NSString
menu_path : NSString
Dati di sessione
Dati del menu Coda
session_id: NSString
type: @"call", @"chat"
end_user_identifier: NSString
Configurare la condivisione schermo
Se vuoi utilizzare la funzionalità Condivisione schermo, integra
UJETCobrowseKit.xcframework.
CocoaPods:aggiungi la seguente specifica secondaria al target dell'app.
ruby
target 'MyApp' do
pod 'UJET'
pod 'UJET/Cobrowse'
end
Carthage: aggiungi la seguente riga al Cartfile:
binary "https://sdk.ujet.co/ios/UJETKit.json"
SwiftPM: seleziona i prodotti UJET e UJETCobrowse e aggiungili al target dell'app.
e imposta la proprietà UJETGlobalOptions.cobrowseKey.
swift
let options = UJETGlobal
Options()options.cobrowseKey = cobrowseKey
UJET.setGlobalOptions(options)
Condivisione schermo dell'intero dispositivo (facoltativa)
La condivisione dello schermo dell'intero dispositivo consente agli agenti dell'assistenza di visualizzare le schermate delle applicazioni esterne alla tua. Questo è spesso utile quando gli agenti dell'assistenza devono controllare lo stato delle impostazioni di sistema o devono vedere l'utente navigare tra più applicazioni. Se non vuoi questa funzionalità, puoi saltare questa sezione.
Personalizzare la finestra di dialogo per il consenso alla condivisione dello schermo
Per personalizzare la finestra di dialogo per il consenso alla condivisione dello schermo, devi implementare il protocollo UJETCobrowseAlertProvider nella classe del provider. In questa
implementazione, restituisci un UIViewController personalizzato o qualsiasi altro oggetto ereditato
UIViewController tramite il rispettivo metodo del protocollo. UIViewController
deve avere due pulsanti, uno per accettare e l'altro per rifiutare.
Dopo aver ottenuto il consenso, passalo al nostro SDK chiamando la chiusura
consentStatus. UIViewController da cobrowseFullDeviceRequestAlert. Il
delegato deve contenere RPSystemBroadcastPickerView con titolo (vedi
icodice campioneio di seguito) e deve avere un altro pulsante di rifiuto. Richiama la chiusura
ignorata facendo clic sul pulsante Rifiuta.
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
}
}
Il controller di visualizzazione personalizzato deve avere una chiusura per trasmettere lo stato del consenso all'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)
}
}
}
Il controller di visualizzazione personalizzato per l'avviso di richiesta di accesso completo al dispositivo deve avere
RPSystemBroadcastPickerView e una chiusura per passare lo stato dismiss all'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?()
}
}
}
Non dimenticare di passare questo provider al nostro SDK tramite la seguente API:
let provider = CobrowseAlertProvider()
UJET.setCobrowseAlertProvider(provider)
Estensione di trasmissione
La funzionalità richiede l'aggiunta di un'estensione di trasmissione.
Apri il progetto Xcode.
Vai a File > Destinazione.
Scegli Broadcast Upload Extension.
Inserisci un nome per il target.
Deseleziona Includi estensione UI.
Crea la destinazione, annotando il relativo ID bundle.
Modifica l'SDK di destinazione dell'estensione di trasmissione in iOS 12.0 o versioni successive.
Integrare l'SDK
CocoaPods:aggiungi la seguente specifica secondaria al target dell'estensione:
target 'MyApp' do
pod 'UJET'
pod 'UJET/Cobrowse'
end
target 'MyAppExtension' do
pod 'UJET/CobrowseExtension'
end
Se utilizzi SwiftPM, seleziona il prodotto UJETCobrowseExtension e aggiungilo al target dell'estensione.
Configurare la condivisione del portachiavi
L'app e l'estensione dell'app che hai creato in precedenza devono condividere alcuni segreti tramite il portachiavi iOS. Lo fanno utilizzando il proprio gruppo Keychain, in modo da essere isolati dal resto del Keychain delle tue app.
Sia nel target app che nel target estensione aggiungi un diritto di condivisione del portachiavi
per il gruppo di portachiavi io.cobrowse.
Aggiungi l'ID bundle al file plist
Prendi l'ID bundle dell'estensione che hai creato in precedenza e aggiungi la seguente voce nel file Info.plist delle tue app (nota: non nel file Info.plist delle estensioni), sostituendo il seguente ID bundle con il tuo:
xml
<key>CBIOBroadcastExtension</key>
<string>your.app.extension.bundle.ID.here</string>
Implementare l'estensione
Xcode avrà aggiunto i file SampleHandler.m e SampleHandler.h (o
SampleHander.swift) come parte del target creato in precedenza. Sostituisci
i contenuti dei file con i seguenti:
Swift: seleziona il prodotto UJETCobrowseExtension e aggiungilo al target dell'estensione:
import CobrowseIOAppExtension
class SampleHandler: CobrowseIOReplayKitExtension {
}
ObjC
objc// SampleHandler.h
@import CobrowseIOAppExtension;
@interface SampleHandler : CobrowseIOReplayKitExtension
@end// SampleHandler.m
#import "SampleHandler.h"
@implementation SampleHandler
@end
Crea ed esegui la tua app
Ora puoi creare ed eseguire la tua app. La funzionalità completa del dispositivo è disponibile solo sui dispositivi fisici e non funziona in iOS Simulator.
Ridurre a icona l'SDK
L'SDK Contact Center AI Platform può essere ridotto a icona durante una sessione di chat o una chiamata. Questa opzione può essere utile quando vuoi indirizzare l'utente alla tua app dopo aver ricevuto un evento SDK, ad esempio un clic su una scheda di contenuti. Per ridurre al minimo l'SDK e riportare l'utente alla tua app, puoi utilizzare:
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
}
Risoluzione dei problemi
Rifiuto dell'invio dell'app
L'invio dell'app viene rifiutato a causa dell'inclusione del framework CallKit nel territorio cinese.
Se la tua app viene rifiutata da Apple per questo motivo, lascia un commento perché il sistema è progettato per disattivare il framework CallKit per la regione Cina durante la chiamata VoIP. Questa modifica è effettiva a partire dalla versione 0.31.1 dell'SDK.
Le dimensioni dell'SDK sono troppo grandi
Quando le dimensioni dell'SDK sono troppo grandi e difficili da monitorare su GitHub
In questo articolo, vengono offerte due opzioni. Ti consigliamo di utilizzare Git LFS.
Se non utilizzi Bitcode, la rimozione del bitcode dal binario può essere un'altra opzione. Esegui questo comando nella cartella UJETKit.xcframework.
xcrun bitcode_strip -r UJET -o UJET
dyld: Library not loaded error
Aggiungi @executable_path/Frameworks a Runpath Search Paths da Target > Build Settings > Linking.
Invio di app su iTunes Connect
Durante la procedura di revisione, Apple potrebbe porre la seguente domanda a causa della modalità in background Voice over IP abilitata:
Gli utenti possono ricevere chiamate VoIP nella tua app?
Rispondi Sì alla domanda.
La notifica di avviso non è disponibile all'avvio dell'SDK
Verifica quanto segue:
Utilizza un dispositivo reale, non un simulatore.
Attiva le notifiche push e le modalità in background > funzionalità Voice over IP.
Se non risolvi il problema, prova a creare un profilo di provisioning di distribuzione (Ad Hoc o Apple Store).
Testare la notifica push sull'app di test
Prepara il certificato VoIP e il token dispositivo del dispositivo.
Nel portale della piattaforma CCAI, consulta la sezione Debug notifiche push nel menu Impostazioni > Impostazioni sviluppatore.
Se hai già impostato il certificato per APNS, non devi inserirlo di nuovo.
Inserisci il certificato (facoltativo) e verifica se è una sandbox o meno (facoltativo) e inserisci il token del dispositivo di notifica push dell'app di test.
L'avvio di una nuova chat ha richiesto più di 30 secondi
Controlla se stai rispondendo a un metodo delegato di dati personalizzati. Devi restituire dati personalizzati validi su richiesta o semplicemente restituire nil nel blocco di esito positivo.
Utilizza questo snippet di codice come configurazione di esempio:
public func signPayload(_ payload: [AnyHashable: Any]?, payloadType: UjetPayloadType, success: (String?) -> Void, failure: (Error?) -> Void)
{
if payloadType == .customData {
success(nil)
}
}