I'm trying to access my shared Core Data store from WidgetKit. I created an App Group and added both targets to it. In my PersistenceController, I started with just the default code that comes with a Core Data project, but updated it to use my App Group url as the storage path.
Here's my PersistenceController:
import CoreData
struct PersistenceController {
static let shared = PersistenceController()
let container: NSPersistentContainer
static var preview: PersistenceController = {
let result = PersistenceController(inMemory: true)
let viewContext = result.container.viewContext
let samplePokemon = Pokemon(context: viewContext)
samplePokemon.id = 0
samplePokemon.name = "bulbasaur"
samplePokemon.types = ["grass", "poison"]
samplePokemon.hp = 45
samplePokemon.attack = 49
samplePokemon.defense = 49
samplePokemon.specialAttack = 65
samplePokemon.specialDefense = 65
samplePokemon.speed = 45
samplePokemon.sprite = URL(string: "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/1.png")
samplePokemon.shiny = URL(string: "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/shiny/1.png")
samplePokemon.favorite = false
do {
try viewContext.save()
} catch {
let nsError = error as NSError
print(nsError)
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
return result
}()
init(inMemory: Bool = false) {
let sharedStoreURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.MyAppGroup")!.appending(path: "Poke.sqlite")
let description = NSPersistentStoreDescription(url: sharedStoreURL)
container = NSPersistentContainer(name: "Poke")
if inMemory {
container.persistentStoreDescriptions = [description]
}
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
container.viewContext.automaticallyMergesChangesFromParent = true
}
}
This works just fine running in my main app, and the data gets stored in the database just fine too, but the problem I run into is when I try to fetch data for my widget.
Here's the code that deals with Core Data in my widget Provider:
var randomPokemon: Pokemon {
let context = PersistenceController.shared.container.viewContext
let fetchRequest: NSFetchRequest<Pokemon> = Pokemon.fetchRequest()
var results: [Pokemon] = []
do {
results = try context.fetch(fetchRequest)
} catch {
print("Couldn't fetch: \(error)")
}
print(results)
if let randomPokemon = results.randomElement() {
return randomPokemon
}
return SamplePokemon.samplePokemon
}
It always returns samplePokemon, because the results are always empty.
Here's my console log:
2022-08-06 20:55:49.029944-0600 RandomFavoriteExtension[46125:627790] [error] warning: Multiple NSEntityDescriptions claim the NSManagedObject subclass 'Pokemon' so +entity is unable to disambiguate.
CoreData: warning: Multiple NSEntityDescriptions claim the NSManagedObject subclass 'Pokemon' so +entity is unable to disambiguate.
2022-08-06 20:55:49.030043-0600 RandomFavoriteExtension[46125:627790] [error] warning: 'Pokemon' (0x600002f280b0) from NSManagedObjectModel (0x600003b388c0) claims 'Pokemon'.
CoreData: warning: 'Pokemon' (0x600002f280b0) from NSManagedObjectModel (0x600003b388c0) claims 'Pokemon'.
2022-08-06 20:55:49.030092-0600 RandomFavoriteExtension[46125:627790] [error] warning: 'Pokemon' (0x600002f38160) from NSManagedObjectModel (0x600003b3ccd0) claims 'Pokemon'.
CoreData: warning: 'Pokemon' (0x600002f38160) from NSManagedObjectModel (0x600003b3ccd0) claims 'Pokemon'.
2022-08-06 20:55:49.030185-0600 RandomFavoriteExtension[46125:627790] [error] error: +[Pokemon entity] Failed to find a unique match for an NSEntityDescription to a managed object subclass
CoreData: error: +[Pokemon entity] Failed to find a unique match for an NSEntityDescription to a managed object subclass
Error Domain=NSCocoaErrorDomain Code=133021 "(null)" UserInfo={NSExceptionOmitCallstacks=true, conflictList=(
"NSConstraintConflict (0x600000d2f680) for constraint (\n id\n): database: 0x9147aa890f321eb4 <x-coredata://E5FB4658-9345-4245-B471-FAEE5B98F707/Pokemon/p1>, conflictedObjects: (\n \"0x9147aa890d921eb4 <x-coredata://E5FB4658-9345-4245-B471-FAEE5B98F707/Pokemon/p20>\"\n)"
)}
I have been working on this for weeks now. Any help would be very appreciated.
I figured this out, so I'll put my solution here so the question isn't just left hanging. The problem I was having was trying to use the shared app group while inMemory was true vs false.
My working PersistenceController code is here:
import CoreData
struct PersistenceController {
static let shared = PersistenceController()
let container: NSPersistentContainer
static var preview: PersistenceController = {
let result = PersistenceController(inMemory: true)
let viewContext = result.container.viewContext
let samplePokemon = Pokemon(context: viewContext)
samplePokemon.id = 1
samplePokemon.name = "bulbasaur"
samplePokemon.types = ["grass", "poison"]
samplePokemon.hp = 45
samplePokemon.attack = 49
samplePokemon.defense = 49
samplePokemon.specialAttack = 65
samplePokemon.specialDefense = 65
samplePokemon.speed = 45
samplePokemon.sprite = URL(string: "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/1.png")
samplePokemon.shiny = URL(string: "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/shiny/1.png")
samplePokemon.favorite = false
do {
try viewContext.save()
} catch {
let nsError = error as NSError
print(nsError)
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
return result
}()
init(inMemory: Bool = false) {
let sharedStoreURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.MyAppGroup")!.appending(path: "Poke.sqlite")
container = NSPersistentContainer(name: "Poke")
if inMemory {
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
} else {
container.persistentStoreDescriptions.first!.url = sharedStoreURL
}
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
container.viewContext.automaticallyMergesChangesFromParent = true
}
}
The part to take note of is the if else block inside the init:
if inMemory {
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
} else {
container.persistentStoreDescriptions.first!.url = sharedStoreURL
}
My var randomPokemon
code from my question is still exactly the same. It didn't have anything to do with the error.