Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Crash on retrieving entity name?

When my app is first launched i do some first time setup configuration which is seeding my SQLLite database using the CoreData API with data from a JSON document.

I do this by first getting a reference to my managed object context from my core data stack, and then calling a method which does the JSON configuration. I then call:

....(JSON conversion)....

context.perform {
 _ = Exercise.insert(into: context, name: exerciseName, category: categoryNumber, equipment: equipment, skill: "", primaryMuscle: primaryMuscle, otherMuscles: otherMuscles, info: exerciseDescription, image1: exerciseImage1, image2: exerciseImage2)
   }

On every iteration as i loop through the JSON objects so it persists each object.

The method for inserting the object is this:

public static func insert(into context: NSManagedObjectContext, name: String, category: Int16, equipment: String?, skill: String?, primaryMuscle: String?, otherMuscles: String?, info: String?, image1: String?, image2: String?) -> Exercise {
    let exercise: Exercise = context.insertObject()
    exercise.name = name
    exercise.category = category
    exercise.info = info
    exercise.primaryMuscle = primaryMuscle
    exercise.otherMuscles = otherMuscles
    exercise.equipment = equipment
    exercise.skill = skill

    if image1 != nil {
        exercise.image1Path = image1
    }

    if image2 != nil {
        exercise.image2Path = image2
    }

    return exercise
}

And then i extended NSManagedObjectContext...

extension NSManagedObjectContext {
public func insertObject<Object: NSManagedObject>() -> Object where Object: Managed {
    guard let obj = NSEntityDescription.insertNewObject(forEntityName: Object.entityName, into: self) as? Object else { fatalError("Wrong object type") }
    return obj
}
}

This is my declaration of my "Managed" protocol:

public protocol Managed: class, NSFetchRequestResult {
static var entityName: String {get}
....
   }

extension Managed where Self: NSManagedObject {
public static var entityName: String { return entity().name! }
 }

I then crash when trying to retrieve the entityName from the protocol extension where i'm trying to insert the object...

enter image description here

It gives me the error "fatal error: unexpectedly found nil while unwrapping an Optional value"

I didn't generate my NSManagedObjectSublcasses, i created them myself, but i'm sure i did it correctly. I have a feeling though that Core Data isn't picking up my Model class for "Exercise" when the app runs. And that's why the entity name is returning nothing. So here's my code for my model class:

import UIKit
import CoreData

public class Exercise: NSManagedObject {

@NSManaged public fileprivate(set) var category: Int16
@NSManaged public fileprivate(set) var image1Path: String?
@NSManaged public fileprivate(set) var image2Path: String?
@NSManaged public fileprivate(set) var equipment: String?
@NSManaged public fileprivate(set) var info: String?
@NSManaged public fileprivate(set) var skill: String?
@NSManaged public fileprivate(set) var primaryMuscle: String?
@NSManaged public fileprivate(set) var otherMuscles: String?

public static let normalizedNameKey = "name_normalized"

@NSManaged fileprivate var primitiveName: String

public var name: String {
    set {
        willChangeValue(forKey: #keyPath(Exercise.name))
        primitiveName = newValue
        updateNormalizedName(newValue)
        didChangeValue(forKey: #keyPath(Exercise.name))
    }
    get {
        willAccessValue(forKey: #keyPath(Exercise.name))
        let value = primitiveName
        didAccessValue(forKey: #keyPath(Exercise.name))
        return value
    }
}

fileprivate func updateNormalizedName(_ name: String) {
    setValue(name.normalizedForSearch, forKey: Exercise.normalizedNameKey)
}

public var exerciseImage1: UIImage? {
    guard let image = image1Path else {return nil}
    return UIImage(named: image)
}

public var exerciseImage2: UIImage? {
    guard let image = image2Path else {return nil}
    return UIImage(named: image)
}

 public static func insert(into context: NSManagedObjectContext, name: String, category: Int16, equipment: String?, skill: String?, primaryMuscle: String?, otherMuscles: String?, info: String?, image1: String?, image2: String?) -> Exercise {
    let exercise: Exercise = context.insertObject()
    exercise.name = name
    exercise.category = category
    exercise.info = info
    exercise.primaryMuscle = primaryMuscle
    exercise.otherMuscles = otherMuscles
    exercise.equipment = equipment
    exercise.skill = skill

    if image1 != nil {
        exercise.image1Path = image1
    }

    if image2 != nil {
        exercise.image2Path = image2
    }

    return exercise
}

}

  extension Exercise: Managed {

  public static var defaultSortDescriptors: [NSSortDescriptor] {
    return [NSSortDescriptor(key: #keyPath(name), ascending: false)]
  }

public static func defaultPredicate(muscleCategory: Int) -> NSPredicate 
 {
    return NSPredicate(format: "%K == %ld", 
 #keyPath(Exercise.category), muscleCategory)
}
}

And here's the entity in the data model editor: enter image description here

Any ideas what could be going wrong? Thanks

like image 656
Luke97 Avatar asked Jun 16 '17 07:06

Luke97


3 Answers

I had the exact same issue, using the exact same code (Core Data book by Florian Kugler).

My issue appeared after I added a new entity + relationship and when creating the new entity, the entity name was nil.
I fixed it by deleting the entity and recreating it again. And the errors disappeared.

like image 118
Adrian Avatar answered Oct 19 '22 14:10

Adrian


Maybe it's a not a good idea, but it can avoid crash now... Waiting for better solution.

swift 4:

extension NSObject {
    var className: String {
        return String(describing: type(of: self))
    }

    class var className: String {
        return String(describing: self)
    }
}

then

static var entityName: String {
    return self.className //insead of `entity().name!`
}
like image 2
hstdt Avatar answered Oct 19 '22 16:10

hstdt


Moin,

first unwrapping the optional value.

    public static var entityName: String 
    { 
        if let _returnName = entity().name{
            return _returnName
        }else
        {
            return nil
        } 
   }
}

Optional Chaining

CoreData:

Class Exercise

When you changed your dbModel and execute "Create NSManagedObject Sub.." all functions into the Exercise will overridden.

Use a seperate class like class ExerciseExtension

import UIKit
import CoreData

extension Exercise {
  func setName(name : String)
  {
    self.name = name
  }
  .....
}
like image 1
Nerospeed Avatar answered Oct 19 '22 15:10

Nerospeed