Swift
Das Paket HonkMe ist ein Swift-6.2-Client mit Strict Concurrency, async/await und ohne Abhängigkeiten. Es läuft auf macOS, iOS und Linux, für serverseitiges Swift, Tools und CI.
- Paket
-
HonkMeVeröffentlicht - Quellcode
- github.com/honk-me/honk-swift · MIT-Lizenz
- Voraussetzungen
- Swift 6.2; macOS 14+, iOS 17+ oder Linux.
Installation
In Xcode: File ▸ Add Package Dependencies, dann https://github.com/honk-me/honk-swift einfügen.
// Package.swift
.package(url: "https://github.com/honk-me/honk-swift.git", from: "0.1.0"),
// target:
.product(name: "HonkMe", package: "honk-swift"), Mit einem Ingest-Schlüssel (honk_…) kann jeder an sein Projekt senden. Er gehört auf Server, in Jobs und in CI-Secrets, nie in einen Browser, eine Mobil- oder Desktop-App.
Ein Ereignis senden
Bette nie einen Ingest-Schlüssel in eine iOS- oder macOS-App ein: Alles, was mit einer App ausgeliefert wird, lässt sich auslesen. Eine App, die dich benachrichtigen soll, ruft dein eigenes Backend auf, das dann Honk aufruft.
import HonkMe
let honk = try Honk.fromEnvironment() // HONK_URL, HONK_KEY (+ HONK_SOURCE, HONK_ENVIRONMENT, HONK_CHANNEL)
try await honk.beep("Backup finished", "nightly pg_dump took 42 s") Rezept: eine Kundenanfrage mit Vapor
Message ist Sendable, du kannst die Nachricht also im Handler bauen und außerhalb des Request-Pfads senden. Erstelle beim Start eine einzige Honk-Instanz und teile sie: Sie besitzt eine Keep-Alive-URLSession.
import HonkMe
import Vapor
func routes(_ app: Application, honk: Honk) {
app.post("quote") { req async throws -> HTTPStatus in
let quote = try req.content.decode(QuoteRequest.self)
try await quote.save(on: req.db) // store it first
let id = try quote.requireID()
// Message is Sendable: build it here, send it off the request path.
let message = Message(
"\(quote.name) (\(quote.company)) asked: \(quote.body.prefix(2000))",
title: "New request: \(quote.subject)".prefix(150).description,
severity: .light,
priority: .high, // push right away
category: .customers,
channel: "requests",
groupKey: "requests/\(id)", // one group per request
url: "https://shop.example.com/admin/requests/\(id)", // https only
metadata: ["request_id": .string("\(id)")]
)
let logger = req.logger
Task {
do { try await honk.send(message, idempotencyKey: "request-\(id)") } // same request, same key
catch { logger.warning("honk: \(error)") }
}
return .accepted
}
} Die Honk-Skala und Vorfälle
Die Helfer nehmen eine Closure, die die Nachricht bearbeitet.
try await honk.loud("Disk 91%", "/var on app-01") { $0.groupKey = "disk/app-01/var" }
try await honk.problem(groupKey: "db/backup", "Backup failed", "pg_dump exited with 1") // a long honk by default
try await honk.recovery(groupKey: "db/backup", "Backup OK", "pg_dump finished in 41 s") // a beep by default Fehler
Die Fälle von HonkError tragen ein Failure mit dem Status, dem Code, jedem ungültigen Feld und retryAfter; isRetryable sagt dir, was zu tun ist.
do {
try await honk.long("Payment failed", "Stripe declined order 1042") { $0.groupKey = "payments/stripe" }
} catch HonkError.validation(let failure) {
logger.error("bug: \(failure.fields)")
} catch let error as HonkError where error.isRetryable {
queue.retryLater(key: error.failure?.idempotencyKey)
} Retries ohne Doppelversand
- Jedes Senden hat einen
Idempotency-Key: deinen oder eine neue UUIDv7. Jeder Retry nutzt denselben Schlüssel, und innerhalb von 24 Stunden beantwortet Honk eine erneut gesendete Anfrage mit der ursprünglichen ID undduplicate: true. Geht eine Antwort verloren, entsteht also nie eine zweite Nachricht. - Wiederholt werden nur Netzwerkfehler, Timeouts,
429und5xx, mit exponentiellem Backoff und vollem Jitter, nie früher als dasRetry-Afterdes Servers. Andere4xx-Antworten werden nie wiederholt: Korrigiere stattdessen die Anfrage. - Jeder Versuch bricht nach 5 Sekunden ab, und nach insgesamt 30 Sekunden ist Schluss. Wäre die nötige Wartezeit länger, etwa bis ein Tageskontingent um Mitternacht zurückgesetzt wird, schlägt der Aufruf sofort fehl und sagt dir, wann du es erneut versuchen kannst.
- Felder werden vor dem Senden geprüft, und alle ungültigen Felder werden auf einmal gemeldet.