Last active
June 22, 2021 15:43
-
-
Save garsdle/07413847dbe8c5f2c0b29a705c62aae1 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 Combine | |
import SwiftUI | |
class ScopedGetObservable<T>: ObservableObject { | |
var cancellable: AnyCancellable? | |
let getter: () -> T | |
init<P>(getter: @autoclosure @escaping () -> T, publisher: P) where P: Publisher, P.Output == T, P.Failure == Never { | |
self.getter = getter | |
cancellable = publisher.sink { [weak self] _ in | |
self?.objectWillChange.send() | |
} | |
} | |
} | |
@propertyWrapper | |
struct ScopedGet<T>: DynamicProperty { | |
var wrappedValue: T { | |
scopedObservable.getter() | |
} | |
@ObservedObject private var scopedObservable: ScopedGetObservable<T> | |
init<P>(getter: @autoclosure @escaping () -> T, publisher: P) where P: Publisher, P.Output == T, P.Failure == Never { | |
self.scopedObservable = .init(getter: getter(), publisher: publisher) | |
} | |
} | |
// MARK: - MODELS | |
class AppViewModel: ObservableObject { | |
@Published var count = 0 | |
@Published var favorites: Set<Int> = [] | |
func remove(number: Int) { | |
favorites.remove(number) | |
} | |
func insert(number: Int) { | |
favorites.insert(number) | |
} | |
func incrementCount() { | |
count += 1 | |
} | |
func decrementCount() { | |
count -= 1 | |
} | |
} | |
var viewModel = AppViewModel() | |
// MARK: - VIEWS | |
struct VanillaContentView: View { | |
@ScopedGet var count: Int | |
@ScopedGet var favoriteCount: Int | |
var body: some View { | |
TabView { | |
VanillaCounterView(count: ScopedGet(getter: viewModel.count, | |
publisher: viewModel.$count), | |
favorites: ScopedGet(getter: viewModel.favorites, | |
publisher: viewModel.$favorites), | |
onRemove: viewModel.remove(number:), | |
onInsert: viewModel.insert(number:), | |
onIncrement: viewModel.incrementCount, | |
onDecrement: viewModel.decrementCount) | |
.tabItem { Text("Counter \(count)") } | |
VanillaProfileView(favorites: ScopedGet(getter: viewModel.favorites, | |
publisher: viewModel.$favorites), | |
onRemove: viewModel.remove(number:)) | |
.tabItem { Text("Profile \(favoriteCount)") } | |
} | |
} | |
} | |
struct VanillaCounterView: View { | |
@ScopedGet var count: Int | |
@ScopedGet var favorites: Set<Int> | |
let onRemove: (Int) -> Void | |
let onInsert: (Int) -> Void | |
let onIncrement: () -> Void | |
let onDecrement: () -> Void | |
var body: some View { | |
VStack { | |
HStack { | |
Button("-", action: onDecrement) | |
Text("\(count)") | |
Button("+", action: onIncrement) | |
} | |
if favorites.contains(count) { | |
Button("Remove") { | |
onRemove(count) | |
} | |
} else { | |
Button("Save") { | |
onInsert(count) | |
} | |
} | |
} | |
} | |
} | |
struct VanillaProfileView: View { | |
@ScopedGet var favorites: Set<Int> | |
let onRemove: (Int) -> Void | |
var body: some View { | |
List { | |
ForEach(favorites.sorted(), id: \.self) { number in | |
HStack { | |
Text("\(number)") | |
Spacer() | |
Button("Remove", action: { onRemove(number) }) | |
} | |
} | |
} | |
} | |
} | |
struct ContentView_Previews: PreviewProvider { | |
static var previews: some View { | |
VanillaContentView(count: .init(getter: viewModel.count, publisher: viewModel.$count), | |
favoriteCount: ScopedGet(getter: viewModel.favorites.count, publisher: viewModel.$favorites.map(\.count))) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment