Skip to content

Instantly share code, notes, and snippets.

@thelowlypeon
Created September 26, 2018 18:52
Show Gist options
  • Save thelowlypeon/60be29c0ad59e40a0c371b9e69e7a9a6 to your computer and use it in GitHub Desktop.
Save thelowlypeon/60be29c0ad59e40a0c371b9e69e7a9a6 to your computer and use it in GitHub Desktop.
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