Zum Inhalt springen

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.

Ein iPhone-App-Bildschirm mit der Hooky-Bubble unten rechts. Das Hooky-Gespräch im Dark Mode auf dem iPhone: Nachrichten, Foto und Eingabefeld.
Die Bubble in einer SwiftUI-App, und das Gespräch im Dark Mode.

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-key der 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-ios

Oder 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() }
    }
}
ParameterBedeutung
siteKeyPflicht. Der öffentliche Schlüssel der Website.
languageOptional. 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).
translationOptional, standardmäßig true. false entfernt die Übersetzung der Nachrichten des Teams (siehe Nachrichten übersetzen).
apiURLOptional, 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 Aktion
  • alignment legt die Ecke fest (sonst die unter Websites eingestellte linke oder rechte Seite); padding den Abstand zu den Rändern (standardmäßig 60 Punkte groß, 20 Abstand); hidden blendet 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 wieder

Auf 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.
  • name und email ersetzen die des Gesprächs (nil löscht sie auf dem Gerät); userId: nil lässt sie unverändert.
  • Die userId wird zusammen mit dem Gesprächs-Token gespeichert. Ist die userId eine andere, ist das ein anderes Konto auf demselben Gerät: Das Gespräch wird vergessen, wie bei reset(). 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 configure auf: Sonst tun identify und setAttributes nichts (und halten im Debug-Build an einer Assertion an).

Regeln für die Daten

FeldRegel
Freie DatenFlach: höchstens 50 Schlüssel, insgesamt 8 KB. Neue Schlüssel ersetzen die alten, die übrigen bleiben.
Schlüssel1 bis 64 Zeichen: Buchstaben, Ziffern, Leerzeichen, _, . und -. Sonst wird er ignoriert.
WertEin Text (nach 500 Zeichen abgeschnitten), eine Zahl oder ein Boolean. nil löscht den Schlüssel.
userIdHöchstens 200 Zeichen.
name, emailDer 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 NSCameraUsageDescription zur Info.plist deiner 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 language festgelegten) 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:

DatenWannApp Store-Profil
Nachrichten, Fotos und PDFsWenn der Nutzer schreibtBenutzerinhalte: Kundensupport, Fotos oder Videos
Vorname und E-MailWenn er sie angibt oder du sie an identify übergibstKontaktinformationen: Name, E-Mail-Adresse
Deine userIdWenn du sie an identify übergibstKennungen: Benutzer-ID
Freie DatenWas du an identify und setAttributes übergibstJe nachdem, was du hineinschreibst (ein Warenkorb: Kaufverlauf …)
track-EventsWenn du sie sendest, ohne PersonenkennungNutzungsdaten: Produktinteraktion (Analysen), nicht mit dem Nutzer verknüpft
Gerät, Sprache, Zeitzone, AppMit dem GesprächTechnische 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.0 und X-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 userId im 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“ in UserDefaults. 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-Client aus 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üssel hka_…). 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; identify und setAttributes werden später gesendet. Das Aussehen der Website wird beim Öffnen des Gesprächs neu geladen.
Die Option Kamera erscheint nicht
Füg NSCameraUsageDescription zu deiner Info.plist hinzu.
Ein anderer Nutzer sieht das alte Gespräch
Ruf Hooky.reset() bei der Abmeldung auf, und übergib eine userId an identify: Eine andere ID beginnt mit einem leeren Gespräch.

Versionsverlauf

1.0.0
Erste Version: configure, die Bubble für SwiftUI und UIKit, open und close, identify (mit userId und freien Daten), setAttributes, track, reset, Ungelesene live, Fotos und PDFs, die Übersetzung der Nachrichten des Teams, sechs Sprachen, Dark Mode, VoiceOver und Dynamic Type.