GGirotto GGirotto - 1 year ago 77
Objective-C Question

How to speed up Cloudkit 'fetchRecordWithID'

The first operation that my app does when It's open for the first time Is to check at cloudkit if there is any information connected to the user iCloud account, because if there is, the information is downloaded to the app. My problem is that the method that download the information takes about 13 seconds to collect the information when it's connected over wifi and takes OVER 45 SECONDS when using mobile data. I was looking for optimization and discovered that when simulating using the

development
database the results are faster that simulating with the
produciton
database, so I probably am collection the data in the wrong way, since there is a lot of more data at the production thant the development.

The method the search to the info looks like this

CKRecordID *recordID = [[CKRecordID alloc] initWithRecordName: userKey];

[_publicDatabase fetchRecordWithID:recordID completionHandler:^(CKRecord * _Nullable record, NSError * _Nullable error) {
if (error) {
NSLog(@"Error in fethcing from user db %@", error);
completion(error);
} else {

NSLog(@"User found in db");
NSString *name = record[@"Name"];
CKAsset *file = record[@"Image"];
NSString *url = file.fileURL.absoluteString;
NSMutableArray<NSString*> *codes = record[@"Groups"];

NSArray * paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString * basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
NSData * binaryImageData = [NSData dataWithContentsOfURL:[file fileURL]];
[binaryImageData writeToFile:[basePath stringByAppendingPathComponent:@"profilePicture.png"] atomically:YES];

completion(nil);
}

}];


The information at the cloud are basically the name, profile picture and the groups codes.

There is a way to speed up this fetch from cloudkit? Because i dont thing that is normal to take over 45 seconds the fetch this data over mobile data, this doesnt make sense.

Answer Source

You should use CKFetchRecordsOperation class instead of the fetch function provided by CKDatabase.

The reason is that CKFetchRecordsOperation inherits from Operation class, which allow a more complex and extended performance configuration.

Here's your code modify to use the class mention above.

let fetchOperation: CKFetchRecordsOperation = CKFetchRecordsOperation(recordIDs: [ userKey ])
// Fetch only desired keys
fetchOperation.desiredKeys = [ "Name", "Image", "Groups" ]
// Performance tips
operation.qualityOfService = .userInitiated

fetchOperation.perRecordCompletionBlock = { (record: CKRecord?, recordID: CKRecordID?, error: Error) -> (Void) in
    guard let record = record else
    {
        print("There's no record in this fetch operations")

        if let error = error
        {
            print("err @ \(#function) -> \(error.localizedDescription)")
        }

        return
    }

    Dispatch.async.main 
    {
        // Save your image here...
    }
}

_publicDatabase.add(fetchOperation)

Here are some tips...

  1. Fetch only those fields that you need. Use the dediredKeys property
  2. Set the operation's priority. Think if it's a background operation, or if user need the response right now. More info at qualityOfService property
  3. When you save your image do it in the main queue, as well as the user response.

PS: I notice right now that your code is Objective-C and my example is Swift, sorry.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download