Latest 1.1.0
Homepage https://github.com/zacharyclaysmith/SwiftBinding
License MIT
Platforms ios 8.0, osx 10.10, requires ARC
Authors


# SwiftBinding

This is a terribly simplistic “binding framework” written in Swift. There’s nothing super special about it…it’s just glorified object wrappers that call listener functions when the values change.

This is meant to be a stand-alone library, but to see the companion UI stuff I built on top, check out https://github.com/zacharyclaysmith/SwiftBindingUI.

If you want something with way more stars and more code, please check out https://github.com/SwiftBond/Bond. It’s scarily similar to this project (unintentional), but it looks to have a number of neat additions and more robust documentation. I don’t like the coupling of the View and Binding code in a single project, but whatevs.

TODO: update examples in this document to reflect to some Swift 1.2 changes.

## Installation

1. Carthage
* Add `github “zacharyclaysmith/SwiftBinding”` to your Cartfile
* Run `carthage update`
2. CocoaPods
* `pod ‘SwiftBinding’`
3. Manual Installation
* Clone the repo
* Build
* Include the framework in your project.

## Usage _(work in progress)_

###BindableValue

| Public Variable | Type | Description | Notes |
| ————— | ——- | ———– | —– |
| value | T | Settings this value will trigger any listener functions.
| v | T | Shorthand wrapper for _value_

| Public Method | Returns | Description | Notes |
| ————–| ——- | ———– | —– |
| addChangeListener(owner:NSObject, alertNow:Bool = false, listener:(ValueType) -> Void) | Void | Adds a “change listener” to this object, where the _owner_ parameter acts as a key. These listeners are alerted any time the _value_ for this object is changed or when _alertChangeListeners()_ is called manually. | In a future version, the _owner_ parameter will likely be replaced by a more generic keying system, as _NSObjects_ are kind of stupid.
| removeChangeListener(owner:NSObject) | Void | removes any existing change listener keyed by the _owner_ parameter.
| alertChangeListeners() | Void | A manual way to call listeners added through the _addChangeListener_ method. | Useful when you have to change a value in more subtle ways than just changing its core value (e.g. when a sub-property changes). Also, at this time, this method does _**not**_ call “anyUpdate” listeners. I would call that a bug.
| addAnyUpdateListener(owner:NSObject, listener:() -> Void, alertNow:Bool) | Void | Adds an “Any Update” listener to this object. | This is a redundancy of addChangeListener, but is required to match the _PUpdateListener_ protocol.
| removeAnyUpdateListener(Owner:NSObject) | Void | Removes an “Any Update” listener.
| alertAnyUpdateListeners() | Void | A manual way to call “Any Update” listeners. | Useful when you have to change a value in more subtle ways than just changing its core value (e.g. when a sub-property changes).

## Usage (_extended cut_)

### BindableValue

`BindableValue` is the heart of this bindable library. `BindableValue`’s are pretty simple…they have a `value`, `changeListeners`, `anyUpdateListeners`.

#### `value` property
The value property is the heart of the BindableValue library. The under-the-hood implementation is fairly simple…just a setter and getter, where the setter calls some listener functions.

There is a shorthand `v` property that can be used to access the value property.

#### Change Listeners
Change listeners are called whenever the `value` setter is called (i.e. whenever the value property is changed). Use the `addChangeListener` method to add a change listener (that sounds pretty obvious, but now you know it’s obvious ;) ).

Use the `alertNow` parameter of `addChangeListener` when you want the listener called immediately upon binding.

Call `removeChangeListener` to clean up…typically in a deinit method. This isn’t super-important for values whose lives are tied to that of their listener, but for long-lived values, not cleaning up could seriously impact performance and stability.

#### Any Update Listeners
Update listeners are a protocol-friendly version of change listeners. This is typically used when you need to bind to something generically, e.g. detect modifications over a heterogenous collection of PUpdateable objects. Use the `addAnyUpdateListener` method similarly to the `addChangeListener`.

#### Examples

