Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

255
Visualizações
CoreData: Is there a way to cleanly delete the backuped DB, after database backup via migratePersistentStore?

Currently, our app provides a backup functionality to user, where she can perform snapshot backup of app database.

We are using migratePersistentStore to achieve such functionality.

After running migratePersistentStore, we will execute other I/O operations. Some of the I/O operations might fail. In such case, we need to completely delete the backup-ed database.

However, we notice it is very difficult to delete the backup-ed database!

We notice at certain point, CoreData will re-create the SQLite file, even though we have perform destroyPersistentStore and FileManager.default.removeItem.

Here's our code snippet.

public static func cloneXXXDatabase(dstUrl: URL, directory: Directory, cloneTrash: Bool) -> Bool {
    // Current already opened app database.
    let coreDataStack = CoreDataStack.INSTANCE
    guard let srcUrl = coreDataStack.persistentContainer.persistentStoreDescriptions.first?.url else { return false }
    
    // New destination to backup current app database.
    let coreDataNamedStack = CoreDataNamedStack(srcUrl)
    // Have a new NSPersistentStoreCoordinator solely for migration purpose.
    let psc = coreDataNamedStack.persistentContainer.persistentStoreCoordinator
    
    // Open the SQLite. Is it fine for 2 different NSPersistentStoreCoordinator to access 1 same SQLite?
    guard let srcStore = psc.persistentStore(for: srcUrl) else { return false }

    do {
        // Reference: https://www.avanderlee.com/swift/write-ahead-logging-wal/
        // This is to ensure only 1 SQLite file is produced, without WAL & SHM.
        let options = [NSSQLitePragmasOption: ["journal_mode": "DELETE"]]
        try psc.migratePersistentStore(srcStore, to: dstUrl, options: options, withType: NSSQLiteStoreType)
    } catch {
        error_log(error)
        return false
    }
    
    // ...
    // ... (some other I/O operations)
    // ...
    
    var somethingWentWrong = true
    
    if somethingWentWrong {
        do {
            try psc.destroyPersistentStore(at: dstUrl, ofType: NSSQLiteStoreType)
            
            if dstUrl.exists {
                try FileManager.default.removeItem(at: dstUrl)
            }
            
            print(">>>> DELETE \(dstUrl)")
            
            //
            // WARNING: Such SQLite removal code is not working, and I am not sure why?!
            // It seems that after returning from this function, CoreData will still re-create the backup destination
            // DB, by logging
            //
            // CoreData: annotation: Connecting to sqlite database file at ".../xxx.sqlite".
            //
        } catch {
            error_log(error)
        }
    }
    
    return true
}

Do you have idea, how can we cleanly delete the SQLite file, which is generated via migratePersistentStore? destroyPersistentStore and FileManager.default.removeItem do not seem to work.

Thank you.


Here's the code of CoreDataStack (Core data stack for main app), and CoreDataNamedStack (Core data stack which points to the backup destination)

CoreDataStack (Core data stack for main app)

class CoreDataStack {
    static let INSTANCE = CoreDataStack()
    
    private init() {
    }
    
    private(set) lazy var persistentContainer: NSPersistentContainer = {
        let container = NSPersistentContainer(name: "xxx", managedObjectModel: NSManagedObjectModel.wenote)
        
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                // This is a serious fatal error. We will just simply terminate the app, rather than using error_log.
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        
        // So that when backgroundContext write to persistent store, container.viewContext will retrieve update from
        // persistent store.
        container.viewContext.automaticallyMergesChangesFromParent = true
        
        return container
    }()
}

CoreDataNamedStack (Core data stack which points to the backup destination)

class CoreDataNamedStack: CoreDataStackable {
    let url: URL
    
    init(_ url: URL) {
        self.url = url
    }
    
    private(set) lazy var persistentContainer: NSPersistentContainer = {
        let container = NSPersistentContainer(name: "xxx", managedObjectModel: NSManagedObjectModel.wenote)
        
        let storeDescription = NSPersistentStoreDescription(url: url)
        container.persistentStoreDescriptions = [storeDescription]
        
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                // This is a serious fatal error. We will just simply terminate the app, rather than using error_log.
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        
        // So that when backgroundContext write to persistent store, container.viewContext will retrieve update from
        // persistent store.
        container.viewContext.automaticallyMergesChangesFromParent = true
        
        return container
    }()
}

Finding

Instead of executing the following deletion code immediately

var somethingWentWrong = true

if somethingWentWrong {
    do {
        try psc.destroyPersistentStore(at: dstUrl, ofType: NSSQLiteStoreType)
        
        if dstUrl.exists {
            try FileManager.default.removeItem(at: dstUrl)
        }
        
        print(">>>> DELETE \(dstUrl)")
    } catch {
        error_log(error)
    }
}

If we delay a few seconds before executing deletion code.

    var somethingWentWrong = true
    
    DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
            
        if somethingWentWrong {
            do {
                try psc.destroyPersistentStore(at: dstUrl, ofType: NSSQLiteStoreType)
                
                if dstUrl.exists {
                    try FileManager.default.removeItem(at: dstUrl)
                }
                
                print(">>>> DELETE \(dstUrl)")
            } catch {
                error_log(error)
            }
        }
    }

Then such removal will success.

My guess is, CoreData has a background thread commit changes to disk. If the deletion happens before the disk changes committed, database file re-creation will happen again.

May I know what is a proper way to solve this, without having an arbitrary/ random delayed code block?

over 4 years ago · Santiago Trujillo
1 Respostas
Responde à pergunta

0

Observations that I have noted from your code snippets:

  1. You used the deprecated method on NSPersistentStoreCoordinator. Apple Documentation
  2. To migrate from srcStore URL to dstStore it will take time for migration using migratePersistentStore() so, you have to check if the whole migration is completed before deleting the backup-ed database.

To do it you can check the status of migration and destroyPersistentStore ple:

let migrationResult = try psc.migratePersistentStore(srcStore, to: dstUrl, options: options, withType: NSSQLiteStoreType)

if migrationResult {
    do {
        try psc.destroyPersistentStore(at: dstUrl, ofType: NSSQLiteStoreType)
        
        if dstUrl.exists {
            try FileManager.default.removeItem(at: dstUrl)
        }
        
        print(">>>> DELETE \(dstUrl)")
        
        //
        // WARNING: Such SQLite removal code is not working, and I am not sure why?!
        // It seems that after returning from this function, CoreData will still re-create the backup destination
        // DB, by logging
        //
        // CoreData: annotation: Connecting to sqlite database file at ".../xxx.sqlite".
        //
    } catch {
        error_log(error)
    }
}
over 4 years ago · Santiago Trujillo Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda