Store is not deleted on model mismatch with .recreateStoreOnModelMismatch option #105

Closed
opened 2025-12-29 15:24:42 +01:00 by adam · 4 comments
Owner

Originally created by @andriichernenko on GitHub (Nov 25, 2016).

I include .recreateStoreOnModelMismatch in the store options, but it doesn't seem to do what it is supposed to. Application still crashes after I change the model.

The issue seems to be somewhere in this code:

    @discardableResult
    public func addStorageAndWait<T: LocalStorage>(_ storage: T) throws -> T {
            // ...
            do {
                // ...            
                do {
                    // ...
                }
                catch let error as NSError where storage.localStorageOptions.contains(.recreateStoreOnModelMismatch) && error.isCoreDataMigrationError {
                    
                    let metadata = try NSPersistentStoreCoordinator.metadataForPersistentStore(
                        ofType: type(of: storage).storeType,
                        at: fileURL,
                        options: storeOptions
                    )

                    // self.model[metadata] returns nil, so storage.eraseStorageAndWait is not called
                    _ = try self.model[metadata].flatMap(storage.eraseStorageAndWait)

                    // another attempt to create storage throws exception which crashes the app
                    _ = try self.createPersistentStoreFromStorage(
                        storage,
                        finalURL: fileURL,
                        finalStoreOptions: storeOptions
                    )
                    return storage
                }
            }
            catch {
                // ...
            }
    }

As I understand, self.model[metadata] subscript tries to find the model by entity hashes (NSStoreModelVersionHashesKey) which is suspicious (I thought these hashes are different by definition when models are different?):

    @nonobjc
    internal subscript(metadata: [String: Any]) -> NSManagedObjectModel? {
        
        guard let modelHashes = metadata[NSStoreModelVersionHashesKey] as? [String : Data] else {
            
            return nil
        }
        for modelVersion in self.modelVersions ?? [] {
            // suspicious comparison, can we use entity hashes here?
            if let versionModel = self[modelVersion], modelHashes == versionModel.entityVersionHashesByName {
              
                return versionModel
            }
        }
        return nil
    }
Originally created by @andriichernenko on GitHub (Nov 25, 2016). I include `.recreateStoreOnModelMismatch` in the store options, but it doesn't seem to do what it is supposed to. Application still crashes after I change the model. The issue seems to be somewhere in this code: ```swift @discardableResult public func addStorageAndWait<T: LocalStorage>(_ storage: T) throws -> T { // ... do { // ... do { // ... } catch let error as NSError where storage.localStorageOptions.contains(.recreateStoreOnModelMismatch) && error.isCoreDataMigrationError { let metadata = try NSPersistentStoreCoordinator.metadataForPersistentStore( ofType: type(of: storage).storeType, at: fileURL, options: storeOptions ) // self.model[metadata] returns nil, so storage.eraseStorageAndWait is not called _ = try self.model[metadata].flatMap(storage.eraseStorageAndWait) // another attempt to create storage throws exception which crashes the app _ = try self.createPersistentStoreFromStorage( storage, finalURL: fileURL, finalStoreOptions: storeOptions ) return storage } } catch { // ... } } ``` As I understand, `self.model[metadata]` subscript tries to find the model by entity hashes (`NSStoreModelVersionHashesKey`) which is suspicious (I thought these hashes are different by definition when models are different?): ```swift @nonobjc internal subscript(metadata: [String: Any]) -> NSManagedObjectModel? { guard let modelHashes = metadata[NSStoreModelVersionHashesKey] as? [String : Data] else { return nil } for modelVersion in self.modelVersions ?? [] { // suspicious comparison, can we use entity hashes here? if let versionModel = self[modelVersion], modelHashes == versionModel.entityVersionHashesByName { return versionModel } } return nil } ```
adam added the fixed label 2025-12-29 15:24:42 +01:00
adam closed this issue 2025-12-29 15:24:42 +01:00
Author
Owner

@JohnEstropia commented on GitHub (Nov 26, 2016):

// self.model[metadata] returns nil, so storage.eraseStorageAndWait is not called

I completely missed this case, thanks so much! Will try to push a fix within the day

@JohnEstropia commented on GitHub (Nov 26, 2016): > // self.model[metadata] returns nil, so storage.eraseStorageAndWait is not called I completely missed this case, thanks so much! Will try to push a fix within the day
Author
Owner

@JohnEstropia commented on GitHub (Nov 26, 2016):

Just to note:

I thought these hashes are different by definition when models are different?

The original assumption is that all previous model versions are bundled in the app (or theres no way we can infer how to migrate).

I missed the case where we change our model while debugging, which was what this flag was originally designed for :(

@JohnEstropia commented on GitHub (Nov 26, 2016): Just to note: > I thought these hashes are different by definition when models are different? The original assumption is that all previous model versions are bundled in the app (or theres no way we can infer how to migrate). I missed the case where we change our model while debugging, which was what this flag was originally designed for :(
Author
Owner

@JohnEstropia commented on GitHub (Nov 26, 2016):

@deville I updated the swift3_develop branch. Try it out :)

@JohnEstropia commented on GitHub (Nov 26, 2016): @deville I updated the swift3_develop branch. Try it out :)
Author
Owner

@andriichernenko commented on GitHub (Nov 26, 2016):

The problem has been resolved, thank you!

@andriichernenko commented on GitHub (Nov 26, 2016): The problem has been resolved, thank you!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/CoreStore#105