“`
func example_addChangeListener(){
let someBindableString = BindableValue(initialValue:”test”)

someBindableString.addListener(self, listener: someListener)

someBindableString.value = “newValue” //EFFECT: someListener is called with a value of “newValue”
}

func someListener(value:String){
//DO SOMETHING
}
“`

“`
func example_alertNow(){
let someBindableString = BindableValue(initialValue:”test”)

someBindableString.addListener(self, listener: someListener, alertNow:true) //EFFECT: someListener is called with a value of “test”

someBindableString.value = “newValue” //EFFECT: someListener is called with a value of “newValue”
}

func someListener(value:String){
//DO SOMETHING
}
“`

“`
func example_removeChangeListener(){
let someBindableString = BindableValue(initialValue:”test”)

someBindableString.addChangeListener(self, listener: someListener, alertNow:true) //EFFECT: someListener is called with a value of “test”

someBindableString.value = “newValue” //EFFECT: someListener is called with a value of “newValue”

someBindableString.removeChangeListener(self)

someBindableString.value = “anotherValue” //EFFECT: someListener is NOT called.
}

func someListener(value:String){
//DO SOMETHING
}
“`
### `CalculatedValue`

The `CalculatedValue` is where the real power of this framework starts to shine through. These are essentially `BindableValue`’s that actively listen to other `BindableValue`’s and perform a claculation (via the `calculator` property) and to produce a new value. `CalculatedValue`’s can listen to any number of `PUpdateable` protocol implementing classes (fun side-note, this is why that protocol was created in the first place).

For ease of interoperability (and because the Swift Laws of Protocols and Generics can be pretty frustrating, `CalculatedValue`’s are just subtypes of `BindableValue`. This means, though the `value` and `v` property setters are exposed publicly, they should not be used, or expect DIRE consequences (or at least unintended behavior…whatevs, you’ve been warned).

#### `calculator`
This is just a function that produces a value of the given `T` value for the `CalculatedValue` object. This method takes no parameters…bound values should be referenced from a larger scope. If none of this makes sense, look at the examples.

#### Examples
“`
func example_basicCalculatedValueUsage(){
let bindableInteger = BindableValue(value: 0)

let calculatedCurrencyString = CalculatedValue([bindableInteger], calculator:{() -> String in
return “$” + toString(bindableInteger.value) + “.00”
})

//EFFECT: calculatedCurrencyString.v == “$0.00”

bindableInteger.value = 15

//EFFECT: calculatedCurrencyString.v == “$15.00”
}
“`

Keep in mind that you can created CalculatedValues from other CalculatedValues (Just be careful not to recursively bind a value to itself…black holes will form ;) ).

### `BindableValue` -> `CalculatedValue` Extensions

To make things more syntactically/semantically appealing, I wrote the following extension methods which really clean up some common use cases. These methods are `transform` and `combine`.

`transform` allows you to easily convert 1 BindableValue into another

#### Examples
“`
func example_transform(){
let bindableInteger = BindableValue(value: 0)

let calculatedCurrencyString = bindableInteger.transform({“$” + toString(bindableInteger.value) + “.00”}) //EFFECT: calculatedCurrencyString.v == “$0.00”

bindableInteger.value = 15 //EFFECT: calculatedCurrencyString.v == “$15.00”
}
“`

“`
func example_combineSingle(){
let bindableInteger = BindableValue(value: 1)
let bindableInteger2 = BindableValue(value: 5.0)

let sumCurrencyString = bindableInteger.combine(bindableInteger2, calculator: “$” + toString((bindableInteger.value + bindableFloat.value)) + “.00”) //EFFECT: calculatedCurrencyString.v == “$6.00”

bindableInteger.value = 15 //EFFECT: calculatedCurrencyString.v == “$20.00”
}

func example_combineMultiple(){
let bindableInteger = BindableValue(value: 1)
let bindableInteger2 = BindableValue(value: 5.0)
let bindableInteger3 = BindableValue(value: 10.0)

let sumDollarString = bindableInteger.combine([bindableInteger2, bindableInteger3],
calculator: “$” + toString((bindableInteger.value + bindableFloat.value + bindableFloat2.value)) + “.00”)
//EFFECT: calculatedCurrencyString.v == “$16.00”

bindableInteger.value = 15 //EFFECT: calculatedCurrencyString.v == “$31.00”
}
“`

