Guide du SDK iOS

Le SDK mobile Contact Center AI Platform (CCAI Platform) pour le système d'exploitation Apple iOS permet d'intégrer l'expérience mobile CCAI Platform dans les applications mobiles iOS.

Conditions requises

Le SDK mobile iOS doit répondre aux exigences suivantes :

  • iOS 12.0 et versions ultérieures

Récupérer les identifiants de l'entreprise

  1. Connectez-vous au portail Contact Center AI Platform (CCAI Platform) à l'aide de vos identifiants d'administrateur.

  2. Accédez à Paramètres > Paramètres du développeur.

  3. Sous Clé d'entreprise et Code secret, notez la clé d'entreprise et le code secret de l'entreprise.

Premiers pas

Vous trouverez ci-dessous un guide de démarrage avec le SDK mobile iOS de CCAI Platform.

Installation

Pour commencer, vous devez installer le SDK iOS.

Télécharger l'exemple d'application

  1. Téléchargez l'exemple d'application iOS.

  2. Accédez au dossier et installez les dépendances à l'aide de CocoaPods :

    $ pod install --project-directory=ExampleApp
    
  3. Pour configurer rapidement les paramètres du projet, exécutez un script shell :

    $ ./setup.sh
    

    Vous pouvez également modifier manuellement les paramètres du projet en procédant comme suit :

    1. Ouvrez ExampleApp.xcworkspace.

    2. Remplacez les valeurs UJETCompanyKey et UJETCompanySecret dans Info.plist par les valeurs Company Key (Clé d'entreprise) et Company Secret Code (Code secret d'entreprise) de la page Settings> Developer Settings (Paramètres > Paramètres pour les développeurs) du portail de la plate-forme CCAI.

    3. Remplacez la valeur UJETSubdomain dans Info.plist par le sous-domaine de l'URL de votre portail CCAI Platform. Le sous-domaine précède directement .ujet.com dans l'URL (par exemple, your-subdomain dans https://your-subdomain.ujet.com/settings/developer-setting).

Intégrer à votre projet

L'intégration du SDK iOS à votre application dépend de votre environnement de développement.

Gestionnaire de packages Swift

  1. Ajoutez le package Swift pour le SDK iOS depuis GitHub.

  2. Dans vos paramètres de compilation, ajoutez -ObjC à "Other Linker Flags" (Autres options de l'éditeur de liens).

  3. Depuis Xcode 13.2, il existe un problème connu concernant l'utilisation de frameworks binaires distribués à l'aide de Swift Package Manager. La solution actuelle à ce problème consiste à ajouter une phase d'exécution de script aux phases de compilation de votre projet Xcode. Cette phase d'exécution du script doit intervenir après la phase de compilation "Embed Frameworks". Cette nouvelle phase d'exécution de script doit contenir le code suivant :

    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. Ajoutez la ligne suivante au fichier Podfile :

    pod 'UJET', :podspec =>
    'https://sdk.ujet.co/ios/x.y.z/ujet.podspec' #specific version
    x.y.z
    
  2. Exécutez pod install. Si le SDK iOS a déjà été intégré, exécutez plutôt pod update CCAI Platform.

Carthage

Google Cloud recommande d'utiliser un gestionnaire de dépendances ou une intégration manuelle, car une dépendance de CCAI Platform n'est pas compatible avec Carthage.

Pour utiliser un autre gestionnaire de dépendances, procédez comme suit :

  1. Ajoutez les lignes suivantes :

    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. Exécutez carthage bootstrap --use-xcframeworks (ou carthage update --use-xcframeworks si vous mettez à jour les dépendances).

Intégration manuelle
  1. Téléchargez UJETKit.xcframework, UJETFoundationKit.xcframework, UJETChatRedKit.xcframework, UJETChatBlueKit.xcframework, UJETTwilioCallKit.xcframework et toutes les dépendances TwilioVoice.xcframework et TwilioConversationsClient.xcframework.

  2. Ajoutez UJETKit.xcframework à votre cible en le faisant glisser dans la section "Frameworks, Libraries, and Embedded Content" (Frameworks, bibliothèques et contenu intégré).

  3. Répétez les étapes 2 et 3 pour toutes les dépendances de l'étape 1.

  4. Dans vos paramètres de compilation, définissez -ObjC sur Other Linker Flags.

  5. Ajoutez libc++.tbd en tant que dépendance dans la section Linked Frameworks de la cible.

Si vous souhaitez compiler le SDK manuellement avec l'exemple de projet, suivez les étapes de la section suivante.

Créer manuellement le SDK avec l'exemple de projet

Suivez ces étapes dans l'ordre :

  1. Téléchargez tous les frameworks, y compris UJETKit.xcframework et d'autres dépendances.

  2. Créez le dossier CCAI Platform à la racine du projet et extrayez tous les frameworks.

  3. Sélectionnez la cible Objc-Manual ou Swift-Manual, puis compilez.

Framework d'importation

Les sections suivantes expliquent comment importer le framework.

Projet Objective-C

@import UJETKit;

Projet Swift

swiftimport
UJETimport UJETKit

Initialiser le SDK

Initialisez CCAI Platform avec UJET_COMPANY_KEY et 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;
}

Vous pouvez modifier le niveau de journalisation de verbose à error. Le niveau de journalisation par défaut est UjetLogLevelInfo.

[UJET.setLogLevel:UjetLogLevelVerbose];

Authentification de l'utilisateur final

Accédez au SDK iOS via l'application iOS.

Pour nous assurer que l'utilisateur final est authentifié, nous introduisons le mécanisme de signature JWT.

Le SDK iOS demandera à signer la charge utile lorsque l'authentification sera nécessaire. Si la signature réussit, l'application échange le jeton JWT signé contre le jeton d'authentification de l'utilisateur final. Le bloc de réussite ou d'échec doit être appelé avant que le délégué ne renvoie un résultat.

Pour un utilisateur anonyme (identifiant = nil), l'application crée un UUID pour l'utilisateur. Si l'utilisateur est authentifié ultérieurement avec un identifiant, l'application tente de fusionner les deux utilisateurs en fonction de l'UUID.

Dans UJETObject.h de l'exemple de projet :

@import UJETKit;

@interface UJETObject : NSObject <UJETDelegate>

Implémentez la méthode déléguée 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;
}

