Search code examples
iosswift3cloudkitckasset

Saving an Asset List (array) to specific CKRecord


I've a CKRecord type created in the CloudKit backend with some properties related to that class.

I've String properties, Bytes and I have a Asset List property, so store some images (multiple images related to a single record).

Asset List

Now I'm trying so store some images and then fill the property and then trying to save it to CloudKit, but it's not working.

Code goes as it follows:

var images_array = [CKAsset]()

// append the an image to the array 
images_array.append(CKAsset(fileURL: writeImage(image: selectedImage) as URL))


let record = CKRecord(recordType: recordName)
record["class_title"] = someString as CKRecordValue
record["class_body"]  = someString as CKRecordValue
record["images_array"] = images_array as CKRecordValue
saveRecord(record)

func saveRecord(_ xrecord: CKRecord) {
    let publicData = CKContainer.default().publicCloudDatabase
    let record: [CKRecord] = [xrecord] 
    let saveOperation = CKModifyRecordsOperation.init(recordsToSave: record, recordIDsToDelete: nil)

    saveOperation.perRecordCompletionBlock = {(record, error) -> Void in
        if (error != nil) {
            print("error")
        }
    }
    publicData.add(saveOperation)
}


func writeImage(image: UIImage) -> URL {
    let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
    let fileURL = NSURL(fileURLWithPath: documentsURL.absoluteString).appendingPathComponent(".jpg")

    if let imageData = image.lowestQualityJPEGNSData {
        do {
            try imageData.write(to: fileURL!)
        } catch {
            print("ERRO 001 = \(error.localizedDescription)")
        }
    }
    return fileURL!
}

extension UIImage {
    var uncompressedPNGData: Data?      { return UIImagePNGRepresentation(self)        }
    var highestQualityJPEGNSData: Data? { return UIImageJPEGRepresentation(self, 1.0)  }
    var highQualityJPEGNSData: Data?    { return UIImageJPEGRepresentation(self, 0.75) }
    var mediumQualityJPEGNSData: Data?  { return UIImageJPEGRepresentation(self, 0.5)  }
    var lowQualityJPEGNSData: Data?     { return UIImageJPEGRepresentation(self, 0.25) }
    var lowestQualityJPEGNSData:Data?   { return UIImageJPEGRepresentation(self, 0.0)  }
}

If I only save the strings, everything works perfectly but with images it doesn't save the record.

I know there might be any issue with the appending, or I have to save the array in other way, or I shouldn't save it as CKRecordValue.

Do you have any tip on how to achieve this?

Thanks


Solution

  • When you create your local asset file you should do so with the atomic write option. This will ensure that the file is completely written before CloudKit attempts to upload the asset.

    This is the asset file creation function I use in the Seam 3 library:

    fileprivate func createAsset(data: Data) -> CKAsset? {
    
        var returnAsset: CKAsset? = nil
    
        let tempStr = ProcessInfo.processInfo.globallyUniqueString
        let filename = "\(tempStr)_file.bin"        
        let baseURL = URL(fileURLWithPath: NSTemporaryDirectory())       
        let fileURL = baseURL.appendingPathComponent(filename, isDirectory: false)
    
        do {
            try data.write(to: fileURL, options: [.atomicWrite])         
            returnAsset = CKAsset(fileURL: fileURL)      
        } catch {
            print("Error creating asset: \(error)")
        }
    
        return returnAsset
    
    }