Last active
May 20, 2021 11:44
-
-
Save ohitsdaniel/bf6675f73bcff7a1de5e5c0e23fe40f3 to your computer and use it in GitHub Desktop.
Child stores
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 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