Core Data Swift 3 Relationship - ios

I have two entities, Device and MDM. Device has two attributes, asset_tag and location. MDM has two attributes, asset_tag and os. I am trying to fetch asset_tag, os, and location for each asset_tag device. I had Xcode create my subclasses:
extension Device {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Device> {
return NSFetchRequest<Device>(entityName: "Device")
}
#NSManaged public var asset_tag: String?
#NSManaged public var location: String?
#NSManaged public var devices: MDM?
}
extension MDM {
#nonobjc public class func fetchRequest() -> NSFetchRequest<MDM> {
return NSFetchRequest<MDM>(entityName: "MDM")
}
#NSManaged public var asset_tag: String?
#NSManaged public var os: String?
#NSManaged public var mdms: Device?
}
My fetch request is as follows:
var request = NSFetchRequest<NSFetchRequestResult>()
request = Device.fetchRequest()
request.returnsObjectsAsFaults = false
let results = try context.fetch(request) as! [Device]
Not sure how to get something like device.mdms.os to work to get the OS of a specific device.

It looks like you have the names of your relationships backwards. Right now, Device has a relationship called devices of type MDM, and MDM has a relationship called mdms of type Device. That means you'd get the value of os for a Device with device.devices.os, which is probably not how you want to do it.
To fix it you probably want to reverse the names of those relationships-- in Device, change the name from devices to mdms, and in MDM, change the name from mdms to devices. In general the name of a relationship should describe the thing it relates to, not the thing that has the relationship.

Related

Created record in CloudKit with CKAsset but the asset is always nil when fetching, other attributes are ok

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.

Sending Core Data Entity To FireBase?

I have an object in core data and wanted to send the order to firebase.
The model in core data looks like this:
extension CustomerOrder {
#nonobjc public class func fetchRequest() -> NSFetchRequest<CustomerOrder> {
return NSFetchRequest<CustomerOrder>(entityName: "CustomerOrder")
}
#NSManaged public var reference: String?
#NSManaged public var orderTotal: Double
#NSManaged public var orderID: NSObject?
#NSManaged public var orderDrinks: NSSet?
}
}
From what ive seen firebase likes to follow its own format for transferring models, so I was wondering how I would achieve transmitting this model to my firebase db?
Thanks for any assistance on the approach to take!
Fetch data from CoreData.
https://www.appcoda.com/introduction-to-core-data/
For each entity, post it to Firebase.
https://www.appcoda.com/firebase/

CoreData relationship Fault throws unimplemented SQL generation for predicate

I'm using CoreData with MagicalRecord for store management in my app. I made the xcdatamodel and generated the NSManagedObjects subclasses. My problem is that when I try to use one of the generated accessors for a relationship I get an 'unimplemented SQL generation for predicate' error. This is happening only with one of the generated accessors.
To be more specific, here's the code for the classes I'm using. The problem I have is in the Crop -> Row relationship, which is many to many:
extension Crop {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Crop> {
return NSFetchRequest<Crop>(entityName: "Crop");
}
#NSManaged public var phLevels: Int16
#NSManaged public var row: NSSet?
#NSManaged public var states: NSSet?
// MARK: Generated accessors for row:
extension Crop {
#objc(addRowObject:)
#NSManaged public func addToRow(_ value: Row)
#objc(removeRowObject:)
#NSManaged public func removeFromRow(_ value: Row)
#objc(addRow:)
#NSManaged public func addToRow(_ values: NSSet)
#objc(removeRow:)
#NSManaged public func removeFromRow(_ values: NSSet)
Here's the Row:
extension Row {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Row> {
return NSFetchRequest<Row>(entityName: "Row");
}
#NSManaged public var name: String?
#NSManaged public var crops: NSSet?
#NSManaged public var paddock: Paddock?
}
// MARK: Generated accessors for crops
extension Row {
#objc(addCropsObject:)
#NSManaged public func addToCrops(_ value: Crop)
#objc(removeCropsObject:)
#NSManaged public func removeFromCrops(_ value: Crop)
#objc(addCrops:)
#NSManaged public func addToCrops(_ values: NSSet)
#objc(removeCrops:)
#NSManaged public func removeFromCrops(_ values: NSSet)
I'm trying to add a Row to the Crop:
plantingCrop?.addToRow(row)
And there's when I get the
unimplemented SQL generation for predicate
Or if i try the other way around the same result:
row.addToCrops(plantingCrop!)
I'm a bit lost here, plantingCrop is generated ok, i use other accessors before I set the row in the crop and they are working ok (they are one-to-many relationships), I tried to regenerate the subclasses and the same result. In the debugger I can see at that point that the relationship is faulting when I try to use it. There's probably something wrong that I'm missing but i cannot find it!
UPDATE
Tried with the code below and the same result.
if let rowsFromCrop = plantingCrop?.mutableSetValue(forKey: "row") {
rowsFromCrop.add(row)
}
EDIT
From Apple Docs, the tick on the transient property means that the attribute is a property that you define as part of the model, but which is not saved to the persistent store as part of an entity instance’s data. Core Data does track changes you make to transient properties, so they are recorded for undo operations. Then says:
"If you undo a change to a transient property that uses nonmodeled information, Core Data does not invoke your set accessor with the old value — it simply updates the snapshot information."
I don't quite completely understand this last line, but it might be that because it was a transient property CD wasn't invoking the right accessor?

Issue setting up one to many relationship in Swift Core Data

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.

CoreData: Unable to load class for entity

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

Resources