Ecosyste.ms: Awesome

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

Awesome Lists | Featured Topics | Projects

https://github.com/blendle/Hanson

Lightweight observations and bindings in Swift
https://github.com/blendle/Hanson

binding kvc kvo observation swift

Last synced: about 1 month ago
JSON representation

Lightweight observations and bindings in Swift

Awesome Lists containing this project

README

        





Language: Swift
Platform: macOS | iOS | watchOS | tvOS
Carthage
License

## What is Hanson?

Hanson is a simple, lightweight library to observe and bind values in Swift. It's been developed to support the MVVM architecture in our [Blendle iOS app](https://itunes.apple.com/nl/app/blendle/id947936149). Hanson provides several advantages to using KVO in Swift, such as a Swiftier syntax, no boilerplate code, and the ability to use it in pure Swift types.

## Example Usage

The most basic use case is to simply observe an `Observable` for changes:
```swift
let observable = Observable("Hello World")
observe(observable) { event in
// Invoked whenever observable.value is set.
print("Value changed from \(event.oldValue) to \(event.newValue)")
}
```

Hanson also provides a wrapper around KVO, so you can do the following to observe a `UITextField`'s `text` property for changes:
```swift
let textField = UITextField()
let textFieldObservable = textField.dynamicObservable(keyPath: #keyPath(UITextField.text), type: String.self)
observe(textFieldObservable) { event in
print("Text field value changed from \(event.oldValue) to \(event.newValue)")
}
```

Furthermore, you can also use Hanson to bind an observable to another observable. Let's say we have a view model that's responsible for loading data, and we want the view to show an activity indicator while the view model is loading data:
```swift
class ViewModel {

let isLoadingData = Observable(false)

}

class View {

let showsActivityIndicator = Observable(false)

}

let viewModel = ViewModel()
let view = View()
bind(viewModel.isLoadingData, to: view.showsActivityIndicator)
```

Now, whenever the view model's `isLoadingData` property is set to a different value, it will automatically be set to the view's `showsActivityIndicator` property.

Binding is also supported from and to KVO-backed observables. To bind a text field's content to a label:
```swift
let textField = UITextField()
let textFieldObservable = textField.dynamicObservable(keyPath: #keyPath(UITextField.text), type: String.self)

let label = UILabel()
let labelObservable = label.dynamicObservable(keyPath: #keyPath(UILabel.text), type: String.self)

bind(textFieldObservable, to: labelObservable)
```

If you want to handle the binding yourself, you can also provide a closure that will be invoked when a new value should be set. In the following example, we'll bind an `isLoadingData` observable to a `UIActivityIndicatorView`:
```swift
let isLoadingData = Observable(false)
let activityIndicatorView = UIActivityIndicatorView()
bind(isLoadingData, to: activityIndicatorView) { activityIndicatorView, isLoadingData in
if isLoadingData {
activityIndicatorView.startAnimating()
} else {
activityIndicatorView.stopAnimating()
}
}
```

Hanson also supports observering notifications sent through a `NotificationCenter`. For example, to observe when an application is entering the background:
```swift
let observable = NotificationCenter.default.observable(for: Notification.Name.UIApplicationDidEnterBackground)
observe(observable) { notification in
print("Application did enter background")
}
```

### Schedulers

Schedulers can be used to schedule the events of your observation. By default, Hanson uses the `CurrentThreadScheduler`, which immediatly sends events on whatever thread it currently is. Hanson also offers the `MainThreadScheduler`, which ensures events are sent on the main thread. This is useful when observing a value that can change from a background thread and you want to do UI changes based on that value. For example:

```swift
let observable = Observable("Hello World")
observe(observable, with: MainThreadScheduler()) { event in
// It's safe to do UI work here without calling DispatchQueue.main.async here
}

performOnBackground {
observable.value = "Hello from a background"
}
```

Schedulers are also supported when binding observables:

```swift
let isLoadingData = Observable(true)
let activityIndicatorView = UIActivityIndicatorView()
bind(isLoadingData, with: MainThreadScheduler(), to: activityIndicatorView) { activityIndicatorView, isLoadingData in
// It's safe to do UI work here without calling DispatchQueue.main.async here
}

performOnBackground {
isLoadingData.value = false
}
```

You can create your own scheduler by conforming to the `EventScheduler` protocol.

## Requirements

* iOS 8.0+ / macOS 10.9+ / tvOS 9.0+
* Xcode 8

## Installation

Hanson is available through either [CocoaPods](http://cocoapods.org) or [Carthage](https://github.com/Carthage/Carthage).

### Cocoapods

1. Add `pod 'Hanson'` to your `Podfile`.
2. Run `pod install`.

### Carthage

1. Add `github 'blendle/Hanson'` to your `Cartfile`.
2. Run `carthage update`.
3. Link the framework with your target as described in [Carthage Readme](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application).

### Swift Package Manager

1. In Xcode, select your project and scroll to `Frameworks, Libraries, and Embedded Content`.
2. Click the `+`.
3. At the bottom of the frameworks and libraries window that opens, select `Add other...` and then `Add package dependency...`.
4. Paste `https://github.com/blendle/Hanson.git` in the search textfield and follow through with the assistant.

## Building

The project obviously builds fine through Xcode, just load up `Hanson.xcodeproj` and run it.

For convenience, we've included a few scripts and a `Makefile` that allow you to build Hanson from the command line and through continuous integration. They are inspired by GitHub's [Scripts to Rule Them All](https://github.com/github/scripts-to-rule-them-all) boilerplate:

```
|-- script/
|-- etc/
|-- config.sh # Contains basic configuration parameters
|-- bootstrap # Prepares the project
|-- setup # Sets up the local building process
|-- test # Runs tests locally
|-- cisetup # Sets up the CI building process
|-- citest # Runs tests in a CI environment
```

To get started:

`$ make`

To skip setup and immediately start testing:

`$ make test`

Make sure all tests pass before opening a Pull Request.

## Release Notes

See [CHANGELOG.md](https://github.com/blendle/Hanson/blob/master/CHANGELOG.md) for a list of changes.

## License

Hanson is released under the ISC license. See [LICENSE](https://github.com/blendle/Hanson/blob/master/LICENSE) for details.