Das iOS-SDK von Hooky
Das Hooky-Gespräch, nativ in deiner App für iPhone, iPad oder Mac (Catalyst): in SwiftUI geschrieben, ohne WebView, in den Farben deiner Website. Deine Nutzer schreiben, dein Team antwortet aus demselben Posteingang wie bei der Web-Bubble.
Voraussetzungen
- Ab iOS 15, und ab Mac Catalyst 15.
- Ab Xcode 16 (das Paket ist in Swift 6 geschrieben). Deine App kann bei Swift 5 bleiben.
- Keine Abhängigkeiten von Drittanbietern: URLSession, WebSocket, PhotosUI und der Schlüsselbund, sonst nichts.
- Ein Hooky-Konto und eine Website: Ihr öffentlicher Schlüssel steht unter Websites › Bubble installieren, derselbe wie das
data-site-keyder Web-Bubble.
Installation
Nur Swift Package Manager wird unterstützt. In Xcode: File › Add Package Dependencies…, die Adresse des Pakets einfügen, die Regel Up to Next Major Version ab 1.0.0 beibehalten, dann das Produkt Hooky zum Target deiner App hinzufügen.
https://github.com/Defdjamel/hooky-iosOder in einer Package.swift:
dependencies: [
.package(url: "https://github.com/Defdjamel/hooky-ios", from: "1.0.0"),
],
targets: [
.target(name: "MeineApp", dependencies: [
.product(name: "Hooky", package: "hooky-ios"),
]),
]Das Paket bringt sein Privacy Manifest (PrivacyInfo.xcprivacy) mit; Xcode übernimmt es in den Datenschutzbericht deiner App. Der Quellcode steht unter MIT-Lizenz: github.com/Defdjamel/hooky-ios.
Loslegen in 3 Zeilen
import Hooky
// 1. Beim Start (App.init oder application(_:didFinishLaunchingWithOptions:))
Hooky.configure(siteKey: "DEIN_OEFFENTLICHER_SCHLUESSEL")
// 2. Die schwebende Bubble, auf jeder SwiftUI-View
ContentView().hookyBubble()
// 3. Oder das Gespräch über deinen eigenen Button
Hooky.open()configure lädt Name, Farbe und Begrüßungsnachricht der Website, nimmt das auf dem Gerät gespeicherte Gespräch wieder auf und öffnet die Live-Verbindung. Ruf es einmal auf, vor jedem anderen Aufruf.
@main
struct MeineApp: App {
init() { Hooky.configure(siteKey: "DEIN_OEFFENTLICHER_SCHLUESSEL") }
var body: some Scene {
WindowGroup { ContentView().hookyBubble() }
}
}| Parameter | Bedeutung |
|---|---|
siteKey | Pflicht. Der öffentliche Schlüssel der Website. |
language | Optional. Legt die Sprache des Gesprächs fest: "fr", "en", "es", "pt", "it" oder "de". Ohne Angabe die des Geräts (siehe Sprache, Dark Mode, Barrierefreiheit). |
translation | Optional, standardmäßig true. false entfernt die Übersetzung der Nachrichten des Teams (siehe Nachrichten übersetzen). |
apiURL | Optional, zum Testen gegen deinen eigenen Server. Standardmäßig https://api.heyhooky.com. |
Die Bubble
Ein schwebender Button in der Farbe deiner Website, mit dem Hooky-Logo und einem roten Badge für noch nicht gelesene Antworten. Ein Tippen öffnet das Gespräch.
SwiftUI
ContentView()
.hookyBubble() // in der unteren Ecke, auf der für die Website eingestellten Seite
ContentView()
.hookyBubble(alignment: .bottomLeading, padding: 24, hidden: istBeiZahlung)
HookyBubble(size: 56) // nur der Button, den du selbst platzierst
HookyBubble { zeigeMeinSheet = true } // mit deiner eigenen Aktionalignmentlegt die Ecke fest (sonst die unter Websites eingestellte linke oder rechte Seite);paddingden Abstand zu den Rändern (standardmäßig 60 Punkte groß, 20 Abstand);hiddenblendet sie auf einem Bildschirm aus, auf dem sie stören würde.- Die Bubble verschwindet, solange das Gespräch offen ist.
UIKit
// In der unteren Ecke, mit Safe Area (leading: true für links)
Hooky.showBubble(in: view)
// Oder eine UIView, die du selbst platzierst
let bubble = HookyBubbleView(size: 60)Gespräch öffnen und schließen
Hooky.open() // Sheet in voller Höhe, über dem obersten View-Controller
Hooky.open(from: self) // UIKit: von diesem View-Controller präsentiert
Hooky.close() // schließt es wiederAuf dem iPhone öffnet sich das Gespräch als Sheet in voller Höhe, auf iPad und Mac als zentriertes Sheet. Dein Nutzer schließt es mit dem ×-Button oder indem er es nach unten wischt. Um es selbst zu präsentieren (in einem .sheet, einer Navigation …), nimm die View:
.sheet(isPresented: $chat) { HookyConversationView() }Ungelesene Nachrichten
Die Bubble zeigt ihr Badge schon selbst. Für deine eigenen Buttons (ein Hilfe-Tab, ein Menü-Icon) lässt sich die Zahl der noch nicht gesehenen Antworten des Teams live verfolgen. Sie fällt auf 0, sobald sich das Gespräch öffnet.
// SwiftUI: Hooky.shared ist ein ObservableObject
@ObservedObject var hooky = Hooky.shared
HilfeView()
.tabItem { Label("Hilfe", systemImage: "questionmark.bubble") }
.badge(hooky.unreadCount)
// UIKit: ein AsyncStream (der aktuelle Wert, dann jede Änderung)
Task {
for await n in Hooky.unreadCountUpdates() {
tabBarItem.badgeValue = n > 0 ? "\(n)" : nil
}
}
// Oder eine Closure, auf dem Main-Thread aufgerufen
Hooky.shared.onUnreadCountChange = { n in print(n) }Hooky.shared veröffentlicht außerdem siteName, isOpen und isConfigured, für deine Oberfläche. Antworten kommen live an, solange die App im Vordergrund ist; kommt sie wieder in den Vordergrund, holt das SDK nach, was es verpasst hat. Version 1.0.0 schickt deinen Nutzern keine Push-Benachrichtigungen.
Nutzer identifizieren
Deine App weiß, wer angemeldet ist: Sag es Hooky. Dein Team sieht in den Details des Gesprächs den Namen, die E-Mail, deine eigene Nutzer-ID, das Gerät und die Daten, die du mitgibst. Mit einer E-Mail fragt das Gespräch weder nach Vornamen noch nach E-Mail.
// Nach der Anmeldung
Hooky.identify(
name: "Lila",
email: "lila@example.com",
userId: "u_1042", // deine eigene ID
attributes: ["tarif": "Pro", "warenkorb": 49.9, "beta": true]
)
// Später: mit den bisherigen zusammengeführt
Hooky.setAttributes(["warenkorb": 12, "letzter_bildschirm": "Zahlung"])
Hooky.setAttributes(["beta": nil]) // nil löscht einen Schlüssel
// Bei der Abmeldung
Hooky.reset()Die Werte sind HookyValue: ein Text, eine Zahl oder ein Boolean. Literale schreibst du einfach hin; bei einer Variable gibst du den Typ an: ["tarif": .string(user.tarif), "warenkorb": .number(summe), "beta": .bool(beta)].
So funktioniert es
- Noch kein Gespräch: Alles bleibt im Speicher und wird mit der ersten Nachricht gesendet. Danach wird jede Änderung sofort gesendet, gebündelt (eine einzige Anfrage für alles, was sich in derselben Sekunde ändert). Offline geht sie mit der nächsten Nachricht raus oder wenn die App zurückkommt.
nameundemailersetzen die des Gesprächs (nillöscht sie auf dem Gerät);userId: nillässt sie unverändert.- Die
userIdwird zusammen mit dem Gesprächs-Token gespeichert. Ist dieuserIdeine andere, ist das ein anderes Konto auf demselben Gerät: Das Gespräch wird vergessen, wie beireset(). Der neue Nutzer sieht den Verlauf des vorherigen nicht. Hooky.reset()schließt das Gespräch und vergisst Verlauf, Identität, Daten und Entwurf. Ruf es bei der Abmeldung auf.- Keiner dieser Aufrufe wirft einen Fehler. Ruf vorher
configureauf: Sonst tunidentifyundsetAttributesnichts (und halten im Debug-Build an einer Assertion an).
Regeln für die Daten
| Feld | Regel |
|---|---|
| Freie Daten | Flach: höchstens 50 Schlüssel, insgesamt 8 KB. Neue Schlüssel ersetzen die alten, die übrigen bleiben. |
| Schlüssel | 1 bis 64 Zeichen: Buchstaben, Ziffern, Leerzeichen, _, . und -. Sonst wird er ignoriert. |
| Wert | Ein Text (nach 500 Zeichen abgeschnitten), eine Zahl oder ein Boolean. nil löscht den Schlüssel. |
userId | Höchstens 200 Zeichen. |
name, email | Der Name: höchstens 100 Zeichen. Die E-Mail: eine gültige Adresse, sonst wird sie ignoriert. |
Hooky räumt auf, was darüber hinausgeht, und lehnt die Nachricht deines Nutzers deswegen nie ab.
Diese Daten sind Angaben, keine Beweise: Der Schlüssel der Website ist öffentlich, und jeder kann ihn nutzen, um einen beliebigen Namen, eine E-Mail oder eine userId seiner Wahl zu senden. Sie helfen deinem Team, die Person einzuordnen, beweisen aber nicht, wer sie ist. Leg dort nie etwas Geheimes ab (Passwort, Token, Kartennummer) und nutze sie nie, um Zugriff zu gewähren oder an einem Konto etwas zu ändern: Prüf immer auf deiner Seite nach.
Stats
Zähl, was in deiner App passiert, wie mit Hooky.track im Web: Die Kacheln erscheinen unter Statistiken, zusammen mit denen der Website. Ruf es direkt auf, nachdem die Aktion geklappt hat.
Hooky.track("anmeldung")
Hooky.track("bestellung", value: 49.9, properties: ["tarif": "pro", "plaetze": 3, "testphase": false])value: ein Betrag oder eine Menge, in der Kachel summiert. Die Properties sind flach (Texte, Zahlen, Booleans).- Von jedem Thread aus aufrufbar, sogar vor
configure: Die Events warten (höchstens 100). Wirft nie einen Fehler; offline geht das Event verloren. - Dieselben Regeln wie im Web (Name, höchstens 20 Properties, Limit): siehe Stats in der Seite.
Schick nie personenbezogene Daten (E-Mail, Name, Telefon) in einem Event. Zähl Aktionen, keine Personen.
Was das Gespräch kann
- Die erste Nachricht, mit optionalem Vornamen und optionaler E-Mail (oder denen aus
identify), dann ein Verlauf wie in der Web-Bubble: Namen des Teams, „… schreibt…“, „Gelesen“, anklickbare Links, große Emojis, ältere Nachrichten auf Abruf. - Bis zu 4 Anhänge pro Nachricht, je 10 MB: Fotos (JPEG, PNG, HEIC, WebP, GIF) oder PDFs. Fotos aus der Mediathek kommen über die Auswahl des Systems (keine Berechtigung nötig) und werden vor dem Senden auf 2048 px verkleinert; Dateien kommen aus der App Dateien.
- Um die Kamera anzubieten, füg
NSCameraUsageDescriptionzurInfo.plistdeiner App hinzu. Ohne den Eintrag ist die Option einfach ausgeblendet. - Empfangene Fotos öffnen sich im Vollbild (Zoom, Wischen, Teilen), Dateien in der Übersicht (Quick Look).
- Der Entwurf übersteht das Schließen des Gesprächs und einen Neustart der App. Die Tastatur verdeckt nie das Eingabefeld.
- Verständliche Fehlermeldungen, in der Sprache des Nutzers: offline, Datei zu groß, zu viele Dateien, zu viele Nachrichten auf einmal.
Nachrichten übersetzen
Dein Nutzer und dein Team schreiben jeweils in ihrer Sprache, wie mit der Web-Bubble (siehe Nachrichten übersetzen).
- Unter einer Nachricht des Teams in einer anderen Sprache als der des Gesprächs (der des Geräts oder der mit
languagefestgelegten) steht ein dezenter Link Übersetzen. Übersetzt erscheint die Nachricht in der Sprache des Nutzers, mit „Übersetzt aus dem Englischen · Original anzeigen“, um zum ursprünglichen Text zurückzukehren. - Der Übersetzungsknopf in der Kopfzeile bietet Immer übersetzen an: Die Nachrichten des Teams kommen dann schon übersetzt an. Die Wahl wird auf dem Gerät gespeichert, pro Website-Schlüssel.
- Schlägt eine Übersetzung fehl, erscheint ein kurzer Fehler mit Erneut versuchen. Die Nachrichten des Nutzers werden bei ihm nie übersetzt; dein Team hat seinen eigenen Knopf Übersetzen, im Dashboard und in der Hooky-App.
- Nur Text wird übersetzt, keine Anhänge. Die Übersetzung ist standardmäßig aktiv; um sie aus deiner App zu entfernen:
Hooky.configure(siteKey: "DEIN_OEFFENTLICHER_SCHLUESSEL", translation: false).
Was das SDK sendet
Was die Web-Bubble sendet, und sonst nichts. Ohne Werbe-ID, ohne App-übergreifendes Tracking, ohne Bibliotheken von Drittanbietern. Zum Ausfüllen des App-Datenschutzes deines App Store-Profils:
| Daten | Wann | App Store-Profil |
|---|---|---|
| Nachrichten, Fotos und PDFs | Wenn der Nutzer schreibt | Benutzerinhalte: Kundensupport, Fotos oder Videos |
| Vorname und E-Mail | Wenn er sie angibt oder du sie an identify übergibst | Kontaktinformationen: Name, E-Mail-Adresse |
Deine userId | Wenn du sie an identify übergibst | Kennungen: Benutzer-ID |
| Freie Daten | Was du an identify und setAttributes übergibst | Je nachdem, was du hineinschreibst (ein Warenkorb: Kaufverlauf …) |
track-Events | Wenn du sie sendest, ohne Personenkennung | Nutzungsdaten: Produktinteraktion (Analysen), nicht mit dem Nutzer verknüpft |
| Gerät, Sprache, Zeitzone, App | Mit dem Gespräch | Technische Angaben für dein Team (siehe unten) |
- Alle diese Daten dienen der App-Funktionalität (dem Kundensupport), die Stats deinen Analysen. Keine dient dem Tracking im Sinne von Apple: keine Abfrage für App Tracking Transparency.
- Das Gerät ist lesbar: „iPhone 15 Pro · iOS 18.2“, „iPad Air 11-inch (M2) · iPadOS 18.1“, „Mac · macOS 15.1“. Jede Anfrage trägt
X-Hooky-Client: hooky-ios/1.0.0undX-Hooky-App: <bundle id> <version> (<build>): Dein Team sieht, dass das Gespräch von „iOS · com.example.app 2.3 (45)“ kommt. - Auf dem Gerät: das Gesprächs-Token (der einzige Schlüssel des Nutzers) und die
userIdim Schlüsselbund (nur dieses Gerät, nach dem ersten Entsperren), pro Website-Schlüssel; der Entwurf, die zuletzt gesehene Nachricht und die Wahl „Immer übersetzen“ inUserDefaults. Eine Übersetzung sendet nur die ID der Nachricht und die Sprache des Gesprächs. - Hooky verarbeitet diese Daten in deinem Auftrag (Auftragsverarbeiter im Sinne der DSGVO): siehe die Datenschutzerklärung. Für dein Profil bleibst du verantwortlich: Gib an, was deine App wirklich sendet.
Sprache, Dark Mode, Barrierefreiheit
- Sprache
- Die Oberfläche gibt es auf Französisch, Englisch, Spanisch, Portugiesisch, Italienisch und Deutsch. Sie folgt den bevorzugten Sprachen des Geräts, standardmäßig Englisch;
Hooky.configure(siteKey:language:)legt eine fest. Die Sprache wird mit der ersten Nachricht gesendet, damit dein Team in der richtigen Sprache und zur richtigen Uhrzeit antwortet (mit der Zeitzone). - Dark Mode
- Das Gespräch folgt dem hellen oder dunklen Modus des Geräts, mit der Farbe deiner Website. Nichts einzustellen.
- Barrierefreiheit
- Jedes Bedienelement hat sein VoiceOver-Label (die Bubble: „Chat mit … öffnen“, und die Zahl der ungelesenen Nachrichten); Dynamic Type überall, und die Tastatur verdeckt nie die Eingabe.
Fehlerbehebung
- Meine Website hat ihre Adressen eingetragen: Wird die App abgelehnt?
- Nein. Eine App hat keine Adresse: Sie weist sich mit ihrem Header
X-Hooky-Clientaus und kommt auch dann durch, wenn die Website ihre Domains eingetragen hat. Trag unter Websites nichts ein. - Die Bubble behält die Standardfarben, oder nichts kommt an
- Prüf den Schlüssel: Es ist der öffentliche Schlüssel der Website (nicht der geheime Schlüssel
hks_…und kein API-Schlüsselhka_…). Mit einem unbekannten Schlüssel zeigt die Xcode-Konsole „Hooky: the site couldn’t be loaded (HTTP 404…)“. - Offline
- Die Nachricht zeigt „Keine Verbindung“ und bleibt im Eingabefeld;
identifyundsetAttributeswerden später gesendet. Das Aussehen der Website wird beim Öffnen des Gesprächs neu geladen. - Die Option Kamera erscheint nicht
- Füg
NSCameraUsageDescriptionzu deinerInfo.plisthinzu. - Ein anderer Nutzer sieht das alte Gespräch
- Ruf
Hooky.reset()bei der Abmeldung auf, und übergib eineuserIdanidentify: Eine andere ID beginnt mit einem leeren Gespräch.
Versionsverlauf
- 1.0.0
- Erste Version:
configure, die Bubble für SwiftUI und UIKit,openundclose,identify(mituserIdund freien Daten),setAttributes,track,reset, Ungelesene live, Fotos und PDFs, die Übersetzung der Nachrichten des Teams, sechs Sprachen, Dark Mode, VoiceOver und Dynamic Type.