An open API service indexing awesome lists of open source software.

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.

Awesome Lists containing this project

README

          

SwiftEvents
===========

[![Swift](https://img.shields.io/badge/Swift-6-orange.svg?style=flat)](https://swift.org)
[![Platform](https://img.shields.io/badge/platform-iOS%20%7C%20macOS%20%7C%20watchOS%20%7C%20tvOS%20%7C%20Linux-lightgrey.svg)](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)