Post on 19-Jan-2017
AGENDAWhat does information distribution mean?
Delegation
Closures
Notifications
Property Observers
Bindings / Key Value Observation
INFORMATION DISTRIBUTIONMobile apps are highly interactive
Multiple interfaces drive data changes:
UI
Network
Main task is responding to events and distributing new
data
CODE LOCALITYEach piece of code needs well
defined responsibility
Example: The code that triggers network request is not necessarily the code that is interested in response
EntryView
FolderView
updateImage
downloadButtonTapped
downloadImage
(1)
(2)
(3)Image
OtherView
updateImage
TYPE TO TYPE COMMUNICATION
class UserViewController: UIViewController { func infoButtonTapped() { // communicate with business logic } }
class UserView { var userViewController: UserViewController? func infoButtonTapped() { userViewController?.infoButtonTapped() } }
Tight coupling!
TYPE TO TYPE COMMUNICATION
func infoButtonTapped()//…init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?)var view: UIView!func loadView()var nibName: String? { get }var nibBundle: NSBundle? { get }var storyboard: UIStoryboard? { get }//…
The UserView could call any method provided by UserViewController, including all the ones inherited from UIView Controller
UserView can become dependent on UIViewController
UserView has to deal with huge interface:
TYPE TO TYPE COMMUNICATION
Typically used to establish a life long connection
Easy to use
Results in tight coupling
No tight interface for communication between two types
Mostly only useful for 1-1 communication
DELEGATION
Create a formal protocol that describes the
communication interface
Use this protocol to create an indirect
connection between the two types
DELEGATION
class UserView { var delegate: UserViewResponder? func infoButtonTapped() { responder?.infoButtonTapped() } }
Indirection = looser coupling
class UserViewController: UIViewController, UserViewResponder { func infoButtonTapped() { // communicate with business logic } }
protocol UserViewResponder { func infoButtonTapped() }
DELEGATION
Typically used to establish a life long connection
Decouples communication, easy to replace delegate with any other type conforming to protocol
Tight interface that contains only methods that are relevant for this specific communication channel
Mostly only useful for 1-1 communication
CLOSURES
class APIClient { func userDetails(userId: String, callback: UserDetailsCallback) { // network request callback(user) } }
Indirection = looser coupling
class UserViewController: UIViewController { override func viewDidAppear(animated: Bool) { APIClient().userDetails("13") { user in // do something with user } } }
typealias UserDetailsCallback = User? -> ()
CLOSURE GOTCHAS
Code localityclass UserViewController: UIViewController { override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated) APIClient().userDetails("13") { user in
// do something with user } } }
Code localityclass UserViewController2: UIViewController { override func viewDidAppear(animated: Bool) { super.viewDidAppear(animated)
APIClient().userDetails("13", callback: receivedUser) } func receivedUser(user: User?) { // do something with user } }
CLOSURE GOTCHAS
Retain Cycles
class UserViewController: UIViewController { var callback: UserDetailsCallback?
override func viewDidAppear(animated: Bool) { callback = { user in self.showUser(user) } } func showUser(user: User?) { //... } }
Object retains the callback
Callback retains the object through
self reference
Results in retain cycle
CLOSURE GOTCHAS
No Retain Cycles
class UserViewController: UIViewController { var callback: UserDetailsCallback?
override func viewDidAppear(animated: Bool) { callback = { [unowned self] user in self.showUser(user) } } func showUser(user: User?) { //... } }
Use capture list to capture self
weakly
Callback will no longer retain self
Breaks retain cycle!
CLOSURES
Typically used for short lived relationships
Decouples communication
Provides a communication interface with only a single function
Requires caution regarding code locality
Need to be careful to avoid retain cycles
NOTIFICATIONS
Notifications allow us to broadcast information (1
to N)
Sender has no information about which objects
have subscribed to notifications
NOTIFICATIONS
func synchronize() { // network request NSNotificationCenter.defaultCenter().postNotificationName(
"MyApp.SynchronizationCompleted", object: self)
}
Posting a notification:
Notifications are delivered on the same thread on which they are posted!
Optionally you can use a userData argument to attach arbitrary data to the notification
NOTIFICATIONS
class Listener { init() { NSNotificationCenter.defaultCenter().addObserver(self, selector: "syncComplete", name: nil, object: nil)
} @objc func syncComplete() { // work print("ok") } }
Registering for Notifications: Specify which notification you want to listen to
Specify which method on which object should be called once this notification occurs
Mark the target method with @objc if you are not subclassing from an Objective-C object
NOTIFICATION GOTCHAS
Don’t forget to unsubscribe!
class Listener { deinit { NSNotificationCenter.defaultCenter().removeObserver(self) } //…}
If a deallocated object is registered
with the notification center, your app
will crash on an attempt to deliver a
notification to the dead object class UserViewController: UIViewController { override func viewDidDisappear(animated: Bool) { super.viewDidDisappear(animated) NSNotificationCenter.defaultCenter().removeObserver(self) }
}
NOTIFICATIONSUsed for broadcasting information
Easy to communicate with different parts of the program without explicit references
No well defined communication interface / no type information
Causes crashes if you forget to unregister
Can create dependencies between code that should not be coupled
PROPERTY OBSERVERS
class UserViewController: UIViewController { var label: UILabel! var user: User? { didSet { if let label = label, let user = user { label.text = user.name } } } }
Implicitly propagate changes within
an instance of a type
PROPERTY OBSERVERS
Used for information propagation within an instance
Easy to use, type safe
Not applicable in many scenarios
KEY VALUE OBSERVATION (KVO)
Objective-C API that relies on the Objective-C Runtime
Generates notifications when an observed property on
an observed object changes
Think: property observers for other objects
KVOclass Observer: NSObject { var user: User init(user: User) { self.user = user super.init() self.user.addObserver(self, forKeyPath: "name", options: NSKeyValueObservingOptions.New, context: nil) } override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) { if let newValue = change?[NSKeyValueChangeNewKey] { print("Name changed: \(newValue)") } else { super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context) } } deinit { self.user.removeObserver(self, forKeyPath: "name") } }
API was not designed for Swift!
KVOUsed to observe changing values on other objects
Allows observation of (almost) any property without additional work on class that is being observed
API doesn’t provide type information of observed values
API is arcane, e.g. one callback for all observer properties
Not available on Swift classes, need to inherit from NSObject and use the dynamic keyword
KVO / BINDINGS
Swift doesn’t have it’s own KVO mechanism, but there
are third party alternatives and it’s easy to implement
your own KVO alternative [1]
One framework that provides KVO and Binding
capabilities is Bond [2]
[1]: Exploring KVO Alternatives with Swift[2]: Summer Academy lecture discussing Bond
DYNAMIC PROPERTIES IN BOND
var image: Observable<UIImage?> = Observable(nil)
image.value = otherImage
Declaring an Observable property:
Setting an Observable property:
OBSERVING NEW VALUES
post.likes.observe { [unowned self] users inself.likingUsers = users//…
}
Calls callback whenever new value is available:
OBSERVING NEW VALUES
post.image.bindTo(postImageView.bnd_image)
Updates image view whenever new value is available:
BONDS
Used to observe changing values on other objects
Allows observation of (almost) any property without much additional work on class that is being observed
Communication protocol is implicit, harder to understand for other developers
ADDITIONAL RESOURCES
NSNotificationCenter class reference
Swift Language Reference: Property Observers
NSHipster: Key-Value Observing
Exploring KVO Alternatives with Swift
Bond Framework on Github