I have defined a class which has a calculated property. When I try to access the property in my code, I get EXC_BAD_ACCESS. I set a breakpoint in the getter of the property and noticed it was never called. I don't know what is causing this. I can access other properties of the object.
Here is the code
import UIKit
import CoreData
#objc(Person)
class Person: NSManagedObject {
struct Keys {
static let Name = "name"
static let ProfilePath = "profile_path"
static let Movies = "movies"
static let ID = "id"
}
#NSManaged var name: String
#NSManaged var id: NSNumber
#NSManaged var imagePath: String?
#NSManaged var movies: [Movie]
override init(entity: NSEntityDescription, insertIntoManagedObjectContext context: NSManagedObjectContext?) {
super.init(entity: entity, insertIntoManagedObjectContext: context)
}
init(dictionary: [String : AnyObject], context: NSManagedObjectContext) {
let entity = NSEntityDescription.entityForName("Person", inManagedObjectContext: context)!
super.init(entity: entity, insertIntoManagedObjectContext: context)
name = dictionary[Keys.Name] as! String
id = dictionary[Keys.ID] as! Int
imagePath = dictionary[Keys.ProfilePath] as? String
}
var image: UIImage? {
get {
return TheMovieDB.Caches.imageCache.imageWithIdentifier(imagePath)
}
set {
TheMovieDB.Caches.imageCache.storeImage(image, withIdentifier: imagePath!)
}
}
}
This is how I try to access the image property and get a
Execution was interrupted, reason:
EXC_BAD_ACCESS (code=1, address=0x20)
When I do actor.image.
actor is an object of Person class and is properly initialized.
I put a breakpoint in the getter for image property and it was never called.
if let localImage = actor.image {
cell.actorImageView.image = localImage
} else if actor.imagePath == nil || actor.imagePath == "" {
cell.actorImageView.image = UIImage(named: "personNoImage")
}
What am I doing wrong?
I just figured out the problem, I had not set the class for the entity in the data model inspector. That solved it
Related
I am creating an entity and saving it in the background to core data. Most of the time this works, however, occasionally on a clean install of my app I get a crash but I can't figure out why.
Update: I can only produce this the very first time it's installed on to my device. Subsequent deletes and reinstalls are fine!?
Here's my code:
class CoreDataStack {
static let managedObject: NSManagedObjectModel = {
guard let modelUrl = Bundle.module.url(
forResource: "Model",
withExtension: "momd"
) else {
return .init()
}
return NSManagedObjectModel(contentsOf: modelUrl) ?? .init()
}()
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(
name: "Model",
managedObjectModel: Self.managedObject
)
container.loadPersistentStores { _, error in
if let error = error as NSError? {
print("error")
}
}
return container
}()
}
class Foo: NSManagedObject {
#nonobjc
class func fetchRequest() -> NSFetchRequest<Foo> {
return NSFetchRequest<Foo>(entityName: "Foo")
}
#NSManaged var id: String
init(
context: NSManagedObjectContext,
id: String
) {
let entity = NSEntityDescription.entity(
forEntityName: "Foo",
in: context
)! // <- Crash happens here!
super.init(entity: entity, insertInto: context)
self.id = id
}
}
class MyObject {
func save() {
let container = coreDataStack.persistentContainer
container.performBackgroundTask { [weak self] context in
let foo = Foo(
context: context,
id: "someId",
)
self?.coreDataStack.saveContext(context)
}
}
}
The crash happens on this line when creating the entity Foo:
let entity = NSEntityDescription.entity(
forEntityName: "Foo",
in: context
)!
It doesn't like the force unwrapping there. The only way that I can see that happening is if context is nil. Because Apple state:
Raises internalInconsistencyException if context is nil.
I get the context automatically from performBackgroundTask. Why would that ever be nil?
I have these classes:
import Foundation
import CoreData
public class Friend: NSManagedObject {
}
and
import Foundation
import CoreData
extension Friend {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Friend> {
return NSFetchRequest<Friend>(entityName: "Friend")
}
#NSManaged public var profileImageName: String?
#NSManaged public var name: String?
#NSManaged public var messages: NSSet?
}
// MARK: Generated accessors for messages
extension Friend {
#objc(addMessagesObject:)
#NSManaged public func addToMessages(_ value: Message)
#objc(removeMessagesObject:)
#NSManaged public func removeFromMessages(_ value: Message)
#objc(addMessages:)
#NSManaged public func addToMessages(_ values: NSSet)
#objc(removeMessages:)
#NSManaged public func removeFromMessages(_ values: NSSet)
}
and
import UIKit
import CoreData
extension FriendsController {
func setupData(){
let context = AppDelegate().context
let mark = Friend(context: context)
mark.name = "mark"
mark.profileImageName = "zuck"
let message1 = Message(context: context)
message1.text = "Hello, my name is mark, nice to meet you"
message1.date = Date()
message1.friend = mark
let steve = Friend(context: context)
steve.name = "steve"
steve.profileImageName = "steve"
let message2 = Message(context: context)
message2.text = "Hello, my name is steve"
message2.date = Date()
message2.friend = steve
messages = [message1, message2]
}
}
and later, I'm trying to access 'message?.friend?.name', but it's always nil...
class MessageCell: BaseCell {
var message: Message? {
didSet {
nameLabel.text = message?.friend?.name
if let profileImageName = message?.friend?.profileImageName {
profileImageView.image = UIImage(named: profileImageName)
}
messageLabel.text = message?.text
if let date = message?.date {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "h:m a"
timeLabel.text = dateFormatter.string(from: date)
}
}
}
}
Should I do something else with the context to load these managed objects?
Doesn't make much sense, since I've just create them, just passing as reference to a different class, they should be available.
I think the problem is here:
let context = AppDelegate().context
This creates a new instance of the AppDelegate, it doesn't reference the existing instance. Since that new instance is created in the setupData method, it is deallocated when that method completes. And because you haven't saved, the data isn't persisted before the method completes. So the messages array contains NSManagedObjects which no longer have a reference to a valid context, and their values are therefore nil.
You should probably access the existing AppDelegate instance using:
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.context
I have a model which looks like this and contains NSManagedObject properties, namely the blendsWith property which is a type of [Tag]:
extension Oil {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Oil> {
return NSFetchRequest<Oil>(entityName: "Oil")
}
#NSManaged public var blendsWith: [Tag]?
#NSManaged public var color: String?
#NSManaged public var commentsCount: Int64
#NSManaged public var id: Int64
#NSManaged public var imageURL: String?
#NSManaged public var latinName: String?
#NSManaged public var name: String?
#NSManaged public var properties: NSObject?
#NSManaged public var research: String?
#NSManaged public var resourceType: String?
#NSManaged public var viewsCount: Int64
}
public class Oil: NSManagedObject, Codable {
enum CodingKeys: String, CodingKey {
case resourceType = "resource_type"
case id, name
case imageURL = "image_url"
case color
case latinName = "latin_name"
case emotions
case safetyInformation = "safety_information"
case fact, research
case viewsCount = "views_count"
case commentsCount = "comments_count"
case blendsWith = "blends_with"
case foundInBlends = "found_in_blends"
case properties
case sourcingMethods = "sourcing_methods"
case usages
}
required convenience public init(from decoder: Decoder) throws {
let context = CoreDataHelper.sharedInstance.persistentContainer.viewContext
guard let entity = NSEntityDescription.entity(forEntityName: "Oil", in: context) else { fatalError() }
self.init(entity: entity, insertInto: context)
let container = try decoder.container(keyedBy: CodingKeys.self)
self.resourceType = try! container.decodeIfPresent(String.self, forKey: .resourceType)!
self.id = try! container.decodeIfPresent(Int64.self, forKey: .id)!
self.name = try! container.decodeIfPresent(String.self, forKey: .name)!
self.imageURL = try! container.decodeIfPresent(String.self, forKey: .imageURL)!
self.color = try! container.decodeIfPresent(String.self, forKey: .color)!
self.viewsCount = try! container.decodeIfPresent(Int64.self, forKey: .viewsCount)!
self.viewsCount = try! container.decodeIfPresent(Int64.self, forKey: .viewsCount)!
self.commentsCount = try! container.decodeIfPresent(Int64.self, forKey: .commentsCount)!
self.latinName = try! container.decodeIfPresent(String.self, forKey: .latinName)!
if let blendsWith = try container.decodeIfPresent([Tag].self, forKey: CodingKeys.blendsWith) {
self.blendsWith = blendsWith
}
}
public func encode(to encoder: Encoder) throws {
}
}
Tag looks like this:
extension Tag {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Tag> {
return NSFetchRequest<Tag>(entityName: "Tag")
}
#NSManaged public var id: Int64
#NSManaged public var name: String?
#NSManaged public var resourceType: String?
#NSManaged public var tagType: String?
#NSManaged public var viewsCount: Int64
}
public class Tag: NSManagedObject, Codable {
enum CodingKeys: String, CodingKey {
case resourceType = "resource_type"
case id, name
case viewsCount = "views_count"
case tagType = "tag_type"
}
required convenience public init(from decoder: Decoder) throws {
let context = CoreDataHelper.sharedInstance.persistentContainer.viewContext
guard let entity = NSEntityDescription.entity(forEntityName: "Tag", in: context) else { fatalError() }
self.init(entity: entity, insertInto: context)
let container = try decoder.container(keyedBy: CodingKeys.self)
self.resourceType = try! container.decodeIfPresent(String.self, forKey: .resourceType)!
self.id = try! container.decodeIfPresent(Int64.self, forKey: .id)!
self.name = try! container.decodeIfPresent(String.self, forKey: .name)!
if let viewsCount = try container.decodeIfPresent(Int64.self, forKey: .viewsCount) {
self.viewsCount = viewsCount
} else {
self.viewsCount = 0
}
if let tagType = try container.decodeIfPresent(String.self, forKey: .tagType) {
self.tagType = tagType
} else {
self.tagType = "lol"
}
}
public func encode(to encoder: Encoder) throws {
}
}
When I go to fetch the Oil data stored locally, I get this crash:
2018-08-29 20:31:30.602764+0100 EL[27994:14799374] -[EL.Tag initWithCoder:]: unrecognized selector sent to instance 0x60c000679980
2018-08-29 20:31:30.603905+0100 EL[27994:14799374] [error] error: exception handling request: <NSSQLFetchRequestContext: 0x608000181ee0> , -[EL.Tag initWithCoder:]: unrecognized selector sent to instance 0x60c000679980 with userInfo of (null)
CoreData: error: exception handling request: <NSSQLFetchRequestContext: 0x608000181ee0> , -[EL.Tag initWithCoder:]: unrecognized selector sent to instance 0x60c000679980 with userInfo of (null)
2018-08-29 20:31:30.612185+0100 EL[27994:14799374] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[EL initWithCoder:]: unrecognized selector sent to instance 0x60c000679980'
What could be causing this crash?
For reference, my fetching method looks like this:
func getItems<T : NSManagedObject>(predicate : NSPredicate? = nil) -> [T]{
do {
let reqest = T.fetchRequest()
reqest.predicate = predicate
if let items = try persistentContainer.viewContext.fetch(reqest) as? [T] {
return items
} else {
return [T]()
}
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
return [T]()
}
}
And works like this:
let arrat : [Oil] = CoreDataHelper.sharedInstance.getItems()
If you debug this code does it ever step into the Tag init function? It appears the compiler is not seeing your Tag.init function which is most likely due to your Data Model for the Oil object not correctly setting the class type of the blendsWith property.
Check your data model for Oil and make sure that blendsWith is set to the correct type of Tag.
EDIT:
For Core Data to pick up your class setting you might need to add the objc flag before your class definition:
#objc(Tag)
public class Tag ...
A Transformable is usually coded by CoreData to Data (stored just as that), and coding an NSManagedObject may do things you do not expect. Decoding it could make things „more“ unexpected. (Coding done by NSKeyedArchiver and NSKeyedUnarchiver automatically on assignment/use.)
If you want to use Transformable then making it an NSManagedObject is pointless due to the argument above. (I at least have no experience with it, and have not heard what use it would have.)
So its usually Transformable OR CoreData-relationships(with NSManagedObjects) to model one relationship in CoreData, but not both to model a single one.
I want to store data as data model in iOS.
I am trying this but it is not working.
My data model class
class FileSaveModel: NSManagedObject {
#NSManaged var id: String
#NSManaged var name: String
#NSManaged var path: String
#NSManaged var time: String
#NSManaged var type: String
#NSManaged var uid: String
// TODO: - Need to get the use of this
// lazy var uidd: NSManagedObjectID = NSManagedObjectID()
override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?) {
super.init(entity: entity, insertInto: context)
}
init(name:String, path: String, contentType: ContentType, entity: NSEntityDescription, insertInto context: NSManagedObjectContext?) {
super.init(entity: entity, insertInto: context)
self.path = path
self.name = name
self.type = contentType.rawValue
self.time = "\(NSDate())"
self.uid = "56"
self.id = "789"
}
}
Saving like this
func save(name: String, path: String, type: ContentType, modelContent: ModelTest) {
guard let entity = self.entityWith(name: entityName) else {
return
}
When i try to save with this it saves
//let modelFile = FileSaveModel(name: name, path: path, contentType: //type, entity: entity, insertInto: appDelegate.context())
But this crahses with error "cannot cast NSManagedObject to FileSaveModel"
let model = NSEntityDescription.insertNewObject(forEntityName: entityName, into: appDelegate.context()) as! FileSaveModel
model.name = name
model.path = path
model.type = type.rawValue
save(model: model, modelContent: modelContent)
}
func save(model: FileSaveModel, modelContent: ModelTest) {
do {
try appDelegate.context().save()
print("Save successful")
} catch {
fatalError("Failure to save context: \(error)")
}
}
Getting data like this but not returning the FileSaveModel array only returning the NSManagedObject array
func fetchAllModels() {
let fetchItems = NSFetchRequest<NSFetchRequestResult>(entityName: entityName)
do {
let fetchedItems = try appDelegate.context().fetch(fetchItems) as! [FileSaveModel]
if fetchedItems.count>0 {
print(fetchedItems[0].name)
}
} catch {
fatalError("Failed to fetch: \(error)")
}
}
If I understand your question.
Select xcdatamodeld -> Your Entity -> Data Module Inspector -> Module
If it is current product module change it to Global namespace
I am working on iPhone application where I need to implement one to many relationship from one entity to another entity.I have export NSManagedObject subclass from coredata database but could not find relation accessors in core data model file like Objective-C.
Although I'm able to set data in relationship NSSet but this only persist while iPhone app is running. Once I kill and restart the application I didn't get entity relationship for in fetch request from core data.
I am not sure what I am doing wrong. It will be great if anyone can tell me how to set data in NSSet relationship object. Any sample example will be a great help
Here is my core data model files. One folder can content multiple content as folder detail
extension FolderContent {
#NSManaged var contentID: NSNumber?
#NSManaged var contentTitle: String?
#NSManaged var contentType: String?
#NSManaged var publishDate: String?
#NSManaged var folderList: NSSet?
}
extension FolderList {
#NSManaged var folderID: NSNumber?
#NSManaged var folderName: String?
#NSManaged var folderDetail: NSSet?
}
func updateFolderList()
{
// Initialize Fetch Request
let fetchRequest = NSFetchRequest()
// Create Entity Description
let entityDescription = NSEntityDescription.entityForName(FOLDER_LIST, inManagedObjectContext: self.managedObjectContext)
// Configure Fetch Request
fetchRequest.entity = entityDescription
do {
let result = try self.managedObjectContext.executeFetchRequest(fetchRequest).last as! FolderList
let content = result.mutableSetValueForKey("folderDetail")
content.addObject(self.getContent())
var folderContent:FolderContent = result.folderDetail?.allObjects.first as! FolderContent
print(folderContent.contentTitle)
self.save()
print(result)
} catch {
let fetchError = error as NSError
print(fetchError)
}
}
func getContent()->FolderContent
{
let folderContent = NSEntityDescription.insertNewObjectForEntityForName(FOLDER_CONTENT, inManagedObjectContext: self.managedObjectContext) as! FolderContent
folderContent.contentID = 1
folderContent.contentTitle = "Sandeep"
folderContent.contentType = "Product"
return folderContent
}
If the relationship of FolderContent and FolderList is defined as
A FolderContent have many FolderList(s)
A FolderList only belongs to a FolderContent
FolderContent
extension FolderContent {
#NSManaged var contentID: NSNumber?
#NSManaged var contentTitle: String?
#NSManaged var contentType: String?
#NSManaged var publishDate: String?
#NSManaged var folderList: Set<FolderList>?
}
FolderList
extension FolderList {
#NSManaged var folderID: NSNumber?
#NSManaged var folderName: String?
#NSManaged var folderDetail: FolderContent?
}
Let say you want to persist the record and its relationship
func persistRecords() {
// Insert the new records
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let folderContentEntity = NSEntityDescription.entityForName("FolderContent", inManagedObjectContext: managedContext)
let folderListEntity = NSEntityDescription.entityForName("FolderList", inManagedObjectContext: managedContext)
//Create FolderContent record
let folderContentObject = FolderContent(entity: folderContentEntity!, insertIntoManagedObjectContext: managedContext)
folderContentObject.setValue(CONTENTID, forKeyPath: "contentID")
...
//Create FolderList record
let folderListObject = FolderList(entity: folderListEntity!, insertIntoManagedObjectContext: managedContext)
folderListObject.setValue(FOLDERID, forKeyPath: "folderID")
...
//Set relationship here
folderListObject.folderDetail = folderContentObject
do {
try managedContext.save()
} catch let error as NSError {
print("Could not save \(error), \(error.userInfo)")
}
}
I am guessing you did something like folder.folderDetail.addObject. With core data that won't work because core data behind the scenes is doing lots of things to maintain graph integrity. You have to use mutableSetValueForKey to get the set and have core data work its magic.