Nous vous recommandons vivement de signer la charge utile à partir de votre serveur d'application, et non dans le client.

Cet exemple utilise la signature locale à des fins de test. Consultez signDataInRemote: success: failure: dans le fichier UJETObject.m.

Pour en savoir plus, consultez Authentification des utilisateurs finaux du SDK.

Configurer les notifications push

La plate-forme CCAI envoie des notifications push pour demander des actions intelligentes telles que la validation et la photo, ainsi que pour signaler un appel entrant. Si vos files d'attente de chat utilisent l'enregistrement (nécessite le SDK iOS 2.16.0 ou version ultérieure), CCAI Platform envoie une notification push invitant les clients en attente à revenir au chat et à confirmer qu'ils sont prêts. Consultez Enregistrement pour le SDK mobile. CCAI Platform nécessite l'enregistrement de deux types de certificats différents (VoIP et APNs) dans le portail d'administration CCAI Platform.

Configurer des certificats

CCAI Platform nécessite l'enregistrement de deux types de certificats push Apple différents dans le portail d'administration CCAI Platform :

  • Un certificat SSL Apple Push Notification service (APNs), utilisé pour les notifications push à distance standards.

  • Un certificat de services VoIP, utilisé pour les notifications d'appels entrants et certaines actions intelligentes liées aux appels.

Les deux certificats sont créés sur le portail des développeurs Apple, convertis au format PEM, puis importés sur le portail d'administration de la plate-forme CCAI.

Créer un certificat SSL APNs

Suivez les étapes ci-dessous, basées sur le guide officiel d'Apple Établir une connexion basée sur un certificat aux APN, pour créer un certificat SSL APN.

  • Connectez-vous à developer.apple.com et accédez à Certificates, identifiers & profiles (Certificats, identifiants et profils).

  • Cliquez sur le bouton + pour créer un certificat.

  • Sous Services, sélectionnez Apple push notification service SSL (Sandbox & production) (SSL du service de notifications push Apple (bac à sable et production)).

  • Choisissez l'ID d'application correspondant à l'identifiant du bundle de votre application, puis cliquez sur Continue (Continuer).

  • Importez une demande de signature de certificat (CSR) générée à partir de l'accès au trousseau sur votre Mac :

    • Ouvrez Trousseaux d'accès > Assistant de certification > Demander un certificat à une autorité de certification.

    • Saisissez votre adresse e-mail, laissez le champ Adresse e-mail de l'autorité de certification vide, sélectionnez Enregistré sur le disque, puis cliquez sur Continuer.

  • Cliquez sur Continuer, puis sur Télécharger le fichier .cer généré.

  • Double-cliquez sur le fichier .cer pour l'installer dans votre trousseau d'accès.

  • Dans Trousseaux d'accès, recherchez le certificat installé, effectuez un clic droit dessus, puis sélectionnez Exporter… pour l'enregistrer en tant que fichier .p12. Vous serez invité à définir un mot de passe d'exportation.

  • Convertissez le fichier .p12 au format PEM à l'aide de 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

Créer un certificat de services VoIP

Les certificats de services VoIP permettent à votre application de recevoir des notifications push à priorité élevée via le framework PushKit d'Apple, qui peut réactiver votre application à partir d'un état d'arrêt pour gérer les appels entrants. Pour connaître la procédure officielle la plus récente, consultez Créer des certificats de services VoIP dans la documentation pour les développeurs Apple.

  • Connectez-vous à developer.apple.com et accédez à "Certificates, Identifiers & Profiles" (Certificats, identifiants et profils).

  • Cliquez sur le bouton + pour créer un certificat.

  • Sous Services, sélectionnez Certificat des services VoIP.

  • Choisissez l'ID d'application correspondant à l'identifiant du bundle de votre application, puis cliquez sur Continue (Continuer).

  • Importez la même demande de signature de certificat (CSR) que celle que vous avez générée dans la section précédente (ou créez-en une autre).

  • Cliquez sur Continuer, puis sur Télécharger le fichier .cer généré.

  • Double-cliquez sur le fichier .cer pour l'installer dans votre trousseau.

  • Dans Trousseaux d'accès, recherchez le certificat de services VoIP installé, effectuez un clic droit dessus, puis sélectionnez Exporter… pour l'enregistrer en tant que fichier .p12.

  • Convertissez le fichier .p12 au format PEM à l'aide de 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

Importer des certificats sur le portail d'administration de CCAI Platform

  • Connectez-vous au portail d'administration de la plate-forme CCAI.

  • Accédez à Paramètres > Paramètres du développeur > Application mobile.

  • Importez le certificat PEM APNs.

  • Importez le certificat PEM VoIP.

