Running a CKFetchRecordsOperation while in UIApplicationStateBackground










1















My app is setup to receive CloudKit remote notifications. Sometimes it receives these notifications while it is running in the background. I have not done anything with these notifications yet - I have simply passed UIBackgroundFetchResultNewData to the completion handler. The reason is that I didn't think I could simply download from CloudKit while in the background - I thought that could only be done via Background Fetch or setting up a special job to run in the background. But I am revisiting this now, and I see that running the CKFetchRecordsOperation and downloading the related CKAsset file while in UIApplicationStateBackground actually does seem to work.



So my current code is this:



- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler 

__ENTERING_METHOD__
CKNotification *ckNotification = [CKNotification notificationFromRemoteNotificationDictionary:userInfo];
if (ckNotification)
if ([ckNotification.subscriptionID isEqualToString:kCKDocumentChangeSubscription])
CKRecordID *recordID = [(CKQueryNotification*)ckNotification recordID];
if (recordID)
if([UIApplication sharedApplication].applicationState == UIApplicationStateBackground)

if (completionHandler)
completionHandler(UIBackgroundFetchResultNewData);


else
CKFetchRecordsOperation *fetchRecordsOperation = [[CKFetchRecordsOperation alloc] initWithRecordIDs:@[recordID]];
fetchRecordsOperation.fetchRecordsCompletionBlock = ^(NSDictionary *recordsByRecordID, NSError *error)

if (error)
dispatch_async(dispatch_get_main_queue(), ^
if (completionHandler)
completionHandler(UIBackgroundFetchResultFailed);

);

else
CKRecord *ckDocumentRecord = recordsByRecordID[recordID];
CKAsset *documentAsset = [ckDocumentRecord objectForKey:ckDocumentAsset];
NSData *data = [NSData dataWithContentsOfURL:documentAsset.fileURL];
[self handleData:data];

UIBackgroundFetchResult result = (data == nil)?UIBackgroundFetchResultFailed:UIBackgroundFetchResultNewData;
if (completion)
completion(result);



;
CKContainer *defaultContainer = [CKContainer defaultContainer];
CKDatabase *publicDatabase = [defaultContainer publicCloudDatabase];
[publicDatabase addOperation:fetchRecordsOperation];


else
if (completionHandler)
completionHandler(UIBackgroundFetchResultFailed);



else
if (completionHandler)
completionHandler(UIBackgroundFetchResultNoData);



else
if (completionHandler)
completionHandler(UIBackgroundFetchResultNoData);





And I want this code to be this:



- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler 

__ENTERING_METHOD__
CKNotification *ckNotification = [CKNotification notificationFromRemoteNotificationDictionary:userInfo];
if (ckNotification)
if ([ckNotification.subscriptionID isEqualToString:kCKDocumentChangeSubscription])
CKRecordID *recordID = [(CKQueryNotification*)ckNotification recordID];
if (recordID)
CKFetchRecordsOperation *fetchRecordsOperation = [[CKFetchRecordsOperation alloc] initWithRecordIDs:@[recordID]];
fetchRecordsOperation.fetchRecordsCompletionBlock = ^(NSDictionary *recordsByRecordID, NSError *error)

if (error)
dispatch_async(dispatch_get_main_queue(), ^
if (completionHandler)
completionHandler(UIBackgroundFetchResultFailed);

);

else
CKRecord *ckDocumentRecord = recordsByRecordID[recordID];
CKAsset *documentAsset = [ckDocumentRecord objectForKey:ckDocumentAsset];
NSData *data = [NSData dataWithContentsOfURL:documentAsset.fileURL];
[self handleData:data];

UIBackgroundFetchResult result = (data == nil)?UIBackgroundFetchResultFailed:UIBackgroundFetchResultNewData;
if (completion)
completion(result);



;
CKContainer *defaultContainer = [CKContainer defaultContainer];
CKDatabase *publicDatabase = [defaultContainer publicCloudDatabase];
[publicDatabase addOperation:fetchRecordsOperation];

else
if (completionHandler)
completionHandler(UIBackgroundFetchResultFailed);



else
if (completionHandler)
completionHandler(UIBackgroundFetchResultNoData);



else
if (completionHandler)
completionHandler(UIBackgroundFetchResultNoData);





