Guida all'SDK iOS

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

  1. Accedi al portale Contact Center AI Platform (CCAI Platform) utilizzando le credenziali amministratore.

  2. Vai a Impostazioni > Impostazioni sviluppatore.

  3. 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

  1. Scarica l'app di esempio per iOS.

  2. Vai alla cartella e installa le dipendenze utilizzando CocoaPods:

    $ pod install --project-directory=ExampleApp
    
  3. Per configurare rapidamente le impostazioni del progetto, esegui uno script shell:

    $ ./setup.sh
    

    In alternativa, puoi modificare manualmente le impostazioni del progetto seguendo questi passaggi:

    1. Apri ExampleApp.xcworkspace.

    2. Sostituisci i valori UJETCompanyKey e UJETCompanySecret in Info.plist con i valori di Chiave aziendale e Codice segreto aziendale della pagina Impostazioni > Impostazioni sviluppatore nel portale della piattaforma CCAI.

    3. Sostituisci il valore UJETSubdomain in Info.plist con il sottodominio nell'URL del portale della piattaforma CCAI. Il sottodominio precede direttamente .ujet.com nell'URL, ad esempio your-subdomain in https://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

  1. Aggiungi lo Swift Package per l'SDK iOS.

  2. Nelle impostazioni di build, inserisci -ObjC in Other Linker Flags (Altri flag del linker).

  3. 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
  1. Aggiungi la seguente riga al Podfile:

    pod 'UJET', :podspec =>
    'https://sdk.ujet.co/ios/x.y.z/ujet.podspec' #specific version
    x.y.z
    
  2. Esegui 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

  1. Esegui carthage bootstrap --use-xcframeworks (o carthage update --use-xcframeworks (se stai aggiornando le dipendenze).

  2. Scarica UJETKit.xcframework, UJETFoundationKit.xcframework, UJETChatRedKit.xcframework, UJETChatBlueKit.xcframework, UJETTwilioCallKit.xcframework e tutte le dipendenze TwilioVoice.xcframework e TwilioConversationsClient.xcframework.

  3. Aggiungi UJETKit.xcframework al tuo target trascinandolo nella sezione Frameworks, Libraries, and Embedded Content (Framework, librerie e contenuti incorporati).

  4. Ripeti i passaggi 2 e 3 per tutte le dipendenze del passaggio 1.

  5. Nelle impostazioni di build, imposta -ObjC su Other Linker Flags.

  6. Aggiungi libc++.tbd come dipendenza nella sezione Linked Frameworks della 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:

  1. Scarica tutti i framework, inclusi UJETKit.xcframework e altre dipendenze.

  2. Crea la cartella CCAI Platform nella radice del progetto ed estrai tutti i framework.

  3. Seleziona la build e il target Objc-Manual o Swift-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.

  1. Crea e scarica il certificato VoIP dal sito per sviluppatori Apple.

  2. Fai doppio clic sul certificato per aggiungerlo al portachiavi.

  3. Avvia l'applicazione Accesso Portachiavi sul Mac.

  4. Scegli la categoria I miei certificati nella barra laterale a sinistra.

  5. Fai clic con il tasto destro del mouse su Servizi VoIP: il certificato your.app.id.

  6. Nel menu popup, scegli Esporta.

  7. Salvalo come cert.p12 senza proteggerlo con una password lasciando il campo password vuoto.

  8. Esegui questo comando nel terminale.

    openssl s_client -connect gateway.push.apple.com:2195 -cert cert.pem -debug -showcert
    
  9. La parte superiore di cert.pem è il certificato e la parte inferiore è la chiave privata.

  10. 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 -showcerts
    

    In 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)
    ---
    
  11. Accedi al portale della piattaforma CCAI con le credenziali di amministratore e vai a Impostazioni > Impostazioni sviluppatore > App mobile.

  12. 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.

  13. 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

  1. Seleziona il target e apri la scheda Funzionalità.

  2. 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:

  1. Accedi al portale della piattaforma CCAI con le credenziali di amministratore.

  2. Vai a Impostazioni > Coda.

  3. Seleziona una coda dalla struttura del menu.

  4. Seleziona Crea punto di accesso diretto.

  5. Inserisci la chiave nel modulo di testo.

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

Viene mostrato un esempio di tema della chat personalizzato.

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

Viene mostrato un esempio di scheda dei contenuti.

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:

  1. Estrai le informazioni pertinenti dal dizionario degli eventi (smart_action_id, external_form_id e signature).

  2. Genera un URI del modulo e una firma per i dati del modulo.

  3. Trasferisci i dati del modulo all'SDK come dizionario FormDataEvent utilizzando completion closure.

  4. Se si verifica un errore durante la generazione dell'URI/della firma, richiama il callback utilizzando callback.onError() con Error.

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:

  1. Accedi al portale della piattaforma CCAI come amministratore.

  2. Vai a Impostazioni > Impostazioni sviluppatore > App mobile.

  3. Individua la sezione Soglia del numero di telefono di riserva. Il valore predefinito è 0,85.

  4. Specifica il nuovo valore di soglia.

  5. 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.

  1. Lingua selezionata dalla schermata iniziale all'interno dell'app.

  2. Lingua predefinita selezionata da UJETGlobalOptions. Puoi impostare la lingua predefinita con la proprietà preferredLanguage. I codici lingua supportati sono disponibili nel file UJETGlobalOptions.h.

  3. Se supportata dall'app, verrà utilizzata la lingua del dispositivo selezionata nel dispositivo (da Impostazioni > Generali > Lingua e regione).

  4. 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.

  5. 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:

  1. Metodo sicuro: firma dei dati predefinita con JWT.

  2. 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:

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.

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.

  1. Apri il progetto Xcode.

  2. Vai a File > Destinazione.

  3. Scegli Broadcast Upload Extension.

  4. Inserisci un nome per il target.

  5. Deseleziona Includi estensione UI.

  6. Crea la destinazione, annotando il relativo ID bundle.

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