Created
January 22, 2017 13:42
-
-
Save alexdrone/a30b7a1908518df73194572ccaa114d5 to your computer and use it in GitHub Desktop.
Unidirectional data-flow in Swift with Dependency Injection.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
protocol AnyStateful: class { | |
/// The children of this node. | |
var statefulChildren: [AnyStateful] { get } | |
// Internal call to propagate the render call. | |
func _render(state: Any?) | |
/// The controller that is used as delegate for this stateful object. | |
weak var _controller: StatefulControllerType? { get set } | |
} | |
extension AnyStateful { | |
/// Assign and propagates down the controller. | |
func assign(controller: StatefulControllerType?) { | |
self._controller = controller | |
self.statefulChildren.forEach { $0.assign(controller: controller) } | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
protocol InjectionContextType { } | |
protocol InjectableState: Stateful, AnyInjectable { | |
associatedtype C: InjectionContextType | |
/// The context for this node. | |
var context: C { get set } | |
/// Initialise the class with the context passed as argument. | |
init(context: C) | |
/// Called when a new context is being injected. | |
func onContextChange(_ context: C?) | |
} | |
extension InjectableState { | |
var contextType: Any.Type { | |
return C.self | |
} | |
/// Recursively inject the new context. | |
func inject(context: C) { | |
self.context = context | |
self.onContextChange(context) | |
self.statefulChildren.flatMap { | |
$0 as? AnyInjectable | |
}.filter { | |
$0.contextType == C.self | |
}.forEach { | |
$0._inject(context: context) | |
} | |
} | |
// Private type-erased injector. | |
func _inject(context: InjectionContextType) { | |
guard let context = context as? C else { | |
return | |
} | |
self.inject(context: context) | |
} | |
} | |
protocol AnyInjectable { | |
var contextType: Any.Type { get } | |
func _inject(context: InjectionContextType) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
protocol StatefulControllerType: class { } | |
protocol Stateful: AnyStateful { | |
associatedtype S | |
associatedtype D: StatefulControllerType | |
/// Update 'this' object with the current state passed as argument. | |
func onRender(state: S?) | |
} | |
extension Stateful { | |
var controller: D? { | |
return self._controller as? D | |
} | |
// Internal call to propagate the render call. | |
func _render(state: Any?) { | |
if state == nil { | |
self.render(state: nil) | |
return | |
} | |
guard let state = state as? S else { | |
return | |
} | |
render(state: state) | |
} | |
/// Recursively render the state. | |
func render(state: S?) { | |
self.onRender(state: state) | |
self.statefulChildren.forEach { $0._render(state: state) } | |
} | |
} | |
extension Stateful where Self: UIView { | |
/// In the case of 'UIView' the 'render' command gets propagated to its subviews. | |
var statefulChildren: [AnyStateful] { | |
return self.subviews.flatMap { $0 as? AnyStateful } | |
} | |
} | |
extension Stateful where Self: UIViewController { | |
/// In the case of 'UIViewController' the 'render' command gets propagated to its child-vcs. | |
var statefulChildren: [AnyStateful] { | |
return self.childViewControllers.flatMap { $0 as? AnyStateful } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment