A lightweight, type-safe event bus for Swift code.
EventBus is implemented as a thread-safe final class backed by NSLock. It supports weak owner lifecycle, token-based cancellation, one-shot subscriptions, ordered snapshot delivery, AsyncStream consumption, and a MainActor-friendly overload for UI owners.
- iOS 13+
Add the package dependency in Swift Package Manager and depend on the EventBus product.
import EventBus
struct UserDidLoginEvent: EventBusEvent {
let userID: String
}
let bus = EventBus.default
final class AnalyticsOwner: @unchecked Sendable {}
let owner = AnalyticsOwner()
let token = bus.subscribe(owner: owner, to: UserDidLoginEvent.self) { _, event in
print("Logged in:", event.userID)
}
bus.publish(UserDidLoginEvent(userID: "u-1"))
token.cancel()Any event must conform to EventBusEvent (Sendable).
struct AppReadyEvent: EventBusEvent {
let timestamp: Date
}- Every
subscribe(...)call creates a new independent subscription. - One owner may have multiple subscriptions for the same event type.
SubscriptionToken.cancel()cancels one specific subscription.unsubscribe(owner:from:)removes all subscriptions for one owner and event type.unsubscribeAll(for:)removes all subscriptions for one owner across all event types.
Consume events with for await:
let stream = bus.stream(AppReadyEvent.self)
Task {
for await event in stream {
print(event.timestamp)
}
}publish(_:)subscribe(...)with(Owner, Event)handlersubscribe(...)withEvent-only handlersubscribeOnce(...)subscribeOnMain(...)unsubscribe(owner:from:)unsubscribeAll(for:)stream(_:bufferingPolicy:)
EventBusis thread-safe.- Internal state is protected by
NSLock. - Dead subscriptions are cleaned lazily on subscribe and publish.
publish(_:)uses snapshot delivery.- Subscribers added during publish do not receive the current event.
- Subscribers removed during publish may already be in the snapshot, but
CancellationStateprevents cancelled handlers from running. - Delivery order is registration order.
See:
Sources/EventBus/Example/Demo.swiftSources/EventBus/Example/SwiftUIPlayground.swift
Unit tests are located in:
Tests/EventBusTests/EventBusTests.swift
They cover delivery order, token-based cancellation, owner-based unsubscription, one-shot semantics, MainActor delivery, AsyncStream, and concurrent publishing.