NOTE: although the above examples are all integers for simplicity (and because I’m a lazy example writer), keep in mind that you can combine any and all value types into a single calculated value.

### BindableArray
Bindable values are fun, but bindable arrays are where binding frameworks make the cheese (or whatever idiom you want to insert). I’ve tried to keep `BindableArray` simple; you have the ability to listen to array and detect when an index has changed or when the structure of the array has changed.

`BindableArray` is a wrapper for the Swift `Array` class, meaning `BindableArray` reimplements many of the methods and functionality of the Swift `Array` class, but inserts some binding triggers into them where they make sense. For example, subarray access is wrapped so that `someStringArray[5] = “newValue”` triggers indexChanged listeners. 1-1 Wrapping is still a work in progress, so a public property `internalArray` and public `notifyIndexChangedListeners` and `notifyChangedListeners` functions are exposed so that manual changes and triggers can be made.

#### Index Changed Listener
In general, this is called whenever a single index has been affected by an operation. Such operations include: subscript assignment/modification and `append`.

NOTE: I would recommend, in the case of bulk updates to many array values, to modify the values on the `internalArray` property, and then call `notifyChangedListeners` at the end to conserve execution cycles.

#### Changed Listeners
Change listeners are called whenever a change is made to the overall array, such as in deletes, splices, or bulk changes.

#### Room for Improvement
I tend to take the “optimize it when it’s broken” approach to development, so I haven’t spent too much time fine-tuning array operations, as they haven’t caused me any headaches yet. Feel free to contact me if you need something in `BindableArray` or `CalculatedArray` made betterer, or if you want to contribute yourself :).

#### Examples

“`
func example_append(){
let bindableArray = BindableArray(initialArray:[])

bindableArray.addIndexChangedListener(self, listener:someListener)

bindableArray.append(“someString”) //EFFECT: someListener is called with index == bindableArray.length – 1
}

func someListener(index:Int){
//DO SOMETHING
}

“`

### `CalculatedArray`

TODO…It’s actually pretty awesome, but I can only write so much documentation in a day.

## History

I wanted a simple binding framework for iOS/Swift, so I started this. It’s proven itself pretty useful. I’ve used several binding frameworks over the years and have learned that basic value and array binding support are really all you need, plus some UI sugar on top (see https://github.com/zacharyclaysmith/SwiftBindingUI for that mess).

I don’t consider this library complete or well tested, yet, but it’s functional and a good starting point. I’d like to keep it as simple as possible, however, and build other libraries around it.

## Credits

Viewers like you. And my absolute hatred for managing UI data in iOS.

## License

MIT

Latest podspec

{
    "name": "SwiftBinding",
    "version": "1.1.0",
    "summary": "A Binding Library for the Swift language.",
    "description": "This is a terribly simplistic "binding framework" written in Swift. There's nothing super special about it...it's just glorified object wrappers that call listener functions when the values change.nnThis is meant to be a stand-alone library, but to see the companion UI stuff I built on top, check out https://github.com/zacharyclaysmith/SwiftBindingUI.nnIf you want something with way more stars and more code, please check out https://github.com/SwiftBond/Bond. It's scarily similar to this project (unintentional), but it looks to have a number of neat additions and more robust documentation. I don't like the coupling of the View and Binding code in a single project, but whatevs.n",
    "homepage": "https://github.com/zacharyclaysmith/SwiftBinding",
    "license": {
        "type": "MIT",
        "file": "LICENSE.md"
    },
    "authors": {
        "Zachary Clay Smith": "[email protected]"
    },
    "platforms": {
        "ios": "8.0",
        "osx": "10.10"
    },
    "source": {
        "git": "https://github.com/zacharyclaysmith/SwiftBinding.git",
        "tag": "1.1.0"
    },
    "source_files": "SwiftBinding/**/*.swift",
    "requires_arc": true
}

Pin It on Pinterest

Share This