Terrified of ruining users' data when submitting an updated app!

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??

Advice and/or reassurance would be most welcome!

Thank you.

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 :slight_smile:

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.

Thanks for the reply, Fahim. I appreciate it.

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!! :flushed:

…actually… I think I might have worked it out.

BRB :nerd_face:

Hmm… nope.

I created a global function

let applicationDocumentsDirectory: URL = {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
return paths[0]
}()

Then I referenced this location in an updated version in AppDelegate.swift

   lazy var persistentContainer: NSPersistentContainer = {
      let container = NSPersistentContainer(name: "DataModel")

    container.loadPersistentStores(completionHandler: {
        applicationDocumentsDirectory, error in
        if let error = error {
            fatalError("Could load data store: \(error)")
        }
    })        
    return container
}()

That still didn’t work. Still seeing data from the ‘new’ app and not the ‘old’ app.

I feel like I’m close… but no cigar! I’m clearly missing something.

You are close :slight_smile:

You can simply set the URL for the data by doing something like this:

let storeURL = applicationDocumentsDirectory
let description = NSPersistentStoreDescription(url: storeURL)

container.persistentStoreDescriptions = [ description ]

You need to do that before you load the persistent store. See if that works and let me know how it goes.

Thanks Fahim for your help. I feel we’re getting closer.

I changed the code to the following:

lazy var persistentContainer: NSPersistentContainer = {

    let storeURL = applicationDocumentsDirectory
    let description = NSPersistentStoreDescription(url: storeURL)
    let container = NSPersistentContainer(name: "DataModel")
    
    container.persistentStoreDescriptions = [description]
    container.loadPersistentStores(completionHandler: {
        description, error in
        if let error = error {
            fatalError("Could load data store: \(error)")
        }
    })        
    return container
}()

When it runs this line (using breakpoints):

    container.loadPersistentStores(completionHandler: {

It throws this error in the console:

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?

Got it!

I just had to append the path component for the actual sqlite file in the URL variable!

        let storeURL = applicationDocumentsDirectory.appendingPathComponent("DataStore.sqlite")

Thanks for your help. By pointing me in the right direction and letting me do a bit of searching and reading, I’ve learnt even more.

:pray:t2:

Sorry, I had not actually checked your applicationDocumentDirectory code to make sure that it was appending the file name - my bad :slight_smile: 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 :wink:

This topic was automatically closed after 166 days. New replies are no longer allowed.