Skip to content

Commit 588722d

Browse files
Refresh README for SPM-first integration and release 5.3.0.
Rewrite docs around Swift Package Manager, document legacy CocoaPods/Carthage as unmaintained, and bump the podspec for the 5.3.0 tag. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 80888b8 commit 588722d

2 files changed

Lines changed: 172 additions & 121 deletions

File tree

README.md

Lines changed: 171 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -1,197 +1,248 @@
1-
# SwiftEventBus
1+
<p align="center">
2+
<h1 align="center">SwiftEventBus</h1>
3+
<p align="center">
4+
A tiny, thread-safe publish/subscribe bus for Apple platforms — decouple components without wiring delegates everywhere.
5+
</p>
6+
<p align="center">
7+
<a href="https://github.com/cesarferreira/SwiftEventBus/actions/workflows/ci.yml"><img src="https://github.com/cesarferreira/SwiftEventBus/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
8+
<a href="Package.swift"><img src="https://img.shields.io/badge/Swift%20PM-compatible-brightgreen.svg" alt="Swift PM"></a>
9+
<img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="MIT License">
10+
<img src="https://img.shields.io/badge/Swift-5-green.svg?style=flat" alt="Swift 5">
11+
</p>
12+
<p align="center">
13+
<a href="#install">Install</a>
14+
&nbsp;·&nbsp;
15+
<a href="#quickstart">Quickstart</a>
16+
&nbsp;·&nbsp;
17+
<a href="#highlights">Highlights</a>
18+
&nbsp;·&nbsp;
19+
<a href="#api">API</a>
20+
&nbsp;·&nbsp;
21+
<a href="#platforms">Platforms</a>
22+
</p>
23+
</p>
224

