iCloudkit refused to read asset records, crashes

Running Xcode 7.1.1 under El Capitan on an iPad with iOS 9.2.


Running this code against a very simple database ... with a database containing a record type defined as a string, a reference + an asset field.


-(void)slideLoader:(CKRecordID*)slideReference
{

privateDB = [[CKContainer defaultContainer] privateCloudDatabase];

NSPredicate* predicate = [NSPredicate predicateWithFormat:@"presentationReference == %@", slideReference];

CKQuery *query = [[CKQuery alloc] initWithRecordType:@"Slides" predicate:predicate];

[privateDB performQuery:query inZoneWithID:nil completionHandler:^(NSArray *results, NSError *error){

if(error) { NSLog(@"Error: %@", error.localizedDescription); }

if(results) { for(CKRecord *record in results) {

NSString *blah = [NSString stringWithFormat:@"%@",record[@"Order"]];

CKRecordID *blahR = record.recordID;

[pickerNames addObject:blah];

[slidesReferenceID addObject:blahR]; } } }];

}

It crashes with this error message [below] ... and wait it doesn't even get to the for loop; it crashed before that line. Now if I go into the iCloud dashboard and delete the asset field, it works perfectly... so what am I missing here? Its ok too if I leave it in, as long as I don't add any assets... if I do I see this.


<NSXPCConnection: 0x1557293e0> connection to service named com.apple.cloudd: Warning: Exception caught during decoding of received message, dropping incoming message. Exception: Exception while decoding argument 0 (#2 of invocation): <NSInvocation: 0x1555672e0> return value: {v} void target: {@} 0x0 selector: {:} handleOperationCompletion:forOperationWithID: argument 2: {@} 0x0 argument 3: {@} 0x0 Exception: value for key 'NS.keys' was of unexpected class 'CKRecordID'. Allowed classes are '{( NSURL, NSString, NSDate, NSData, NSNumber, NSDictionary, NSError, NSArray )}'.


Tried a slightly more simplistic version with Swift 2.0; same error. Here is that code, included as a mixed objective C/Swift 2.0 build. The Swift file is called "iCloudMethods.swift".


import Foundation

import CloudKit

@objc class iCloudMethods: NSObject {


func slideLoader(slideReference: CKRecordID) {

let container = CKContainer.defaultContainer()

let privateDB = container.privateCloudDatabase

let predicate = NSPredicate(format: "presentationReference == %@", slideReference)

let query = CKQuery(recordType: "Slides", predicate: predicate)

privateDB.performQuery(query, inZoneWithID: nil) { (results, error) -> Void in if error != nil { print(error) } if((results) != nil) { for result in results! { print(result["Order"]) } } } } }


Called with the statements...


#import "projectName-Swift.h" iCloudMethods *iCloudMethod = [[iCloudMethods alloc] init]; [iCloudMethod slideLoader:presentationReferenceID[row]];


Sadly still crashes, same error message when I add assets to the dashboard, works perfectly if I leave them empty? Even tried running a query as an operation, not using a convenience routine.


Even tried to do this with an basic query operation, still fails.

func slideLoaderV2(slideReference: CKRecordID) {

let container = CKContainer.defaultContainer()

let privateDB = container.privateCloudDatabase

let predicate = NSPredicate(format: "presentationReference == %@", slideReference)

let query = CKQuery(recordType: "Slides", predicate: predicate)

let operation = CKQueryOperation(query: query) operation.recordFetchedBlock = { (record) in print(record["Order"]) } operation.queryCompletionBlock = { [unowned self] (cursor, error) in dispatch_async(dispatch_get_main_queue()) { if error == nil { print("ok") } else { print(error!.localizedDescription) } } } _ = privateDB.addOperation(operation) }


Sadly it crashes too, with the same error message? Could be that my iCloud instance is corrupted perhaps? I find no answers to this?? and nobody else who seems to be having the same problem?

Replies

I can't comment on the particular error but your code should not continue to handle the returned record if error != nil. You should code: If(error){ //handle error }else{ // only now can you trust results } The array "results' can be a non-nil empty array.

And if it is crashing because of the asset field perhaps the asset field is being returned in the results array and you are trying to process it as if it were a CKRecord. Try adding a test for the class of 'record' to be sure it is a CKRecord.

Post not yet marked as solved Up vote reply of PBK Down vote reply of PBK

PBK,


I think your on the right track here; I tried the swift version, with desired keys option [see here] and even with assets it works...


func slideLoaderV2(slideReference: CKRecordID) {

print(slideReference.recordName)

let container = CKContainer.defaultContainer()

let privateDB = container.privateCloudDatabase

/

let predicate = NSPredicate(format: "presentationReference == %@", slideReference)

let query = CKQuery(recordType: "Slides", predicate: predicate)

let operation = CKQueryOperation(query: query)

operation.desiredKeys = ["Order"]

operation.recordFetchedBlock = { (record) in

print(record["Order"])

}

operation.queryCompletionBlock = { [unowned self] (cursor, error) in

dispatch_async(dispatch_get_main_queue()) {

if error == nil {

print("ok")

} else {

print(error!.localizedDescription)

}

}

}

_ = privateDB.addOperation(operation)

}


But this means what; I need to fetch the keys first and then download the records a second fetch call?

You need to avoid doing this: [(CKAsset *)record objectForKey:@"Order"]; You can avoid it by selecting which keys contribute to "results" or you can avoid it by checking the class of each "record" in "results" before assuming they are CKRecord and not CKAssets.

And the code would be:

if(error){
    // handle error
}else{
   for(id record in results){
       if([record isKindOfClass:[CKRecord class]] ){
            NSString *blah = [NSString stringWithFormat:@"%@",record[@"Order"]];
            CKRecordID *blahR = record.recordID;
            [pickerNames addObject:blah];
            [slidesReferenceID addObject:blahR]; } } }];
       }
    }
}
Post not yet marked as solved Up vote reply of PBK Down vote reply of PBK

Bon,

Fixed this of sorts. It appears to be a BUG in iCloud DASHBOARD; which isn't evidently uploading Assets correctly. Uploaded an asset with the API via some code and all my iCloudKit issues disappeared.

You may have fixed how you loaded your asset but your original code is still wrong.

Thanks, Here is the reference for the bug filed with Apple http://stackoverflow.com/questions/24470956/how-do-i-use-assets-in-cloudkit

Open to ideas about best coding practice, will double check my code again, you surely put me on the right path PBK.