I'm having an issue using relationships in Core Data. I created my data model including the following entities: User, Conversation, Message, Participant - each containing a one-to-many relationship with the entity following it. I generated the classes for each entity using Editor -> Create NSManagedObject Subclass, and it correctly created the .Swift files for each class. The project builds, but when attempting to create and save a new user I get the following error:
2014-12-01 12:31:28.450 Messenger[2627:151403] CoreData: warning: Unable to load class named 'Messenger.User' for entity 'User'. Class not found, using default NSManagedObject instead.
I made sure that my entity classes were prefixed with the project/module name (Messenger.User).
I also added "#ObjC(User)" directly above the User class, and added "-ObjC" to "Other Linker Flags" for the project, as suggested by people in various other posts. These are all the fixes that I could find, but I still get the same error. Here's what my User class looks like, for reference:
import Foundation
import CoreData
#objc(User)
class User: NSManagedObject {
#NSManaged var api : API
#NSManaged var username: String
#NSManaged var userID: String
#NSManaged var passcode: String
#NSManaged var conversations: NSSet
func findConversations(sourceView: MessengerViewController) {
api.findConversations(sourceView)
}
func addConversation(newConversation: Conversation) {
self.addConversationObject(newConversation)
}
}
extension User {
func addConversationObject(value: Conversation) {
var items = self.mutableSetValueForKey("conversations");
items.addObject(value)
}
func removeConversationObject(value: Conversation) {
var items = self.mutableSetValueForKey("conversations");
items.removeObject(value)
}
}
Does anybody have an idea what else I did wrong? I've tried every fix I could come across, but nothing has seemed to work so far.
EDIT: The warning occurs when trying to create a new User object, at the third line below:
let userContext : NSManagedObjectContext = self.appDel.managedObjectContext!
let userEntity : NSEntityDescription = NSEntityDescription.entityForName("User", inManagedObjectContext: userContext)!
var newUser = User(entity: userEntity, insertIntoManagedObjectContext: userContext)
Referring to my own answer, maybe you should also make sure you cast any fetch result to the appropriate class. E.g.
let result = context.executeFetchRequest(request, error:nil) as [User]
In response to your code update, you should perhaps try to insert new instances as follows.
var user = NSEntityDescription.insertNewObjectForEntityForName( "User",
inManagedObjectContext: context) as User
Related
I create a record in CloudKit using the CloudKit Dashboard. The record also contains an attribute for a photo, which I upload before saving the record.
The photo data is stored in an attribute of the type CKAsset.
In the entity core data date model it is represented as an attribute of type Data.
When I do a NSFetchRequest later on my local sqlLite DB which synchronises with CloudKit the attribute which is supposed to hold the binary data of the image is always nil.
All the other attributes - which are just strings - are filled with valid data. When I change these attributes and do a NSFetchRequest again the changes are reflected in the fetch result.
I have no idea why the photo attribute is always nil and the other string attributes contain the current valid value.
EDIT - sample code provided.
This is the code which fetches it from the local sqlite DB which is backed by CloudKit and where the photo attribute is nil even it is provided in CloudKit:
let bgContext = self.newBackgroundContext()
bgContext.perform {
do {
fetchRequest.propertiesToFetch = ["title", "categoryValue", "date", "photo", "amount"]
let results = try fetchRequest.execute() as! [ReceiptEntity]
for record in results {
let title = record.title
let photo = record.photo
if let photo_local = photo {
log.info("| Photo attribute is present!!")
}
}
}
} catch let error {
let result: Result<[ReceiptEntity], Error> = .failure(error)
cb(result)
}
This is the Entity definition, generated by Xcode:
extension ReceiptEntity {
#nonobjc public class func fetchRequest() -> NSFetchRequest<ReceiptEntity> {
return NSFetchRequest<ReceiptEntity>(entityName: "ReceiptEntity")
}
#NSManaged public var additionalInformation: String?
#NSManaged public var amount: Double
#NSManaged public var categoryValue: String?
#NSManaged public var currencyValue: String?
#NSManaged public var date: Date?
#NSManaged public var placeMark: String?
#NSManaged public var title: String?
#NSManaged public var photo: Data?
}
As already mentioned before: When I fetch a certain record from CloudKit directly using the following code - the photo attribute has a CKAsset instance and the photo is there:
privateDB.fetch(withRecordID: configRecordId) { (record, error) -> Void in
let photo = record?.value(forKey: "CD_photo") as? CKAsset
let title = record?.value(forKey: "CD_title") as? String
if let url = photo?.fileURL {
log.info("| Asset URL: \(url)")
}
}
I finally found the problem.
During the iteration of expanding my data model I added attributes manually in CloudKit using the CK dashboard and added the these attributes to my data model.
By adding a CKAsset attribute to CK named CD_photo. But this is wrong. The photo attribute should have had the type "Bytes" and then another attribute named CD_photo_ckAsset of type CKAsset.
But the easiest way to get it right is to let the NSPersistentCloudKitContainer by creating the schema of the App for you.
So far I can create my own Data Models in a swift file. Something like:
User.swift:
class User {
var name: String
var age: Int
init?(name: String, age: Int) {
self.name = name
self.age = age
}
}
When I create a Core Data model, ie. a UserData entity, (1) do I have to add the same number of attributes as in my own data model, so in this case two - the name and age?
Or (2) can it has just one attribute eg. name (and not age)?
My core data model:
UserData
name
age
The second problem I have is that when I start the fetch request I get a strange error in Xcode. This is how I start the fetchRequest (AppDelegate is set up like it is suggested in the documentation):
var users = [User]()
var managedObjectContext: NSManagedObjectContext!
...
func loadUserData() {
let dataRequest: NSFetchRequest<UserData> = UserData.fetchRequest()
do {
users = try managedObjectContext.fetch(dataRequest)
....
} catch {
// do something here
}
}
The error I get is "Cannot assign values of type '[UserData] to type [User].
What does this error mean? In the official documentation are some of the errors described, but not this particularly one.
If you are designing a user model in core data you don't have to write the class yourself. In fact by default Xcode will generate subclasses of NSManagedObject automatically once you create them in your project's, but you can also manually generate them if you would like to add additional functionality:
Then you can go to Editor and manually generate the classes
Doing this will give you User+CoreDataClass.swift and User+CoreDataProperties.swift. I see in your question you are asking about how the core data model compares to your 'own' model, but if you're using core data then that IS the model. The generated User class, which subclasses NSManagedObject, is all you need.
Then you might fetch the users like this:
let userFetch = NSFetchRequest(entityName: "User")
do {
users = try managedObjectContext.executeFetchRequest(userFetch) as! [User]
} catch {
fatalError("Failed to fetch users: \(error)")
}
You cannot use custom (simple) classes as Core Data model.
Classes used as Core Data model must be a subclass of NSManagedObject.
If your entity is named UserData you have to declare the array
var users = [UserData]()
The User class is useless.
I have looked at other questions and tried their solutions but the error persists.
Also, this is my first iOS project so please explain like I'm five >.<
Ok, so I have a diary entry entity, looking like such:
import Foundation
import CoreData
class DiaryEntry: NSManagedObject {
#NSManaged var title: String
#NSManaged var text: String
#NSManaged var date: NSDate
#NSManaged var extra: String
#NSManaged var backup: NSNumber
}
as well, i have a DAO file like this (abridged)
import Foundation
import CoreData
class coreDataDao : NSManagedObject, DiaryDAO{
func createEntry(title:String,text:String,date:NSDate,backup:NSNumber,extra:String){
let newItem = NSEntityDescription.insertNewObjectForEntityForName("DiaryEntry", inManagedObjectContext: self.managedObjectContext!) as! DiaryEntry
newItem.title=title
newItem.text=text
newItem.date=date
newItem.extra=extra
newItem.backup=backup.integerValue
}
}
and finally, called in my viewcontroller, i have this function:
let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
func jTest(){
var dao = coreDataDao()
dao.createEntry("a title",text:"some text", date: NSDate(), backup: 2, extra: "extras")
}
However when the code runs, I get the following error:
MyApp[9786:438760] CoreData: error: Failed to call designated initializer on NSManagedObject class 'MyAppName.coreDataDao'
fatal error: unexpectedly found nil while unwrapping an Optional value
Ive been trying things all day, including most of the SO solutions regarding this error but I can't get it going. Thanks in advance
The key here is
unexpectedly found nil while unwrapping an Optional value
Maybe your backup or backup.integer or any other assigned value is nil. Please debug to check.
The solution turned out to be dealing with ManagedObjectContext. I was atempting to generate it in my DAO file, when it needed to be passed from the ViewController.
DAO
class func createEntry(title:String,context:NSManagedObjectContext)->DiaryEntry{
if let newItem = NSEntityDescription.insertNewObjectForEntityForName("DiaryEntry", inManagedObjectContext: context) as? DiaryEntry{
and viewcontroller
coreDataDao.createEntry("a title",context: self.managedObjectContext!)
I am in the process of learning Swift and have come across an issue that I can't seem to piece together a solution for.
Currently I have two Entities in my data model: Card and Set
A Set can have many cards, but a Card can only belong to one Set.
My cards relationship is set as To Many:
While my set relationship is set to To One:
For these two Entities, I have the following subclass code:
import Foundation
import CoreData
#objc(Set) class Set: NSManagedObject {
#NSManaged var code: String
#NSManaged var name: String
#NSManaged var cards: NSSet
}
extension Set {
func addCard(value: Card) {
self.mutableSetValueForKey("cards").addObject(value)
}
func getCards() -> [Card] {
var cards: [Card]
cards = self.cards.allObjects as [Card]
return cards
}
}
import Foundation
import CoreData
#objc(Card) class Card: NSManagedObject {
#NSManaged var name: String
#NSManaged var set: Set
}
I have successfully created and verified a Set with code such as this:
var set = NSEntityDescription.insertNewObjectForEntityForName("Set", inManagedObjectContext: context) as Set
set.name = setName;
set.code = setCode;
context.save(nil)
However, later when I attempt to create Card objects and add them to this set I run into an error. Here is the code I am using for that:
// The json data here is already verified as working fine elsewhere in my code, it just serves as the basis for creating the Card objects
var cards: [AnyObject] = json.valueForKey("cards") as NSArray
for var i = 0; i < cards.count; i++ {
var cardData = cards[i] as NSDictionary
var card = NSEntityDescription.insertNewObjectForEntityForName("Card", inManagedObjectContext: context) as Card
card.name = cardData.valueForKey("name") as String
set.addCard(card)
context.save(nil)
}
The error being fired currently reads as follows:
2015-01-19 00:25:42.803 <app name>[4667:113927] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSSet intersectsSet:]: set argument is not an NSSet'
I tried tracking the error as close to the point of failure as possible. It seems to happen in the addCard function in the Set extension. I am guessing I have some very minor error in my code, but since I am pretty new to debugging Swift code I am currently at a loss for where to go next. I actually had the assignment of Cards to a Set working previously, but something I changed must have caused this issue to occur.
You should rename getCards() function in the Set extension, I believe core data use this function name and the issue happened because you override it.
I have a dictionary which I have all data that I want to insert into database as new object. The problem is when I try to cast the newly created object it gives me exception in:
libswift_stdlib_core.dylib`swift_dynamicCast:
part of assembly code.
The code that i am using is like this:
var group:Group
if (array.count == 0) {
group = NSEntityDescription.insertNewObjectForEntityForName("Group", inManagedObjectContext:appDelegate.managedObjectContext) as Group
}
and the structure of Group class is like this:
#objc(Group)
class Group:NSManagedObject{
#NSManaged var createDate:NSString
#NSManaged var groupPictureUrl:NSString
#NSManaged var groupTypeId:NSString
#NSManaged var isDepartment:NSString
#NSManaged var lastMessageRead:NSString
#NSManaged var name:NSString
#NSManaged var unreadMessageCount:NSString
#NSManaged var groupId:NSString
#NSManaged var lastSync:NSString
}
I have also a subclass of NSManagedObject named AppData which has the same structure with Group class. And at inserting part of my code if I replace Group with AppData it works and I can insert into AppData table. As I said before they have the same structure except parameters. But when I try to insert Group object it gives me dynamic cast exception. How can I solve this problem?
I solved the problem by initializing the entity object differently, if you implement the initiation method from super class(NSManagedObject) like: (in Group.swift)
init(entity: NSEntityDescription!, insertIntoManagedObjectContext context: NSManagedObjectContext!) {
super.init(entity: entity, insertIntoManagedObjectContext: context)
}
you can create the object like this:
var desc:NSEntityDescription = NSEntityDescription.entityForName("Group",inManagedObjectContext:appDelegate.managedObjectContext)
var group:Group = Group(entity:desc, insertIntoManagedObjectContext: appDelegate.managedObjectContext)
most of solutions i have read was making the initiation like this:
var group:Group = NSEntityDescription.insertNewObjectForEntityForName("Group", inManagedObjectContext:appDelegate.managedObjectContext) as Group
so this was the problem and object could not be casted to custom managed object class with this way. Thanks to everyone.