Does running a CKFetchRecordsOperation and downloading the related CKAsset file while in the background constitute any sort of a violation of what can (or SHOULD) be done in the background?










share|improve this question
























  • CKAssets are used to store large files (Apple recommends anything more than a few kilobytes in size, but no more than 250MB) in iCloud and associate them with specific CKRecords. When downloading a record with a CKAsset, the asset’s data is temporarily saved to disk, but it is not guaranteed to stay there long, and may be cleaned up when the system needs to free memory. So it is definitely a good idea to get the data from the asset as soon as the record is downloaded. The file can be accessed from the asset’s fileURL property.

    – Kousic
    Nov 22 '18 at 5:49















1















My app is setup to receive CloudKit remote notifications. Sometimes it receives these notifications while it is running in the background. I have not done anything with these notifications yet - I have simply passed UIBackgroundFetchResultNewData to the completion handler. The reason is that I didn't think I could simply download from CloudKit while in the background - I thought that could only be done via Background Fetch or setting up a special job to run in the background. But I am revisiting this now, and I see that running the CKFetchRecordsOperation and downloading the related CKAsset file while in UIApplicationStateBackground actually does seem to work.



So my current code is this:



- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler 

__ENTERING_METHOD__
CKNotification *ckNotification = [CKNotification notificationFromRemoteNotificationDictionary:userInfo];
if (ckNotification)
if ([ckNotification.subscriptionID isEqualToString:kCKDocumentChangeSubscription])
CKRecordID *recordID = [(CKQueryNotification*)ckNotification recordID];
if (recordID)
if([UIApplication sharedApplication].applicationState == UIApplicationStateBackground)

if (completionHandler)
completionHandler(UIBackgroundFetchResultNewData);


else
CKFetchRecordsOperation *fetchRecordsOperation = [[CKFetchRecordsOperation alloc] initWithRecordIDs:@[recordID]];
fetchRecordsOperation.fetchRecordsCompletionBlock = ^(NSDictionary *recordsByRecordID, NSError *error)

if (error)
dispatch_async(dispatch_get_main_queue(), ^
if (completionHandler)
completionHandler(UIBackgroundFetchResultFailed);

);

else
CKRecord *ckDocumentRecord = recordsByRecordID[recordID];
CKAsset *documentAsset = [ckDocumentRecord objectForKey:ckDocumentAsset];
NSData *data = [NSData dataWithContentsOfURL:documentAsset.fileURL];
[self handleData:data];

UIBackgroundFetchResult result = (data == nil)?UIBackgroundFetchResultFailed:UIBackgroundFetchResultNewData;
if (completion)
completion(result);



;
CKContainer *defaultContainer = [CKContainer defaultContainer];
CKDatabase *publicDatabase = [defaultContainer publicCloudDatabase];
[publicDatabase addOperation:fetchRecordsOperation];


else
if (completionHandler)
completionHandler(UIBackgroundFetchResultFailed);



else
if (completionHandler)
completionHandler(UIBackgroundFetchResultNoData);



else
if (completionHandler)
completionHandler(UIBackgroundFetchResultNoData);





And I want this code to be this:



- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler 

__ENTERING_METHOD__
CKNotification *ckNotification = [CKNotification notificationFromRemoteNotificationDictionary:userInfo];
if (ckNotification)
if ([ckNotification.subscriptionID isEqualToString:kCKDocumentChangeSubscription])
CKRecordID *recordID = [(CKQueryNotification*)ckNotification recordID];
if (recordID)
CKFetchRecordsOperation *fetchRecordsOperation = [[CKFetchRecordsOperation alloc] initWithRecordIDs:@[recordID]];
fetchRecordsOperation.fetchRecordsCompletionBlock = ^(NSDictionary *recordsByRecordID, NSError *error)

if (error)
dispatch_async(dispatch_get_main_queue(), ^
if (completionHandler)
completionHandler(UIBackgroundFetchResultFailed);

);

else
CKRecord *ckDocumentRecord = recordsByRecordID[recordID];
CKAsset *documentAsset = [ckDocumentRecord objectForKey:ckDocumentAsset];
NSData *data = [NSData dataWithContentsOfURL:documentAsset.fileURL];
[self handleData:data];

UIBackgroundFetchResult result = (data == nil)?UIBackgroundFetchResultFailed:UIBackgroundFetchResultNewData;
if (completion)
completion(result);



;
CKContainer *defaultContainer = [CKContainer defaultContainer];
CKDatabase *publicDatabase = [defaultContainer publicCloudDatabase];
[publicDatabase addOperation:fetchRecordsOperation];

else
if (completionHandler)
completionHandler(UIBackgroundFetchResultFailed);



else
if (completionHandler)
completionHandler(UIBackgroundFetchResultNoData);



else
if (completionHandler)
completionHandler(UIBackgroundFetchResultNoData);





Does running a CKFetchRecordsOperation and downloading the related CKAsset file while in the background constitute any sort of a violation of what can (or SHOULD) be done in the background?










share|improve this question
























  • CKAssets are used to store large files (Apple recommends anything more than a few kilobytes in size, but no more than 250MB) in iCloud and associate them with specific CKRecords. When downloading a record with a CKAsset, the asset’s data is temporarily saved to disk, but it is not guaranteed to stay there long, and may be cleaned up when the system needs to free memory. So it is definitely a good idea to get the data from the asset as soon as the record is downloaded. The file can be accessed from the asset’s fileURL property.

    – Kousic
    Nov 22 '18 at 5:49













1












1








1


1






My app is setup to receive CloudKit remote notifications. Sometimes it receives these notifications while it is running in the background. I have not done anything with these notifications yet - I have simply passed UIBackgroundFetchResultNewData to the completion handler. The reason is that I didn't think I could simply download from CloudKit while in the background - I thought that could only be done via Background Fetch or setting up a special job to run in the background. But I am revisiting this now, and I see that running the CKFetchRecordsOperation and downloading the related CKAsset file while in UIApplicationStateBackground actually does seem to work.



So my current code is this:



- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler 

__ENTERING_METHOD__
CKNotification *ckNotification = [CKNotification notificationFromRemoteNotificationDictionary:userInfo];
if (ckNotification)
if ([ckNotification.subscriptionID isEqualToString:kCKDocumentChangeSubscription])
CKRecordID *recordID = [(CKQueryNotification*)ckNotification recordID];
if (recordID)
if([UIApplication sharedApplication].applicationState == UIApplicationStateBackground)

if (completionHandler)
completionHandler(UIBackgroundFetchResultNewData);


else
CKFetchRecordsOperation *fetchRecordsOperation = [[CKFetchRecordsOperation alloc] initWithRecordIDs:@[recordID]];
fetchRecordsOperation.fetchRecordsCompletionBlock = ^(NSDictionary *recordsByRecordID, NSError *error)

if (error)
dispatch_async(dispatch_get_main_queue(), ^
if (completionHandler)
completionHandler(UIBackgroundFetchResultFailed);

);

else
CKRecord *ckDocumentRecord = recordsByRecordID[recordID];
CKAsset *documentAsset = [ckDocumentRecord objectForKey:ckDocumentAsset];
NSData *data = [NSData dataWithContentsOfURL:documentAsset.fileURL];
[self handleData:data];

UIBackgroundFetchResult result = (data == nil)?UIBackgroundFetchResultFailed:UIBackgroundFetchResultNewData;
if (completion)
completion(result);



;
CKContainer *defaultContainer = [CKContainer defaultContainer];
CKDatabase *publicDatabase = [defaultContainer publicCloudDatabase];
[publicDatabase addOperation:fetchRecordsOperation];


else
if (completionHandler)
completionHandler(UIBackgroundFetchResultFailed);



else
if (completionHandler)
completionHandler(UIBackgroundFetchResultNoData);



else
if (completionHandler)
completionHandler(UIBackgroundFetchResultNoData);





And I want this code to be this:



- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler 

__ENTERING_METHOD__
CKNotification *ckNotification = [CKNotification notificationFromRemoteNotificationDictionary:userInfo];
if (ckNotification)
if ([ckNotification.subscriptionID isEqualToString:kCKDocumentChangeSubscription])
CKRecordID *recordID = [(CKQueryNotification*)ckNotification recordID];
if (recordID)
CKFetchRecordsOperation *fetchRecordsOperation = [[CKFetchRecordsOperation alloc] initWithRecordIDs:@[recordID]];
fetchRecordsOperation.fetchRecordsCompletionBlock = ^(NSDictionary *recordsByRecordID, NSError *error)

if (error)
dispatch_async(dispatch_get_main_queue(), ^
if (completionHandler)
completionHandler(UIBackgroundFetchResultFailed);

);

else
CKRecord *ckDocumentRecord = recordsByRecordID[recordID];
CKAsset *documentAsset = [ckDocumentRecord objectForKey:ckDocumentAsset];
NSData *data = [NSData dataWithContentsOfURL:documentAsset.fileURL];
[self handleData:data];

UIBackgroundFetchResult result = (data == nil)?UIBackgroundFetchResultFailed:UIBackgroundFetchResultNewData;
if (completion)
completion(result);



;
CKContainer *defaultContainer = [CKContainer defaultContainer];
CKDatabase *publicDatabase = [defaultContainer publicCloudDatabase];
[publicDatabase addOperation:fetchRecordsOperation];

else
if (completionHandler)
completionHandler(UIBackgroundFetchResultFailed);



else
if (completionHandler)
completionHandler(UIBackgroundFetchResultNoData);



else
if (completionHandler)
completionHandler(UIBackgroundFetchResultNoData);





Does running a CKFetchRecordsOperation and downloading the related CKAsset file while in the background constitute any sort of a violation of what can (or SHOULD) be done in the background?










share|improve this question
















My app is setup to receive CloudKit remote notifications. Sometimes it receives these notifications while it is running in the background. I have not done anything with these notifications yet - I have simply passed UIBackgroundFetchResultNewData to the completion handler. The reason is that I didn't think I could simply download from CloudKit while in the background - I thought that could only be done via Background Fetch or setting up a special job to run in the background. But I am revisiting this now, and I see that running the CKFetchRecordsOperation and downloading the related CKAsset file while in UIApplicationStateBackground actually does seem to work.



So my current code is this:



- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler 

__ENTERING_METHOD__
CKNotification *ckNotification = [CKNotification notificationFromRemoteNotificationDictionary:userInfo];
if (ckNotification)
if ([ckNotification.subscriptionID isEqualToString:kCKDocumentChangeSubscription])
CKRecordID *recordID = [(CKQueryNotification*)ckNotification recordID];
if (recordID)
if([UIApplication sharedApplication].applicationState == UIApplicationStateBackground)

if (completionHandler)
completionHandler(UIBackgroundFetchResultNewData);


else
CKFetchRecordsOperation *fetchRecordsOperation = [[CKFetchRecordsOperation alloc] initWithRecordIDs:@[recordID]];
fetchRecordsOperation.fetchRecordsCompletionBlock = ^(NSDictionary *recordsByRecordID, NSError *error)

if (error)
dispatch_async(dispatch_get_main_queue(), ^
if (completionHandler)
completionHandler(UIBackgroundFetchResultFailed);

);

else
CKRecord *ckDocumentRecord = recordsByRecordID[recordID];
CKAsset *documentAsset = [ckDocumentRecord objectForKey:ckDocumentAsset];
NSData *data = [NSData dataWithContentsOfURL:documentAsset.fileURL];
[self handleData:data];

UIBackgroundFetchResult result = (data == nil)?UIBackgroundFetchResultFailed:UIBackgroundFetchResultNewData;
if (completion)
completion(result);



;
CKContainer *defaultContainer = [CKContainer defaultContainer];
CKDatabase *publicDatabase = [defaultContainer publicCloudDatabase];
[publicDatabase addOperation:fetchRecordsOperation];


else
if (completionHandler)
completionHandler(UIBackgroundFetchResultFailed);



else
if (completionHandler)
completionHandler(UIBackgroundFetchResultNoData);



else
if (completionHandler)
completionHandler(UIBackgroundFetchResultNoData);





And I want this code to be this:



- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler 

__ENTERING_METHOD__
CKNotification *ckNotification = [CKNotification notificationFromRemoteNotificationDictionary:userInfo];
if (ckNotification)
if ([ckNotification.subscriptionID isEqualToString:kCKDocumentChangeSubscription])
CKRecordID *recordID = [(CKQueryNotification*)ckNotification recordID];
if (recordID)
CKFetchRecordsOperation *fetchRecordsOperation = [[CKFetchRecordsOperation alloc] initWithRecordIDs:@[recordID]];
fetchRecordsOperation.fetchRecordsCompletionBlock = ^(NSDictionary *recordsByRecordID, NSError *error)