Activer les notifications push

  1. Sélectionnez votre cible, puis ouvrez l'onglet Capabilities (Fonctionnalités).

  2. Activez l'option Notifications push.

Tester les notifications push

Les sections suivantes expliquent comment tester les notifications push.

Section de débogage des notifications push

Dans le portail administrateur, accédez à Settings> Developer Settings (Paramètres > Paramètres du développeur). Sur cette page, recherchez la section intitulée Débogage des notifications push :

Copiez et collez le jeton de l'appareil dans la zone de texte de droite, puis sélectionnez l'application mobile appropriée.

Obtenir le jeton d'appareil

Voici un exemple de chaîne de jeton d'appareil :

7db0bc0044c8a203ed87cdab86a597a2c43bf16d82dae70e8d560e88253364b7

Les notifications push sont généralement définies dans la classe qui est conforme au protocole UIApplicationDelegate ou PKPushRegistryDelegate. À un moment donné, le jeton d'appareil est disponible. Vous pouvez l'imprimer avant de le transmettre au SDK iOS. Pour obtenir le jeton de votre appareil, utilisez l'extrait de code.

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]);
}
Résultat

Une fois que vous avez saisi le fichier PEM du certificat et le jeton de l'appareil, cliquez sur le bouton.

Le résultat affiche le message Notification push configurée si la notification push de test a bien été envoyée.

La distribution d'une notification push n'est pas garantie, car elle dépend de la connexion réseau de l'appareil.

Configurations de projet

Les sections suivantes décrivent les modifications nécessaires pour configurer le projet.

Capacités

Dans les paramètres de la cible, activez les fonctionnalités suivantes :

  • Notifications push

  • Modes d'arrière-plan (vérifiez ces éléments)

  • Audio et AirPlay

  • Voix sur IP

Info.plist

Pour protéger la confidentialité des utilisateurs, toute application iOS associée à iOS 10.0 ou version ultérieure qui accède aux micros, à la bibliothèque de photos et à l'appareil photo de l'appareil doit déclarer son intention de le faire. Incluez les clés suivantes avec une valeur de chaîne dans le fichier Info.plist de votre application et fournissez une chaîne d'objectif pour cette clé. Si votre application tente d'accéder à l'un des micros, à la bibliothèque de photos et à l'appareil photo de l'appareil sans chaîne d'objectif correspondante, l'application se ferme.

  • NSMicrophoneUsageDescription : permet d'accéder au micro pour appeler et parler aux équipes d'assistance ou de dépannage, et pour envoyer des vidéos avec du son concernant des demandes de renseignements sur les produits.

  • NSCameraUsageDescription : autorise l'accès à l'appareil photo pour que le client puisse prendre et envoyer des photos en lien avec sa demande d'assistance client.

  • NSPhotoLibraryUsageDescription : permet au client d'envoyer des photos liées à sa demande d'assistance.

  • NSFaceIDUsageDescription : permet d'accéder à la validation à l'aide de Face ID.

Démarrer le SDK iOS

Ajoutez la ligne suivante à l'endroit où vous souhaitez démarrer le SDK iOS :

[UJET startWithOptions:nil];

Vous pouvez également démarrer le SDK iOS à partir d'un point spécifique du menu avec cette clé à l'aide d'un point d'accès direct :

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

Le menuKey peut être créé en créant un point d'accès direct (DAP). Pour créer un DAP, procédez comme suit :

  1. Connectez-vous au portail CCAI Platform avec vos identifiants d'administrateur.

  2. Accédez à Paramètres > File d'attente.

  3. Sélectionnez une file d'attente dans la structure du menu.

  4. Sélectionnez Créer un point d'accès direct.

  5. Saisissez la clé dans le formulaire de texte.

  6. Cliquez sur Enregistrer.

Effacer le cache local si les données utilisateur ont été mises à jour

Nous mettons en cache le jeton d'authentification dans le trousseau pour le réutiliser et réduire la fréquence des requêtes de signature de la charge utile de l'application hôte. Le SDK l'utilisera jusqu'à son expiration ou sa révocation via l'appel clearUserData. L'application hôte est chargée de révoquer ce cache chaque fois que des données liées à l'utilisateur ont été modifiées ou mises à jour, par exemple lors d'un événement de déconnexion.

[UJET clearUserData];

Vérifier s'il existe une session avant de démarrer Contact Center AI Platform

Avant de démarrer une session, vérifiez qu'aucune session n'est en cours. Ceci est particulièrement important lorsque l'ID utilisateur a changé.

[UJET getStatus];

S'il existe une session, nous devons inviter l'utilisateur à la reprendre ou à annuler l'action :

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

Personnaliser

Plusieurs options sont disponibles pour le thème du SDK listé dans UJETGlobalTheme.h.

Définissez votre thème après [UJET initialize], par exemple :

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

Le nom de l'entreprise est récupéré depuis Admin Portal> Settings > Support Center Details > Display Name (Console d'administration > Paramètres > Informations du centre d'assistance > Nom à afficher).

Vous pouvez définir l'image du logo au lieu du nom de l'entreprise comme suit :

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

Si l'image est trop grande, elle sera redimensionnée pour s'adapter à la zone.

Strings

Vous pouvez également personnaliser les chaînes en remplaçant la valeur. Par exemple, placez cette clé-valeur dans votre fichier Localizable.strings :

"ujet_greeting_title" = "Title";

"ujet_greeting_description" = "Description";

Les chaînes personnalisables disponibles sont listées dans le fichier ujet.strings.

