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 présente les exigences suivantes :
- iOS 12.0 et versions ultérieures
Récupérer les identifiants de l'entreprise
Connectez-vous au portail Contact Center AI Platform (CCAI Platform) à l'aide d'identifiants d'administrateur.
Accédez à Paramètres> Paramètres pour les développeurs.
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 pour vous aider à démarrer avec le SDK mobile iOS CCAI Platform.
Installation
Pour commencer, vous devez installer le SDK iOS.
Télécharger l'application exemple
Téléchargez l'application exemple iOS.
Accédez au dossier et installez les dépendances à l'aide de CocoaPods :
$ pod install --project-directory=ExampleAppPour configurer rapidement les paramètres du projet, exécutez un script shell :
$ ./setup.shVous pouvez également modifier manuellement les paramètres du projet en procédant comme suit :
Ouvrez
ExampleApp.xcworkspace.Remplacez les valeurs
UJETCompanyKeyetUJETCompanySecretdansInfo.plistpar les valeurs Clé de l'entreprise et Code secret de l'entreprise de la page Paramètres > Paramètres pour les développeurs du portail de la plate-forme CCAI.Remplacez la valeur
UJETSubdomaindansInfo.plistpar le sous-domaine de l'URL de votre portail CCAI Platform. Le sous-domaine précède directement.ujet.comdans l'URL (par exemple,your-subdomaindanshttps://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.
Swift Package Manager
Ajoutez le package Swift pour le SDK iOS.
Dans vos paramètres de compilation, placez -ObjC dans "Other Linker Flags" (Autres indicateurs Linker).
Depuis la dernière version d'Xcode (actuellement la version 13.2), un problème connu se produit lors de l'utilisation de frameworks binaires distribués à l'aide de Swift Package Manager. La solution de contournement 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 de script doit avoir lieu après la phase de compilation "Embed Frameworks" (Intégrer les 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
Ajoutez la ligne suivante au Podfile :
pod 'UJET', :podspec => 'https://sdk.ujet.co/ios/x.y.z/ujet.podspec' #specific version x.y.zExé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 CCAI Platform n'est pas compatible avec Carthage. Pour ce faire, ajoutez les lignes suivantes :
binary "https://sdk.ujet.co/ios/UJETKit.json
binary "https://sdk.ujet.co/ios/UJETFoundationKit.json
binary https://raw.githubusercontent.com/twilio/twilio-voice-ios/Releases/twilio-voice-ios.json
Intégration manuelle
Cette fonctionnalité n'est pas disponible : https://github.com/twilio/conversations-ios/issues/12.
binary https://raw.githubusercontent.com/twilio/conversations-ios/master/twilio-convo-ios.json
Exécutez
carthage bootstrap --use-xcframeworks(oucarthage update --use-xcframeworkssi vous mettez à jour les dépendances).Téléchargez
UJETKit.xcframework,UJETFoundationKit.xcframework,UJETChatRedKit.xcframework,UJETChatBlueKit.xcframework,UJETTwilioCallKit.xcframeworket toutes les dépendancesTwilioVoice.xcframeworketTwilioConversationsClient.xcframework.Ajoutez UJETKit.xcframework à votre cible en le faisant glisser dans la section "Frameworks, Libraries, and Embedded Content" (Frameworks, bibliothèques et contenu intégré).
Répétez les étapes 2 et 3 pour toutes les dépendances de l'étape 1.
Dans vos paramètres de compilation, définissez
-ObjCsurOther Linker Flags.Ajoutez
libc++.tbden tant que dépendance dans la sectionLinked Frameworksde la cible.
Si vous prévoyez de compiler le SDK manuellement avec l'exemple de projet, suivez les étapes de la section suivante.
Compiler le SDK manuellement avec l'exemple de projet
Suivez ces étapes dans l'ordre :
Téléchargez tous les frameworks, y compris
UJETKit.xcframeworket d'autres dépendances.Créez le dossier CCAI Platform à la racine du projet et extrayez tous les frameworks.
Sélectionnez la cible
Objc-ManualouSwift-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 de 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 le retour du délégué.
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 tentera 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 signPayload : payloadType : succès : échec : méthode du délégué.
- (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 du serveur de votre 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
L'application envoie des notifications push pour demander des actions intelligentes telles que la validation et la prise de photos, ainsi que pour signaler un appel entrant. L'application nécessite que deux types de certificats différents (VoIP et APNs) soient enregistrés dans le portail d'administration.
Préparer le certificat des services VoIP
La documentation de référence est disponible pour les notifications push VoIP d'Apple.
Créez et téléchargez le certificat VoIP depuis le site Apple Developer.
Double-cliquez sur le certificat pour l'ajouter au trousseau.
Démarrez l'application Trousseau d'accès sur votre Mac.
Sélectionnez la catégorie "Mes certificats" dans la barre latérale de gauche.
Effectuez un clic droit sur le certificat "Services VoIP : your.app.id".
Dans le menu pop-up, sélectionnez Exporter.
Enregistrez-le sous le nom cert.p12 sans le protéger par un mot de passe en laissant le mot de passe vide.
Exécutez la commande suivante dans le terminal.
openssl s_client -connect gateway.push.apple.com:2195 -cert cert.pem -debug -showcertLa partie supérieure de cert.pem correspond au certificat et la partie inférieure à la clé privée.
Vérifiez que votre certificat fonctionne avec le serveur de notifications push d'Apple.
openssl s_client -connect gateway.push.apple.com:2195 -cert cert.pem -debug -showcertsSi l'opération réussit, le résultat suivant s'affiche :
--- New, TLSv1/SSLv3, Cipher is AES256-SHA Server public key is 2048 bit Secure Renegotiation IS supported Compression: NONE Expansion: NONE SSL-Session: Protocol : TLSv1 Cipher : AES256-SHA Session-ID: Session-ID-ctx: Master-Key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Key-Arg : None Start Time: 1475785489 Timeout : 300 (sec) Verify return code: 0 (ok) ---Connectez-vous au portail de la plate-forme CCAI avec des identifiants d'administrateur, puis accédez à Paramètres > Paramètres pour les développeurs > Application mobile.
Renseignez le certificat dans la section "Certificat des services VoIP", puis enregistrez. Veillez à inclure des limites (
-----BEGIN-----et-----END-----) pour le certificat et la clé privée.Cochez la case "Bac à sable" si vous exécutez une application avec un profil d'approvisionnement de développement, par exemple pour le débogage dans Xcode. Si votre application est archivée pour une distribution ad hoc ou sur l'App Store et qu'elle utilise un profil de provisionnement de distribution, décochez la case "Bac à sable".
Préparer le certificat SSL Apple Push Notification Service
La procédure est semblable à celle des certificats de service VOIP. Dans ce cas, le certificat SSL Apple Push Notification Service (bac à sable et production) est utilisé. Pour savoir comment créer le certificat, consultez la documentation sur le serveur de notifications à distance d'Apple.
Intégrer les notifications push
Dans AppDelegate.m :
@import PushKit;
@interface AppDelegate() <PKPushRegistryDelegate>
In application:didFinishLaunchingWithOptions: method:
// Initialize CCAI Platform
[UJET] initialize:UJET_COMPANY_KEY subdomain:UJET_SUBDOMAIN delegate:self];
// Register for VoIP notifications on launch.
PKPushRegistry *voipRegistry = [[PKPushRegistry alloc] initWithQueue: dispatch_get_main_queue()];
voipRegistry.delegate = self;
voipRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP];
Ajoutez les méthodes de délégué suivantes dans le fichier de protocole UIApplicationDelegate :
Veuillez imprimer le jeton de votre appareil pour tester les notifications push.
// PKPushRegistryDelegate
- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type {
[UJET updatePushToken:credentials.token type:UjetPushTypeVoIP];
}
- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type withCompletionHandler:(void (^)(void))completion {
if (payload.dictionaryPayload[@"ujet"]) {
[UjetreceivedNotification:payload.dictionaryPayload completion:completion];
} else {
completion();
}
}
// UIApplicationDelegate
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
[UjetupdatePushToken:deviceToken type:UjetPushTypeAPN];
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
if (userInfo[@"ujet"]) {
[UJET receivedNotification:userInfo completion:nil];
}
}
// UserNotificationsDelegate overrides [UIApplicationDelegate didReceiveRemoteNotification:]
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler {
NSDictionary *userInfo = notification.request.content.userInfo;
if (userInfo[@"ujet"] != nil) {
[UJET receivedNotification:userInfo completion:nil];
}
}
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler {
NSDictionary *userInfo = response.notification.request.content.userInfo;
if (userInfo[@"ujet"] != nil) {
[UJET receivedNotification:userInfo completion:nil];
}
}
Activer les notifications push
Sélectionnez votre cible et ouvrez l'onglet Fonctionnalités.
Activez le bouton 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 d'administration, accédez à Paramètres > Paramètres pour les développeurs. Sur cette page, recherchez la section Débogage des notifications push :

