Swift
Le paquet HonkMe est un client Swift 6.2 avec concurrence stricte, async/await et aucune dépendance. Il fonctionne sur macOS, iOS et Linux, pour Swift côté serveur, les outils et la CI.
- Paquet
-
HonkMePublié - Code source
- github.com/honk-me/honk-swift · Licence MIT
- Prérequis
- Swift 6.2 ; macOS 14+, iOS 17+ ou Linux.
Installation
Dans Xcode : File ▸ Add Package Dependencies, puis collez https://github.com/honk-me/honk-swift.
// Package.swift
.package(url: "https://github.com/honk-me/honk-swift.git", from: "0.1.0"),
// target:
.product(name: "HonkMe", package: "honk-swift"), Une clé d’ingestion (honk_…) permet à quiconque la détient d’envoyer des messages dans son projet. Gardez-la sur vos serveurs, dans vos tâches et dans les secrets de votre CI, jamais dans un navigateur ni dans une app mobile ou de bureau.
Envoyer un événement
N’intégrez jamais de clé d’ingestion dans une app iOS ou macOS : tout ce qui est embarqué dans une app peut en être extrait. Si une app doit vous prévenir, faites-la passer par votre propre backend, qui appelle ensuite Honk.
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") Recette : une demande client avec Vapor
Message est Sendable : vous pouvez le construire dans le handler et l’envoyer en dehors du traitement de la requête. Créez un seul Honk au démarrage et partagez-le : il possède une URLSession keep-alive.
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
}
} L’échelle Honk et les incidents
Les helpers acceptent une closure qui modifie le message.
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 Erreurs
Les cas de HonkError portent un Failure avec le statut, le code, chaque champ invalide et retryAfter ; isRetryable vous indique quoi faire.
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)
} Des nouvelles tentatives sans doublon
- Chaque envoi porte une
Idempotency-Key: la vôtre, ou un UUIDv7 généré pour l’occasion. La même clé est réutilisée à chaque nouvelle tentative, et pendant 24 heures Honk répond à une requête rejouée avec l’identifiant d’origine etduplicate: true: une réponse perdue ne crée jamais de second message. - Une nouvelle tentative n’a lieu qu’en cas d’erreur réseau, de délai dépassé, de
429ou de5xx, avec un backoff exponentiel et un jitter complet, jamais avant leRetry-Afterdu serveur. Les autres réponses4xxne donnent lieu à aucune nouvelle tentative : corrigez plutôt la requête. - Chaque tentative expire au bout de 5 secondes, et tout s’arrête au bout de 30 secondes au total. Une attente qui dépasserait cette échéance, comme un quota quotidien remis à zéro à minuit, échoue immédiatement et indique quand réessayer.
- Les champs sont vérifiés localement avant l’envoi, et tous les champs invalides sont signalés en une seule fois.