Mode sombre

Vous pouvez spécifier une teinte de la couleur que vous souhaitez pour le mode sombre afin d'améliorer la lisibilité des polices.

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

Si vous ne définissez pas la propriété, UJETGlobalTheme.tintColor sera utilisé pour le mode sombre. Nous vous recommandons de définir cette propriété si votre application est compatible avec le mode sombre. Consultez les articles Apple suivants pour savoir comment choisir la bonne couleur de teinte pour le mode sombre :

Thème de la discussion

Pour personnaliser l'écran de chat, vous pouvez utiliser une chaîne JSON ou chaque classe de thème.

Pour référence, consultez l'exemple d'application et annulez la mise en commentaire de la méthode 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)
}

Thème des fiches de contenu

Vous pouvez personnaliser les fiches de contenu en même temps que le chat. Pour ce faire, vous pouvez utiliser le fichier JSON (voir la propriété content_card) ou 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)
}

Thème de la fiche de formulaire

Vous pouvez ajouter la personnalisation des fiches de formulaire en même temps que la personnalisation du chat. Pour ce faire, utilisez le fichier JSON (voir form_card property) ou 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)
}

Configuration du formulaire Web

Pour configurer la fonctionnalité de formulaire Web, implémentez la méthode ujetWebFormDidReceive du protocole UJETDelegate. Cette méthode reçoit un événement (un dictionnaire FormMessageReceivedEvent) en tant que paramètre, contenant des informations liées au formulaire. Le dictionnaire d'événements (FormMessageReceivedEvent) inclut la structure JSON suivante :

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

Pour gérer l'événement, procédez comme suit :

  1. Extrayez les informations pertinentes du dictionnaire d'événements (smart_action_id, external_form_id et signature).

  2. Générez un URI de formulaire et une signature pour les données du formulaire.

  3. Transmettez les données du formulaire au SDK en tant que dictionnaire FormDataEvent à l'aide de completion closure.

  4. Si une erreur se produit lors de la génération de l'URI/de la signature, appelez le rappel à l'aide de callback.onError() avec Error.

Le dictionnaire (FormDataEvent) transmis au SDK doit avoir la structure suivante :

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

La signature (HMAC-SHA:256) doit être générée à l'aide de data, signée avec la clé secrète partagée. Les clés d'objet des données doivent être classées par ordre alphabétique avant de générer des signatures, et le même data doit être envoyé au SDK.

Transfert après la session

Vous pouvez ajouter une personnalisation pour l'agent virtuel post-session en même temps que la personnalisation du chat. Pour ce faire, vous pouvez utiliser le fichier JSON (voir la propriété post_session) ou la classe UJETChatPostSessionVaTheme. La largeur de la bordure ne peut être que de 0 ou 1. Si vous ne souhaitez pas différencier l'expérience de l'agent virtuel après la session, vous pouvez définir containerColor sur blanc et la bordure sur 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 d'actions du chat

Vous pouvez personnaliser le menu des actions de chat en même temps que le chat. Pour ce faire, vous pouvez utiliser le fichier JSON (voir la propriété form_card) ou 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)
}

Autres apparitions

Vous pouvez personnaliser d'autres aspects, comme la taille de la police et la couleur de l'arrière-plan.

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

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

CallKit

Sur iOS 10.0 et versions ultérieures, CallKit est activé pour tous les appels.

Avec CallKit, un appel entrant dans l'application s'affiche avec l'écran d'appel et l'appel s'affiche dans l'historique des appels du téléphone.

Pour démarrer une nouvelle session d'assistance CCAI Platform à partir de l'historique des appels, ajoutez le bloc suivant à votre 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 permet d'afficher une icône de 40 x 40 sur l'écran de verrouillage lors de la réception d'un appel lorsque l'appareil est verrouillé. Placez une image dans vos Xcassets nommée "icon-call-kit".

Configurer le SDK

Vous pouvez définir plusieurs options avant de démarrer le SDK.

Pour en savoir plus, consultez la classe UJETGlobalOptions.

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

[UJET setGlobalOptions:options];

Afficher ou masquer le bouton de téléchargement de la transcription

Vous pouvez configurer le SDK pour afficher ou masquer le bouton de téléchargement de la transcription dans le menu d'options de chat et sur l'écran post-chat.

Le code suivant montre comment configurer le bouton de téléchargement de la transcription :

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

@property (nonatomic, assign) UJETChatDownloadTranscriptVisibilityOptions transcriptVisibilityOptions;

Action de remplacement RTCP

Nous fournissons un basculement vers le RTPC dans plusieurs situations :

  • Le réseau mobile est hors connexion.

  • Le backend de l'application n'est pas accessible.

  • La VoIP n'est pas disponible

    • La connexion réseau n'est pas assez bonne pour se connecter. Pour en savoir plus, consultez la propriété UJETGlobalOptions.pstnFallbackSensitivity.

    • Une erreur s'est produite lors de la connexion en raison d'un problème de configuration du pare-feu ou du fournisseur.

Nous vous recommandons de définir le numéro de réponse vocale interactive de votre entreprise dans UJETGlobalOptions.fallbackPhoneNumber. Le format recommandé est le signe "+" suivi du code pays et du numéro de téléphone. Par exemple : +18001112222.

Sensibilité du basculement vers le RTCP

Vous pouvez ajuster le niveau de sensibilité de la vérification de l'état du réseau pour le basculement vers le RTPC.

@property (nonatomic, assign) float pstnFallbackSensitivity;