Copiez et collez le jeton de l'appareil dans la zone de texte de droite, puis sélectionnez la bonne application mobile.
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 de l'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 le fichier PEM du certificat et le jeton de l'appareil saisis, cliquez sur le bouton.
Si la notification push de test a bien été envoyée, le message Notification push configurée s'affiche.
La réception des notifications push n'est pas garantie à 100 %, 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 à partir d'iOS 10.0 qui accède à l'un des microphones, à la photothèque ou à 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 photothèque ou à l'appareil photo de l'appareil sans chaîne d'objectif correspondante, elle se ferme.
NSMicrophoneUsageDescription: Autorise l'accès au micro pour passer des appels et parler aux équipes d'assistance ou de dépannage, et pour envoyer des vidéos avec du son en lien avec des demandes concernant les produits.
NSCameraUsageDescription: Allows access to the camera for customer to take and send photos related to their customer support inquiry.
NSPhotoLibraryUsageDescription: Allows access for customer to send photos related to their customer support inquiry.
NSFaceIDUsageDescription : autorise l'accès à 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 à l'aide de cette clé et 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, Direct Access Point). Les étapes suivantes vous expliquent comment créer une DAP :
Connectez-vous au portail de la plate-forme CCAI avec des identifiants administrateur.
Accédez à Paramètres > File d'attente.
Sélectionnez une file d'attente dans la structure du menu.
Sélectionnez Créer un point d'accès direct.
Saisissez la clé dans le formulaire de texte.
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 demandes de signature de la charge utile à partir de l'application hôte. Le SDK l'utilisera jusqu'à son expiration ou sa révocation par l'appel clearUserData. L'application hôte est chargée de révoquer ce cache chaque fois que des données utilisateur ont été modifiées ou mises à jour, par exemple lors d'un événement de déconnexion.
[UJET clearUserData];
Vérifier si une session existe 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 de thème du SDK sont listées 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é à partir du portail 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 paire 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 nuance de la couleur souhaitée 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 teinte pour le mode sombre :
Thème de 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'application exemple et décommentez 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 cartes 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 du formulaire
Vous pouvez personnaliser les fiches de formulaire en même temps que le 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 :
Extrayez les informations pertinentes du dictionnaire d'événements (
smart_action_id,external_form_idetsignature).Générez un URI de formulaire et une signature pour les données du formulaire.
Transmettez les données du formulaire au SDK sous forme de dictionnaire
FormDataEventà l'aide decompletion closure.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()avecError.
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 et 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 la génération des signatures. Le même data doit être envoyé au SDK.
Transfert post-session
Vous pouvez personnaliser la VA post-session en même temps que le 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 VA post-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 dans 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 s'affiche dans l'application avec l'écran d'appel et l'appel s'affiche dans l'historique des appels du téléphone.
Pour démarrer une 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 40x40 sur l'écran de verrouillage lors de la réception d'un appel lorsque l'appareil est verrouillé. Placez une image dans votre fichier Xcassets et nommez-la "icon-call-kit".
Configurer le SDK
Vous pouvez définir plusieurs options avant de démarrer le SDK.
Pour en savoir plus, veuillez consulter la classe UJETGlobalOptions.
UJETGlobalOptions *options = [UJETGlobalOptions new];
options.fallbackPhoneNumber = @"+18001112222";
options.preferredLanguage = @"en";
[UJET setGlobalOptions:options];
Afficher ou masquer le bouton "Télécharger la transcription"
Vous pouvez configurer le SDK pour afficher ou masquer le bouton "Télécharger la transcription" dans le menu des 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;
Remplacement par le réseau téléphonique public
Nous proposons un basculement vers le réseau téléphonique public commuté (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
Les conditions du réseau ne sont pas suffisantes pour se connecter. Pour en savoir plus, consultez la propriété UJETGlobalOptions.pstnFallbackSensitivity.
Une erreur s'est produite lors de la connexion en raison de la configuration du pare-feu ou d'un problème lié au fournisseur.
Nous vous recommandons de définir le numéro SVI 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 réseau téléphonique commuté (RTC).
@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. La 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 15 Ko/s.
Pour configurer cette valeur, procédez comme suit :
Connectez-vous au portail de la plate-forme CCAI en tant qu'administrateur.
Accédez à Paramètres > Paramètres pour les développeurs > Applications mobiles.
Recherchez la section Seuil du numéro de téléphone de secours. La valeur par défaut est 0,85.
Spécifiez la nouvelle valeur de seuil.
Cliquez sur Enregistrer.
Désactiver les notifications push au niveau mondial
Vous pouvez désactiver les notifications Push au niveau global. Si vous définissez la propriété suivante sur false, vous contournez toutes les dépendances des notifications push et vous empêchez 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 en particulier 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 avec 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 qui permet à 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 le 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.
Langue sélectionnée sur l'écran de démarrage de l'application.
Langue par défaut sélectionnée :
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 fichierUJETGlobalOptions.h.La langue de l'appareil sélectionnée dans l'appareil (Paramètres> Général> Langue et région) sera utilisée si elle est prise en charge par l'application.
Le dialecte le plus proche de la langue de l'appareil sera utilisé lorsque l'application ne prend pas en charge la langue de l'appareil, mais prend en charge le dialecte parent le plus proche. Par exemple, si l'utilisateur a sélectionné l'espagnol de Cuba comme langue sur l'appareil et que l'application ne prend pas en charge l'espagnol de Cuba, mais prend en charge la langue mère espagnole, c'est l'espagnol qui sera utilisé.
L'anglais sera utilisé si la langue de l'appareil n'est pas prise en charge par l'application.
Configurer les icônes de liens de déviation externe
Vous pouvez personnaliser l'icône dans le canal de lien de redirection externe en l'important dans le catalogue d'éléments de votre application. Veillez à utiliser le même nom d'icône lorsque vous créez un lien de redirection externe dans Paramètres > Chat > Liens de redirection externe > Afficher les liens > Ajouter un lien de redirection 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 utilisera l'icône par défaut. Pour savoir comment ajouter des images au catalogue de composants, consultez ce lien.



Action de remplacement
Vous pouvez utiliser la fonction didHandleUjetError pour la solution de repli en cas d'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 de notation. |
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 cette opération. |
voipLibraryNotFound |
1001 | Le système s'attend à ce qu'un appel se connecte via un fournisseur VoIP, mais il n'en trouve aucun. Cela peut se produire si vous intégrez le mauvais 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 lorsque vous intégrez le mauvais SDK ou que 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
}
}
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 :
Méthode sécurisée : signature de données prédéfinie avec JWT.
Méthode non sécurisée : données prédéfinies avec un code JSON brut (non recommandé).
Utiliser la méthode sécurisée pour envoyer des données personnalisées
Vous devez implémenter la 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 par 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 vulnérabilité potentielle qui pourrait exposer votre application à une attaque MITM ("man in the middle"). Si vous choisissez d'utiliser cette méthode, nous ne serons pas responsables des risques de 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 de 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 des 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: string
agent : dictionnaire
name: string
avatar: string [url of agent avatar, optional]
transcription : tableau
sender: string ["end_user" ou "agent"]
timestamp: string [ie "2021-03-15 12:00:00Z"]
content: array
type : chaîne [text, media]
text: string [obligatoire pour le type "text"]
media: dictionary [required for media type]
type : chaîne [image ou vidéo]
url: string [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, les listes à puces, les liens hypertextes et le soulignement (--text--).
Exemple de données personnalisées { :#example-of-custom-data }
JSON encodé en JWT
Le fichier JSON doit valider le JWT. L'objet de données personnalisées correspond à 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 le libellé.
La clé est un identifiant unique pour les données. "label" est le nom à afficher sur la page du CRM. "type" est le type de la valeur.
string
- Chaîne JSON
nombre
- entier, float
date
- Format de code temporel UNIX UTC à 13 chiffres. (contient des millisecondes)
URL
- Format d'URL HTTP
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 de 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é, voici ce qui se passe :
- Les données personnalisées sont incluses dans le fichier de métadonnées de session.
- Les données personnalisées ne sont pas incluses dans les fiches CRM.
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": "VERIFIED_CUSTOMER_BOOLEAN": ,
"type": "boolean"
},
"reserved_bad_actor": {
"label": "Bad Actor",
"value": "VERIFIED_BAD_ACTOR_BOOLEAN": ,
"type": "boolean"
},
"reserved_repeat_customer": {
"label": "Repeat Customer",
"value": "REPEAT_CUSTOMER_BOOLEAN": ,
"type": "boolean"
}
}
}
Remplacez les éléments suivants :
VERIFIED_CUSTOMER_BOOLEAN: "True" si vous considérez que cet utilisateur final est un client légitime.VERIFIED_BAD_ACTOR_BOOLEAN: "True" si vous considérez que cet utilisateur final peut être un acteur malveillant.REPEAT_CUSTOMER_BOOLEAN: "True" si vous avez déterminé que cet utilisateur final a déjà contacté votre centre d'appels.
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émenter une méthode de délégué 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;
}
}
Connecter un événement reporté
[UJET connect:identifier forType:UjetIncomingTypeCall];
Configurer un lien profond
Cela permet aux agents qui reçoivent des appels RTCP d'utiliser les 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) dans 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é le lien universel ou le schéma d'URL personnalisé. Vous pouvez choisir l'une ou l'autre.
Implémenter la méthode de délégué pour gérer le lien profond
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. Dans le portail d'administration, sous "URL de l'application", saisissez l'un de vos liens sans paramètres de requête. Par exemple, saisissez your-company://support si vous utilisez un schéma d'URL personnalisé.
Dans la méthode du délégué, assurez-vous de 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 if agent didn't join
duration: (NSNumber) only for call
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) subdomain
device_model: (NSString)
device_version: (NSString)
sdk_version: (NSString)
timestamp : (NSString) ISO 8601
Données du menu de la file d'attente
Métadonnées
menu_id: NSString
menu_key: NSString, nullable
menu_name: NSString
menu_path : NSString
Données de session
Données du menu de la file d'attente
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 la sous-spécification suivante à la cible de votre application.
ruby
target 'MyApp' do
pod 'UJET'
pod 'UJET/Cobrowse'
end
Carthage : ajoutez la ligne suivante au Cartfile :
binary "https://sdk.ujet.co/ios/UJETKit.json"
SwiftPM : sélectionnez les produits UJET et UJETCobrowse, puis ajoutez-les à la cible de votre application.
Définissez également la propriété UJETGlobalOptions.cobrowseKey.
swift
let options = UJETGlobal
Options()options.cobrowseKey = cobrowseKey
UJET.setGlobalOptions(options)
Partage d'écran complet de l'appareil (facultatif)
Le partage de l'écran complet de l'appareil permet à vos agents d'assistance de voir les écrans des applications autres que les vôtres. 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.
Personnaliser la boîte de dialogue de consentement pour le partage d'écran
Pour personnaliser la boîte de dialogue de consentement au 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é UIViewController via la méthode de protocole respective. UIViewController doit comporter deux boutons : un pour accepter et un pour refuser.
Une fois le consentement obtenu, 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 "Refuser". Appelez la fermeture ignorée 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 comporter 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'accès complet à l'appareil 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.
Ouvrez votre projet Xcode.
Accédez à Fichier > Cible.
Sélectionnez Extension Broadcast Upload.
Saisissez un nom pour la cible.
Décochez Inclure l'extension d'UI.
Créez la cible et notez son ID de bundle.
Modifiez le SDK cible de votre extension de diffusion sur iOS 12.0 ou version ultérieure.
Intégrer le SDK
CocoaPods : ajoutez la sous-spécification suivante à la cible de votre 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 à la cible de votre extension.
Configurer le partage du trousseau
Votre application et l'extension d'application que vous avez créée précédemment doivent partager des secrets via le trousseau d'accès iOS. Pour ce faire, ils utilisent leur propre groupe Trousseau afin d'être isolés du reste du trousseau de vos applications.
Dans votre cible d'application et dans votre cible d'extension, ajoutez un droit de partage Keychain pour le groupe Keychain io.cobrowse.
Ajouter l'ID de bundle à votre fichier plist
Prenez l'ID de bundle de l'extension que vous avez créée précédemment et ajoutez l'entrée suivante dans le fichier Info.plist de votre application (pas dans le fichier Info.plist des extensions), en remplaçant l'ID de bundle suivant par le vôtre :
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 SampleHander.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 à la cible de 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 sont 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, comme 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
}
Dépannage
Refus de l'envoi d'une application
Le dépôt de l'application est refusé en raison de l'inclusion du framework CallKit sur le territoire chinois.
Si Apple refuse votre application pour cette raison, il vous suffit de laisser un commentaire, car le système est conçu pour désactiver le framework CallKit pour la région chinoise sur les appels 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, deux choix sont proposés. 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" (Bibliothèque non chargée)
Ajoutez @executable_path/Frameworks aux chemins de recherche Runpath dans Cible > Paramètres de compilation > Liaison.
Envoyer une application sur iTunes Connect
Apple peut poser la question suivante lors du processus d'examen en raison du mode d'arrière-plan Voice over IP activé :
Les utilisateurs peuvent-ils recevoir des appels VoIP dans votre application ?
Répondez Oui à la question.
La notification d'alerte est indisponible au démarrage du SDK
Vérifiez les éléments suivants :
Utilisez un appareil réel, et non un simulateur.
Activez les notifications push et Modes d'arrière-plan > Fonctionnalité Voice over IP.
Si cela ne fonctionne pas, essayez de créer un profil de provisionnement de distribution (Ad-hoc ou App Store).
Tester les notifications push sur votre application de test
Préparez votre certificat VoIP et le jeton de l'appareil.
Dans le portail CCAI Platform, consultez la section Débogage des notifications push du 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) et indiquez si vous utilisez le bac à sable (facultatif), puis saisissez le jeton d'appareil de notification 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 de délégation de données personnalisées. Vous devez renvoyer des données personnalisées valides sur demande ou simplement renvoyer nil dans 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)
}
}