I'm new to swift and I have trouble with understanding how environment variables works.
In Core Data, I created new Entity called "API" with one attribute id: Int32.
Then in SwiftUI, I wanted to find maximum value of id. I wrote a request, but whenever I used passed to view as environment variable managedObjectContext, it always crashed my app/preview. Here's crash info after using NSManagedObjectContext.fetch(NSFetchRequest) (using FetchRequest gives only stacktrace with exception EXC_BAD_INSTRUCTION)
...
Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note: EXC_CORPSE_NOTIFY
External Modification Warnings:
Thread creation by external task.
Application Specific Information:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'The fetch request's entity 0x600003c54160 'API' appears to be from a different NSManagedObjectModel than this context's'
terminating with uncaught exception of type NSException
abort() called
CoreSimulator 704.12 - Device: iPhone 11 (8356FF2A-5F0A-42F7-AA32-396FADCF2BF6) - Runtime: iOS 13.4 (17E255) - DeviceType: iPhone 11
Application Specific Backtrace 1:
0 CoreFoundation 0x00007fff23e3dcce __exceptionPreprocess + 350
1 libobjc.A.dylib 0x00007fff50b3b9b2 objc_exception_throw + 48
2 CoreData 0x00007fff239c6b99 -[NSManagedObjectContext executeFetchRequest:error:] + 5004
3 libswiftCoreData.dylib 0x00007fff513b63d4 $sSo22NSManagedObjectContextC8CoreDataE5fetchySayxGSo14NSFetchRequestCyxGKSo0gH6ResultRzlF + 68
...
Keep in mind, that this error is changing depending on which project, I'm using. In my main project I had error like that:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+entityForName: nil is not a legal NSPersistentStoreCoordinator for searching for entity name 'WebsiteAPI''
Here is the code I'm using
import SwiftUI
import CoreData
struct test: View {
private var id: Int32
@Environment(\.managedObjectContext) var managedObjectContext
var body: some View {
Text("id=\(id)")
}
public init(context: NSManagedObjectContext) {
self.id = -1
//this crashes and gives no usefull information
// let request2 = FetchRequest<API>(
// entity: API.entity(),
// sortDescriptors: [NSSortDescriptor(keyPath: \API.id, ascending: false)]
// )
// self.id = request2.wrappedValue.first?.id ?? 1
guard let context2 = (UIApplication.shared.delegate as? AppDelegate)?.persistentContainer.viewContext else {
fatalError("Unable to read managed object context.")
}
let request = NSFetchRequest<API>(entityName: "API")
request.sortDescriptors = [NSSortDescriptor(keyPath: \API.id, ascending: false)]
do {
var commits = try context.fetch(request) // OK
commits = try context2.fetch(request) // OK
//commits = try self.managedObjectContext.fetch(request) // causing crash
self.id = Int32(commits.count)
} catch let error {
print(error.localizedDescription)
}
}
}
struct test_Previews: PreviewProvider {
static var previews: some View {
guard let context = (UIApplication.shared.delegate as? AppDelegate)?.persistentContainer.viewContext else {
fatalError("Unable to read managed object context.")
}
return test(context: context).environment(\.managedObjectContext, context)
}
}
All commented lines crash app. Why getting context from AppDelegate.persistentContainer.viewContext works just fine, but using environment variable managedObjectContext, which in my opinion should be same, doesn't work? I spent 5 hours on this, checked pretty much everything, tried a lot of things but with no success. In the end I can just keep getting context from AppDelegate, but what's wrong with environment variable? Am I missing some common knowledge or is just a bug? I'm getting headache from bugs that I'm encountering in Xcode, starting from missing autocompletion after clearing build folder to hundreds of errors after changing struct/file name on all references, despite successfully building afterwards. Restarting Xcode few times every day to make it working properly is normal for me.
Also some things I noticed, when I created FetchRequest as a variable and used it in some list inside body, it worked. The problem is only, when I'm trying to fetch things manually in code/function/init, like button action or methods onAppear, init etc. I tried to run app on both physical device and showing preview. Same effect.
I'm using Xcode 11.4 with Swift 5.