SDK iOS
Un paquet Swift pour les apps iOS classiques : un chat de support natif en SwiftUI et UIKit. Les utilisateurs écrivent dans leur langue, votre équipe leur répond dans la leur.
Vous faites un jeu sous Unity ? Prenez le SDK Unity, qui couvre déjà les builds iOS. Ce paquet s'adresse aux apps écrites en Swift.
Installation
Dans Xcode, File → Add Package Dependencies, puis ajoutez cette URL, à partir de la version 0.2.3 :
https://github.com/inngame/sdk-ios
iOS 16 minimum (le paquet compile aussi pour macOS 13). Le paquet s'appelle Inngame. Le jeton de l'appareil est gardé dans le trousseau.
Configurer
Configurez une fois au lancement avec la clé publique de l'app (tableau de bord → votre app → Installation) :
import SwiftUI
import Inngame
@main
struct MyApp: App {
init() {
Inngame.configure(appKey: "app_XXXXXXXX")
}
var body: some Scene {
WindowGroup { ContentView() }
}
}
configure(appKey:baseURL:) parle à https://app.inngame.app par défaut ; ne passez baseURL que pour un autre serveur Inngame.
Identifier l'utilisateur
Les utilisateurs commencent anonymes : le chat marche avant toute connexion. Après la connexion, identifiez-les. Passez un token, un JWT signé par votre serveur avec le secret d'identité de l'app, pour les marquer vérifiés : seuls les utilisateurs vérifiés retrouvent leur historique sur tous leurs appareils. Voir Identité des joueurs.
// Unverified
Inngame.identify(userId: user.id, name: user.displayName, email: user.email)
// Verified, with attributes your team sees in the profile
let jwt = try await api.inngameToken()
Inngame.identify(userId: user.id,
name: user.displayName,
email: user.email,
token: jwt,
metadata: ["team": "blue", "signupYear": 2024])
metadata : jusqu'à 50 clés ; texte (500 caractères au plus), nombres, booléens. Les champs d'abonnement comme plan, paid ou subscription_status sont posés par le serveur seul et ignorés. Un token invalide ou expiré est refusé ; la raison arrive dans Inngame.shared.lastError.
Afficher le chat
SwiftUI :
struct SettingsView: View {
@State private var showHelp = false
var body: some View {
List {
Button("Help & support") { showHelp = true }
}
.sheet(isPresented: $showHelp) {
InngameChatView()
}
}
}
InngameChatView(showsCloseButton: false) masque le bouton de fermeture, pour une destination de navigation ou un onglet. UIKit (le chat s'ouvre en feuille) :
@objc func helpTapped() {
Inngame.present(from: self)
}
Le chat prend son allure dans le tableau de bord (Réglages → Apps → votre app → Apparence), sans mise à jour de l'app : votre logo dans la barre de titre à côté du titre, et les couleurs d'accent, de fond, de texte, d'en-tête, de bulle de l'équipe et de bulle de l'utilisateur pour les modes clair et sombre (ou un modèle : Inngame, Dark fantasy, Cartoon, Sci-fi, Minimal). Les couleurs laissées vides gardent l'allure du système.
Les messages écrits sans réseau sont gardés sur l'appareil, affichés « Envoi… » et renvoyés au retour du réseau ou au lancement suivant (une seule fois, grâce à un identifiant client). Un message refusé pour de bon par le serveur (utilisateur bloqué, par exemple) quitte la file au lieu d'être retenté sans fin.
Ce que l'utilisateur trouve dans le chat
- Les réponses de votre équipe traduites par le serveur dans la langue de l'utilisateur. L'interface elle-même existe en 25 langues (en, fr, de, es, it, pt, ru, ja, ko, zh, tr, pl, nl, sv, da, nb, fi, cs, uk, ar, hi, th, vi, id, ro).
- Les réponses en temps réel par WebSocket, un indicateur de saisie, et les messages plus anciens au défilement.
- Un bouton photo (sélecteur de photos du système) ; les photos sont réduites à 1600 px, envoyées en JPEG et modérées par le serveur. Il disparaît quand l'app désactive les photos (Réglages → Modération, ou mode mineurs).
- Si vous avez une FAQ dans l'inbox, un champ de recherche dans vos articles d'aide sur l'écran d'accueil (
Inngame.shared.searchFAQ(_:)dans le code). - Votre annonce en haut (une panne, un bug connu), traduite, jusqu'à l'heure choisie dans l'inbox.
- Sous le titre : votre délai de réponse habituel, ou « Absents · de retour lun. 09:00 » hors horaires, à l'heure locale de l'utilisateur (
Inngame.shared.config?.availability). - Les cartes envoyées par l'équipe ou l'IA :
reviewouvre la page « Rédiger un avis » de votre app sur l'App Store (https://apps.apple.com/app/id…?action=write-review), avec l'identifiant App Store de la carte ou des réglages Avis de l'app. Elle n'affiche jamais la fenêtre de note du système, qu'Apple déconseille d'afficher en réponse à un bouton.refundouvre la feuille de remboursement d'Apple pour la transaction de la carte (Transaction.beginRefundRequest) ; Apple décide. Envoyez-la depuis l'inbox avec un remboursement Apple (voir Intégrations). Sans identifiant de transaction, la carte ouvre la page « Signaler un problème » d'Apple.ratingest la question de satisfaction de 1 à 5 envoyée à la fermeture d'une conversation (si vous l'activez). Elle sert aux statistiques de votre équipe et ne décide jamais de qui reçoit une carte d'avis.offeretscreenferment le chat et appellentInngame.onOffer(avec l'identifiant de l'offre) ouInngame.onScreen(avec l'identifiant de l'écran et la valeur).itemaffiche Récupérer et appelleInngame.onItemGrantedune fois la réclamation confirmée par le serveur (une fois par carte, même sur plusieurs appareils).restoreappelleInngame.onRestorePurchases, ouAppStore.sync()de StoreKit si vous ne le définissez pas. Sans gestionnaire, le tap est signalé comme échoué et les cartes d'objet restent désactivées.- Les cartes
link, et toute autre carte qui a uneurl, l'ouvrent (http(s) ou lien profond de l'app).
defaultLabel: true) affichent le texte traduit du SDK (InngameCard.usesDefaultLabel). Les taps sont signalés à l'équipe. - Un utilisateur bloqué voit « Vous ne pouvez plus envoyer de messages ici. » à la place de la zone de saisie.
Branchez une fois, au lancement, les cartes qui ont besoin de votre code :
Inngame.onOffer = { offerId in paywall.show(offerId) }
Inngame.onScreen = { screen, value in router.open(screen, value) }
Inngame.onItemGranted = { item in inventory.refresh() } // credit it on your server (item.claimed webhook)
Inngame.onRestorePurchases = { Task { try? await AppStore.sync() } } // what happens when you leave it nil
Pastille de non-lus
Inngame.shared est un ObservableObject ; unreadCount est publié :
struct SupportRow: View {
@ObservedObject var support = Inngame.shared
@Binding var showHelp: Bool
var body: some View {
Button("Help & support") { showHelp = true }
.badge(support.unreadCount) // shown in List rows and tab items
}
}
En UIKit, abonnez-vous avec Combine : Inngame.shared.$unreadCount.sink { … }.
Contexte et événements
Le modèle d'appareil, la version d'iOS, la version de l'app, la langue et la région sont relevés à l'ouverture de la session. Ajoutez vos propres clés ; elles sont jointes aux messages suivants de l'utilisateur et visibles par l'équipe seulement :
Inngame.setContext("screen", "Checkout")
Inngame.setContext("orderId", order.id)
Inngame.setContext("orderId", nil) // remove
Envoyez des événements pour les automatisations, qui peuvent répondre par un message ou ouvrir le chat :
Inngame.track("checkout_failed", properties: ["step": "payment"])
Quand une automatisation demande d'ouvrir le chat, Inngame.shared.wantsToOpen passe à true : liez-le à votre feuille. Noms d'événements : lettres, chiffres, _, . et -, 64 caractères au plus.
Achats
Chaque utilisateur a un jeton d'achat émis par Inngame. Passez-le à StoreKit pour que les notifications de l'App Store retrouvent l'utilisateur (voir Intégrations) :
if let token = Inngame.purchaseToken { // UUID, nil before the first session
let result = try await product.purchase(options: [.appAccountToken(token)])
}
Notifications push
Transmettez le jeton APNs de l'appareil pour que les réponses atteignent l'utilisateur quand le chat est fermé. Envoyez votre clé APNs dans le tableau de bord (voir Intégrations).
func application(_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
#if DEBUG
Inngame.setPushToken(deviceToken, sandbox: true)
#else
Inngame.setPushToken(deviceToken)
#endif
}
Quand une notification est touchée, laissez Inngame la reconnaître. La fonction renvoie true pour les notifications Inngame et met Inngame.shared.wantsToOpen à vrai, que vous pouvez observer pour afficher le chat :
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse) async {
if Inngame.handleNotification(response.notification.request.content.userInfo) {
// Open the chat (or observe Inngame.shared.wantsToOpen in SwiftUI)
}
}
Déconnexion et suppression des données
// The user signed out: this device starts over as a new anonymous user
Inngame.logout()
// Right to erasure: deletes the user, their conversations and photos
try await Inngame.shared.deleteMyData()
Référence de l'API
| Appel | Rôle |
|---|---|
Inngame.configure(appKey:baseURL:) | Démarre le SDK. |
Inngame.identify(userId:name:email:token:metadata:) | Lie l'appareil à votre compte ; vérifié avec un JWT. |
Inngame.purchaseToken | Le jeton d'achat Inngame (UUID?) pour l'appAccountToken de StoreKit. |
Inngame.logout() | Nouvel utilisateur anonyme sur cet appareil. |
Inngame.setContext(_:_:) | Contexte personnalisé sur les messages suivants (nil pour retirer). |
Inngame.track(_:properties:) | Événement pour les automatisations. |
Inngame.setPushToken(_:sandbox:) | Enregistre le jeton APNs. |
Inngame.handleNotification(_:) | true si la notification vient d'Inngame ; met wantsToOpen à vrai. |
InngameChatView(showsCloseButton:) | Vue SwiftUI du chat (bouton de fermeture affiché par défaut). |
Inngame.present(from:) | Présente le chat depuis un contrôleur de vue UIKit. |
Inngame.shared.unreadCount | Nombre de non-lus, publié. |
Inngame.shared.wantsToOpen | Publié ; mis à vrai par un push ou une automatisation qui demande d'ouvrir le chat. |
Inngame.shared.messages · user · config · isBlocked · lastError | État publié : messages chargés, l'utilisateur vu par le serveur, réglages de l'inbox (apparence, photos, disponibilité…), blocage, dernière erreur. |
Inngame.shared.send(_:imageData:) | Envoie un message depuis le code (async). |
Inngame.shared.searchFAQ(_:) | Cherche dans vos articles d'aide (async). |
Inngame.shared.pending | Messages publiés pas encore acceptés par le serveur (file hors ligne). |
Inngame.isUnavailable | true tant que votre support est indisponible (votre espace Inngame est suspendu) ; aussi publié sous Inngame.shared.isUnavailable. Le chat affiche alors « Le support est indisponible pour le moment. Réessayez plus tard. » dans la langue de l'utilisateur et masque la zone de saisie ; les messages non envoyés attendent dans pending et partent au retour du support. Le SDK revérifie toutes les 10 minutes chat ouvert et à chaque ouverture, et revient à la normale tout seul. En attendant, deleteMyData() lève une InngameError avec isSuspension. |
Inngame.shared.deleteMyData() | Efface les données de l'utilisateur (async throws). |