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...
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:
Any ideas what could be going wrong? Thanks
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.
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!`
}
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
}
.....
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With