Sending Core Data Entity To FireBase? - ios

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/

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.

CoreData & Codable: Avoiding duplicate

I have NSManagedObject subclass (CarBrand) with just two properties:
public class CarBrand: NSManagedObject, CDCodable {
#NSManaged public var id: Int64
#NSManaged public var name: String
}
Also I'm using Codable protocol to parse it from server response's JSON. In case if response matches (or nearly matches) my model - there isn't problems with duplication of object (I have static method on that class, that returns object from CoreData or Creates new and returns it).
But one of the responses has following structure:
class CarBrandsResponse: Codable {
let offset: Int
let count: Int
let results: [CarBrand]
}
In this case I cannot find solution to call that static method for each CarBrand, because if I write
self.results = try container.decode([CarBrand].self, forKey: .results)
It calls init(from:) in CarBrand for each entry, that means duplication of entity...
I've tried to repeat Decodable behavior, but there I've not found suitable API-method for that. (unkeyedContainer(), nestedContainer(), etc.)
Hope you will help me to solve this problem or suggest another way to avoid duplicating.
Thank you.

Core Data Swift 3 Relationship

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.

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?

Using constructors to init instance vars subclass PFObject

I am working on implementing a subclass of PFObject called Event, in Swift. I followed the subclassing guide in Parse's docs, but I don't understand how and where to write the code that adds data to the ivars. Below is my what I have in my class so far, including the ivars.
#NSManaged var name:String
#NSManaged var time:NSDate
#NSManaged var favorite:Bool
#NSManaged var moderator: String
#NSManaged var speakers: [String]
#NSManaged var slides: PFFile?
#NSManaged var files: [PFFile]?
override class func initialize() {
var onceToken : dispatch_once_t = 0;
dispatch_once(&onceToken) {
self.registerSubclass()
}
}
class func parseClassName() -> String! {
return "Event"
}
Normally, I would implement an init() constructor or something similar. However, I realized that the data would already be contained in the PFObject's dictionary when it is fetched from the server. Where would I put the code to copy across and put this data in the instance vars from the PFObject's dictionary? This is presuming that I would instantiate the object via a query and fetch from the server and not locally using the object() method.
Based on the comment by deadbeef above, I realized that I can just use Swift's getters and setters for computed properties to read and write values from the PFObject's data dictionary.
For example a property that I intend to have as read-only:
var name:String
{
return self["name"] as String
}
And a property I intend to have as read-write
var favorite:Bool
{
get {
return self["favorite"] as Bool
}
set(value) {
self["favorite"] = value
}
}
You don't have to copy manually.
That's the Parse framework job. The objects will be populated with data after a fetch.

Resources