Search code examples
ioscore-datacloudkitckasset

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)")
        }

    }

Solution

  • 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.