Created
September 26, 2018 18:52
-
-
Save thelowlypeon/60be29c0ad59e40a0c371b9e69e7a9a6 to your computer and use it in GitHub Desktop.
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
import Foundation | |
import UIKit | |
public class ObservableProperty<T> { | |
public var value: T { | |
didSet { notifyObservers() } | |
} | |
private var observers = [(T) -> Void]() | |
public init(_ initialValue: T) { | |
self.value = initialValue | |
} | |
public func set(_ newValue: T) { | |
self.value = newValue | |
} | |
//note: in reality, this would likely be a #selector, not callback, so we can un-observe | |
public func onNext(_ callback: @escaping (T) -> Void) { | |
observers.append(callback) | |
callback(value) //send initial value | |
} | |
private func notifyObservers() { | |
for observer in observers { | |
observer(value) | |
} | |
} | |
} | |
extension ObservableProperty: CustomStringConvertible { | |
public var description: String { | |
if let printable = value as? CustomStringConvertible { | |
return printable.description | |
} else { | |
return "\(value)" | |
} | |
} | |
} | |
public class Person { | |
public var firstName: ObservableProperty<String> | |
public var middleInitial: ObservableProperty<String?> | |
public var lastName: ObservableProperty<String> | |
public init(firstName: String, middleInitial: String?, lastName: String) { | |
self.firstName = ObservableProperty<String>(firstName) | |
self.middleInitial = ObservableProperty<String?>(middleInitial) | |
self.lastName = ObservableProperty<String>(lastName) | |
} | |
public func abbreviateName() { | |
firstName.value.removeLast() | |
} | |
} | |
public class PersonViewModel { | |
// observable properties | |
public var personNameText = ObservableProperty<String?>(nil) | |
public var abbreviateNameButtonEnabled = ObservableProperty<Bool>(true) | |
public init(person: Person?) { | |
self.person = ObservableProperty<Person?>(person) | |
bindPerson() | |
} | |
@objc public func didPressAbbreviateName() { | |
person.value?.abbreviateName() | |
} | |
// MARK: observe changes to data models | |
public var person: ObservableProperty<Person?> | |
private func bindPerson() { | |
self.person.onNext {[unowned self] (person) in | |
guard let person = person else { | |
self.abbreviateNameButtonEnabled.set(false) | |
return | |
} | |
self.abbreviateNameButtonEnabled.set(true) | |
person.firstName.onNext {[unowned self] (_) in | |
self.updatePersonName() | |
} | |
person.middleInitial.onNext {[unowned self] (_) in | |
self.updatePersonName() | |
} | |
person.lastName.onNext {[unowned self] (_) in | |
self.updatePersonName() | |
} | |
} | |
} | |
private func updatePersonName() { | |
if let person = person.value { | |
self.personNameText.value = "\(person.firstName.value) \(person.lastName.value)" | |
} else { | |
self.personNameText.value = "no person" | |
} | |
} | |
} | |
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) | |
bindViewModel() | |
} | |
func changePerson(person: Person?) { | |
viewModel.person.set(person) | |
} | |
private var observers = [NSKeyValueObservation]() | |
func bindViewModel() { | |
abbreviateNameButton.addTarget(viewModel, action: #selector(PersonViewModel.didPressAbbreviateName), for: .touchUpInside) | |
viewModel.abbreviateNameButtonEnabled.onNext {(isEnabled) in | |
self.abbreviateNameButton.isEnabled = isEnabled | |
} | |
viewModel.personNameText.onNext {(text) in | |
self.personNameLabel.text = text | |
} | |
} | |
} | |
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