Swift
Pachetul HonkMe este un client Swift 6.2 cu concurență strictă, async/await și fără dependențe. Rulează pe macOS, iOS și Linux, pentru Swift pe server, unelte și CI.
- Pachet
-
HonkMePublicat - Cod sursă
- github.com/honk-me/honk-swift · Licență MIT
- Cerințe
- Swift 6.2; macOS 14+, iOS 17+ sau Linux.
Instalare
În Xcode: File ▸ Add Package Dependencies, apoi lipește 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"), Oricine are o cheie API (honk_…) poate trimite mesaje în proiectul ei. Ține-o pe servere, în joburi și în secretele din CI, niciodată într-o aplicație de browser, de mobil sau de desktop.
Trimite un eveniment
Nu pune niciodată o cheie API într-o aplicație iOS sau macOS: tot ce livrezi într-o aplicație poate fi extras. O aplicație care trebuie să te anunțe apelează propriul tău backend, iar acesta apelează 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") Rețetă: cererea unui client, cu Vapor
Message este Sendable, așa că îl poți construi în handler și îl poți trimite în afara fluxului cererii. Creează un singur Honk la pornire și folosește-l peste tot: are propriul URLSession cu 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
}
} Scala Honk și incidentele
Helperele primesc o closure care editează mesajul.
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 Erori
Cazurile HonkError au un Failure cu statusul, codul, fiecare câmp invalid și retryAfter; isRetryable îți spune ce să faci.
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)
} Reîncercări care nu trimit nimic de două ori
- Fiecare trimitere are un
Idempotency-Key: al tău sau un UUIDv7 nou. Aceeași cheie e refolosită la fiecare reîncercare, iar timp de 24 de ore Honk răspunde la o repetare cu id-ul original șiduplicate: true, așa că un răspuns pierdut nu creează niciodată un al doilea mesaj. - Se reîncearcă doar erorile de rețea, cererile care expiră,
429și5xx, cu backoff exponențial și jitter complet, niciodată mai devreme decâtRetry-Afterde la server. Celelalte răspunsuri4xxnu se reîncearcă niciodată: corectează cererea. - Fiecare încercare expiră după 5 secunde, iar totul se oprește după 30. Dacă ar trebui să aștepte mai mult, de exemplu până se resetează cota zilnică la miezul nopții, eșuează imediat și îți spune când să reîncerci.
- Câmpurile sunt verificate înainte de trimitere, iar toate câmpurile invalide sunt raportate deodată.