3-
[![CI](https://github.com/cesarferreira/SwiftEventBus/actions/workflows/ci.yml/badge.svg)](https://github.com/cesarferreira/SwiftEventBus/actions/workflows/ci.yml)
4-
[![Swift Package Manager](https://img.shields.io/badge/Swift%20PM-compatible-brightgreen.svg)](Package.swift)
5-
[![CocoaPods](https://img.shields.io/badge/CocoaPods-compatible-brightgreen.svg)](https://cocoapods.org/pods/SwiftEventBus)
6-
[![Carthage](https://img.shields.io/badge/Carthage-compatible-brightgreen.svg)](https://github.com/Carthage/Carthage)
7-
[![Swift 5](https://img.shields.io/badge/Swift-5-green.svg?style=flat)](https://swift.org)
8-
9-
Allows publish-subscribe-style communication between components without requiring the components to explicitly be aware of each other.
25+
---
1026

11-
## Features
27+
## Why SwiftEventBus
1228

13-
- [x] simplifies the communication between components
14-
- [x] decouples event senders and receivers
15-
- [x] avoids complex and error-prone dependencies and life cycle issues
16-
- [x] makes your code simpler
17-
- [x] is fast
18-
- [x] is tiny
19-
- [x] Thread-safe
20-
- [x] iOS, macOS, tvOS, and watchOS (Swift Package Manager)
29+
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.
2130

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

24-
### Swift Package Manager
38+
## Quickstart
2539

26-
In Xcode: **File → Add Package Dependencies…** and enter:
40+
Add the package in Xcode (**File → Add Package Dependencies…**):
2741

2842
```
2943
https://github.com/cesarferreira/SwiftEventBus.git
3044
```
3145

32-
Or add to your `Package.swift`:
46+
Then wire a subscriber and post an event:
3347

3448
```swift
35-
.package(url: "https://github.com/cesarferreira/SwiftEventBus.git", from: "5.2.0")
36-
```
49+
import SwiftEventBus
3750

38-
### CocoaPods
51+
final class ProfileViewController: UIViewController {
52+
override func viewDidLoad() {
53+
super.viewDidLoad()
3954

40-
```bash
41-
pod 'SwiftEventBus', '~> 5.2'
42-
```
55+
SwiftEventBus.onMainThread(self, name: "profileUpdated") { notification in
56+
self.refreshUI(with: notification?.object)
57+
}
58+
}
4359

44-
Or pin a release:
60+
func saveProfile() {
61+
SwiftEventBus.post("profileUpdated", sender: currentUser)
62+
}
4563

46-
```bash
47-
pod 'SwiftEventBus', :tag => '5.2.0', :git => 'https://github.com/cesarferreira/SwiftEventBus.git'
64+
deinit {
65+
SwiftEventBus.unregister(self)
66+
}
67+
}
4868
```
4969

50-
### Carthage
51-
52-
```bash
53-
github "cesarferreira/SwiftEventBus" ~> 5.2
54-
```
70+
**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`.
5571

56-
### Versions
72+
## Install
5773

58-
- `5.2.+` — Swift 5, iOS 12+, macOS 10.15+, tvOS 12+, watchOS 6+
59-
- `5.+` — Swift 5
60-
- `3.+` — Swift 4.2
61-
- `2.+` — Swift 3
62-
- `1.1.0` — Swift 2.2
74+
SwiftEventBus is distributed as a **Swift package**. That is the supported integration path for new apps and libraries.
6375

64-
## macOS
76+
### Xcode
6577

66-
SwiftEventBus is **Foundation-only** (no UIKit), so it works on **macOS 10.15+** with the same API as on iOS.
78+
1. **File → Add Package Dependencies…**
79+
2. Enter `https://github.com/cesarferreira/SwiftEventBus.git`
80+
3. Choose **Up to Next Major** from `5.3.0` (or a newer release tag)
81+
4. Add the **SwiftEventBus** product to your app or framework target
6782

68-
**Recommended:** add the package in Xcode (**File → Add Package Dependencies…**) or depend on it from your app's `Package.swift` (see above). CI builds and tests the library on macOS on every push.
83+
### Package.swift
6984

70-
**CocoaPods** on Mac:
85+
Add the dependency to your package manifest:
7186

72-
```bash
73-
pod 'SwiftEventBus', '~> 5.2'
87+
```swift
88+
dependencies: [
89+
.package(url: "https://github.com/cesarferreira/SwiftEventBus.git", from: "5.3.0"),
90+
],
91+
targets: [
92+
.target(
93+
name: "MyApp",
94+
dependencies: ["SwiftEventBus"]
95+
),
96+
]
7497
```
7598

76-
Example in an `NSViewController` subclass:
99+
Pin an exact version when you need reproducible builds:
77100

78101
```swift
79-
import AppKit
80-
import SwiftEventBus
102+
.package(url: "https://github.com/cesarferreira/SwiftEventBus.git", exact: "5.3.0"),
103+
```
81104

82-
final class MainViewController: NSViewController {
83-
override func viewDidLoad() {
84-
super.viewDidLoad()
105+
### Command line
85106

86-
SwiftEventBus.onMainThread(self, name: "refresh") { _ in
87-
self.view.needsLayout = true
88-
}
89-
}
107+
From an existing Swift package directory:
90108

91-
deinit {
92-
SwiftEventBus.unregister(self)
93-
}
94-
}
109+
```bash
110+
swift package resolve
111+
swift build
112+
swift test # when developing against a local checkout of this repo
95113
```
96114

97-
The **Sample** app in this repo is iOS-only (storyboard demo). For Mac apps, integrate via Swift Package Manager or CocoaPods as above.
115+
### Platforms (SPM)
116+
117+
| Release | Swift | Minimum OS |
118+
| ------- | ----- | ---------- |
119+
| `5.3.+` | 5 | iOS 12, macOS 10.15, tvOS 12, watchOS 6 |
120+
| `5.2.+` | 5 | Same platform mins; prefer `5.3.+` for macOS Xcode framework fixes |
121+
| `5.+` | 5 | See `Package.swift` `platforms` for the tag you pin |
122+
| Older tags | 2–4 | Legacy; not recommended for new projects |
123+
124+
### Legacy integrators
98125

99-
## Usage
126+
[CocoaPods](https://cocoapods.org/) and [Carthage](https://github.com/Carthage/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.
100127

101-
### 1 — Prepare subscribers
128+
## Highlights
102129

103-
Subscribers implement event handling methods that will be called when an event is received.
130+
### Payloads and filtering
131+
132+
Attach a sender object or `userInfo` like any notification:
104133

105134
```swift
106-
SwiftEventBus.onMainThread(target, name: "someEventName") { result in
107-
// UI thread
135+
SwiftEventBus.post("personFetched", sender: person)
136+
SwiftEventBus.post("syncProgress", sender: nil, userInfo: ["percent": 42])
137+
138+
SwiftEventBus.onMainThread(self, name: "personFetched") { note in
139+
let person = note?.object as! Person
140+
print(person.name)
108141
}
142+
```
109143

110-
// or
144+
Register with a specific `sender` to only receive posts that use the same object:
111145

112-
SwiftEventBus.onBackgroundThread(target, name: "someEventName") { result in
113-
// API access
146+
```swift
147+
SwiftEventBus.onBackgroundThread(self, name: "jobDone", sender: jobID) { _ in
148+
// only fires when post(..., sender: jobID) matches
114149
}
115150
```
116151

117-
### 2 — Post events
152+
### Background work → main UI
118153

119-
Post an event from any part of your code. All subscribers matching the event type will receive it.
154+
`NotificationCenter` delivers on the thread where you post. For UI updates, hop explicitly:
120155

121156
```swift
122-
SwiftEventBus.post("someEventName")
157+
SwiftEventBus.onBackgroundThread(self, name: "fetchData") { _ in
158+
let result = loadFromNetwork()
159+
SwiftEventBus.postToMainThread("fetchDataDone", sender: result)
160+
}
161+
162+
SwiftEventBus.onMainThread(self, name: "fetchDataDone") { note in
163+
self.apply(result: note?.object)
164+
}
123165
```
124166

125-
---
167+
Credit for the original pattern: [@nunogoncalves](https://github.com/nunogoncalves).
126168

127-
### Event bus with parameters
169+
### macOS
128170

129-
Post event:
171+
Add the package in Xcode or your `Package.swift` (see [Install](#install)). Example with AppKit:
130172

131173
```swift
132-
SwiftEventBus.post("personFetchEvent", sender: Person(name: "john doe"))
133-
```
174+
import AppKit
175+
import SwiftEventBus
134176

135-
Expecting parameters:
177+
final class MainViewController: NSViewController {
178+
override func viewDidLoad() {
179+
super.viewDidLoad()
180+
SwiftEventBus.onMainThread(self, name: "refresh") { _ in
181+
self.view.needsLayout = true
182+
}
183+
}
136184

137-
```swift
138-
SwiftEventBus.onMainThread(target, name: "personFetchEvent") { result in
139-
let person = result?.object as! Person
140-
print(person.name) // will output "john doe"
185+
deinit {
186+
SwiftEventBus.unregister(self)
187+
}
141188
}
142189
```
143190

144-
### Posting events from the background thread to the main thread
191+
The **Sample** target in this repo is an iOS storyboard demo only; Mac apps should add the Swift package dependency.
145192

146-
Quoting the official Apple [documentation](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Notifications/Articles/Threading.html):
193+
## API
147194

148-
> Regular notification centers deliver notifications on the thread in which the notification was posted
195+
### Subscribe
149196

150-
Regarding this limitation, [@nunogoncalves](https://github.com/nunogoncalves) implemented the feature and provided a working example:
197+
| Method | Delivery |
198+
| ------ | -------- |
199+
| `onMainThread(_:name:sender:handler:)` | `OperationQueue.main` |
200+
| `onBackgroundThread(_:name:sender:handler:)` | Background `OperationQueue` |
201+
| `on(_:name:sender:queue:handler:)` | Custom queue |
151202

152-
```swift
153-
@IBAction func clicked(sender: AnyObject) {
154-
count += 1
155-
SwiftEventBus.post("doStuffOnBackground")
156-
}
203+
Handlers receive `(Notification?) -> Void`. Registration returns an observer token (usually you rely on `unregister` instead).
157204

158-
@IBOutlet weak var textField: UITextField!
205+
### Post
159206

160-
var count = 0
207+
| Method | Behavior |
208+
| ------ | -------- |
209+
| `post(_:sender:)` | Post on the current thread |
210+
| `post(_:sender:userInfo:)` | Post with dictionary payload |
211+
| `postToMainThread(_:sender:)` | Async dispatch to main, then post |
212+
| `postToMainThread(_:sender:userInfo:)` | Same, with `userInfo` |
161213

162-
override func viewDidLoad() {
163-
super.viewDidLoad()
214+
### Unregister
164215

165-
SwiftEventBus.onBackgroundThread(self, name: "doStuffOnBackground") { _ in
166-
print("doing stuff in background thread")
167-
SwiftEventBus.postToMainThread("updateText")
168-
}
216+
```swift
217+
SwiftEventBus.unregister(target) // all events for target
218+
SwiftEventBus.unregister(target, name: "x") // one event name for target
219+
```
169220

170-
SwiftEventBus.onMainThread(self, name: "updateText") { _ in
171-
self.textField.text = "\(self.count)"
172-
}
173-
}
221+
## Platforms
174222

175-
// Perhaps on viewDidDisappear depending on your needs
176-
override func viewWillDisappear(_ animated: Bool) {
177-
super.viewWillDisappear(animated)
223+
SwiftEventBus is tested on every push via [GitHub Actions](https://github.com/cesarferreira/SwiftEventBus/actions):
178224

179-
SwiftEventBus.unregister(self)
180-
}
181-
```
225+
| Job | What it verifies |
226+
| --- | ---------------- |
227+
| **macOS (Swift Package Manager)** | `swift build` / `swift test` on macOS (14 unit tests) |
228+
| **macOS (Xcode framework)** | Framework build for `generic/platform=macOS` |
229+
| **iOS (Xcode framework)** | Framework build for iOS |
230+
| **Sample iOS app** | Sample app build, Simulator launch, smoke event path |
182231

183-
---
232+
**Requirements:** Swift 5, Xcode 15+ recommended for local development.
184233

185-
## Unregistering
234+
## Sample app
186235

187-
Remove all the observers from the target:
236+
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.
188237

189-
```swift
190-
SwiftEventBus.unregister(target)
191-
```
238+
## Contributing
192239

193-
Remove observers of the same name from the target:
240+
Issues and PRs welcome. Please keep changes focused; CI must stay green.
194241

195-
```swift
196-
SwiftEventBus.unregister(target, name: "someEventName")
197-
```
242+
1. Fork and branch from `master`
243+
2. Run tests locally: `swift test` (macOS or Linux Docker with Swift 5.10+)
244+
3. Open a PR — the workflow runs SPM, iOS/macOS framework builds, and the Sample smoke test
245+
246+
## License
247+
248+
[MIT](LICENSE) © César Ferreira

SwiftEventBus.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = 'SwiftEventBus'
3-
s.version = '5.2.0'
3+
s.version = '5.3.0'
44
s.license = 'MIT'
55
s.summary = 'Publish/subscribe event bus for iOS and macOS'
66
s.homepage = 'https://github.com/cesarferreira/SwiftEventBus'

0 commit comments

Comments
 (0)