Guida all'SDK iOS

L'SDK mobile di 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 ha i seguenti requisiti:

  • iOS 12.0 e versioni successive

Recupera le credenziali aziendali

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

  2. Vai a Impostazioni > Impostazioni sviluppatore.

  3. In Chiave aziendale e Token segreto, prendi nota della chiave aziendale e del token segreto aziendale.

Per iniziare

Di seguito è riportata una guida su come iniziare a utilizzare l'SDK mobile per 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 progetto, esegui uno script shell:

    $ ./setup.sh
    

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

    1. Apri ExampleApp.xcworkspace.

    2. Sostituisci i valori UJETCompanyKey e UJETCompanySecret in Info.plist con i valori Company Key (Chiave azienda) e Company Secret Code (Codice secret azienda) della pagina Settings > Developer Settings (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.

Integra nel tuo progetto

L'integrazione dell'SDK per iOS con la tua applicazione dipende dall'ambiente di sviluppo.

Swift Package Manager

  1. Aggiungi il pacchetto Swift per l'SDK per iOS da GitHub.

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

  3. A partire da Xcode 13.2, esiste un problema noto relativo all'utilizzo di framework binari distribuiti tramite Swift Package Manager. L'attuale soluzione alternativa a questo problema consiste nell'aggiungere una fase di esecuzione dello script alle fasi di creazione del progetto Xcode. Questa fase di esecuzione dello script deve essere successiva alla fase di build Embed Frameworks. Questa nuova fase di esecuzione dello 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 pod install. Se l'SDK per iOS è stato integrato in precedenza, esegui invece pod update CCAI Platform.

Cartagine

Google Cloud consiglia di utilizzare un gestore delle dipendenze o l'integrazione manuale perché una dipendenza di CCAI Platform non supporta Carthage.

Per utilizzare un gestore delle dipendenze diverso, segui questi passaggi:

  1. Aggiungi le seguenti righe:

    binary "https://sdk.ujet.co/ios/UJETKit.json"
    binary "https://sdk.ujet.co/ios/UJETFoundationKit.json"
    binary "https://sdk.ujet.co/ios/UJETCallRedKit.json"
    binary "https://sdk.ujet.co/ios/UJETChatRedKit.json"
    # optional
    # binary "https://sdk.ujet.co/ios/UJETChatBlueKit.json"
    binary "https://raw.githubusercontent.com/twilio/twilio-voice-ios/Releases/twilio-voice-ios.json"
    # this is not supported: https://github.com/twilio/conversations-ios/issues/12
    # binary "https://raw.githubusercontent.com/twilio/conversations-ios/master/twilio-convo-ios.json"
    
  2. Esegui carthage bootstrap --use-xcframeworks (o carthage update --use-xcframeworks se stai aggiornando le dipendenze).

Integrazione manuale
  1. Scarica UJETKit.xcframework, UJETFoundationKit.xcframework, UJETChatRedKit.xcframework, UJETChatBlueKit.xcframework, UJETTwilioCallKit.xcframework e tutte le dipendenze TwilioVoice.xcframework e TwilioConversationsClient.xcframework.

  2. Aggiungi UJETKit.xcframework alla destinazione trascinandolo nella sezione Frameworks, Libraries, and Embedded Content (Framework, librerie e contenuti incorporati).

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

  4. In Build Settings (Impostazioni build), imposta -ObjC su Other Linker Flags.

  5. 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 descritti nella sezione seguente.

Crea l'SDK manualmente con il progetto di esempio

Segui questi passaggi nell'ordine indicato:

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

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

  3. Seleziona il target Objc-Manual o Swift-Manual e crea la build.

Framework di importazione

Le sezioni seguenti forniscono istruzioni su come importare il framework.

Progetto Objective-C

@import UJETKit;

Progetto Swift

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 dell'utente finale

Accedi all'SDK per iOS tramite l'app per iOS.

Per garantire l'autenticazione dell'utente finale, 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 esito positivo o negativo deve essere chiamato prima che il delegato restituisca il risultato.

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 il metodo delegato signPayload: payloadType: success: failure:.

-   (void)signPayload:(NSDictionary *)payload payloadType:(UjetPayloadType)payloadType success:(void (^)(NSString *))success failure:(void (^)(NSError *))failure {
  if (payloadType == UjetPayloadAuthToken) {
    [self signAuthTokenInLocal:payload success:success failure:failure];
  }
}

-   (void)signAuthTokenInLocal:(NSDictionary *)payload success:(void (^)(NSString *))success failure:(void (^)(NSError *))failure {
    NSMutableDictionary *payloadData = [payload mutableCopy];

    NSDictionary *userData = [[NSUserDefaults standardUserDefaults] objectForKey:@"user-data"];
    [payloadData addEntriesFromDictionary:userData];
    payloadData[@"iat"] = [NSNumber numberWithDouble:[[NSDate date] timeIntervalSince1970]]; // required
    payloadData[@"exp"] = [NSNumber numberWithDouble:([[NSDate date] timeIntervalSince1970] + 600)]; // required

    NSString *signedToken = [self encodeJWT:payloadData];

    if (signedToken.length > 0) {
        success(signedToken);

    } else {
        NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: @"Failed to sign token" };
        NSError *error = [NSError errorWithDomain:@"ExampleApp" code:0 userInfo:userInfo];
        failure(error);
    }
}

