https://github.com/denissimon/swiftevents
The easiest way to implement data binding and notifications. Includes Event<T> and Observable<T>. Has a thread-safe version.
https://github.com/denissimon/swiftevents
binder binding bindings data-binding event events ios macos mvvm notifications observable observer reactive swift swift-library swift-package-manager
Last synced: 8 months ago
JSON representation
The easiest way to implement data binding and notifications. Includes Event<T> and Observable<T>. Has a thread-safe version.
- Host: GitHub
- URL: https://github.com/denissimon/swiftevents
- Owner: denissimon
- License: mit
- Created: 2019-05-30T20:55:25.000Z (about 7 years ago)
- Default Branch: master
- Last Pushed: 2025-07-24T14:27:44.000Z (11 months ago)
- Last Synced: 2025-09-26T05:02:07.536Z (9 months ago)
- Topics: binder, binding, bindings, data-binding, event, events, ios, macos, mvvm, notifications, observable, observer, reactive, swift, swift-library, swift-package-manager
- Language: Swift
- Homepage:
- Size: 282 KB
- Stars: 8
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
SwiftEvents
===========
[](https://swift.org)
[](https://developer.apple.com/swift/)
SwiftEvents is a lightweight library for creating and observing events.
It includes:
* `Observable` - a type-safe class for data binding that can be particularly used in MVVM.
* `Event` - a type-safe class for any notifications, including with multiple subscribers.
SwiftEvents has a thread-safe version - `EventTS` and `ObservableTS` classes. This way, its properties and methods can be safely accessed by multiple threads at the same time.
Another important feature is the automatic unsubscription of subscribers/observers when they are deallocated.
Comprehensive [unit test](https://github.com/denissimon/SwiftEvents/blob/master/Tests/SwiftEventsTests) coverage.
Installation
------------
#### Swift Package Manager
To install SwiftEvents using [Swift Package Manager](https://swift.org/package-manager), add the following in your `Package.swift`:
```swift
dependencies: [
.package(url: "https://github.com/denissimon/SwiftEvents.git", from: "2.3.1"),
]
```
Or through Xcode:
```txt
File -> Add Package Dependencies
Enter Package URL: https://github.com/denissimon/SwiftEvents
```
#### CocoaPods
To install SwiftEvents using [CocoaPods](https://cocoapods.org), add this line to your `Podfile`:
```ruby
pod 'SwiftEvents', '~> 2.3'
```
#### Carthage
To install SwiftEvents using [Carthage](https://github.com/Carthage/Carthage), add this line to your `Cartfile`:
```ruby
github "denissimon/SwiftEvents"
```
#### Manually
Copy `SwiftEvents.swift` into your project.
Usage
-----
### Observable
Example:
```swift
class ViewModel {
let items: Observable<[Item]> = Observable([])
let infoLabel: Observable = Observable("")
...
}
```
```swift
class ViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
@IBOutlet weak var infoLabel: UILabel!
private var viewModel = ViewModel()
override func viewDidLoad() {
super.viewDidLoad()
bind()
}
private func bind() {
viewModel.items.bind(self) { [weak self] _ in self?.refreshTable() }
viewModel.infoLabel.bind(self) { [weak self] in self?.updateInfoLabel($0) }
}
private func refreshTable() { ... }
private func updateInfoLabel(_ text: String) {
infoLabel.text = text
}
}
```
In this example, every time the ViewModel changes the value of the observable property `items` or `infoLabel`, the ViewController is notified and updates its UI.
The `<<<` infix operator can be used to set a new value for an observable property:
```swift
items.value = newItems
items <<< newItems
```
### Event
Example implementation of the closure-based delegation pattern:
```swift
class ImageProcessingService {
let didProcess = Event()
func processImage(_ image: Image) {
/* time-consuming processing code */
didProcess.notify(newImage)
}
}
```
```swift
class ViewModel {
private let service: ImageProcessingService
var image: Image?
init(service: ImageProcessingService) {
self.service = service
self.service.didProcess.subscribe(self) { [weak self] image in
self?.image = image
}
}
}
```
You can also create several events (didProcess, didDownload, onNetworkError etc.) and trigger only what is needed.
The Event and Observable classes conform to `Unsubscribable` and `Unbindable` protocols respectively, which allows to pass a reference to an object that should only call `unsubscribe` / `unbind`.
### More examples
More usage examples can be found in [iOS-MVVM-Clean-Architecture](https://github.com/denissimon/iOS-MVVM-Clean-Architecture). Also [tests](https://github.com/denissimon/SwiftEvents/blob/master/Tests/SwiftEventsTests) contain additional examples including a NotificationCenter-like implementation where a shared event and multiple subscribers are used.
### Advanced features
#### Removing a subscriber / observer
Deallocated subscribers/observers are automatically removed from the Event/Observable, but they can also be removed manually:
```swift
someEvent.subscribe(self) { [weak self] in self?.setValue($0) }
someEvent.unsubscribe(self)
someObservable.bind(cell) { [weak cell] in cell?.update($0) }
someObservable.unbind(cell)
```
#### Removing all subscribers / observers
```swift
someEvent.unsubscribeAll()
someObservable.unbindAll()
```
#### Number of subscribers / observers
```swift
someEvent.subscribersCount
someObservable.observersCount
```
#### Number of triggers
```swift
someEvent.triggersCount
someObservable.triggersCount
```
#### queue: DispatchQueue
By default, a provided handler is executed on the thread that triggers the Event/Observable. To change this default behaviour:
```swift
// This executes the handler on the main thread
someEvent.subscribe(self, queue: .main) { [weak self] in self?.updateTable($0) }
someObservable.bind(self, queue: .main) { [weak self] in self?.updateTable($0) }
```
#### One-time notification
To ensure that the handler will be executed only once:
```swift
someEvent.subscribe(self) { [weak self] data in
guard let self else { return }
self.useData(data)
self.someEvent.unsubscribe(self)
}
```
#### N-time notification
To ensure that the handler will be executed no more than `n` times:
```swift
someEvent.subscribe(self) { [weak self] data in
guard let self else { return }
self.useData(data)
if self.someEvent.triggersCount >= n { self.someEvent.unsubscribe(self) }
}
```
License
-------
Licensed under the [MIT license](https://github.com/denissimon/SwiftEvents/blob/master/LICENSE)