La valeur doit être comprise entre 0,0 et 1,0. Si la valeur est définie sur 1, l'appel sera toujours connecté via le RTCP plutôt que via la VoIP. Le seuil de latence maximale et le seuil de bande passante minimale sont respectivement de 10 000 ms et de 10 Ko/s pour la valeur 0. Par exemple, une valeur de 0, 5 signifie que la latence et la bande passante minimales sont respectivement de 5 000 ms et de 15 Ko/s.

Pour configurer cette valeur, procédez comme suit :

  1. Connectez-vous au portail CCAI Platform en tant qu'administrateur.

  2. Accédez à Settings > Developer Settings > Mobile Apps (Paramètres > Paramètres du développeur > Applications mobiles).

  3. Recherchez la section Seuil du numéro de téléphone de remplacement. La valeur par défaut est 0,85.

  4. Spécifiez la nouvelle valeur de seuil.

  5. Cliquez sur Enregistrer.

Désactiver les notifications push au niveau global

Vous pouvez désactiver les notifications push au niveau global. La définition de la propriété suivante sur false contourne toutes les dépendances des notifications push et empêche les notifications push d'atteindre les utilisateurs finaux :

@property (nonatomic, assign) BOOL allowsPushNotifications;

Ignorer le mode sombre

Vous pouvez ignorer le mode sombre dans le SDK CCAI Platform spécifiquement avec cette propriété :

@property (nonatomic, assign) BOOL ignoreDarkMode;

Masquer la barre d'état

Vous pouvez contrôler la visibilité de la barre d'état à l'aide de cette propriété :

  @property (nonatomic, assign) BOOL hideStatusBar;

Par défaut, hideStatusBar est défini sur false et visible.

Ignorer l'enquête CSAT

Vous pouvez ajouter un bouton permettant à l'utilisateur d'ignorer l'enquête CSAT. L'exemple de code suivant montre comment ajouter le bouton :

let options = UJETGlobalOptions()
options.skipCsat = true

Personnaliser l'indicateur d'activité

Vous pouvez ajouter votre propre animation de chargement (dans un UIView) au SDK et remplacer l'animation UIActivityIndicatorView par défaut. Implémentez la méthode ujet_activityIndicator à partir de UJETDelegate et renvoyez la vue personnalisée.

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
}

Si vous avez déjà défini UIUserInterfaceStyle sur "Light" dans le fichier Info.plist de votre application pour désactiver complètement le mode sombre, vous pouvez ignorer cette propriété.

La langue

Le SDK CCAI Platform utilise l'ordre de priorité suivant pour déterminer la langue préférée.

  1. Langue sélectionnée sur l'écran de démarrage de l'application.

  2. Langue par défaut sélectionnée dans UJETGlobalOptions. Vous pouvez définir la langue par défaut avec la propriété preferredLanguage. Les codes de langue acceptés sont disponibles dans le fichier UJETGlobalOptions.h.

  3. La langue sélectionnée sur l'appareil (dans Réglages> Général > Langue et région) sera utilisée, si elle est compatible avec l'application.

  4. Le dialecte le plus proche de la langue de l'appareil sera utilisé lorsque l'application n'est pas compatible avec la langue de l'appareil, mais qu'elle est compatible avec le dialecte parent le plus proche. Par exemple, si l'utilisateur a sélectionné l'espagnol (Cuba) comme langue sur l'appareil et que l'application ne prend pas en charge l'espagnol (Cuba), mais qu'elle prend en charge le dialecte parent espagnol, l'espagnol sera utilisé.

  5. L'anglais sera utilisé si la langue de l'appareil n'est pas compatible avec l'application.

Configurer les icônes de lien de déviation externe

Vous pouvez personnaliser l'icône du canal de lien de déviation externe en l'important dans le catalogue d'assets de votre application. Veillez à utiliser le même nom d'icône lorsque vous créez un lien de déviation externe dans Paramètres > Chat > Liens de déviation externe > Afficher les liens > Ajouter un lien de déviation dans le portail d'administration. Si le nom de l'icône dans le portail d'administration ne correspond pas à l'icône importée dans l'application, le SDK utilise l'icône par défaut. Pour savoir comment ajouter des images au catalogue des éléments, consultez ce lien.

Envoyer des données personnalisées à votre CRM

Vous pouvez envoyer des données personnalisées à la demande CRM.

Il existe deux méthodes pour envoyer des données personnalisées :

  1. Méthode sécurisée : signature de données prédéfinie avec JWT.

  2. Méthode non sécurisée : données prédéfinies avec du JSON brut (non recommandé).

Utiliser la méthode sécurisée pour envoyer des données personnalisées

Vous devez implémenter une méthode de signature. Tout d'abord, vous pouvez placer vos données personnalisées côté client et les envoyer à votre serveur pour les signer. Sur votre serveur, vous pouvez ajouter des données supplémentaires à l'aide d'un formulaire défini, les signer avec votre secret d'entreprise et les renvoyer par 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];
    }
}

Utilisation d'une méthode non sécurisée pour envoyer des données personnalisées

Cette méthode n'est pas recommandée, car elle crée une faille potentielle qui pourrait exposer votre application à une attaque de type "man-in-the-middle". Si vous choisissez d'utiliser cette méthode, nous ne sommes pas responsables de l'exposition de la sécurité et des dommages potentiels qui pourraient survenir. Nous vous encourageons à utiliser la méthode sécurisée décrite précédemment pour envoyer des données personnalisées dans votre application. Vous pouvez également démarrer le SDK iOS avec l'instance UJETCustomData. Dans ce cas, le délégué signPayload pour UJETPayloadCustomData doit simplement appeler 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];

