0

I changed the name of my Xcode 8 project how described in this post How do I completely rename an Xcode project (i.e. inclusive of folders)?

Now when running the new version of the app on a device where the old version is installed the app crashes with the following error message:

[NSKeyedUnarchiver decodeObjectForKey:]: cannot decode object of class (OLD_PROJECTNAME.SomeObject) for key (NS.objects); the class may be defined in source code or a library that is not linked

So it seems that the old name doesn't fit to the core data stack of the renamed version. How can I change this name to make the app executable?

EDIT: This is the core data initialization code inside my AppDelegate.Swift:

// MARK: - Core Data stack

lazy var persistentContainer: NSPersistentContainer = {
    /*
     The persistent container for the application. This implementation
     creates and returns a container, having loaded the store for the
     application to it. This property is optional since there are legitimate
     error conditions that could cause the creation of the store to fail.
    */

    let container = NSPersistentContainer(name: "Data")


    container.loadPersistentStores(completionHandler: { (storeDescription, error) in
        if let error = error as NSError? {
            // Replace this implementation with code to handle the error appropriately.
            // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

            /*
             Typical reasons for an error here include:
             * The parent directory does not exist, cannot be created, or disallows writing.
             * The persistent store is not accessible, due to permissions or data protection when the device is locked.
             * The device is out of space.
             * The store could not be migrated to the current model version.
             Check the error message to determine what the actual problem was.
             */
            fatalError("Unresolved error \(error), \(error.userInfo)")
        }
    })
    return container
}()

// MARK: - Core Data Saving support

func saveContext () {
    let context = persistentContainer.viewContext
    if context.hasChanges {
        do {
            try context.save()
        } catch {
            // Replace this implementation with code to handle the error appropriately.
            // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            let nserror = error as NSError
            fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
        }
    }
}

It's a production app and the user should not lose any data after the update.

SteffenK
  • 582
  • 6
  • 17
  • What were you trying to accomplish with the project rename? Were you just trying to rename the app so it shows a different name under the app icon? Is this a production app where this is a valid use case, or just wondering about this during the dev phase? – HammondSuisse Jun 02 '17 at 00:46
  • It is a production app and I wanted to change the full name of the project. – SteffenK Jun 02 '17 at 11:01
  • Can I manually set a name for the core data model (to fit with the old one) or how can I import the old data model to the new one without an app crash? – SteffenK Jun 02 '17 at 11:08
  • Can you post a snippet of your CoreData initialization code (probably in the AppDelegate if it was created for you)? You can change the name of the core data model in this initialization code, but you may lose existing on-device data for existing users. Is that acceptable? – HammondSuisse Jun 02 '17 at 15:33
  • I edited the question. No the user should not use any data. – SteffenK Jun 02 '17 at 16:55
  • Do you have any attributes with the `transformable` type? Your error looks like you might. – Tom Harrington Jun 02 '17 at 19:02
  • Yes I have some transformable types. – SteffenK Jun 02 '17 at 19:16

1 Answers1

3

You mentioned in a comment that you have some transformable attributes. This is consistent with your error message, since [NSKeyedUnarchiver decodeObjectForKey:] is part of NSCoding, and since Core Data uses NSCoding with transformable attributes.

This isn't a Core Data problem. You'd get the same error if you used NSCoding on the values of those attributes and wrote the result to a file. In Swift, the full name of a class is something like AppName.ClassName. If AppName doesn't match, NSCoding can't decode the object. You saved objects with a name like OLD_PROJECTNAME.SomeObject, but now your class name is something like NEW_PROJECTNAME.SomeObject. NSCoding can't handle that on its own. You need to help it.

To fix this, you need to tell the archiving system what class to use. You can do that with the NSKeyedUnarchiver class method setClass(_:forClassName:). You might have to experiment a little to get the syntax right but you'll have something like

NSKeyedUnarchiver.setClass(SomeClass.self, forClassName:"OLD_PROJECT_NAME.SomeObject")

You must do this before you attempt to create any objects that use this class so that it will affect how those objects are created. Since you're using Core Data that means before doing anything at all that touches Core Data in any way.

Tom Harrington
  • 69,312
  • 10
  • 146
  • 170