Created
September 26, 2018 19:07
-
-
Save thelowlypeon/94d2fd7a61148e857902eee2adc805a3 to your computer and use it in GitHub Desktop.
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
import Foundation | |
import UIKit | |
public class Event<T> { | |
public typealias EventHandler = (T) -> Void | |
private var eventHandlers = [EventHandler]() | |
public func addHandler(handler: @escaping EventHandler, withInitialEvent initialEvent: T? = nil) { | |
eventHandlers.append(handler) | |
if let event = initialEvent { | |
handler(event) | |
} | |
} | |
public func raise(_ data: T) { | |
for handler in eventHandlers { | |
handler(data) | |
} | |
} | |
} | |
public enum PersonProperty { | |
case initial, firstName, middleInitial, lastName | |
} | |
public class Person { | |
public let propertyChanged = Event<PersonProperty>() | |
public var firstName: String { didSet { propertyChanged.raise(.firstName) } } | |
public var middleInitial: String? { didSet { propertyChanged.raise(.middleInitial) } } | |
public var lastName: String { didSet { propertyChanged.raise(.lastName) } } | |
public init(firstName: String, middleInitial: String?, lastName: String) { | |
self.firstName = firstName | |
self.middleInitial = middleInitial | |
self.lastName = lastName | |
} | |
public func abbreviateName() { | |
firstName.removeLast() | |
} | |
} | |
let me = Person(firstName: "Peter", middleInitial: nil, lastName: "Compernolle") | |
let daisy = Person(firstName: "Daisy", middleInitial: nil, lastName: "Jiang") | |
public enum PersonViewModelProperty { | |
case initial, person, personNameText, abbreviateNameButtonEnabled | |
} | |
public class PersonViewModel { | |
// observable properties | |
public let propertyChanged = Event<PersonViewModelProperty>() | |
public var personNameText: String? | |
public var abbreviateNameButtonEnabled: Bool { return person != nil } | |
public init(person: Person?) { | |
self.person = person | |
bindPerson() | |
} | |
// MARK: observe changes to data models | |
public var person: Person? { didSet { propertyChanged.raise(.person) } } | |
private func bindPerson() { | |
self.propertyChanged.addHandler(handler: {[unowned self](property) in | |
switch property { | |
case .initial, .person: | |
self.person?.propertyChanged.addHandler(handler: {[unowned self](property) in | |
self.propertyChanged.raise(.abbreviateNameButtonEnabled) | |
guard let person = self.person else { return } | |
self.personNameText = "\(person.firstName) \(person.lastName)" | |
self.propertyChanged.raise(.personNameText) | |
}, withInitialEvent: .initial) | |
default: return | |
} | |
}, withInitialEvent: .initial) | |
} | |
@objc public func didPressAbbreviateName() { | |
self.person?.abbreviateName() | |
} | |
} | |
class PersonView { | |
var personNameLabel: UILabel | |
var abbreviateNameButton: UIButton | |
var backButton: UIButton | |
private var viewModel: PersonViewModel | |
init(person: Person?) { | |
viewModel = PersonViewModel(person: person) | |
personNameLabel = UILabel(frame: CGRect.zero) | |
abbreviateNameButton = UIButton(frame: CGRect.zero) | |
backButton = UIButton(frame: CGRect.zero) | |
backButton.addTarget(self, action: #selector(PersonView.viewWillDisappear), for: .touchUpInside) | |
viewWillAppear(animated: true) | |
} | |
func changePerson(person: Person?) { | |
viewModel.person = person | |
} | |
// mock | |
func viewWillAppear(animated: Bool) { | |
bindViewModel() | |
} | |
// mock | |
@objc func viewWillDisappear() { | |
unbindViewModel() | |
} | |
func bindViewModel() { | |
abbreviateNameButton.addTarget(viewModel, action: #selector(PersonViewModel.didPressAbbreviateName), for: .touchUpInside) | |
viewModel.propertyChanged.addHandler(handler: {(property) in | |
switch property { | |
case .personNameText, .initial: | |
self.personNameLabel.text = self.viewModel.personNameText | |
case .abbreviateNameButtonEnabled: | |
self.abbreviateNameButton.isEnabled = self.viewModel.abbreviateNameButtonEnabled | |
default: break | |
} | |
}, withInitialEvent: .initial) | |
} | |
func unbindViewModel() { | |
abbreviateNameButton.removeTarget(viewModel, action: #selector(PersonViewModel.didPressAbbreviateName), for: .touchUpInside) | |
} | |
} | |
func main() { | |
let me = Person(firstName: "Peter", middleInitial: nil, lastName: "C") | |
let daisy = Person(firstName: "Daisy", middleInitial: nil, lastName: "J") | |
let view = PersonView(person: nil) | |
print("button enabled? \(view.abbreviateNameButton.isEnabled)") | |
print(view.personNameLabel.text ?? "nil") | |
view.changePerson(person: me) | |
print("button enabled? \(view.abbreviateNameButton.isEnabled)") | |
print(view.personNameLabel.text ?? "nil") | |
view.abbreviateNameButton.sendActions(for: .touchUpInside) | |
print(view.personNameLabel.text ?? "nil") | |
view.changePerson(person: daisy) | |
print(view.personNameLabel.text ?? "nil") | |
} | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment