L’SDK iOS di Hooky
La conversazione Hooky, nativa nella tua app per iPhone, iPad o Mac (Catalyst): scritta in SwiftUI, senza WebView, con i colori del tuo sito. I tuoi utenti scrivono, il tuo team risponde dalla stessa inbox della bolla web.
Requisiti
- iOS 15 o successivo, e Mac Catalyst 15 o successivo.
- Xcode 16 o successivo (il pacchetto è scritto in Swift 6). La tua app può restare in Swift 5.
- Nessuna dipendenza di terze parti: URLSession, WebSocket, PhotosUI e il Portachiavi, nient’altro.
- Un account Hooky e un sito: la sua chiave pubblica è in Siti › Installa, la stessa del
data-site-keydella bolla web.
Installazione
È supportato solo Swift Package Manager. In Xcode: File › Add Package Dependencies…, incolla l’indirizzo del pacchetto, mantieni la regola Up to Next Major Version a partire da 1.0.0, poi aggiungi il prodotto Hooky al target della tua app.
https://github.com/Defdjamel/hooky-iosOppure in un Package.swift:
dependencies: [
.package(url: "https://github.com/Defdjamel/hooky-ios", from: "1.0.0"),
],
targets: [
.target(name: "MiaApp", dependencies: [
.product(name: "Hooky", package: "hooky-ios"),
]),
]Il pacchetto include il suo manifesto sulla privacy (PrivacyInfo.xcprivacy), che Xcode aggiunge al report sulla privacy della tua app. Il codice sorgente è sotto licenza MIT: github.com/Defdjamel/hooky-ios.
Iniziare in 3 righe
import Hooky
// 1. All’avvio (App.init o application(_:didFinishLaunchingWithOptions:))
Hooky.configure(siteKey: "LA_TUA_CHIAVE_PUBBLICA")
// 2. La bolla fluttuante, su qualsiasi vista SwiftUI
ContentView().hookyBubble()
// 3. O la conversazione dal tuo pulsante
Hooky.open()configure carica il nome, il colore e il messaggio di benvenuto del sito, riprende la conversazione salvata sul dispositivo e apre la connessione in diretta. Chiamalo una volta, prima di qualsiasi altra chiamata.
@main
struct MiaApp: App {
init() { Hooky.configure(siteKey: "LA_TUA_CHIAVE_PUBBLICA") }
var body: some Scene {
WindowGroup { ContentView().hookyBubble() }
}
}| Parametro | Ruolo |
|---|---|
siteKey | Obbligatorio. La chiave pubblica del sito. |
language | Facoltativo. Impone la lingua della conversazione: "fr", "en", "es", "pt", "it" o "de". Senza, quella del dispositivo (vedi Lingua, modalità scura, accessibilità). |
translation | Facoltativo, true per impostazione predefinita. false rimuove la traduzione dei messaggi del team (vedi Traduzione dei messaggi). |
apiURL | Facoltativo, per fare test sul tuo server. Per impostazione predefinita https://api.heyhooky.com. |
La bolla
Un pulsante fluttuante del colore del tuo sito, con il logo Hooky e un badge rosso per le risposte non ancora lette. Toccandolo si apre la conversazione.
SwiftUI
ContentView()
.hookyBubble() // nell’angolo in basso, dal lato impostato per il sito
ContentView()
.hookyBubble(alignment: .bottomLeading, padding: 24, hidden: inPagamento)
HookyBubble(size: 56) // il pulsante da solo, da posizionare tu
HookyBubble { mostraMioFoglio = true } // con la tua azionealignmentimpone l’angolo (altrimenti, il lato sinistro o destro impostato in Siti);paddingla distanza dai bordi (60 punti di lato, 20 di margine per impostazione predefinita);hiddenla nasconde su una schermata dove darebbe fastidio.- La bolla scompare mentre la conversazione è aperta.
UIKit
// Nell’angolo in basso, rispettando l’area sicura (leading: true per la sinistra)
Hooky.showBubble(in: view)
// O una UIView da posizionare tu
let bolla = HookyBubbleView(size: 60)Aprire e chiudere la conversazione
Hooky.open() // foglio a tutta altezza, sopra il controller più in alto
Hooky.open(from: self) // UIKit: presentata da questo controller
Hooky.close() // la richiudeSu iPhone, la conversazione si apre in un foglio a tutta altezza; su iPad e Mac, in un foglio centrato. Il tuo utente la chiude con il pulsante × o trascinandola verso il basso. Per presentarla tu (in uno .sheet, in una navigazione…), usa la vista:
.sheet(isPresented: $chat) { HookyConversationView() }Messaggi non letti
La bolla mostra già il suo badge. Per i tuoi pulsanti (una scheda Aiuto, un’icona di menu), il numero di risposte del team non ancora viste si segue in diretta. Torna a 0 quando la conversazione si apre.
// SwiftUI: Hooky.shared è un ObservableObject
@ObservedObject var hooky = Hooky.shared
AiutoView()
.tabItem { Label("Aiuto", systemImage: "questionmark.bubble") }
.badge(hooky.unreadCount)
// UIKit: un AsyncStream (il valore attuale, poi ogni modifica)
Task {
for await n in Hooky.unreadCountUpdates() {
tabBarItem.badgeValue = n > 0 ? "\(n)" : nil
}
}
// O una closure, chiamata sul thread principale
Hooky.shared.onUnreadCountChange = { n in print(n) }Hooky.shared pubblica anche siteName, isOpen e isConfigured, per la tua interfaccia. Le risposte arrivano in diretta finché l’app è in primo piano; quando torna in primo piano, l’SDK recupera ciò che si è perso. La versione 1.0.0 non invia notifiche push ai tuoi utenti.
Identificare l’utente
La tua app sa chi ha effettuato l’accesso: dillo a Hooky. Nella scheda della conversazione, il tuo team legge il nome, l’email, il tuo identificativo dell’utente, il dispositivo e i dati che aggiungi. Con un’email, la conversazione non chiede più né il nome né l’email.
// Dopo l’accesso
Hooky.identify(
name: "Lila",
email: "lila@example.com",
userId: "u_1042", // il tuo identificativo
attributes: ["piano": "Pro", "carrello": 49.9, "beta": true]
)
// Più tardi: uniti ai precedenti
Hooky.setAttributes(["carrello": 12, "ultima_schermata": "Pagamento"])
Hooky.setAttributes(["beta": nil]) // nil cancella una chiave
// All’uscita
Hooky.reset()I valori sono degli HookyValue: un testo, un numero o un booleano. I letterali si scrivono così come sono; per una variabile, specifica il tipo: ["piano": .string(user.piano), "carrello": .number(totale), "beta": .bool(beta)].
Come funziona
- Nessuna conversazione ancora: tutto resta in memoria e parte con il primo messaggio. Dopo, ogni modifica parte subito, raggruppata (un solo invio per tutto ciò che cambia nello stesso secondo). Offline, riparte con il messaggio successivo o quando l’app torna attiva.
nameedemailsostituiscono quelli della conversazione (nilli cancella sul dispositivo);userId: nillo lascia invariato.- Lo
userIdviene conservato con il token della conversazione. UnouserIddiverso significa un altro account sullo stesso dispositivo: la conversazione viene dimenticata, come conreset(). Il nuovo utente non vede la conversazione del precedente. Hooky.reset()chiude la conversazione e dimentica i messaggi, l’identità, i dati e la bozza. Chiamalo all’uscita.- Nessuna di queste chiamate genera errori. Chiama prima
configure: altrimenti,identifyesetAttributesnon fanno nulla (e si fermano su un’asserzione in Debug).
Le regole dei dati
| Campo | Regola |
|---|---|
| Dati liberi | Piatti: 50 chiavi al massimo, 8 KB in tutto. Le nuove chiavi sostituiscono le vecchie, le altre restano. |
| Chiave | Da 1 a 64 caratteri: lettere, cifre, spazi, _, . e -. Altrimenti viene ignorata. |
| Valore | Un testo (tagliato a 500 caratteri), un numero o un booleano. nil cancella la chiave. |
userId | 200 caratteri al massimo. |
name, email | Il nome: 100 caratteri al massimo. L’email: un indirizzo valido, altrimenti viene ignorata. |
Hooky ripulisce ciò che supera i limiti, senza mai rifiutare il messaggio del tuo utente.
Questi dati sono dichiarativi: la chiave del sito è pubblica, e chiunque può usarla per inviare un nome, un’email o uno userId a sua scelta. Aiutano il tuo team a inquadrare la persona, non provano chi è. Non metterci mai segreti (password, token, numero di carta) e non usarli mai per concedere un accesso o agire su un account: verifica sempre dalla tua parte.
Statistiche
Conta quello che succede nella tua app, come Hooky.track sul web: le schede compaiono in Statistiche, insieme a quelle del sito. Chiamalo subito dopo che l’azione è andata a buon fine.
Hooky.track("iscrizione")
Hooky.track("ordine", value: 49.9, properties: ["piano": "pro", "posti": 3, "prova": false])value: un importo o una quantità, sommato nella scheda. Le proprietà sono piatte (testi, numeri, booleani).- Si può chiamare da qualsiasi thread, e anche prima di
configure: gli eventi aspettano (100 al massimo). Non genera mai errori; offline, l’evento va perso. - Stesse regole del web (nome, 20 proprietà al massimo, limite): vedi Statistiche nella pagina.
Non inviare mai dati personali (email, nome, telefono) in un evento. Conta le azioni, non le persone.
Cosa fa la conversazione
- Il primo messaggio, con nome ed email facoltativi (o quelli di
identify), poi una conversazione come quella della bolla web: nomi del team, «… sta scrivendo…», «Letto», link cliccabili, emoji in grande, messaggi precedenti su richiesta. - Fino a 4 allegati per messaggio, 10 MB ciascuno: foto (JPEG, PNG, HEIC, WebP, GIF) o PDF. Le foto della libreria passano dal selettore di sistema (nessuna autorizzazione da chiedere) e vengono ridotte a 2048 px prima dell’invio; i file arrivano dall’app File.
- Per proporre la fotocamera, aggiungi
NSCameraUsageDescriptionall’Info.plistdella tua app. Senza, l’opzione è semplicemente nascosta. - Le foto ricevute si aprono a schermo intero (zoom, scorrimento, condivisione), i file in Visualizzazione rapida.
- La bozza sopravvive alla chiusura della conversazione e al riavvio dell’app. La tastiera non copre mai il campo di testo.
- Errori chiari, nella lingua dell’utente: offline, file troppo pesante, troppi file, troppi messaggi insieme.
Traduzione dei messaggi
Il tuo utente e il tuo team scrivono ognuno nella propria lingua, come con la bolla web (vedi Traduzione dei messaggi).
- Sotto un messaggio del team scritto in una lingua diversa da quella della conversazione (quella del dispositivo, o quella imposta da
language), un link discreto Traduci. Una volta tradotto, il messaggio compare nella lingua dell’utente, con «Tradotto dall’inglese · Mostra l’originale» per tornare al testo di partenza. - Il pulsante di traduzione nell’intestazione propone Traduci sempre: i messaggi del team arrivano allora già tradotti. La scelta viene conservata sul dispositivo, per ogni chiave del sito.
- Una traduzione non riuscita mostra un breve errore con Riprova. I messaggi dell’utente non vengono mai tradotti dalla sua parte; il tuo team ha il suo pulsante Traduci, nella dashboard e nell’app Hooky.
- Si traduce solo il testo, non gli allegati. La traduzione è attiva per impostazione predefinita; per toglierla dalla tua app:
Hooky.configure(siteKey: "LA_TUA_CHIAVE_PUBBLICA", translation: false).
Cosa invia l’SDK
Quello che invia la bolla web, e nient’altro. Nessun identificativo pubblicitario, nessun tracciamento tra app, nessuna libreria di terze parti. Per compilare la sezione Privacy dell’app della tua scheda App Store:
| Dato | Quando | Scheda App Store |
|---|---|---|
| Messaggi, foto e PDF | Quando l’utente scrive | Contenuti utente: Assistenza clienti, Foto o video |
| Nome ed email | Se li fornisce, o se li passi a identify | Informazioni di contatto: Nome, Indirizzo email |
Il tuo userId | Se lo passi a identify | Identificativi: ID utente |
| Dati liberi | Quello che passi a identify e setAttributes | Secondo quello che ci metti (un carrello: Acquisti › Cronologia acquisti…) |
Eventi track | Quando li invii, senza identificativo della persona | Dati di utilizzo: Interazione con il prodotto (Analisi), non collegati all’utente |
| Dispositivo, lingua, fuso orario, app | Con la conversazione | Informazioni tecniche per il tuo team (vedi sotto) |
- Tutti questi dati servono alla Funzionalità dell’app (l’assistenza clienti), e le statistiche alla tua Analisi. Nessuno serve al tracciamento nel senso di Apple: nessuna richiesta App Tracking Transparency.
- Il dispositivo è leggibile: «iPhone 15 Pro · iOS 18.2», «iPad Air 11-inch (M2) · iPadOS 18.1», «Mac · macOS 15.1». Ogni richiesta porta
X-Hooky-Client: hooky-ios/1.0.0eX-Hooky-App: <bundle id> <version> (<build>): il tuo team vede che la conversazione arriva da «iOS · com.example.app 2.3 (45)». - Sul dispositivo: il token della conversazione (l’unica chiave dell’utente) e lo
userIdnel Portachiavi (solo questo dispositivo, dopo il primo sblocco), per chiave del sito; la bozza, l’ultimo messaggio visto e la scelta «Traduci sempre» inUserDefaults. Una traduzione invia solo l’identificativo del messaggio e la lingua della conversazione. - Hooky tratta questi dati per tuo conto (responsabile del trattamento ai sensi del GDPR): vedi l’informativa sulla privacy. Resti responsabile della tua scheda: dichiara ciò che la tua app invia davvero.
Lingua, modalità scura, accessibilità
- Lingua
- L’interfaccia esiste in francese, inglese, spagnolo, portoghese, italiano e tedesco. Segue le lingue preferite del dispositivo, l’inglese per impostazione predefinita;
Hooky.configure(siteKey:language:)ne impone una. La lingua parte con il primo messaggio, perché il tuo team risponda nella lingua giusta e all’ora giusta (con il fuso orario). - Modalità scura
- La conversazione segue la modalità chiara o scura del dispositivo, con il colore del tuo sito. Niente da impostare.
- Accessibilità
- Ogni controllo ha la sua etichetta VoiceOver (la bolla: «Apri la chat con …», e il numero di non letti); Dynamic Type ovunque, e la tastiera non copre mai il campo di testo.
Risoluzione dei problemi
- Il mio sito ha dichiarato i suoi indirizzi: l’app viene rifiutata?
- No. Un’app non ha un indirizzo: si presenta con la sua intestazione
X-Hooky-Cliente funziona anche quando il sito ha dichiarato i suoi domini. Non aggiungere nulla in Siti. - La bolla mantiene i colori predefiniti, o non arriva nulla
- Controlla la chiave: è la chiave pubblica del sito (non la chiave segreta
hks_…, né una chiave APIhka_…). Con una chiave sconosciuta, la console di Xcode mostra «Hooky: the site couldn’t be loaded (HTTP 404…)». - Offline
- Il messaggio mostra «Nessuna connessione» e resta nel campo;
identifyesetAttributesripartono più tardi. L’aspetto del sito si ricarica all’apertura della conversazione. - L’opzione Fotocamera non compare
- Aggiungi
NSCameraUsageDescriptional tuoInfo.plist. - Un altro utente vede la vecchia conversazione
- Chiama
Hooky.reset()all’uscita, e passa unouserIdaidentify: un identificativo diverso riparte da una conversazione vuota.
Cronologia delle versioni
- 1.0.0
- Prima versione:
configure, la bolla SwiftUI e UIKit,openeclose,identify(conuserIde dati liberi),setAttributes,track,reset, i non letti in diretta, foto e PDF, la traduzione dei messaggi del team, sei lingue, modalità scura, VoiceOver e Dynamic Type.