-   (NSString *)encodeJWT:(NSDictionary *)payload {
    id<JWTAlgorithm> algorithm = [JWTAlgorithmHSBase algorithm384];
    NSString *secret = NSBundle.mainBundle.infoDictionary[@"UJETCompanySecret"];
    return [JWTBuilder encodePayload:payload].secret().algorithm(algorithm).encode;
}

Ti consigliamo vivamente di firmare il payload dal server dell'applicazione, non nel client.

Questo esempio utilizza la firma locale a scopo di test. Consulta signDataInRemote: success: failure: nel file UJETObject.m.

Per saperne di più, consulta Autenticazione degli utenti finali dell'SDK.

Configura le notifiche push

CCAI Platform invia notifiche push per richiedere smart action come la verifica e la foto, nonché per segnalare una chiamata in arrivo. Se le tue code di chat utilizzano il check-in (richiede l'SDK per iOS 2.16.0 o versioni successive), la piattaforma CCAI invia una notifica push che invita i consumatori in attesa a tornare alla chat e confermare che sono pronti. Consulta Check In per l'SDK Mobile. CCAI Platform richiede il salvataggio di due diversi tipi di certificati (VoIP e APN) nel portale di amministrazione di CCAI Platform.

Configura i certificati

CCAI Platform richiede il salvataggio di due diversi tipi di certificati push Apple nel portale di amministrazione di CCAI Platform:

  • Un certificato SSL del servizio di notifiche push di Apple (APN), utilizzato per le notifiche push remote standard.

  • Un certificato per i servizi VoIP, utilizzato per le notifiche delle chiamate in arrivo e per alcune azioni smart relative alle chiamate.

Entrambi i certificati vengono creati nel portale per sviluppatori Apple, convertiti in formato PEM e poi caricati nel portale di amministrazione della piattaforma CCAI.

Crea un certificato SSL APN

Segui questi passaggi successivi basati sulla guida ufficiale di Apple Establishing a Certificate-Based Connection to APNs per creare un certificato SSL APN.

  • Accedi a developer.apple.com e vai a Certificati, identificatori e profili.

  • Fai clic sul pulsante + per creare un nuovo certificato.

  • In Services (Servizi), seleziona Apple push notification service SSL (Sandbox & production) (SSL del servizio di notifiche push di Apple (Sandbox e produzione)).

  • Scegli l'ID app corrispondente all'identificatore pacchetto della tua applicazione, quindi fai clic su Continua.

  • Carica una richiesta di firma del certificato (CSR) generata dall'accesso portachiavi sul tuo Mac:

    • Apri Accesso Portachiavi > Assistente Certificato > Richiedi un certificato da un'autorità di certificazione.

    • Inserisci il tuo indirizzo email, lascia vuoto il campo Indirizzo email CA, seleziona Salva su disco e fai clic su Continua.

  • Fai clic su Continua, quindi su Scarica il file .cer generato.

  • Fai doppio clic sul file .cer per installarlo nel portachiavi.

  • In Accesso Portachiavi, individua il certificato installato, fai clic con il tasto destro del mouse e seleziona Esporta… per salvarlo come file .p12. Ti verrà chiesto di impostare una password di esportazione.

  • Converti il file .p12 nel formato PEM utilizzando openssl:

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

Crea un certificato per i servizi VoIP

I certificati dei servizi VoIP consentono alla tua app di ricevere notifiche push ad alta priorità tramite il framework PushKit di Apple, che può riattivare l'app da uno stato di interruzione per gestire le chiamate in arrivo. Per i passaggi ufficiali più recenti, consulta Crea certificati per i servizi VoIP nella documentazione per lo sviluppo di Apple.

  • Accedi a developer.apple.com e vai a Certificates, Identifiers & Profiles (Certificati, identificatori e profili).

  • Fai clic sul pulsante + per creare un nuovo certificato.

  • In Services (Servizi), seleziona VoIP services certificate (Certificato servizi VoIP).

  • Scegli l'ID app corrispondente all'identificatore pacchetto della tua applicazione, quindi fai clic su Continua.

  • Carica la stessa richiesta di firma del certificato (CSR) generata nella sezione precedente (o creane una nuova).

  • Fai clic su Continua, quindi su Scarica il file .cer generato.

  • Fai doppio clic sul file .cer per installarlo nel tuo Portachiavi.

  • In Accesso Portachiavi, individua il certificato dei servizi VoIP installato, fai clic con il tasto destro del mouse e seleziona Esporta… per salvarlo come file .p12.

  • Converti il file .p12 nel formato PEM utilizzando openssl:

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

Carica i certificati nel portale di amministrazione della piattaforma CCAI

  • Accedi al portale di amministrazione della piattaforma CCAI.

  • Vai a Impostazioni > Impostazioni sviluppatore > App mobile.

  • Carica il certificato PEM di APN.

  • Carica il certificato PEM VoIP.

Attiva notifiche push

  1. Seleziona il target e apri la scheda Capabilities.

  2. Attiva l'opzione Notifiche push.

Testa le notifiche push

Le sezioni seguenti forniscono indicazioni su come testare le notifiche push.

Sezione di debug delle notifiche push

Nel portale dell'amministratore, vai a Settings (Impostazioni) > Developer Settings (Impostazioni sviluppatore). In questa pagina, trova la sezione intitolata Debug delle notifiche push:

Copia e incolla il token del dispositivo nell'area di testo a destra e seleziona l'app mobile corretta.

Ottieni il token del dispositivo

Ecco un esempio di stringa di token del dispositivo:

7db0bc0044c8a203ed87cdab86a597a2c43bf16d82dae70e8d560e88253364b7

Le notifiche push vengono solitamente impostate nella classe conforme al protocollo UIApplicationDelegate o PKPushRegistryDelegate. A un certo punto, il token del dispositivo diventa 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.

Il risultato mostrerà il messaggio Notifica push configurata correttamente se la notifica push di test è stata inviata correttamente.

La consegna di una notifica push non è garantita, 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 di destinazione, attiva le seguenti funzionalità:

  • Notifiche push

  • Background Modes (seleziona queste voci)

  • Audio e AirPlay

  • Voice over IP

Info.plist

Per proteggere la privacy dell'utente, qualsiasi app per iOS collegata a partire da iOS 10.0 che accede ai microfoni, alla raccolta di foto e alla fotocamera del dispositivo deve dichiarare l'intenzione di farlo. Includi le seguenti chiavi con un valore di stringa nel file Info.plist della tua app e fornisci una stringa di scopo per questa chiave. Se l'app tenta di accedere a uno dei microfoni, alla raccolta di 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 di risoluzione dei problemi e per inviare video con audio relativi 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 per 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 questa chiave utilizzando un punto di accesso diretto:

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

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.

Svuota la cache locale se i dati utente sono stati aggiornati

Memorizziamo nella cache il token di autenticazione nel portachiavi per riutilizzarlo ed effettuare richieste meno frequenti per firmare il 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 vengono modificati o aggiornati, ad esempio in caso di evento di disconnessione.

[UJET clearUserData];

Verifica la presenza di una sessione esistente prima di avviare Contact Center AI Platform

Prima di avviare una sessione, controlla che non ce ne sia già una in corso. Questo è particolarmente importante quando l'ID utente è cambiato.

[UJET getStatus];

Se esiste una sessione, dobbiamo chiedere all'utente di riprenderla o di annullare l'azione:

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

Personalizza

Esistono diverse opzioni per il tema dell'SDK elencate in UJETGlobalTheme.h.

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 > Settings > Support Center Details > Nome visualizzato.

Puoi impostare l'immagine del logo anziché il nome dell'azienda in questo modo:

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

Se l'immagine è troppo grande, verrà ridimensionata per adattarla all'area.

Strings

Puoi anche personalizzare le stringhe eseguendo l'override del 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à, verrà utilizzato UJETGlobalTheme.tintColor per la modalità Buio. Ti consigliamo di impostare questa proprietà se la tua app supporta la modalità Buio. Consulta i seguenti articoli di 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 del tema.

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 di contenuti

Puoi aggiungere la personalizzazione delle schede di 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 (consulta 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 dell'evento (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. Passa i dati del modulo all'SDK come dizionario FormDataEvent utilizzando completion closure.

  4. Se si verifica un errore durante la generazione dell'URI o 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, firmata con la chiave secret condivisa. Le chiavi degli oggetti dei dati devono essere ordinate in ordine alfabetico prima di generare le firme e lo stesso data deve essere inviato all'SDK.

Trasferimento dopo la sessione

Puoi aggiungere la personalizzazione per l'assistente virtuale post-sessione insieme alla personalizzazione della chat. Puoi farlo utilizzando il file JSON (vedi la proprietà post_session) o la classe UJETChatPostSessionVaTheme. Lo spessore del bordo può essere solo 0 o 1. Se non vuoi differenziare l'esperienza dell'agente virtuale post-sessione, puoi impostare containerColor su 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 Azioni della 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 le dimensioni 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 è abilitato per tutte le chiamate.

Con CallKit, viene visualizzata una chiamata in arrivo nell'app con la schermata di chiamata e la chiamata viene mostrata nella cronologia chiamate del telefono.

Per avviare una nuova sessione di assistenza della piattaforma CCAI dalla cronologia chiamate, aggiungi il seguente blocco al tuo 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 40 x 40 sulla schermata di blocco quando si riceve una chiamata mentre il dispositivo è bloccato. Inserisci un'immagine denominata "icon-call-kit" negli Xcassets.

Configura l'SDK

Puoi impostare diverse opzioni prima di avviare l'SDK.

Per i dettagli, consulta la classe UJETGlobalOptions.

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

[UJET setGlobalOptions:options];

Mostra o nascondi il pulsante per scaricare la trascrizione

Puoi configurare l'SDK in modo da mostrare o nascondere il pulsante per scaricare la trascrizione nel menu 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.

  • VoIP non disponibile

    • La condizione della rete non è sufficientemente buona per la connessione. Per maggiori dettagli, consulta la proprietà UJETGlobalOptions.pstnFallbackSensitivity.

    • Si è verificato un errore durante la connessione a causa di un problema di configurazione del firewall o del provider.

Ti consigliamo di impostare il numero IVR della tua azienda in UJETGlobalOptions.fallbackPhoneNumber. Il formato consigliato è + seguito dal codice paese e dal numero di telefono, ad esempio +18001112222.

Sensibilità del fallback PSTN

Puoi regolare il livello di sensibilità del controllo delle condizioni della rete per il fallback alla rete PSTN.

@property (nonatomic, assign) float pstnFallbackSensitivity;

Il valore deve essere compreso tra 0,0 e 1,0. Se impostato su 1, la chiamata verrà sempre connessa tramite 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 che la latenza e la larghezza di banda minime sono rispettivamente di 5000 ms e 15 kB/s.

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.

Disattiva le notifiche push a livello globale

Puoi disattivare le notifiche push a livello globale. Se imposti la seguente proprietà su false, vengono ignorate tutte le dipendenze delle notifiche push e viene impedito l'invio delle notifiche push agli 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.

Salta 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

Personalizza l'indicatore di attività

Puoi aggiungere la tua animazione del caricatore (all'interno di un UIView) all'SDK ed eseguire l'override del 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 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. Verrà utilizzata la lingua del dispositivo selezionata (da Impostazioni > Generali > Lingua e Zona), se supportata dall'app.

  4. Verrà utilizzato il dialetto più vicino alla lingua del dispositivo quando l'applicazione non supporta la lingua del dispositivo, ma supporta il dialetto principale più vicino. Ad esempio, se l'utente ha selezionato lo spagnolo di Cuba come lingua sul dispositivo e l'app non supporta lo spagnolo di Cuba, ma supporta il dialetto principale, lo spagnolo, verrà utilizzato lo spagnolo.

  5. Se la lingua del dispositivo non è supportata dall'app, verrà utilizzato l'inglese.

Configura le icone dei link di deviazione esterna

Puoi personalizzare l'icona nel canale del link di deviazione esterno caricandola nel catalogo asset 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 a quello dell'icona caricata nell'app, l'SDK utilizzerà l'icona predefinita. Puoi consultare questo link su come aggiungere immagini al catalogo asset.

Invia 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 normale (non consigliato).

Utilizzo del metodo sicuro per inviare dati personalizzati

Devi implementare il metodo di firma. Innanzitutto, puoi inserire i dati personalizzati sul lato client e inviarli al server per la firma. Sul tuo server puoi aggiungere dati aggiuntivi tramite un modulo definito, firmarli con il tuo secret aziendale 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 l'invio di dati personalizzati

Questo metodo non è consigliato in quanto 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 della 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 semplicemente 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 avviata con dati dei clienti non firmati chiamando il metodo setExternalChatTransfer: o setExternalChatTransferWithDictionary: per impostare i dati JSON rispettivamente con NSString o NSDictionary.

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

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

[UJET startWithOptions:options];

Formato JSON:

  • greeting_override: string

  • agent: dizionario

    • name: string

    • avatar: stringa [URL dell'avatar dell'agente, facoltativo]

  • transcript: array

    • sender: stringa [end_user o agent]

    • timestamp: stringa [ad esempio, 2021-03-15 12:00:00Z]

    • content: array

      • type: stringa [uno tra text, media]

      • text: stringa [obbligatoria per il tipo di testo]

      • media: dizionario [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 sottolineato (--text--).

Esempio di dati personalizzati

Questa sezione mostra un esempio di dati personalizzati.

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 del 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 URL HTTP
Esempio di CRM

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

Impedisci la visualizzazione di dati dei clienti

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 dell'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 Visualizza i dati della sessione nell'adattatore dell'agente.

Proprietà dei dati riservate

Puoi inviare proprietà dei dati riservati 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 prenotati.

Di seguito è riportato un esempio di proprietà dei dati riservate nei dati personalizzati:

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

Imposta ogni valore su true o false.

  • reserved_verified_customer: true se ritieni che questo utente finale sia un cliente legittimo.
  • reserved_bad_actor: true se ritieni che questo utente finale possa essere un malintenzionato.
  • reserved_repeat_customer: true se hai stabilito che questo utente finale ha già contattato il tuo contact center.

Personalizza il flusso

Questa sezione descrive come personalizzare il flusso.

Scollega la piattaforma CCAI per la gestione degli eventi dell'app host

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

Posticipa una chiamata o una chat in arrivo sulla piattaforma CCAI

Implementa un metodo delegato per gestire gli eventi in arrivo:

-   (BOOL)shouldConnectUjetIncoming:(NSString *)identifier forType:(UjetIncomingType)type {
  if (weDoingSomething) {
    // save identifier and type
    return NO; // postpone
  } else {
    return YES;
  }
}
Collega evento posticipato
[UJET connect:identifier forType:UjetIncomingTypeCall];

Configura il deep link

In questo modo, gli agenti che effettuano chiamate PSTN possono utilizzare le azioni smart tramite SMS sia quando un utente finale ha l'app sia quando non ce l'ha.

Vai a Impostazioni > Gestione delle operazioni > Abilita l'invio di 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 delegato per gestire il deep link

Il link universale e l'URL personalizzato sono rispettivamente https://your-company.com/support?call_id=x&nonce=y e your-company://support?call_id=x&nonce=y. Inserisci uno dei tuoi link senza parametri di ricerca in URL dell'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 percorsi e i parametri dell'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 saperne di più, 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 della coda
  • UJETEventEmailDidSubmit

    • Dati del menu della 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) identificatore pacchetto

  • app_version: (NSString)

  • company: (NSString) subdomain

  • device_model: (NSString)

  • device_version: (NSString)

  • sdk_version: (NSString)

  • timestamp: (NSString) ISO 8601

Dati del menu Coda

Metadati

  • menu_id: NSString

  • menu_key: NSString, nullable

  • menu_name: NSString

  • menu_path : NSString

Dati di sessione

Metadati

  • session_id: NSString

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

  • end_user_identifier: NSString

Configura la condivisione schermo

Se vuoi utilizzare la funzionalità di condivisione schermo, integra UJETCobrowseKit.xcframework.

CocoaPods

Aggiungi questa sottospecifica al target dell'app:

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

Configura la condivisione schermo con Carthage

Aggiungi questa riga al file Cartfile:

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

SwiftPM

Seleziona i prodotti UJET e UJETCobrowse e aggiungili al target dell'app.

Imposta il valore di UJETGlobalOptions.cobrowseKey sulla chiave di co-browsing della tua azienda.

swift
let options = UJETGlobalOptions()
options.cobrowseKey = cobrowseKey

UJET.setGlobalOptions(options)

Condivisione schermo intero del dispositivo (facoltativa)

La condivisione schermo dell'intero dispositivo consente agli addetti all'assistenza di visualizzare le schermate di 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 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 di cobrowseFullDeviceRequestAlert. Il delegato deve contenere RPSystemBroadcastPickerView con il titolo (vedi il codice campione di seguito) e deve avere un altro pulsante di rifiuto. Richiama la chiusura ignorata quando fai clic sul pulsante di rifiuto.

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 della visualizzazione personalizzata deve avere una chiusura per passare 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 della visualizzazione personalizzata 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 > Target.

  3. Scegli Broadcast Upload Extension.

  4. Inserisci un nome per la destinazione.

  5. Deseleziona la casella di controllo Includi estensione UI.

  6. Crea la destinazione, prendendo nota del suo ID pacchetto.

  7. Modifica l'SDK target della tua estensione di trasmissione impostandolo su iOS 12.0 o versioni successive.

Integra l'SDK

CocoaPods{: aggiungi la seguente subspec alla destinazione 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 alla destinazione dell'estensione.

Configura la condivisione del portachiavi

La tua app e l'estensione dell'app che hai creato in precedenza devono condividere alcuni secret tramite il portachiavi iOS. A questo scopo, utilizzano il proprio gruppo di portachiavi, in modo che siano isolati dal resto del portachiavi delle tue app.

In Xcode, seleziona sia il target dell'app sia quello dell'estensione. Aggiungi un diritto di condivisione del portachiavi per il gruppo di portachiavi io.cobrowse.

Aggiungi l'ID pacchetto al file plist

Prendi l'ID pacchetto dell'estensione che hai creato in precedenza e aggiungi questa voce nel file Info.plist delle tue app.

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

Implementa l'estensione

Xcode avrà aggiunto i file SampleHandler.m e SampleHandler.h (o SampleHandler.swift) come parte della destinazione creata in precedenza. Sostituisci i contenuti dei file con quanto segue:

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, non funzionerà nel simulatore iOS.

Riduci al minimo l'SDK

L'SDK Contact Center AI Platform può essere ridotto a icona quando è in corso una sessione di chat o una chiamata. Questo può essere utile quando vuoi indirizzare l'utente alla tua app dopo la ricezione di un evento SDK, ad esempio un clic su una scheda di contenuti. Per ridurre a icona 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
}

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 per i quali la funzione didHandleUjetError è in ascolto:

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 venga connessa tramite un provider VoIP, ma non riesce a trovarne uno. Questo può accadere se integri l'SDK sbagliato o non aggiungi una libreria del provider VoIP alle tue dipendenze.
chatLibraryNotFound 1100 Si verifica quando il sistema non riesce a trovare la libreria della chat. Questo può accadere quando integri l'SDK sbagliato o non hai aggiunto una libreria di chat Twilio alle tue 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
    }
}

Risoluzione dei problemi

Questa sezione fornisce informazioni per aiutarti a risolvere i problemi più comuni.

Rifiuto dell'invio dell'app

L'invio dell'app viene rifiutato a causa dell'inclusione del framework CallKit nel territorio della Cina.

Se la tua app viene rifiutata da Apple per questo motivo, lascia un commento, poiché il sistema è progettato per disattivare il framework CallKit per la regione della Cina durante le chiamate VoIP. Questa modifica è valida 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, offrono due opzioni. È consigliabile utilizzare Git LFS.

Se non utilizzi Bitcode, un'altra opzione può essere rimuovere il bitcode dal file binario. Esegui questo comando nella cartella UJETKit.xcframework.

xcrun bitcode_strip -r UJET -o UJET

dyld: errore di libreria non caricata

Aggiungi @executable_path/Frameworks in Runpath Search Paths da Target > Build Settings > Linking.

Invio dell'app su iTunes Connect

Apple potrebbe porre la seguente domanda durante la procedura di revisione a causa della modalità in background Voice over IP attivata:

Gli utenti possono ricevere chiamate VoIP nella tua app?

Rispondi Yes 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 la funzionalità Background Modes > Voice over IP.

Se questi passaggi non risolvono il problema, prova a creare con il profilo di provisioning di distribuzione (Ad-hoc o Apple Store).

Testa la notifica push con la tua app di test

Prepara il certificato VoIP e il token del dispositivo.

Nel portale della piattaforma CCAI, consulta la sezione Debug delle notifiche push nel menu Impostazioni > Impostazioni sviluppatore.

Se hai già impostato il certificato per APNS, non è necessario inserirlo di nuovo.

Inserisci il certificato (facoltativo), seleziona o meno la sandbox (facoltativo) e inserisci il token del dispositivo per le notifiche 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)
    }
}