I'm migrating my iOS app to use NSPersistentContainer
. This class by default locates its persistent store files in the Library/Application Support
directory; previously my store files were stored in the Documents
directory.
I've added a little bit of code to move the store files if they were found at the old directory:
func moveStoreFromLegacyLocationIfNecessary(toNewLocation newLocation: URL) {
// The old store location is in the Documents directory
let legacyStoreLocation = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appendingPathComponent("books.sqlite")
// Check whether the old store exists and the new one does not
if FileManager.default.fileExists(atPath: legacyStoreLocation.path) && !FileManager.default.fileExists(atPath: newLocation.path) {
print("Store located in Documents directory; migrating to Application Support directory")
let tempStoreCoordinator = NSPersistentStoreCoordinator()
try! tempStoreCoordinator.replacePersistentStore(at: newLocation, destinationOptions: nil, withPersistentStoreFrom: legacyStoreLocation, sourceOptions: nil, ofType: NSSQLiteStoreType)
// Delete the old store
try? tempStoreCoordinator.destroyPersistentStore(at: legacyStoreLocation, ofType: NSSQLiteStoreType, options: nil)
}
}
After calling destroyPersistentStore(at: url)
, the store files are still present on disk. Will these be automatically cleaned up at some point? Or should I be deleting them? Should I also be deleting the .sqlite-shm
and .sqlite-wal
files?
The documentation for NSPersistentStoreCoordinator.destroyPersistentStore(at:type:options:)
states:
Deletes a specific type of persistent store at the provided location.
In talking with an engineer in a WWDC lab, they explained it does not actually delete the database files at the provided location as the documentation seems to imply. It actually just truncates rather than delete. If you want them gone you can manually delete the files (if you can ensure no other process or a different thread is accessing them).
This is what I've implemented in my app:
try coordinator.replacePersistentStore(at: sharedStoreURL, destinationOptions: nil, withPersistentStoreFrom: defaultStoreURL, sourceOptions: nil, ofType: NSSQLiteStoreType)
try coordinator.destroyPersistentStore(at: defaultStoreURL, ofType: NSSQLiteStoreType, options: nil)
// destroyPersistentStore says it deletes the old store but it actually truncates so we'll manually delete the files
NSFileCoordinator(filePresenter: nil).coordinate(writingItemAt: defaultStoreURL.deletingLastPathComponent(), options: .forDeleting, error: nil, byAccessor: { url in
try? FileManager.default.removeItem(at: defaultStoreURL)
try? FileManager.default.removeItem(at: defaultStoreURL.deletingLastPathComponent().appendingPathComponent("\(container.name).sqlite-shm"))
try? FileManager.default.removeItem(at: defaultStoreURL.deletingLastPathComponent().appendingPathComponent("\(container.name).sqlite-wal"))
try? FileManager.default.removeItem(at: defaultStoreURL.deletingLastPathComponent().appendingPathComponent("ckAssetFiles"))
})
I filed FB10181832 to request the documentation be updated to better explain its behavior.