Skip to content

Repository files navigation

SwiftEventBus

A tiny, thread-safe publish/subscribe bus for Apple platforms — decouple components without wiring delegates everywhere.

CI Swift PM MIT License Swift 5

Install  ·  Quickstart  ·  Highlights  ·  API  ·  Platforms


Why SwiftEventBus

NotificationCenter is built in, but stringly-typed observers, thread hops, and lifecycle cleanup get messy fast. SwiftEventBus wraps the same primitives with a small API: register on a target, post by name, unregister when the target goes away.

  • Decouple senders and receivers. Components talk through event names, not direct references.
  • Thread-aware delivery. onMainThread, onBackgroundThread, or a custom OperationQueue.
  • Main-thread posting. postToMainThread when work finishes off the UI queue.
  • Lifecycle-friendly. Track observers per target; unregister(_:) tears them down in one call.
  • Foundation-only. No UIKit in the library — same API on iOS, macOS, tvOS, and watchOS via Swift Package Manager.
  • Battle-tested. Built on NotificationCenter; registration cache is guarded for concurrent use.

Quickstart

Add the package in Xcode (File → Add Package Dependencies…):

https://github.com/cesarferreira/SwiftEventBus.git

Then wire a subscriber and post an event:

import SwiftEventBus

final class ProfileViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        SwiftEventBus.onMainThread(self, name: "profileUpdated") { notification in
            self.refreshUI(with: notification?.object)
        }
    }

    func saveProfile() {
        SwiftEventBus.post("profileUpdated", sender: currentUser)
    }

    deinit {
        SwiftEventBus.unregister(self)
    }
}

Rule of thumb: pass self (or another long-lived object) as the registration target and call unregister when that object is done — typically deinit or viewWillDisappear.

Install

SwiftEventBus is distributed as a Swift package. That is the supported integration path for new apps and libraries.

Xcode

  1. File → Add Package Dependencies…
  2. Enter https://github.com/cesarferreira/SwiftEventBus.git
  3. Choose Up to Next Major from 5.3.0 (or a newer release tag)
  4. Add the SwiftEventBus product to your app or framework target

Package.swift

Add the dependency to your package manifest:

dependencies: [
    .package(url: "https://github.com/cesarferreira/SwiftEventBus.git", from: "5.3.0"),
],
targets: [
    .target(
        name: "MyApp",
        dependencies: ["SwiftEventBus"]
    ),
]

Pin an exact version when you need reproducible builds:

.package(url: "https://github.com/cesarferreira/SwiftEventBus.git", exact: "5.3.0"),

Command line

From an existing Swift package directory:

swift package resolve
swift build
swift test   # when developing against a local checkout of this repo

Platforms (SPM)

Release Swift Minimum OS
5.3.+ 5 iOS 12, macOS 10.15, tvOS 12, watchOS 6
5.2.+ 5 Same platform mins; prefer 5.3.+ for macOS Xcode framework fixes
5.+ 5 See Package.swift platforms for the tag you pin
Older tags 2–4 Legacy; not recommended for new projects

Legacy integrators

CocoaPods and Carthage are not actively maintained for this project anymore. The last published podspec (SwiftEventBus.podspec) and Xcode framework target remain in the repo for existing users, but new work should use SPM above.

Highlights

Payloads and filtering

Attach a sender object or userInfo like any notification:

SwiftEventBus.post("personFetched", sender: person)
SwiftEventBus.post("syncProgress", sender: nil, userInfo: ["percent": 42])

SwiftEventBus.onMainThread(self, name: "personFetched") { note in
    let person = note?.object as! Person
    print(person.name)
}

Register with a specific sender to only receive posts that use the same object:

SwiftEventBus.onBackgroundThread(self, name: "jobDone", sender: jobID) { _ in
    // only fires when post(..., sender: jobID) matches
}

Background work → main UI

NotificationCenter delivers on the thread where you post. For UI updates, hop explicitly:

SwiftEventBus.onBackgroundThread(self, name: "fetchData") { _ in
    let result = loadFromNetwork()
    SwiftEventBus.postToMainThread("fetchDataDone", sender: result)
}

SwiftEventBus.onMainThread(self, name: "fetchDataDone") { note in
    self.apply(result: note?.object)
}

Credit for the original pattern: @nunogoncalves.

macOS

Add the package in Xcode or your Package.swift (see Install). Example with AppKit:

import AppKit
import SwiftEventBus

final class MainViewController: NSViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        SwiftEventBus.onMainThread(self, name: "refresh") { _ in
            self.view.needsLayout = true
        }
    }

    deinit {
        SwiftEventBus.unregister(self)
    }
}

The Sample target in this repo is an iOS storyboard demo only; Mac apps should add the Swift package dependency.

API

Subscribe

Method Delivery
onMainThread(_:name:sender:handler:) OperationQueue.main
onBackgroundThread(_:name:sender:handler:) Background OperationQueue
on(_:name:sender:queue:handler:) Custom queue

Handlers receive (Notification?) -> Void. Registration returns an observer token (usually you rely on unregister instead).

Post

Method Behavior
post(_:sender:) Post on the current thread
post(_:sender:userInfo:) Post with dictionary payload
postToMainThread(_:sender:) Async dispatch to main, then post
postToMainThread(_:sender:userInfo:) Same, with userInfo

Unregister

SwiftEventBus.unregister(target)              // all events for target
SwiftEventBus.unregister(target, name: "x")   // one event name for target

Platforms

SwiftEventBus is tested on every push via GitHub Actions:

Job What it verifies
macOS (Swift Package Manager) swift build / swift test on macOS (14 unit tests)
macOS (Xcode framework) Framework build for generic/platform=macOS
iOS (Xcode framework) Framework build for iOS
Sample iOS app Sample app build, Simulator launch, smoke event path

Requirements: Swift 5, Xcode 15+ recommended for local development.

Sample app

Open SwiftEventBus.xcodeproj, run the Sample scheme on an iOS Simulator. It exercises login flow events (loginCall → background work → login on the main thread). CI launches Sample with -SmokeTest to assert the bus end-to-end.

Contributing

Issues and PRs welcome. Please keep changes focused; CI must stay green.

  1. Fork and branch from master
  2. Run tests locally: swift test (macOS or Linux Docker with Swift 5.10+)
  3. Open a PR — the workflow runs SPM, iOS/macOS framework builds, and the Sample smoke test

License

MIT © César Ferreira

About

A publish/subscribe EventBus optimized for iOS

Topics

Resources

Stars

1.1k stars

Watchers

32 watching

Forks

Releases

Packages

Used by

Contributors

Languages