Utiliser des données personnalisées non signées pour envoyer une transcription de chat externe

Vous pouvez envoyer la transcription du chat au SDK lorsqu'il est démarré avec des données personnalisées non signées en appelant la méthode setExternalChatTransfer: ou setExternalChatTransferWithDictionary: pour définir les données JSON avec NSString ou NSDictionary, respectivement.

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

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

[UJET startWithOptions:options];

Format JSON :

  • greeting_override : chaîne

  • agent : dictionnaire

    • name : chaîne

    • avatar : chaîne [URL de l'avatar de l'agent, facultatif]

  • transcript : tableau

    • sender : chaîne [end_user ou agent]

    • timestamp : chaîne [par exemple, 2021-03-15 12:00:00Z]

    • content : tableau

      • type : chaîne [text ou media]

      • text : chaîne [obligatoire pour le type de texte]

      • media : dictionnaire [obligatoire pour le type de média]

        • type : chaîne [image ou vidéo]

        • url : chaîne [URL publique pointant vers le fichier multimédia]

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

Vous pouvez utiliser Markdown sur le type de texte. La syntaxe acceptée inclut l'italique, le gras, la liste à puces, le lien hypertexte et le soulignement (--text--).

Exemple de données personnalisées

Cette section présente un exemple de données personnalisées.

JSON encodé au format JWT

Le fichier JSON doit valider le jeton JWT. L'objet de données personnalisées est la valeur de la clé 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"
    }
  }
}

Chaque donnée est semblable au format d'objet JSON et doit contenir la clé, la valeur, le type et l'étiquette.

La clé est l'identifiant unique des données. Le libellé est le nom à afficher sur la page du CRM. Le type est le type de la valeur.

  • string

    • Chaîne JSON
  • nombre

    • integer, float
  • date

    • Format de code temporel Unix UTC à 13 chiffres. (contient des millisecondes)
  • URL

    • Format d'URL HTTP
Exemple de CRM

Voici un exemple de CRM.

Emplacement

Utilisez le framework CoreLocation. Pour en savoir plus, consultez AppDelegate.m.

Version de l'OS de l'appareil
[customData set:@"os_version" label:@"OS Version" stringValue:[[UIDevice currentDevice] systemVersion]];

Empêcher l'affichage des données personnalisées

Vous pouvez utiliser la propriété invisible_to_agent avec un objet de données personnalisé pour empêcher l'affichage de données personnalisées signées ou non signées dans l'adaptateur d'agent. Dans l'exemple précédent, le numéro de sécurité sociale de l'utilisateur final n'est pas affiché dans l'adaptateur d'agent, car "invisible_to_agent" : true est inclus dans l'objet ssn.

Lorsque vous incluez la propriété "invisible_to_agent" : true avec un objet de données personnalisé, vous pouvez vous attendre au comportement suivant :

Pour en savoir plus, consultez Afficher les données de session dans l'adaptateur d'agent.

Propriétés de données réservées

Vous pouvez envoyer des propriétés de données réservées à Contact Center AI Platform (CCAI Platform) en tant que données personnalisées signées au début d'une session. Pour en savoir plus, consultez Envoyer des propriétés de données réservées.

Voici un exemple de propriétés de données réservées dans les données personnalisées :

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

Définissez chaque valeur sur true ou false.

  • reserved_verified_customer : true si vous considérez cet utilisateur final comme un client légitime.
  • reserved_bad_actor : true si vous considérez que cet utilisateur final est potentiellement un acteur malveillant.
  • reserved_repeat_customer : true si vous avez déterminé que cet utilisateur final a déjà contacté votre centre de contact.

Personnaliser Flow

Cette section explique comment personnaliser le flux.

Déconnecter CCAI Platform pour gérer les événements de l'application hôte

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

Reporter un appel ou un chat entrant CCAI Platform

Implémentez une méthode déléguée pour gérer les événements entrants :

-   (BOOL)shouldConnectUjetIncoming:(NSString *)identifier forType:(UjetIncomingType)type {
  if (weDoingSomething) {
    // save identifier and type
    return NO; // postpone
  } else {
    return YES;
  }
}
Associer un événement reporté
[UJET connect:identifier forType:UjetIncomingTypeCall];

Configurer un lien profond

Cela permet aux agents de passer des appels RTCP pour utiliser des actions intelligentes par SMS, que l'utilisateur final dispose ou non de l'application.

Accédez à Settings> Operation Management> Enable Send SMS to Download App (Paramètres > Gestion des opérations > Activer l'envoi de SMS pour télécharger l'application) sur le portail de la plate-forme CCAI.

Vous pouvez définir l'URL de l'application avec votre page Web (par exemple, https://your-company.com/support) après avoir configuré un lien universel ou un schéma d'URL personnalisé. Vous pouvez choisir l'une ou l'autre de ces options.

Implémenter une méthode déléguée pour gérer les liens profonds

Le lien universel et l'URL personnalisée ressemblent respectivement à https://your-company.com/support?call_id=x&nonce=y et your-company://support?call_id=x&nonce=y. Insérez l'un de vos liens sans paramètres de requête sous "URL de l'application" dans le portail d'administration. Par exemple, saisissez your-company://support si vous utilisez un schéma d'URL personnalisé.

Dans la méthode déléguée, veillez à n'appeler [UJET start] que lorsque les chemins d'URL et les paramètres du lien universel ou de l'URL personnalisée sont spécifiques à la plate-forme 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];
  }
  ...
}