if (error)
dispatch_async(dispatch_get_main_queue(), ^
if (completionHandler)
completionHandler(UIBackgroundFetchResultFailed);

);

else
CKRecord *ckDocumentRecord = recordsByRecordID[recordID];
CKAsset *documentAsset = [ckDocumentRecord objectForKey:ckDocumentAsset];
NSData *data = [NSData dataWithContentsOfURL:documentAsset.fileURL];
[self handleData:data];

UIBackgroundFetchResult result = (data == nil)?UIBackgroundFetchResultFailed:UIBackgroundFetchResultNewData;
if (completion)
completion(result);



;
CKContainer *defaultContainer = [CKContainer defaultContainer];
CKDatabase *publicDatabase = [defaultContainer publicCloudDatabase];
[publicDatabase addOperation:fetchRecordsOperation];

else
if (completionHandler)
completionHandler(UIBackgroundFetchResultFailed);



else
if (completionHandler)
completionHandler(UIBackgroundFetchResultNoData);



else
if (completionHandler)
completionHandler(UIBackgroundFetchResultNoData);





Does running a CKFetchRecordsOperation and downloading the related CKAsset file while in the background constitute any sort of a violation of what can (or SHOULD) be done in the background?







ios core-data background-process cloudkit remote-notifications






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 15 '18 at 18:00







SAHM

















asked Nov 15 '18 at 5:09









SAHMSAHM

1,46533266




1,46533266












  • CKAssets are used to store large files (Apple recommends anything more than a few kilobytes in size, but no more than 250MB) in iCloud and associate them with specific CKRecords. When downloading a record with a CKAsset, the asset’s data is temporarily saved to disk, but it is not guaranteed to stay there long, and may be cleaned up when the system needs to free memory. So it is definitely a good idea to get the data from the asset as soon as the record is downloaded. The file can be accessed from the asset’s fileURL property.

    – Kousic
    Nov 22 '18 at 5:49

















  • CKAssets are used to store large files (Apple recommends anything more than a few kilobytes in size, but no more than 250MB) in iCloud and associate them with specific CKRecords. When downloading a record with a CKAsset, the asset’s data is temporarily saved to disk, but it is not guaranteed to stay there long, and may be cleaned up when the system needs to free memory. So it is definitely a good idea to get the data from the asset as soon as the record is downloaded. The file can be accessed from the asset’s fileURL property.

    – Kousic
    Nov 22 '18 at 5:49
















CKAssets are used to store large files (Apple recommends anything more than a few kilobytes in size, but no more than 250MB) in iCloud and associate them with specific CKRecords. When downloading a record with a CKAsset, the asset’s data is temporarily saved to disk, but it is not guaranteed to stay there long, and may be cleaned up when the system needs to free memory. So it is definitely a good idea to get the data from the asset as soon as the record is downloaded. The file can be accessed from the asset’s fileURL property.

– Kousic
Nov 22 '18 at 5:49





CKAssets are used to store large files (Apple recommends anything more than a few kilobytes in size, but no more than 250MB) in iCloud and associate them with specific CKRecords. When downloading a record with a CKAsset, the asset’s data is temporarily saved to disk, but it is not guaranteed to stay there long, and may be cleaned up when the system needs to free memory. So it is definitely a good idea to get the data from the asset as soon as the record is downloaded. The file can be accessed from the asset’s fileURL property.

– Kousic
Nov 22 '18 at 5:49












0






active

oldest

votes












Your Answer






StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);













draft saved

draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53312793%2frunning-a-ckfetchrecordsoperation-while-in-uiapplicationstatebackground%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown

























0






active

oldest

votes








0






active

oldest

votes









active

oldest

votes






active

oldest

votes















draft saved

draft discarded
















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid


  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53312793%2frunning-a-ckfetchrecordsoperation-while-in-uiapplicationstatebackground%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Use pre created SQLite database for Android project in kotlin

Darth Vader #20

Ondo