Originally created by @elprl on GitHub (May 3, 2023).
I'm a little confused as to how to use this lib in a ViewModel SwiftUI context. I'd rather not have the View have a dependency on the CoreStore lib.
importSwiftUIimportCoreDataimportCombinestructContentView:View{@StateObjectvarviewModel=ViewModel()varbody:someView{List{ForEach(viewModel.items){iteminText("Item at \(item.timestamp!)")}}.task{viewModel.setupItemsListener()}}}importCoreStoreclassViewModel:ObservableObject{privateletdataStack=DataStack(xcodeModelName:"CoreStoreTestHarness")privatevarcancellables:Set<AnyCancellable>=[]@Publishedvaritems:[Item]=[]funcsetupItemsListener(){letlistPublisher=dataStack.publishList(From<Item>().orderBy(.ascending(\.timestamp)))listPublisher.reactive.snapshot(emitInitialValue:true).receive(on:RunLoop.main).sink(receiveCompletion:{completioninswitchcompletion{case.finished:print("getItems receiveCompletion finished successfully")case.failure(leterror):print("getItems caught error \(error)")}},receiveValue:{[weakself]itemsSnapshotinprint("getItems receiveValue")// self?.items = itemsSnapshot // what do I do here?}).store(in:&cancellables)}}
I'm not sure what to do with the snapshot and how to convert it to the @Published array. Will this array then be diffable?
I'm more likely to map the objects to a detailedViewModel object. Something like:
listPublisher.reactive.snapshot(emitInitialValue:true).compactMap{DetailedViewModel(item:$0.value)}// what do I do here?
I'm fundamentally not understanding how to get access to the Core Data objects from the listSnapshot.
Originally created by @elprl on GitHub (May 3, 2023).
I'm a little confused as to how to use this lib in a ViewModel SwiftUI context. I'd rather not have the View have a dependency on the CoreStore lib.
```swift
import SwiftUI
import CoreData
import Combine
struct ContentView: View {
@StateObject var viewModel = ViewModel()
var body: some View {
List {
ForEach(viewModel.items) { item in
Text("Item at \(item.timestamp!)")
}
}
.task {
viewModel.setupItemsListener()
}
}
}
import CoreStore
class ViewModel: ObservableObject {
private let dataStack = DataStack(xcodeModelName: "CoreStoreTestHarness")
private var cancellables: Set<AnyCancellable> = []
@Published var items: [Item] = []
func setupItemsListener() {
let listPublisher = dataStack.publishList(
From<Item>()
.orderBy(.ascending(\.timestamp))
)
listPublisher.reactive
.snapshot(emitInitialValue: true)
.receive(on: RunLoop.main)
.sink(receiveCompletion: { completion in
switch completion {
case .finished:
print("getItems receiveCompletion finished successfully")
case .failure(let error):
print("getItems caught error \(error)")
}
}, receiveValue: { [weak self] itemsSnapshot in
print("getItems receiveValue")
// self?.items = itemsSnapshot // what do I do here?
})
.store(in: &cancellables)
}
}
```
I'm not sure what to do with the snapshot and how to convert it to the @Published array. Will this array then be diffable?
I'm more likely to map the objects to a detailedViewModel object. Something like:
```swift
listPublisher.reactive
.snapshot(emitInitialValue: true)
.compactMap { DetailedViewModel(item: $0.value ) } // what do I do here?
```
I'm fundamentally not understanding how to get access to the Core Data objects from the listSnapshot.
Update:
I managed to get access to the Core Data objects in the snapshot via:
itemsSnapshot.compactMap{$0.object}
Not mentioned in the docs, so not sure if this is a good tactic.
@elprl commented on GitHub (May 4, 2023):
Update:
I managed to get access to the Core Data objects in the snapshot via:
```swift
itemsSnapshot.compactMap { $0.object }
```
Not mentioned in the docs, so not sure if this is a good tactic.
Hi, have you checked the CoreStoreDemo project? There are examples on how to use ListPublishers in tandem with ListReaders and ObjectReaders depending on whether you need ObjectPublishers or ObjectSnapshots in your SwiftUI Views
@JohnEstropia commented on GitHub (May 9, 2023):
Hi, have you checked the CoreStoreDemo project? There are examples on how to use `ListPublisher`s in tandem with `ListReader`s and `ObjectReader`s depending on whether you need `ObjectPublisher`s or `ObjectSnapshot`s in your SwiftUI `View`s
Yes, I did thank you. I also looked at the unit tests. Like I said in my intro, I'd rather not have the View have a dependency on the CoreStore library, but the view model is fine.
In my experience, in more complex enterprise use cases, one rarely goes from a Core Data object straight to View. A ViewModel or Interactor grooms, processes, filters, combines, splices the core data before forming a viewModel object.
Additionally, the .object variable isn't mentioned in the readme. I think your Readme section on Combine needs a better example that accounts for this grooming & processing.
listPublisher.reactive.snapshot(emitInitialValue:true).flatMap{// or compactMap { // or map { ...// more grooming.sink...
I went straight to this section and thus was confused as to what the datasource object was. It doesn't help situations that don't need to be coupled with the View.
@elprl commented on GitHub (May 9, 2023):
Yes, I did thank you. I also looked at the unit tests. Like I said in my intro, I'd rather not have the View have a dependency on the CoreStore library, but the view model is fine.
In my experience, in more complex enterprise use cases, one rarely goes from a Core Data object straight to View. A ViewModel or Interactor grooms, processes, filters, combines, splices the core data before forming a viewModel object.
Additionally, the `.object` variable isn't mentioned in the readme. I think your Readme section on Combine needs a better example that accounts for this grooming & processing.
```swift
listPublisher.reactive
.snapshot(emitInitialValue: true)
.flatMap { // or compactMap { // or map {
... // more grooming
.sink...
```
I went straight to this section and thus was confused as to what the `datasource` object was. It doesn't help situations that don't need to be coupled with the View.
The API provides the necessary endpoints for your app. If you prefer not to depend on CoreStore, then you would have to write that layer yourself.
.object is not documented because it's not the recommended way to access values from an ObjectPublisher, especially in SwiftUI where the View works with value types. The framework ensures your Views properly receives notifications because .object instance on its own will not tell your View to refresh if that object gets updated. I'm not sure how you'd even use .object without depending on CoreStore, because that object is of CoreStoreObject type anyway, in which case it is still better to use either ObjectPublisher directly, or ObjectSnapshot which is a value type.
@JohnEstropia commented on GitHub (May 10, 2023):
The API provides the necessary endpoints for your app. If you prefer not to depend on CoreStore, then you would have to write that layer yourself.
`.object` is not documented because it's not the recommended way to access values from an `ObjectPublisher`, especially in SwiftUI where the `View` works with value types. The framework ensures your `View`s properly receives notifications because `.object` instance on its own will not tell your `View` to refresh if that object gets updated. I'm not sure how you'd even use `.object` without depending on CoreStore, because that object is of `CoreStoreObject` type anyway, in which case it is still better to use either `ObjectPublisher` directly, or `ObjectSnapshot` which is a value type.
I'm not sure how you'd even use .object without depending on CoreStore, because that object is of CoreStoreObject type anyway
Ah, I guess you are using NSManagedObjects directly instead of CoreStoreObjects. Nevertheless, you will still be better off using the right wrappers (ObjectPublisher or ObjectSnapshot) to properly sync your views.
@JohnEstropia commented on GitHub (May 10, 2023):
> I'm not sure how you'd even use `.object` without depending on CoreStore, because that object is of `CoreStoreObject` type anyway
Ah, I guess you are using `NSManagedObject`s directly instead of `CoreStoreObject`s. Nevertheless, you will still be better off using the right wrappers (`ObjectPublisher` or `ObjectSnapshot`) to properly sync your views.
Appreciate the comments John, I do like what CoreStore has achieved. I think the library is not addressing a common architectural pattern, where a ViewModel class is doing the syncing as you mentioned. If I wasn't going to use CoreStore, I would be doing something like this: https://www.donnywals.com/observing-changes-to-managed-objects-across-contexts-with-combine/
To give a crude example, let's say you have a Whatsapp style app. Core Data would store the encrypted Message, the ViewModel class would create a listener for new & updated Message objects, and when they arrive would process each Message, decrypt them, convert dates into strings, add colours, etc, and finally create a MessageViewModel object for the View to consume. In this scenario, the View is decoupled from both Core Data and Core Store. We all know mocking Core Data for SwiftUI previews and unit tests is overly complex and hard work.
So how does one achieve this? Something like:
classViewModel:ObservableObject{@PublishedvarmessageViewModels:[MessageViewModel]=[]init(){letlistPublisher=dataStack.publishList(From<Message>().orderBy(.ascending(\.timestamp)))listPublisher.reactive.snapshot(emitInitialValue:true).receive(on:RunLoop.main).flatMap{snapshotin// convert snapshot into MessageViewModel array}.sink(receiveCompletion:{completionin...},receiveValue:{[weakself]messageViewModelsinself?.messageViewModels=messageViewModels}).store(in:&cancellables)}}
@elprl commented on GitHub (May 10, 2023):
Appreciate the comments John, I do like what CoreStore has achieved. I think the library is not addressing a common architectural pattern, where a `ViewModel` class is doing the syncing as you mentioned. If I wasn't going to use CoreStore, I would be doing something like this:
https://www.donnywals.com/observing-changes-to-managed-objects-across-contexts-with-combine/
To give a crude example, let's say you have a Whatsapp style app. Core Data would store the encrypted `Message`, the `ViewModel` class would create a listener for new & updated `Message` objects, and when they arrive would process each `Message`, decrypt them, convert dates into strings, add colours, etc, and finally create a `MessageViewModel` object for the `View` to consume. In this scenario, the `View` is decoupled from both Core Data and Core Store. We all know mocking Core Data for SwiftUI previews and unit tests is overly complex and hard work.
So how does one achieve this? Something like:
```swift
class ViewModel: ObservableObject {
@Published var messageViewModels: [MessageViewModel] = []
init() {
let listPublisher = dataStack.publishList(
From<Message>()
.orderBy(.ascending(\.timestamp))
)
listPublisher.reactive
.snapshot(emitInitialValue: true)
.receive(on: RunLoop.main)
.flatMap { snapshot in
// convert snapshot into MessageViewModel array
}
.sink(receiveCompletion: { completion in
...
}, receiveValue: { [weak self] messageViewModels in
self?.messageViewModels = messageViewModels
})
.store(in: &cancellables)
}
}
```
I understand that there are architectures where you have to provide the per-object ViewModels directly, and in fact we do use similar cases in our projects. The problem is that SwiftUI kind of forces us to wrap our ViewModels in some sort of @State or @ObservableObject or something similar for Views to get updated properly. A large part of the observation logic similar to the one in Donny Wal's article you have linked is already provided in CoreStore's @ListState and @ObjectState and are ready to be used in SwiftUI projects.
(see Modern.ColorsDemo.SwiftUI.ListView.swift and Modern.ColorsDemo.SwiftUI.DetailView.swift in the Demo app)
Now of course you are free to limit the dependency to CoreStore and implement this yourself. In that case, I would still recommend you check how @ListState and @ObjectState does this internally and base your implementation on that. (Or alternatively, ListReader and ObjectReader depending on your requirements)
@JohnEstropia commented on GitHub (May 11, 2023):
I understand that there are architectures where you have to provide the per-object ViewModels directly, and in fact we do use similar cases in our projects. The problem is that SwiftUI kind of forces us to wrap our ViewModels in some sort of `@State` or `@ObservableObject` or something similar for `View`s to get updated properly. A large part of the [observation logic similar to the one in Donny Wal's article you have linked](https://www.donnywals.com/observing-changes-to-managed-objects-across-contexts-with-combine/) is already provided in CoreStore's `@ListState` and `@ObjectState` and are ready to be used in SwiftUI projects.
(see [Modern.ColorsDemo.SwiftUI.ListView.swift](https://github.com/JohnEstropia/CoreStore/blob/25593754913d228777f14634bc6f493dbeb4e9fb/Demo/Sources/Demos/Modern/ColorsDemo/Modern.ColorsDemo.SwiftUI.ListView.swift#L19) and [Modern.ColorsDemo.SwiftUI.DetailView.swift](https://github.com/JohnEstropia/CoreStore/blob/25593754913d228777f14634bc6f493dbeb4e9fb/Demo/Sources/Demos/Modern/ColorsDemo/Modern.ColorsDemo.SwiftUI.DetailView.swift#L20) in the Demo app)
Now of course you are free to limit the dependency to CoreStore and implement this yourself. In that case, I would still recommend you check how [@ListState](https://github.com/JohnEstropia/CoreStore/blob/25593754913d228777f14634bc6f493dbeb4e9fb/Sources/ListState.swift#L38) and [@ObjectState](https://github.com/JohnEstropia/CoreStore/blob/25593754913d228777f14634bc6f493dbeb4e9fb/Sources/ObjectState.swift#L38) does this internally and base your implementation on that. (Or alternatively, [ListReader](https://github.com/JohnEstropia/CoreStore/blob/25593754913d228777f14634bc6f493dbeb4e9fb/Sources/ListReader.swift) and [ObjectReader](https://github.com/JohnEstropia/CoreStore/blob/25593754913d228777f14634bc6f493dbeb4e9fb/Sources/ObjectReader.swift) depending on your requirements)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Originally created by @elprl on GitHub (May 3, 2023).
I'm a little confused as to how to use this lib in a ViewModel SwiftUI context. I'd rather not have the View have a dependency on the CoreStore lib.
I'm not sure what to do with the snapshot and how to convert it to the @Published array. Will this array then be diffable?
I'm more likely to map the objects to a detailedViewModel object. Something like:
I'm fundamentally not understanding how to get access to the Core Data objects from the listSnapshot.
@elprl commented on GitHub (May 4, 2023):
Update:
I managed to get access to the Core Data objects in the snapshot via:
Not mentioned in the docs, so not sure if this is a good tactic.
@JohnEstropia commented on GitHub (May 9, 2023):
Hi, have you checked the CoreStoreDemo project? There are examples on how to use
ListPublishers in tandem withListReaders andObjectReaders depending on whether you needObjectPublishers orObjectSnapshots in your SwiftUIViews@elprl commented on GitHub (May 9, 2023):
Yes, I did thank you. I also looked at the unit tests. Like I said in my intro, I'd rather not have the View have a dependency on the CoreStore library, but the view model is fine.
In my experience, in more complex enterprise use cases, one rarely goes from a Core Data object straight to View. A ViewModel or Interactor grooms, processes, filters, combines, splices the core data before forming a viewModel object.
Additionally, the
.objectvariable isn't mentioned in the readme. I think your Readme section on Combine needs a better example that accounts for this grooming & processing.I went straight to this section and thus was confused as to what the
datasourceobject was. It doesn't help situations that don't need to be coupled with the View.@JohnEstropia commented on GitHub (May 10, 2023):
The API provides the necessary endpoints for your app. If you prefer not to depend on CoreStore, then you would have to write that layer yourself.
.objectis not documented because it's not the recommended way to access values from anObjectPublisher, especially in SwiftUI where theViewworks with value types. The framework ensures yourViews properly receives notifications because.objectinstance on its own will not tell yourViewto refresh if that object gets updated. I'm not sure how you'd even use.objectwithout depending on CoreStore, because that object is ofCoreStoreObjecttype anyway, in which case it is still better to use eitherObjectPublisherdirectly, orObjectSnapshotwhich is a value type.@JohnEstropia commented on GitHub (May 10, 2023):
Ah, I guess you are using
NSManagedObjects directly instead ofCoreStoreObjects. Nevertheless, you will still be better off using the right wrappers (ObjectPublisherorObjectSnapshot) to properly sync your views.@elprl commented on GitHub (May 10, 2023):
Appreciate the comments John, I do like what CoreStore has achieved. I think the library is not addressing a common architectural pattern, where a
ViewModelclass is doing the syncing as you mentioned. If I wasn't going to use CoreStore, I would be doing something like this:https://www.donnywals.com/observing-changes-to-managed-objects-across-contexts-with-combine/
To give a crude example, let's say you have a Whatsapp style app. Core Data would store the encrypted
Message, theViewModelclass would create a listener for new & updatedMessageobjects, and when they arrive would process eachMessage, decrypt them, convert dates into strings, add colours, etc, and finally create aMessageViewModelobject for theViewto consume. In this scenario, theViewis decoupled from both Core Data and Core Store. We all know mocking Core Data for SwiftUI previews and unit tests is overly complex and hard work.So how does one achieve this? Something like:
@JohnEstropia commented on GitHub (May 11, 2023):
I understand that there are architectures where you have to provide the per-object ViewModels directly, and in fact we do use similar cases in our projects. The problem is that SwiftUI kind of forces us to wrap our ViewModels in some sort of
@Stateor@ObservableObjector something similar forViews to get updated properly. A large part of the observation logic similar to the one in Donny Wal's article you have linked is already provided in CoreStore's@ListStateand@ObjectStateand are ready to be used in SwiftUI projects.(see Modern.ColorsDemo.SwiftUI.ListView.swift and Modern.ColorsDemo.SwiftUI.DetailView.swift in the Demo app)
Now of course you are free to limit the dependency to CoreStore and implement this yourself. In that case, I would still recommend you check how @ListState and @ObjectState does this internally and base your implementation on that. (Or alternatively, ListReader and ObjectReader depending on your requirements)