Si votre application adopte UIWindowSceneDelegate, ajoutez cet extrait de code :

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

Pour en savoir plus, consultez les exemples de code du fichier UJETObject+DeepLink.

Observer un événement CCAI Platform

Nous publions les événements suivants via NSNotificationCenter.defaultCenter. Vous pouvez les écouter et personnaliser votre flux en fonction de votre cas d'utilisation (par exemple, une disposition de clavier personnalisée).

  • UJETEventEmailDidClick

    • Données du menu de la file d'attente
  • UJETEventEmailDidSubmit

    • Données du menu de la file d'attente

    • has_attachment : (NSNumber) @YES, @NO

  • UJETEventSessionViewDidAppear

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

    • timestamp : (NSString) ISO 8601

  • UJETEventSessionViewDidDisappear

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

    • timestamp : (NSString) ISO 8601

  • UJETEventSessionDidCreate

    • Données de session
  • UJETEventSessionDidEnd

    • Données de session

    • agent_name : (NSString) "null" si l'agent n'a pas rejoint la session

    • duration : (NSNumber) uniquement pour les appels

    • ended_by: (NSString)

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

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

  • UJETEventSdkDidTerminate

  • UJETEventPostSessionOptInDidSelected

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

Données d'événement

Métadonnées

  • application: @"iOS"

  • app_id : (NSString) identifiant du bundle

  • app_version : (NSString)

  • company : (NSString) sous-domaine

  • device_model : (NSString)

  • device_version : (NSString)

  • sdk_version : (NSString)

  • timestamp : (NSString) ISO 8601

Données du menu des files d'attente

Métadonnées

  • menu_id : NSString

  • menu_key: NSString, nullable

  • menu_name : NSString

  • menu_path : NSString

Données de session

Métadonnées

  • session_id : NSString

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

  • end_user_identifier : NSString

Configurer le partage d'écran

Si vous souhaitez utiliser la fonctionnalité de partage d'écran, intégrez UJETCobrowseKit.xcframework.

CocoaPods

Ajoutez cette sous-spécification à la cible de votre application :

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

Configurer le partage d'écran avec Carthage

Ajoutez cette ligne au fichier Cartfile :

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

SwiftPM

Sélectionnez les produits UJET et UJETCobrowse, puis ajoutez-les à votre cible d'application.

Définissez la valeur de UJETGlobalOptions.cobrowseKey sur la clé de co-navigation de votre entreprise.

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

UJET.setGlobalOptions(options)

Partage de l'intégralité de l'écran de l'appareil (facultatif)

Le partage d'écran complet de l'appareil permet à vos agents d'assistance de voir les écrans d'applications autres que la vôtre. Cela est souvent utile lorsque les agents d'assistance doivent vérifier l'état des paramètres système ou voir l'utilisateur naviguer entre plusieurs applications. Si vous ne souhaitez pas utiliser cette fonctionnalité, vous pouvez ignorer cette section.

Pour personnaliser la boîte de dialogue de recueil du consentement pour le partage d'écran, vous devez implémenter le protocole UJETCobrowseAlertProvider dans votre classe de fournisseur. Dans cette implémentation, renvoyez un UIViewController personnalisé ou tout autre objet hérité de UIViewController via la méthode de protocole respective. UIViewController doit comporter deux boutons : un pour accepter et un pour refuser.

Après avoir obtenu le consentement, transmettez-le à notre SDK en appelant la fermeture consentStatus. UIViewController de cobrowseFullDeviceRequestAlert. Le délégué doit contenir le RPSystemBroadcastPickerView avec le titre (voir l'exemple de code ci-dessous) et doit comporter un autre bouton de refus. Appelez la fermeture "dismissed" lorsque l'utilisateur clique sur le bouton "Refuser".

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

Le contrôleur de vue personnalisé doit disposer d'une fermeture pour transmettre l'état du consentement au 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)
        }
    }
}

Le contrôleur de vue personnalisé pour l'alerte de demande d'appareil complet doit comporter RPSystemBroadcastPickerView et une fermeture pour transmettre l'état dismiss au 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?()
        }
    }
}

N'oubliez pas de transmettre ce fournisseur à notre SDK via l'API suivante :

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

Extension de diffusion

