I had a chance to play a little bit with a nice pod that is called StatefulViewController, created by Alexander Schuch. StatefulViewController works with both UIView
and UIViewController
, and allows you to introduce placeholders for their different states: Loading, Error, Empty or Content. Everything is based on an intuitive protocol and after providing your placeholder views and implementing required methods everything works like a charm. You can find this pod on Alexander’s github. I’ve looked into the code of this pod and I found a few things that I want to share with you today.
Whole idea is based on ViewStateMachine, which handles transitions of views that you have assigned to each state. All states are represented by this nice enum
/// Represents the state of the view state machine | |
public enum ViewStateMachineState : Equatable { | |
case None // No view shown | |
case View(String) // View with specific key is shown | |
} |
Equatable
protocol, which lets us implement "=="
operator for this enum. When you do this, standard library provides an implementation for "!="
. This move allows us to compare states later on. public func == (lhs: ViewStateMachineState, rhs: ViewStateMachineState) -> Bool { | |
switch (lhs, rhs) { | |
case (.None, .None): return true | |
case (.View(let lName), .View(let rName)): return lName == rName | |
default: return false | |
} | |
} |
Another thing that was interesting for me was subscript
provided for a ViewStateMachine
class.”You use subscripts to set and retrieve values by index without needing separate methods for setting and retrieval.” - note from Apple.
public subscript(state: String) -> UIView? { | |
get { | |
return viewForState(state) | |
} | |
set(newValue) { | |
if let value = newValue { | |
addView(value, forState: state) | |
} else { | |
removeViewForState(state) | |
} | |
} | |
} |
Example use:
private func placeholderView(state: StatefulViewControllerState) -> UIView? { | |
return stateMachine[state.rawValue] | |
} | |
private func setPlaceholderView(view: UIView?, forState state: StatefulViewControllerState) { | |
stateMachine[state.rawValue] = view | |
} |
Normally, you can’t add a property to a class via an extension, but you can achive this using an associated object.
extension StatefulViewController { | |
public var stateMachine: ViewStateMachine { | |
return associatedObject(self, key: &stateMachineKey) { [unowned self] in | |
return ViewStateMachine(view: self.backingView) | |
} | |
} | |
} | |
// MARK: Association | |
private var stateMachineKey: UInt8 = 0 | |
private func associatedObject<T: AnyObject>(host: AnyObject, key: UnsafePointer<Void>, initial: () -> T) -> T { | |
var value = objc_getAssociatedObject(host, key) as? T | |
if value == nil { | |
value = initial() | |
objc_setAssociatedObject(host, key, value, .OBJC_ASSOCIATION_RETAIN) | |
} | |
return value! | |
} |
I really liked the documentation that was provided with this pod. In Objective-C we had a nice header file, which was pretty easy to read from. Here we have two files. StatefulViewController.swift
file, which provides nice documentation and StatefulViewControllerImplementation.swift
which containts implementation of StatefulViewController
class.
It was really interesting code, which taught me some new thing. I hope that you liked it too!
This article is cross-posted with my my company blog