A light weight approach to event based programming
When developing applications using architectures like Model-View-Viewmodel, needing to know when a value changes in an object, or
just wanting to have a notification based API, I have found using APIs like NotificationCenter
were diffcult to write good unit tests to validate that execution happened. Also using these existing API's would promote the bad practice of not using a unidirectional dataflow.
You can use CocoaPods to install SGSwiftyBind
by adding it to your Podfile
:
platform :ios, '9.0'
use_frameworks
target 'MyApp' do
pod 'SGSwiftyBind'
end
You can use Carthage to install SGSwiftyBind
by adding it to your Cartfile
:
github "eman6576/SGSwiftyBind"
You can use Swift Package Manager to install SGSwiftyBind
by adding the proper description to your Package.swift
file:
import PackageDescription
let package = Package(
name: "YOUR_PROJECT_NAME",
dependencies: [
.package(url: "https://github.com/eman6576/SGSwiftyBind.git", from: "1.0.0")
],
targets: [
.target(
name: "YOUR_TARGET_NAME",
dependencies: [
"SGSwiftyBind"
])
]
)
NB: This repository contains an examples directory with different examples on how to use this library. We recommend trying the examples out. Also check out the unit tests to see the library in action as well.
To access the available data types, import SGSwiftyBind
into your project like so:
import SGSwiftyBind
SGSwiftyBind
uses generics for storing different data types:
let intBind: SGSwiftyBind<Int> = SGSwiftyBind(1)
let doubleBind: SGSwiftyBind<Double> = SGSwiftyBind(1.2)
let stringBind: SGSwiftyBind<String> = SGSwiftyBind("Hello World")
You can even use your own custom types:
protocol Animal {
var age: Int { get }
init(age: Int)
func makeSound()
func increaseAge()
}
struct Dog: Animal {
var age: Int
init(age: Int) {
self.age = age
}
func makeSound() {
print("Bark")
}
func increaseAge() {
age += 1
}
}
let dog = Dog(age: 3)
let animalBind: SGSwiftyBind<Animal> = SGSwiftyBind(dog)
A great use case for using SGSwiftyBind is for knowing when a variable changes or some event occurs. Lets modify our Animal
protocol to use SGSwiftyBind
:
protocol Animal {
var age: SGSwiftyBindInterface<Int> { get }
init(age: Int)
func makeSound()
func increaseAge()
}
struct Dog: Animal {
var age: SGSwiftyBindInterface<Int> {
return ageBind.interface
}
private let ageBind: SGSwiftyBind<Int>
init(age: Int) {
ageBind = SGSwiftyBind(age)
}
func makeSound() {
print("Bark")
}
func increaseAge() {
ageBind.value += 1
}
}
We modified our Animal
to have a binder interface on the age
property. Our Dog
would need to update its age
property to return a binder interface. and added a private binder called ageBind
of type SGSwiftyBind<Int>
. Now lets use our Dog
object:
let dog = Dog(age: 3)
dog.age.bind({ (newAge) in
print("This is the dog's new age: \(newAge)")
}, for: self)
dog.makeSound()
dog.increaseAge()
In the example, we allocate a Dog
instance. We then register a binder on the age property so that when the value changes, we can get the new value and do something with it. In our case, we are printing the value to the console.
The age
property on a Dog
instance is of type SGSwiftyBindInterface<Int>
and not SGSwiftyBind<Int>
. The idea behind this is so the outside can not mutate the state of ageBind
. Here is an example:
let currentAge = dog.age.value // We can retrieve the current value of age
dog.age.value = currentAge // The complier would give us an error stating that `age.value` is a get-only property
Once we are done using the binder on our Dog
instance, we should perform some clean up and remove it like so:
dog.age.unbind(for: self)
We can also get the current number of observers that have registered a particular binder:
let observerCount = dog.age.observerCount
See the contribute file!
PRs accepted.
Small note: If editing the Readme, please conform to the standard-readme specification.