Last active
February 7, 2016 20:23
-
-
Save amberstar/9ee541467e97132c3814 to your computer and use it in GitHub Desktop.
Counter Ref
This file contains hidden or 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
//: [Previous](@previous) | |
import UIKit | |
import Behavior | |
import XCPlayground | |
//: ## Task: Create a small counter application | |
//: This approach uses ports and behaviors | |
struct Counter { | |
// Inputs | |
let increment = Port<Void>() | |
let decrement = Port<Void>() | |
// Outputs | |
let count = Port<Int>() | |
// Storage | |
var _count: Int = 0 { | |
didSet { | |
// whenever _count is updated send the count event. | |
count.send(_count) | |
} | |
} | |
init() { | |
increment.output { _ in self._count += 1 } | |
decrement.output { _ in self._count -= 1 } | |
} | |
} | |
struct CounterState { | |
let count: Int | |
let title: String | |
} | |
struct CounterUI { | |
// create a formatter | |
let numberFormat = Take<Int>().map { String(format: " %02d", arguments: [$0]) } | |
// inputs | |
let render = Port<(CounterState)> () | |
// outputs | |
let title = Port<String>() | |
let count = Port<String>() | |
func _render(state: CounterState) { | |
title.send(state.title) | |
count.send(numberFormat.next(state.count) ?? "00") | |
} | |
init() { | |
render.output { self._render($0) } | |
} | |
} | |
class CounterViewController: UIViewController { | |
// inputs | |
let _title = Port<String>() | |
let _count = Port<String>() | |
// outputs | |
let incrementAction = Port<Void>() | |
let decrementAction = Port<Void>() | |
let titleLabel = UILabel() | |
let countLabel = UILabel() | |
let incrementButton = UIButton(type: UIButtonType.RoundedRect) | |
let decrementButton = UIButton(type: UIButtonType.RoundedRect) | |
func decrementButtonTapped() { decrementAction.send() } | |
func incrementButtonTapped() { incrementAction.send() } | |
override func viewDidLoad() { | |
view.backgroundColor = .whiteColor() | |
titleLabel.text = "Title Label" | |
titleLabel.frame = CGRect(x: 10, y: 30, width: 300, height: 40) | |
countLabel.text = "00" | |
countLabel.frame = CGRect(x: 10, y: 30 + 50, width: 300, height: 40) | |
view.addSubview(countLabel) | |
view.addSubview(titleLabel) | |
_title.output { self.titleLabel.text = $0 } | |
_count.output { self.countLabel.text = $0 } | |
// buttons | |
incrementButton.setTitle("Increment", forState: UIControlState.Normal) | |
decrementButton.setTitle("Decrement", forState: UIControlState.Normal) | |
incrementButton.frame = CGRect(x: 10, y: 175, width: 300, height: 40) | |
decrementButton.frame = CGRect(x: 10, y: 250, width: 300, height: 40) | |
view.addSubview(incrementButton) | |
view.addSubview(decrementButton) | |
self.decrementButton.addTarget(self, action: "decrementButtonTapped", forControlEvents: .TouchUpInside) | |
self.incrementButton.addTarget(self, action: "incrementButtonTapped", forControlEvents: .TouchUpInside) | |
} | |
} | |
// the counter assembly is a component that holds the counter, and the counterview together | |
struct CounterAssembly { | |
// inputs | |
var loadView = Port<Void>() | |
// output | |
var viewDidLoad = Port<CounterViewController>() | |
// sub-components | |
var counter = Counter() | |
var counterUI = CounterUI() | |
init(title: String, count: Int = 0) { | |
// this creates a behavior that | |
// when given a count value, render the view with a new CounterState | |
let viewAdapter = Behavior { self.counterUI.render.send(CounterState(count: $0, title: title)) } | |
// connect the viewAdapter to the counter | |
counter.count.connect(viewAdapter) | |
// connect the loadView action | |
loadView.output { | |
let viewController = CounterViewController() | |
// connect the viewController outputs to the counter | |
viewController.incrementAction.connect(self.counter.increment) | |
viewController.decrementAction.connect(self.counter.decrement) | |
// connect the UI to the viewController | |
self.counterUI.title.connect(viewController._title) | |
self.counterUI.count.connect(viewController._count) | |
self.viewDidLoad.send(viewController) | |
} | |
} | |
} | |
// create an counter component | |
var counter = CounterAssembly(title: "Counter Component") | |
counter.viewDidLoad.output { vc in | |
vc.view.frame.size = CGSize(width: 320, height: 300) | |
XCPlaygroundPage.currentPage.liveView = vc.view | |
} | |
counter.loadView.send() | |
//: [Next](@next) |
This file contains hidden or 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
struct Counter { | |
var count: Int = 0 | |
// actions as mutating funcs, cause mutation up the tree | |
mutating func increase() {count = count + 1 } | |
mutating func decrease() {count = count - 1 } | |
func viewModel() -> Int { | |
return count | |
} | |
} | |
struct CompositeCounter { | |
var counterA = Counter() | |
var counterB = Counter() | |
func viewModel() -> (Int, Int) { | |
return (counterA.viewModel(), counterB.viewModel()) | |
} | |
} | |
var v = CompositeCounter() { | |
didSet { | |
print(v.viewModel()) | |
} | |
} | |
v.counterA.increase() | |
v.counterB.decrease() |
This file contains hidden or 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
//: [Previous](@previous) | |
import UIKit | |
import Behavior | |
import XCPlayground | |
//: ## Task: Create a small counter application | |
//: This version shows an approach that uses mutating functions | |
//: to trigger state tree changes | |
struct Counter { | |
var count: Int = 0 | |
// actions as mutating funcs, cause mutation up the tree | |
mutating func increment() {count = count + 1 } | |
mutating func decrement() {count = count - 1 } | |
} | |
struct CounterState { | |
let count: Int | |
let title: String | |
} | |
struct CounterViewState { | |
let countText: String | |
let titleText: String | |
init(state: CounterState) { | |
countText = String(format: " %02d", arguments: [state.count]) | |
titleText = state.title | |
} | |
} | |
class CounterViewController: UIViewController { | |
var state : CounterViewState! { | |
didSet { | |
self.render(state) | |
} | |
} | |
func render(state: CounterViewState) { | |
titleLabel.text = state.titleText | |
countLabel.text = state.countText | |
} | |
// Events | |
var increment : () -> () = { } | |
var decrement: () -> () = { } | |
let titleLabel = UILabel() | |
let countLabel = UILabel() | |
let incrementButton = UIButton(type: UIButtonType.RoundedRect) | |
let decrementButton = UIButton(type: UIButtonType.RoundedRect) | |
func decrementButtonTapped() { decrement() } | |
func incrementButtonTapped() { increment() } | |
override func viewDidLoad() { | |
view.backgroundColor = .whiteColor() | |
titleLabel.frame = CGRect(x: 10, y: 30, width: 300, height: 40) | |
countLabel.text = "00" | |
countLabel.frame = CGRect(x: 10, y: 30 + 50, width: 300, height: 40) | |
view.addSubview(countLabel) | |
view.addSubview(titleLabel) | |
// buttons | |
incrementButton.setTitle("Increment", forState: UIControlState.Normal) | |
decrementButton.setTitle("Decrement", forState: UIControlState.Normal) | |
incrementButton.frame = CGRect(x: 10, y: 175, width: 300, height: 40) | |
decrementButton.frame = CGRect(x: 10, y: 250, width: 300, height: 40) | |
view.addSubview(incrementButton) | |
view.addSubview(decrementButton) | |
self.decrementButton.addTarget(self, action: "decrementButtonTapped", forControlEvents: .TouchUpInside) | |
self.incrementButton.addTarget(self, action: "incrementButtonTapped", forControlEvents: .TouchUpInside) | |
} | |
} | |
// the counter assembly is a component that holds the counter, and the counterview together | |
struct CounterComponent { | |
let title: String | |
var counter = Counter() { | |
didSet { | |
updateViewWithState(CounterState(count: counter.count, title: title)) | |
} | |
} | |
func updateViewWithState(state: CounterState) { | |
self.viewController?.state = CounterViewState(state: state) | |
} | |
mutating func loadView() -> UIViewController? { | |
viewController = CounterViewController() | |
viewController?.state = CounterViewState(state: CounterState(count: 0, title: title)) | |
// connect the viewController outputs to the counter | |
self.viewController?.increment = { self.counter.increment() } | |
self.viewController?.decrement = { self.counter.decrement() } | |
return viewController | |
} | |
var viewController : CounterViewController? | |
init(title: String) { | |
self.title = title | |
} | |
} | |
// create an counter component | |
var counter = CounterComponent(title: "Counter Component") | |
let vc = counter.loadView() | |
vc?.view.frame.size = CGSize(width: 320, height: 300) | |
XCPlaygroundPage.currentPage.liveView = vc?.view | |
//: [Next](@next) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment