0
votes

After updating my project to latest swift syntax (Swift 3) I'm getting an error on performing CoreData fetch request. So the app worked for many months and now it's broken.

So this is how I perform my fetch request.

let requestSavedLevel = NSFetchRequest<NSFetchRequestResult>(entityName: "LevelEntity")
let levelNamePredicate = NSPredicate(format: "levelName = %@", levelName) 
requestSavedLevel.predicate = levelNamePredicate 

  do {
     let results = try userDataStack.context.fetch(requestSavedLevel) as? [LevelEntity]
     if (results?.count)! > 0 {
       self.savedLevel = results?.first!

       return results?.first!
     }
    } 
  catch {
      print("\n Error on \(#function): \(error)")
  }

The error occurs on a line

let results = try userDataStack.context.fetch (requestSavedLevel) as? [LevelEntity]

and it immediately takes me to AppDelegate and shows Thread 1: signal SIGABRT

The console prints out message: * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '* -[NSDictionary initWithObjects:forKeys:]: count of objects (0) differs from count of keys (9)'

I have no clue what happened and why the code is no longer working. I've seen other questions about CoreData problems in Swift 3 but that didn't help.

* EDIT *

I tried to find what causes the error and I found next. My NSManagedObject has these properties `extension MyEntity {

@nonobjc public class func fetchRequest() -> NSFetchRequest< MyEntity > {
    return NSFetchRequest< MyEntity >(entityName: "MyEntity");
}

@NSManaged public var horizontal: NSObject?
@NSManaged public var name: String?
@NSManaged public var shapes: NSObject?

}

I want to save the next data to core data: var horizontal: [[Int]] var name: String var shapes: [Int:Shape]

And I do it like this:

let entity = NSEntityDescription.insertNewObject(forEntityName: "MyEntity", into: userDataStack.context) as! MyEntity

    entity.name = name
    entity.horizontal = horizontal as NSObject
    entity.shapes = shapes as NSObject

    userDataStack.saveContext()

However, error occurs when I try to save the last part which is entity.shapes = shapes as NSObject

Shape is a custom class, subclass of NSObject.

So what I get when I try to fetch data from Core Data, is that count of objects (0) differs from count of keys (9)'.

Again, everything has worked great for many month until I updated project to Swift 3.

I tried to use NSArchiver to archive shapes and then assign it to entity.shapes as NSObject, and it works, but if it the only solution then I have to update a lot of code.

Any suggestions why this is not working?

1
It's very unlikely that the error occurs in this code (no dictionary is involved). By the way: If the fetch succeeds, the result is guaranteed to be [LevelEntity], you can safely use as! and if (results?.count)! > 0 { can be replaced with if !results.isEmpty {. PS: In Swift 3 the fetch request is supposed to be created with let requestSavedLevel: NSFetchRequest<LevelEntity> = LevelEntity.fetchRequest() - vadian
1) Just tried to change declaration of requestSavedLevel with LevelEntity.fetchRequest(), and now it is not even initialized. 2) I have a breakpoint on a line with try context.fetch, and if I go to next step it takes me to AppDelegate. - Alexei
The class method fetchRequest() must be implemented in the NSManagedObject subclass - vadian
I see, I'll do that and will update post or add a comment. - Alexei
So, I regenerated NSManagedObject Subclass and fetchRequest() method has been added by xCode. But still getting the same error on try userDataStack.context.fetch(NSFetchRequest) - Alexei

1 Answers

0
votes

So I solved it. The problem was in decoding my custom NSObject subclass.

aDecoder.decodeObject(forKey: "number") as? Int

decodeObject didn't return me an integer value, but I discovered that decodeInteger method does it.

aDecoder.decodeInteger(forKey: "column")

However, decodeInteger method sometimes caused a crash as well, so I ended up with the next condition:

var n = aDecoder.decodeObject(forKey: "number") as? Int
if n == nil {
    n = aDecoder.decodeInteger(forKey: "number")
}