Skip to content

Instantly share code, notes, and snippets.

@ohitsdaniel
Last active May 20, 2021 11:44
Show Gist options
  • Save ohitsdaniel/bf6675f73bcff7a1de5e5c0e23fe40f3 to your computer and use it in GitHub Desktop.
Save ohitsdaniel/bf6675f73bcff7a1de5e5c0e23fe40f3 to your computer and use it in GitHub Desktop.
Child stores
import SwiftUI
import Combine
import PlaygroundSupport
// MARK: Data Model
struct Book: Identifiable, Equatable {
let id: Int
}
// Direct changes to DataModel should propagate
// to the view via "Books" ObservableObject
// but only for books change, not for all change
class DataModel: ObservableObject {
@Published var audiobooks: [Book] = []
@Published var books: [Book] = [] {
didSet {
print("data model did update: \(books)")
}
}
}
extension DataModel {
var booksStore: Books {
Books(self)
}
}
// MARK: Books data projection for the view
// Books data projection of DataModel
class Books: ObservableObject {
@Published var list: [Book]
private var bag = Set<AnyCancellable>()
init(_ dataModel: DataModel) {
self.list = dataModel.books
dataModel.$books
.removeDuplicates()
// The assign operator captures self in this case.
// Consider using sink with a [weak self] capture list,
// or the assign to operator with weak ownership from CombineExt.
.assign(to: \.list, on: self)
.store(in: &bag)
$list
.removeDuplicates()
.assign(to: \.books, on: dataModel)
.store(in: &bag)
dataModel.objectWillChange
.sink { _ in
print("books store did update: \(self.list)")
}
.store(in: &bag)
}
}
// MARK: View
struct ContentView: View {
@EnvironmentObject var books: Books
@ViewBuilder
var body: some View {
VStack {
Button("add to observed object") {
books.list.append(Book(id: books.list.count + 1))
}
Button("delete directly via model") {
books.list = books.list.dropLast()
}
Divider()
ForEach(books.list) { book in
Text("\(book.id)")
}
Spacer()
}
}
}
let dataModel = DataModel()
let contentView = ContentView()
.environmentObject(dataModel.booksStore)
PlaygroundPage.current.setLiveView(
contentView
.frame(width: 300, height: 300, alignment: .leading)
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment