Quick background:
Using the excellent iOS Apprentice (v4) two years ago I created an app which has been doing quite well (Swim Splitz).
Now that iOS 11 is upon us, and Xcode 9, I’ve decided to refactor the code to bring it all up to date (converting Swift 3 to Swift 4) using the latest iOS Apprentice (v6) as a guide (‘Core Locations’ way of storing data as my template).
It’s been a real struggle as I’m hardly a real app developer (I’m a doctor!) but I’m close to releasing an updated version. Now, I have not modified my CoreData entities at all but I’m terrified of my customers losing all their existing data…
The reason why I’m concerned is I have the app on my own iPhone (installed via the App Store) with lots of saved data.
When I ‘test’ the updated version on my iPhone via Xcode, it overwrites the official version
When I run it, I don’t see any of my saved data, but I can save new data - it all seems to work
When I delete the test app and re-download the app from the App Store, all the old data is visible again.
My questions are:
Why doesn’t the ‘test app’ see the existing data?
Will the data all be fine once the app is ‘official’ and downloaded via the App Store, thus preserving users’ data or is it going to make a mess of things?
Does the ‘test app’ run via Xcode use a different document directory, despite ‘overwriting’ the offical version of the App, and does that explain why I can’t see the legacy data??
First of all, congratulations on getting an app on the app store - that’s always something to give yourself a pat on the back for
It does appear as if you have an issue somewhere since you should see your previous data from the new version and if the new version does not see the old data but if you install the app store version over the new version you see the old data but not the new, then it would seem to indicate that the data for the old version and the new version are stored in two different locations.
If that is the case, if you did release the new version, your users would not be able to see their old data either and that is an issue.
If you have a copy of your code before updating to the latest version, I’d suggest that you try running that version of the code on the simulator, add some data, then install the newer version of the app on the same simulator and see if you can see the older data. That should definitely tell you if there is an issue or not.
If there is still an issue and you are unable to figure it out and are able to share the code for the old version and the new version, I can take a look to see what is going on.
The location of my data store for the original version of the app is similar to the one laid out in this ‘Core Location’ iOS Apprentice tutorial from v4 (using Swift 3).
It looks like this:
lazy var managedObjectContext: NSManagedObjectContext = {
guard let modelURL = Bundle.main.url(forResource: "DataModel", withExtension: "momd")
else { fatalError("Could not find data model in app bundle")
}
guard let model = NSManagedObjectModel(contentsOf: modelURL) else {
fatalError("Error initializing model from: \(modelURL)")
}
let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentsDirectory = urls[0]
let storeURL = documentsDirectory.appendingPathComponent("DataStore.sqlite")
print(storeURL)
do {
let coordinator = NSPersistentStoreCoordinator(managedObjectModel: model)
try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: storeURL, options: nil)
let context = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
context.persistentStoreCoordinator = coordinator
return context
} catch {
fatalError("Error adding persistent store at \(storeURL): \(error)")
}
}()
And to bring it in line with Swift 4, etc. (and using the latest iOS Apprentice ‘Core Location’ tutorial - v6 - as a reference), my ‘managedObjectContext’ seems to look a lot simpler and doesn’t point to a specific directory location, but rather a ‘persistentContainer’:
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "DataModel")
container.loadPersistentStores(completionHandler: {
storeDescription, error in
if let error = error {
fatalError("Could load data store: \(error)")
}
})
return container
}()
lazy var managedObjectContext: NSManagedObjectContext =
self.persistentContainer.viewContext
It seems to be about missing a direct reference to the ‘documents directory’, but I’m unsure how to point the new method of creating the managedObjectContext to the old data location.
Any ideas?
On a side note, it’s phenomenal how quickly it all changes - code that I wrote just over a year ago is now totally redundant! I think I need to stay up to date more frequently!!
NSSQLiteErrorDomain = 14;
NSUnderlyingException = "unable to open database file";
It seems like the URL for the database file is close… but not quite right. If I step to the end of the ‘lazy var’ code, it throws a fatal error:
Thread 1: Fatal error: Could load data store: Error Domain=NSCocoaErrorDomain Code=256 “The file couldn’t be opened.” UserInfo={NSSQLiteErrorDomain=14, NSUnderlyingException=unable to open database file}
The previous data store location must be slightly different to this one (is it a file extension issue???). I note that with Swift 4 I no longer have to specify .sqlite or .momd extensions in the URL for the data store. Is that the problem?
Sorry, I had not actually checked your applicationDocumentDirectory code to make sure that it was appending the file name - my bad But I’m glad that you were able to figure it out and also learn a bit more in the process.
Good luck with the app and if you run into any other issues, you know where to come