Skip to content

Instantly share code, notes, and snippets.

@amberstar
Last active February 7, 2016 20:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save amberstar/9ee541467e97132c3814 to your computer and use it in GitHub Desktop.
Save amberstar/9ee541467e97132c3814 to your computer and use it in GitHub Desktop.
Counter Ref
//: [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)
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()
//: [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