Skip to content

Instantly share code, notes, and snippets.

@chunkyguy
Created December 27, 2022 20:40
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chunkyguy/c91593df9919d47e856944aa98541287 to your computer and use it in GitHub Desktop.
Save chunkyguy/c91593df9919d47e856944aa98541287 to your computer and use it in GitHub Desktop.
Poor man's State and Binding without SwiftUI
@dynamicMemberLookup
class Variable<T> {
var value: T {
get { sub.value }
set { sub.value = newValue }
}
var stream: AnyPublisher<T, Never> {
return sub.eraseToAnyPublisher()
}
subscript<P>(dynamicMember keyPath: WritableKeyPath<T, P>) -> P {
get { sub.value[keyPath: keyPath] }
set { sub.value[keyPath: keyPath] = newValue }
}
fileprivate let sub: CurrentValueSubject<T, Never>
init(_ sub: CurrentValueSubject<T, Never>) {
self.sub = sub
}
}
class State<T>: Variable<T> {
var binding: Binding<T> {
return Binding(self)
}
init(_ value: T) {
super.init(CurrentValueSubject(value))
}
}
class Binding<T>: Variable<T> {
init(_ state: State<T>) {
super.init(state.sub)
}
}
import UIKit
struct Story {
var text: String
}
class TextEditViewController: UIViewController, UITextViewDelegate {
private let story: Binding<Story>
init(story: Binding<Story>) {
self.story = story
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
let textVw = UITextView(frame: .zero)
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(textVw)
textVw.text = story.text
textVw.delegate = self
title = "Story Editor"
view.backgroundColor = .white
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
textVw.frame = view.bounds
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
textVw.becomeFirstResponder()
}
func textViewDidChange(_ textView: UITextView) {
story.text = textView.text
}
}
class RootViewController: UIViewController {
let label = UILabel(frame: .zero)
let story = State(Story(text: ""))
var cancellables = Set<AnyCancellable>()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(label)
label.backgroundColor = .white
label.textAlignment = .center
view.backgroundColor = .gray
title = "Story Viewer"
story.stream.sink { [weak self] nextStory in
print("recv: \(nextStory.text)")
assert(Thread.isMainThread)
self?.label.text = nextStory.text
}.store(in: &cancellables)
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .edit, target: self, action: #selector(onEdit))
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
label.frame = CGRect(origin: .zero, size: CGSize(width: view.bounds.width - 40, height: 100))
label.center = view.center
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
label.text = story.value.text
}
@objc func onEdit() {
let textEditVwCtrl = TextEditViewController(story: story.binding)
navigationController?.pushViewController(textEditVwCtrl, animated: true)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment