Context
I am working on a feature, that allows users to add Components
to CoreData
. Those Components
are obviously NSManagedObjects
inserted and saved into a Context
.
In addition, I also want to give the user a variety of predefined Components
. However, I do not like the idea of populating those predefined ones into CoreData
at the first App Launch
, since this is really prone to bugs, especially when utilising CloudKit
. So my idea was to generate a List
of predefined NSManagedObjects
without inserting them into a Context
, which would make them temporarily, but they could be used in the same way as the real ones. However, as far as I understand, creating NSManagedObjects
without a Context
isn't really working.
Code
let predefinedComponents: [Component] {
var components: [Component] = []
for name in names {
let component = Component() // This was my idea of creating a temporary NSManagedObject without inserting it into a Context.
component.name = name
components.append(component)
}
return components
}
struct ComponentsView: View {
@FetchRequest(sortDescriptors: [SortDescriptor(\.name)]) private var components: FetchedResults<Component>
var body: some View {
ForEach(allComponents) { component in
ComponentRow(component: component)
}
}
private var allComponents: [Component] {
var allComponents: [Component] = predefinedComponents
for component in components {
allComponents.append(component)
}
return allComponents
}
}
struct ComponentRow: View {
@ObservedObject var component: Component
var body: some View {
Text(component.name)
}
}
Question
How can I achieve my goal described above while being able to work with predefined Components
without having to populate them into CoreData
at the first AppLaunch
?
CodePudding user response:
You can create "free floating" managed objects that don't belong to a context but you need to provide the entity description to do it-- so you would use Component(entity:insertInto:)
. The first argument is the NSEntityDescription
for Component
. The second one is a context, but it's an optional, so you can make it nil. If you wanted to add it to a context later, use NSManagedObjectContext.insert()
.
It might be better to use an in-memory persistent store instead of a SQLite store. Then you would have a context that only existed while the app was running but did not save to a file. You can set one of those up with NSPersistentContainer
if you change the persistent store description.
I can't tell from your question which of these would be better for you.
CodePudding user response:
The easiest and most elegant way is to create a "throwaway scratchpad" context just for the pre-defined Components.
This scratchpad context will be a child of the viewContext, or any background context depending on your use case.
This is how you create the scratchpad:
let scratchpadContext = NSManagedObjectContext(.mainQueue)
scratchpadContext.parent = dataProvider.container.viewContext
The example above creates a context for the main queue, which I assume is your case based on your question. But if you need to access it from a background thread, you initialise it with .privateQueue.
So, as long as you don't save the scratchpadContext, your temporary pre-defined Components will never be saved on your Persistent store. And when it's de-initialised, any NSManagedObject that you've created with it will be thrown away.