Dynamic Data Binding
in Swift
Gagan Vishal Mishra
Introduction
Data types/Primitive data types does not provide any way to detect changes until we implement & access
their Setters. Swift provided it’s own way to detect any changes in data types & primitive data types.
Swift provides various own ways to implement observer mechanism. In this tutorial we will implement
observer pattern using Swift closure & generics. Swift also allows to crate own data types & trail them as per
our needs.
Explanation
Create a generic class name as Dynamic having a variable say ‘value’ as below.
class Dynamic <T> {
var value : T {
didSet {
print(“Value is : (value)”)
}
}
init(_ value : T) {
self.value = value
}
}
In above code we have also added observer for the object. Now create an object for the class Dynamic as
below
let objectDynamic = Dynamic(“fistValue") // It does not print anything.
objectDynamic.value = “secondValue" // Prints, Value is : secondValue
print(“(objectDynamic.value)") // Prints secondValue
Now in above case as we see that initialisation does not lead to call didSet but assigning object a new value
invokes didiSet. We will this behaviour in our dynamic binding approach. Our aim is, every time when a new
value is assigned or initialised we must have listener for update & print the value.
Explanation
Add another instance method and a closure in our Dynamic class as below
class Dynamic <T>{
typealias Listener = (T) -> Void // Add a closure which takes type ’T’ as an argument and having return type void
var listenerObject : Listener? // Add an object for the closure
func bind(listener : @escaping Listener) // Add a method with input parameter is an instance of closure
{
self.listenerObject = listener // Assign object to instance listener object
}
var value : T {
didSet {
self.listenerObject!(value) // Our declared closure accepts an input of type ’T’.
}
}
init(_ value : T) {
self.value = value
}
}
Again create a new instance for the Dynamic class as below
let objectDynamic = Dynamic("First")
objectDynamic.bind { (value) in
print("(value)")
}
objectDynamic.value = “second" //Prints second
objectDynamic.value = "third" //Prints third
From above, every time when a new value is assign our listener triggered which is our aim but out
‘first’. Above code does not print ‘first’. Our code is still incomplete.
Explanation
Add a new instance method in our Dynamic class say ‘bindAndFire’
class Dynamic <T>{
typealias Listener = (T) -> Void
var listenerObject : Listener?
func bind(listener : @escaping Listener)
{
self.listenerObject = listener
}
func bindAndFire(listener : @escaping Listener) // Add a new method in class which takes Listener object as input
{
self.listenerObject = listener
listener(value)
}
var value : T {
didSet {
self.listenerObject!(value)
}
}
init(_ value : T) {
self.value = value
}
}
Now create a new instance for the Dynamic class as below
let objectDynamic = Dynamic("First")
objectDynamic.bindAndFire { (value) in
print(“(value)") //Prints First, second, third
}
objectDynamic.value = "second"
objectDynamic.value = “third"
Now this is the output what we are looking for and this our binding mechanism. Benefit of above approach is
that there is no need to register & unregister the observer as we do in notification or in KVO.
We will use above approach in our iOS example.
Example
We will use dynamic binding in our app for get X,Y,Z rotation value of the device using CoreMotion
framework.
Create a class name ‘AxisRotation’ as below
class AxisRotation {
var xAxis : Double!
var yAxis : Double!
var zAxis : Double!
}
Create a new Protocol as below
protocol AxisRotaionViewModel: class {
var xAxis : Dynamic<Double> {get}
var yAxis : Dynamic<Double>{get}
var zAxis : Dynamic<Double>{get}
}
Create a new class which confirms above protocol and add variables
class AxisRotaionViewViewModelFromRotation : AxisRotaionViewModel
{
let axis : AxisRotation
var xAxis : Dynamic<Double>
var yAxis : Dynamic<Double>
var zAxis : Dynamic<Double>
}
Example
As we need, listeners get notified when the value of Dynamic changes, but not when Dynamic itself
changes! That means that we have to initialise all of them in the initialiser code of
AxisRotaionViewViewModelFromRotation class as below
init(_ axisRotation : AxisRotation) {
self.axis = axisRotation
self.xAxis = Dynamic(axisRotation.xAxis)
self.yAxis = Dynamic(axisRotation.yAxis)
self.zAxis = Dynamic(axisRotation.zAxis)
if motionManager.isGyroAvailable
{
//set interval time
motionManager.deviceMotionUpdateInterval = 0.25
motionManager.startDeviceMotionUpdates()
motionManager.gyroUpdateInterval = 0.25
motionManager.startGyroUpdates(to: OperationQueue.main, withHandler: { [weak self](motionData, error) in
if (error != nil) {
print("Error in getting motion data with error description :(String(describing: error?.localizedDescription))")
}
else
{
if let data = motionData {
self?.xAxis = Dynamic(data.rotationRate.x)
self?.yAxis = Dynamic(data.rotationRate.y)
self?.zAxis = Dynamic(data.rotationRate.z)
}
}
})
}
}
Example
Finally our viewController looks like below
var rotationViewModel : AxisRotaionViewModel!
{
didSet {
rotationViewModel.xAxis.bindAndFire { (xAxis) in
self.xAxisRotation.text = "(xAxis)"
}
rotationViewModel.yAxis.bindAndFire { (yAxis) in
self.yAxisRotation.text = "(yAxis)"
}
rotationViewModel.zAxis.bindAndFire { (zAxis) in
self.zAxisRotation.text = "(zAxis)"
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
//add dummy data
let dummyData = AxisRotation()
dummyData.xAxis = 0.0
dummyData.yAxis = 0.0
dummyData.zAxis = 0.0
let testViewModel = AxisRotaionViewViewModelFromRotation(dummyData)
self.rotationViewModel = testViewModel
}
Benefit of Dynamic data binding remove pain on registering & unregistering on notification & KVO.
Source Code Link : https://github.com/Gagan5278/DynamicDataBinding

Dynamic databinding

  • 1.
    Dynamic Data Binding inSwift Gagan Vishal Mishra
  • 2.
    Introduction Data types/Primitive datatypes does not provide any way to detect changes until we implement & access their Setters. Swift provided it’s own way to detect any changes in data types & primitive data types. Swift provides various own ways to implement observer mechanism. In this tutorial we will implement observer pattern using Swift closure & generics. Swift also allows to crate own data types & trail them as per our needs.
  • 3.
    Explanation Create a genericclass name as Dynamic having a variable say ‘value’ as below. class Dynamic <T> { var value : T { didSet { print(“Value is : (value)”) } } init(_ value : T) { self.value = value } } In above code we have also added observer for the object. Now create an object for the class Dynamic as below let objectDynamic = Dynamic(“fistValue") // It does not print anything. objectDynamic.value = “secondValue" // Prints, Value is : secondValue print(“(objectDynamic.value)") // Prints secondValue Now in above case as we see that initialisation does not lead to call didSet but assigning object a new value invokes didiSet. We will this behaviour in our dynamic binding approach. Our aim is, every time when a new value is assigned or initialised we must have listener for update & print the value.
  • 4.
    Explanation Add another instancemethod and a closure in our Dynamic class as below class Dynamic <T>{ typealias Listener = (T) -> Void // Add a closure which takes type ’T’ as an argument and having return type void var listenerObject : Listener? // Add an object for the closure func bind(listener : @escaping Listener) // Add a method with input parameter is an instance of closure { self.listenerObject = listener // Assign object to instance listener object } var value : T { didSet { self.listenerObject!(value) // Our declared closure accepts an input of type ’T’. } } init(_ value : T) { self.value = value } } Again create a new instance for the Dynamic class as below let objectDynamic = Dynamic("First") objectDynamic.bind { (value) in print("(value)") } objectDynamic.value = “second" //Prints second objectDynamic.value = "third" //Prints third From above, every time when a new value is assign our listener triggered which is our aim but out ‘first’. Above code does not print ‘first’. Our code is still incomplete.
  • 5.
    Explanation Add a newinstance method in our Dynamic class say ‘bindAndFire’ class Dynamic <T>{ typealias Listener = (T) -> Void var listenerObject : Listener? func bind(listener : @escaping Listener) { self.listenerObject = listener } func bindAndFire(listener : @escaping Listener) // Add a new method in class which takes Listener object as input { self.listenerObject = listener listener(value) } var value : T { didSet { self.listenerObject!(value) } } init(_ value : T) { self.value = value } } Now create a new instance for the Dynamic class as below let objectDynamic = Dynamic("First") objectDynamic.bindAndFire { (value) in print(“(value)") //Prints First, second, third } objectDynamic.value = "second" objectDynamic.value = “third" Now this is the output what we are looking for and this our binding mechanism. Benefit of above approach is that there is no need to register & unregister the observer as we do in notification or in KVO. We will use above approach in our iOS example.
  • 6.
    Example We will usedynamic binding in our app for get X,Y,Z rotation value of the device using CoreMotion framework. Create a class name ‘AxisRotation’ as below class AxisRotation { var xAxis : Double! var yAxis : Double! var zAxis : Double! } Create a new Protocol as below protocol AxisRotaionViewModel: class { var xAxis : Dynamic<Double> {get} var yAxis : Dynamic<Double>{get} var zAxis : Dynamic<Double>{get} } Create a new class which confirms above protocol and add variables class AxisRotaionViewViewModelFromRotation : AxisRotaionViewModel { let axis : AxisRotation var xAxis : Dynamic<Double> var yAxis : Dynamic<Double> var zAxis : Dynamic<Double> }
  • 7.
    Example As we need,listeners get notified when the value of Dynamic changes, but not when Dynamic itself changes! That means that we have to initialise all of them in the initialiser code of AxisRotaionViewViewModelFromRotation class as below init(_ axisRotation : AxisRotation) { self.axis = axisRotation self.xAxis = Dynamic(axisRotation.xAxis) self.yAxis = Dynamic(axisRotation.yAxis) self.zAxis = Dynamic(axisRotation.zAxis) if motionManager.isGyroAvailable { //set interval time motionManager.deviceMotionUpdateInterval = 0.25 motionManager.startDeviceMotionUpdates() motionManager.gyroUpdateInterval = 0.25 motionManager.startGyroUpdates(to: OperationQueue.main, withHandler: { [weak self](motionData, error) in if (error != nil) { print("Error in getting motion data with error description :(String(describing: error?.localizedDescription))") } else { if let data = motionData { self?.xAxis = Dynamic(data.rotationRate.x) self?.yAxis = Dynamic(data.rotationRate.y) self?.zAxis = Dynamic(data.rotationRate.z) } } }) } }
  • 8.
    Example Finally our viewControllerlooks like below var rotationViewModel : AxisRotaionViewModel! { didSet { rotationViewModel.xAxis.bindAndFire { (xAxis) in self.xAxisRotation.text = "(xAxis)" } rotationViewModel.yAxis.bindAndFire { (yAxis) in self.yAxisRotation.text = "(yAxis)" } rotationViewModel.zAxis.bindAndFire { (zAxis) in self.zAxisRotation.text = "(zAxis)" } } } override func viewDidLoad() { super.viewDidLoad() //add dummy data let dummyData = AxisRotation() dummyData.xAxis = 0.0 dummyData.yAxis = 0.0 dummyData.zAxis = 0.0 let testViewModel = AxisRotaionViewViewModelFromRotation(dummyData) self.rotationViewModel = testViewModel } Benefit of Dynamic data binding remove pain on registering & unregistering on notification & KVO. Source Code Link : https://github.com/Gagan5278/DynamicDataBinding