0

I developed an app using CoreData and CloudKit. It gives some warning message in the console but works with no problem. However Apple rejected due to crash which I could not replicate. Really appreciate if you could advise how to address the issue.I implemented CoreData and CloudKit based on this material:

CoreDataImplementation

This is crash log from Apple:

enter image description here

In the splash screen CoreDataStack file is called:

struct SplashView: View {
    
    @EnvironmentObject var iconSettings: IconNames
    let persistenceController = CoreDataStack.shared
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    @AppStorage("isOnboarding") var isOnboarding: Bool = true
    @StateObject private var modelData = ExLibrisModelData()
    
    @State var isActive: Bool = false
    
    var body: some View {
        ZStack {
            if self.isActive {
                ContentView()
                    .environmentObject(modelData)
                    .environmentObject(IconNames())
                    .environment(\.managedObjectContext, CoreDataStack.shared.context)
                    .environment(\.urlImageOptions, URLImageOptions(
                        maxPixelSize: CGSize(width: 600.0, height: 600.

And here is my CoreDataStack file where I get the crash according to the report.

final class CoreDataStack: ObservableObject {
    static let shared = CoreDataStack()
    
    var context: NSManagedObjectContext {
        persistentContainer.viewContext
    }
    
    static let appGroupName = "group.com.Bookshelf"
    
    static let containerURL: URL = {
        FileManager.default.containerURL(
            forSecurityApplicationGroupIdentifier: CoreDataStack.appGroupName)!
    }()
    
    var ckContainer: CKContainer {
        let storeDescription = persistentContainer.persistentStoreDescriptions.first
        
        
        guard let identifier = storeDescription?.cloudKitContainerOptions?.containerIdentifier else {
            fatalError("Unable to get container identifier")
        }
        return CKContainer(identifier: identifier)
    }
    

    var privatePersistentStore: NSPersistentStore {
        guard let privateStore = _privatePersistentStore else {
            fatalError("Private store is not set")
        }
        return privateStore
    }
    
    var sharedPersistentStore: NSPersistentStore {
        guard let sharedStore = _sharedPersistentStore else {
            fatalError("Shared store is not set")
        }
        return sharedStore
    }
    
    
    lazy var persistentContainer: NSPersistentCloudKitContainer = {
        let container = NSPersistentCloudKitContainer(name: "Bookshelf")
        
        guard let privateStoreDescription = container.persistentStoreDescriptions.first else {
            fatalError("Unable to get persistentStoreDescription")
        }
        
        let storesURL = privateStoreDescription.url?.deletingLastPathComponent()
        privateStoreDescription.url = storesURL?.appendingPathComponent("private.sqlite")
        let sharedStoreURL = storesURL?.appendingPathComponent("shared.sqlite")
        
        guard let sharedStoreDescription = privateStoreDescription.copy() as? NSPersistentStoreDescription else {
            fatalError("Copying the private store description returned an unexpected value.")
        }
        sharedStoreDescription.url = sharedStoreURL
        
        guard let containerIdentifier = privateStoreDescription.cloudKitContainerOptions?.containerIdentifier else {
            fatalError("Unable to get containerIdentifier")
        }
        
        let sharedStoreOptions = NSPersistentCloudKitContainerOptions(containerIdentifier: containerIdentifier)
        sharedStoreOptions.databaseScope = .shared
        sharedStoreDescription.cloudKitContainerOptions = sharedStoreOptions
        container.persistentStoreDescriptions.append(sharedStoreDescription)
        
        container.loadPersistentStores { loadedStoreDescription, error in
            if let error = error as NSError? {
                fatalError("Failed to load persistent stores: \(error)")
            } else if let cloudKitContainerOptions = loadedStoreDescription.cloudKitContainerOptions {
                guard let loadedStoreDescriptionURL = loadedStoreDescription.url else {
                    return
                }
                
                if cloudKitContainerOptions.databaseScope == .private {
                    let privateStore = container.persistentStoreCoordinator.persistentStore(for: loadedStoreDescriptionURL)
                    self._privatePersistentStore = privateStore
                } else if cloudKitContainerOptions.databaseScope == .shared {
                    let sharedStore = container.persistentStoreCoordinator.persistentStore(for: loadedStoreDescriptionURL)
                    self._sharedPersistentStore = sharedStore
                }
            }
        }
        
        container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
        container.viewContext.automaticallyMergesChangesFromParent = true
        do {
            try container.viewContext.setQueryGenerationFrom(.current)
        } catch {
            fatalError("Failed to pin viewContext to the current generation: \(error)")
        }
           
        return container
    }()
    
    private var _privatePersistentStore: NSPersistentStore?
    private var _sharedPersistentStore: NSPersistentStore?
    private init() {}
}

The console warnings are below. I have several of those:

CoreData: warning: CoreData+CloudKit: -[NSCloudKitMirroringDelegate _performSetupRequest:]_block_invoke(1093): <NSCloudKitMirroringDelegate: 0x282340540>: Successfully set up CloudKit integration for store (0811C705-3EB2-4C73-BC5B-B96B43E4F2E0): <NSSQLCore: 0x106c15b40> (URL: file:///var/mobile/Containers/Data/Application/CAA6F402-F5D0-496E-8E32-01299A0B7C7E/Library/Application%20Support/private.sqlite)

CoreData: warning: CoreData+CloudKit: -[NSCloudKitMirroringDelegate _performSetupRequest:]_block_invoke(1093): <NSCloudKitMirroringDelegate: 0x2823407e0>: Successfully set up CloudKit integration for store (1F2E3F9B-9DD9-4D61-9DB9-ADD2A3DE9D8D): <NSSQLCore: 0x106c087d0> (URL: file:///var/mobile/Containers/Data/Application/CAA6F402-F5D0-496E-8E32-01299A0B7C7E/Library/Application%20Support/shared.sqlite)

CoreData: warning: CoreData+CloudKit: -[NSCloudKitMirroringDelegate _enqueueRequest:]_block_invoke_2(990): Failed to enqueue request: <NSCloudKitMirroringExportRequest: 0x28397cdc0> 709BC9FC-3172-4F7C-9878-A5E3412E8425

CoreData: warning: CoreData+CloudKit: -NSCloudKitMirroringDelegate finishedAutomatedRequestWithResult:: Finished request '<NSCloudKitMirroringExportRequest: 0x28397cdc0> 709BC9FC-3172-4F7C-9878-A5E3412E8425' with result: <NSCloudKitMirroringResult: 0x2814eb090> storeIdentifier: 0811C705-3EB2-4C73-BC5B-B96B43E4F2E0 success: 0 madeChanges: 0 error: Error Domain=NSCocoaErrorDomain Code=134417 "Request '<NSCloudKitMirroringExportRequest: 0x28397cdc0> 709BC9FC-3172-4F7C-9878-A5E3412E8425' was cancelled because there is already a pending request of type 'NSCloudKitMirroringExportRequest'." UserInfo={NSLocalizedFailureReason=Request '<NSCloudKitMirroringExportRequest: 0x28397cdc0> 709BC9FC-3172-4F7C-9878-A5E3412E8425' was cancelled because there is already a pending request of type 'NSCloudKitMirroringExportRequest'.}

baymak
  • 89
  • 9
  • Please [edit] your question with additional info, don’t put it in the comments. See also [ask]. – koen Aug 09 '23 at 14:20
  • just done it thank you – baymak Aug 09 '23 at 14:50
  • 1
    You can see from the stack trace that your app crashed in the `persistentContainer` code, in the first closure, which is where you load the persistent stores. Have you tested your app on a device that is not logged in to iCloud? Have you tested on a device with no network? Have you tested on a device that does not have your app installed (ie delete before installing from TestFlight)? – Paulw11 Aug 09 '23 at 20:09
  • I'll second what @Paulw11 stated. One of the `fatalError` calls in `persistentContainer` is being called. You need far better error handling so your app doesn't crash on every little check. Always test your app via Test Flight as a fresh install to replicate what the reviewer will be seeing. Test in Airplane mode and other "non-happy path" conditions. – HangarRash Aug 10 '23 at 00:18
  • I was testing both on real devices and simulators for all conditions but never seen it until I install it from TestFlight today. I did not realize that something can be different between run directly from Xcode and run the same code via testflight on the same device. No matter whether network connection is available or not I can see crash on the device. As both you mention it is persistent Container but I could not figure out how to fix it. My aim is to use CoreData in the device and sync with iCloud when network available. – baymak Aug 10 '23 at 11:41
  • @baymak Running through Xcode is using the development containers. In TestFlight (and App Store Review), the app is using the production containers. Have you pushed the development schema to production for the CloudKit container? – HangarRash Aug 10 '23 at 15:24
  • hello @HangarRash yes CloudKit container is in production – baymak Aug 10 '23 at 15:40

1 Answers1

0

It's probably caused by the lazy var persistentContainer. Or it's caused by let persistenceController = CoreDataStack.shared and .environment(\.managedObjectContext, CoreDataStack.shared.context) where as it should be .environment(\.managedObjectContext, persistenceController.viewContext)

But try changing CoreDataStack to a struct to match what is in Xcode's template since that is the tried and tested way to integrate Core Data with SwiftUI and using Combine's ObservableObject class is non-standard.

You'll also need to fix this: .environmentObject(IconNames()) You can't init objects in body like that.

malhal
  • 26,330
  • 7
  • 115
  • 133