Cette fonctionnalité nécessite l'ajout d'une extension de diffusion.

  1. Ouvrez votre projet Xcode.

  2. Accédez à File> Target (Fichier > Cible).

  3. Sélectionnez Broadcast Upload Extension (Extension d'importation de diffusion).

  4. Saisissez un nom pour la cible.

  5. Décochez la case Inclure l'extension d'interface utilisateur.

  6. Créez la cible en notant son ID de bundle.

  7. Remplacez le SDK cible de votre extension de diffusion par iOS 12.0 ou version ultérieure.

Intégrer le SDK

CocoaPods : ajoutez la sous-spécification suivante à votre cible d'extension :

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

Si vous utilisez SwiftPM, sélectionnez le produit UJETCobrowseExtension et ajoutez-le à votre cible d'extension.

Configurer le partage de trousseau

Votre application et l'extension d'application que vous avez créée précédemment doivent partager certains secrets via le trousseau iOS. Pour ce faire, elles utilisent leur propre groupe de trousseaux afin d'être isolées du reste du trousseau de vos applications.

Dans Xcode, sélectionnez à la fois la cible de votre application et celle de votre extension. Ajoutez un droit de partage de trousseau pour le groupe de trousseaux io.cobrowse.

Ajouter l'ID du bundle à votre fichier plist

Prenez l'ID du bundle de l'extension que vous avez créée précédemment et ajoutez cette entrée au fichier Info.plist de votre application.

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

Implémenter l'extension

Xcode aura ajouté les fichiers SampleHandler.m et SampleHandler.h (ou SampleHandler.swift) à la cible que vous avez créée précédemment. Remplacez le contenu des fichiers par le code suivant :

Swift :

Sélectionnez le produit UJETCobrowseExtension et ajoutez-le à votre extension :

import CobrowseIOAppExtension
class SampleHandler: CobrowseIOReplayKitExtension {
}

ObjC :

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

Créer et exécuter votre application

Vous êtes maintenant prêt à créer et à exécuter votre application. La fonctionnalité complète de l'appareil n'est disponible que sur les appareils physiques. Elle ne fonctionnera pas dans le simulateur iOS.

Réduire le SDK

Le SDK Contact Center AI Platform peut être réduit lorsqu'une session de chat ou un appel est en cours. Cela peut être utile lorsque vous souhaitez rediriger l'utilisateur vers votre application après la réception d'un événement SDK, par exemple un clic sur une fiche de contenu. Pour réduire le SDK et rediriger l'utilisateur vers votre application, vous pouvez utiliser :

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
}

Action de remplacement

Vous pouvez utiliser la fonction didHandleUjetError pour le remplacement des erreurs inattendues. Si vous n'utilisez pas cette fonction ou si elle renvoie false, le SDK iOS gère l'erreur.

Le tableau suivant présente les erreurs que la fonction didHandleUjetError écoute :

Type d'erreur Code d'erreur Description
networkError 1 Le réseau n'est pas disponible.

Remarque : Cette erreur ne se déclenche pas lorsque le réseau n'est pas disponible pendant une session de chat ou d'appel, ou sur un écran d'évaluation.

authenticationError 100 Une erreur inattendue s'est produite lors de l'authentification.
authenticationJwtError 101 Une erreur inattendue s'est produite lors de la validation du jeton JWT (par exemple, une erreur d'analyse).
voipConnectionError 1000 Échec de l'établissement d'une connexion au fournisseur VoIP. Un rappel du SDK VoIP gère cela.
voipLibraryNotFound 1001 Le système s'attend à ce qu'un appel se connecte via un fournisseur VoIP, mais n'en trouve pas. Cela peut se produire si vous n'intégrez pas le bon SDK ou si vous n'ajoutez pas de bibliothèque de fournisseur VoIP à vos dépendances.
chatLibraryNotFound 1100 Se produit lorsque le système ne trouve pas la bibliothèque de chat. Cela peut se produire si vous intégrez un SDK incorrect ou si vous n'avez pas ajouté de bibliothèque de chat Twilio à vos dépendances.

L'exemple de code suivant montre comment utiliser la fonction 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
    }
}

Dépannage

Cette section fournit des informations pour vous aider à résoudre les problèmes courants.

Refus de l'envoi de l'application

L'envoi de l'application est refusé, car elle inclut le framework CallKit en Chine.

Si votre application est refusée par Apple pour cette raison, laissez simplement un commentaire, car le système est conçu pour désactiver le framework CallKit pour la région Chine lors d'un appel VoIP. Cette modification est effective à partir de la version 0.31.1 du SDK.

La taille du SDK est trop importante

Lorsque la taille du SDK est trop importante et difficile à suivre sur GitHub

Dans cet article, ils proposent deux choix. Nous vous recommandons d'utiliser Git LFS.

Si vous n'utilisez pas Bitcode, vous pouvez également supprimer le bitcode du binaire. Exécutez cette commande dans le dossier UJETKit.xcframework.

xcrun bitcode_strip -r UJET -o UJET

dyld : erreur "Library not loaded"

Ajoutez @executable_path/Frameworks aux chemins de recherche Runpath à partir de Target> Build Settings> Linking.

Envoi de l'application sur iTunes Connect

Apple peut poser la question suivante lors du processus d'examen, car le mode d'arrière-plan Voice over IP est activé :

Les utilisateurs peuvent-ils recevoir des appels VoIP dans votre application ?

Répondez Yes à la question.

La notification d'alerte n'est pas disponible au démarrage du SDK

Vérifiez les éléments suivants :

  • Utilisez un appareil réel, pas un simulateur.

  • Activez les notifications push et la fonctionnalité Modes en arrière-plan > Voix sur IP.

Si cela ne fonctionne pas, essayez de compiler avec un profil de provisionnement de distribution (Ad-hoc ou Apple Store).

Tester la notification push sur votre application de test

Préparez votre certificat VoIP et le jeton de l'appareil.

Sur le portail de la plate-forme CCAI, consultez la section Débogage des notifications push dans le menu Paramètres > Paramètres du développeur.

Si vous avez déjà défini le certificat pour APNS, vous n'avez pas besoin de le saisir à nouveau.

Saisissez votre certificat (facultatif), indiquez s'il s'agit d'un bac à sable ou non (facultatif), puis saisissez le jeton d'appareil pour les notifications push de votre application de test.

Le démarrage d'une nouvelle discussion a pris plus de 30 secondes

Vérifiez si vous répondez à une méthode déléguée de données client. Vous devez renvoyer des données personnalisées valides sur demande ou simplement renvoyer "nil" sur le bloc de réussite.

Utilisez cet extrait de code comme exemple de configuration :

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