How to sync async with server once pressed the home button? - ios

- (void)applicationDidEnterBackground:(UIApplication *)application
{
[_dataStore saveChanges];
[_sync syncWithServerWithDate:[NSDate dateWithTimeIntervalSince1970:timestamp]];
}
-(void)syncWithServerWithDate:(NSDate *)date
{
void(^postCompletionBlock)(FTjsonEvents *obj, NSError *error) = ^(FTjsonEvents *serverEvents, NSError *error) {
...
NSLog(#"Post Completion block finished!");
};
void(^completionBlock)(FTjsonEvents *obj, NSError *error) = ^(FTjsonEvents *serverEvents, NSError *error) {
....
NSLog(#"Fetch finished!");
[self postRecordsSinceLastServerSyncTimestamp:[date timeIntervalSince1970] WithCompletion:postCompletionBlock];
};
NSLog(#"Syncing data...");
[self fetchRecordsByDate:date WithCompletion:completionBlock];
}
I would like to sync with the server to fetch and post the latest data to.
Since this is happening via async completion blocks, it seems that my classes get garbage collected once I press home button. The sync never reaches the server.
However the local coredata is easily saved when doing this: [_dataStore saveChanges];
Is there a way to keep the async sync alive in the background until its completed?

Your implementation of applicationDidEnterBackground: has approximately five seconds to perform any tasks and return. If you need additional time to perform any final tasks, you can request additional execution time from the system by calling beginBackgroundTaskWithExpirationHandler:. In practice, you should return from applicationDidEnterBackground: as quickly as possible. If the method does not return before time runs out your app is terminated and purged from memory.
You should perform any tasks relating to adjusting your user interface before this method exits but other tasks (such as saving state) should be moved to a concurrent dispatch queue or secondary thread as needed. Because it's likely any background tasks you start in applicationDidEnterBackground: will not run until after that method exits, you should request additional background execution time before starting those tasks. In other words, first call beginBackgroundTaskWithExpirationHandler: and then run the task on a dispatch queue or secondary thread.
Here's an example implementation:
#interface XXAppDelegate (BackgroundStuff)
#property (nonatomic, assign) UIBackgroundTaskIdentifier backgroundTask;
#end
#implementation
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Request additional background time.
self.backgroundTask = [application beginBackgroundTaskWithExpirationHandler:^{
[application endBackgroundTask:self.backgroundTask];
}];
// Start background task.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//Background code goes here
//Cleanup background task id
[application endBackgroundTask:self.backgroundTask];
self.backgroundTask = UIBackgroundTaskInvalid;
});
}
#end

Related

Web service call in background mode - iOS

I need to call a web service in every minute and parse the data when app is in background state.
Since the APP uses location service I have enabled background mode for update Location.
I tried calling location update by using a timer background task, but it not working.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
self.bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
NSLog(#"ending background task");
[[UIApplication sharedApplication] endBackgroundTask:self.bgTask];
self.bgTask = UIBackgroundTaskInvalid;
}];
self.timer = [NSTimer scheduledTimerWithTimeInterval:60
target:self.locationManager
selector:#selector(startUpdatingLocation)
userInfo:nil
repeats:YES];
}
Is there any way to implement this with less battery consumption.
I referred this link
I'm not getting which solution is better here.
AppDelegate.h
#import <UIKit/UIKit.h>
#interface AppDelegate : NSObject {
// Instance member of our background task process
UIBackgroundTaskIdentifier bgTask;
}
#end
AppDelegate.m
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSLog(#"Application entered background state.");
// bgTask is instance variable
NSAssert(self->bgTask == UIBackgroundTaskInvalid, nil);
bgTask = [application beginBackgroundTaskWithExpirationHandler: ^{
dispatch_async(dispatch_get_main_queue(), ^{
[application endBackgroundTask:self->bgTask];
self->bgTask = UIBackgroundTaskInvalid;
});
}];
dispatch_async(dispatch_get_main_queue(), ^{
if ([application backgroundTimeRemaining] > 1.0) {
// Start background service synchronously
[[BackgroundCleanupService getInstance] run];
}
[application endBackgroundTask:self->bgTask];
self->bgTask = UIBackgroundTaskInvalid;
});
}
There are couple key lines in the above implementation:
The first is the line bgTask = [application beginBackgroundTaskWithExpirationHandler..., which requests additional time to run clean up tasks in the background.
The second is the final code block of the delegate method beginning with dispatch_async. It's basically checking whether there's time left to run an operation via the call [application backgroundTimeRemaining]. In this example, I'm looking to run the background service once but alternatively, you can use a loop checking on the backgroundTimeRemaining on each iteration.
The line [[BackgroundCleanupService getInstance] run] will be a call to our singleton service class, which we'll build right now.
With the app delegate ready to trigger our background task, we now need a service class that will communicate with the web server. In the following example, I'm going to a post a fictitious session key and parse a JSON encoded response. Also, I'm using two helpful libraries to make the request and deserialize the returned JSON, specifically JSONKit and ASIHttpRequest.
BackgroundCleanupService.h
#import <Foundation/Foundation.h>
#interface BackgroundCleanupService : NSObject
+ (BackgroundCleanupService *)getInstance;
- (void)run;
#end
BackgroundCleanupService.m
#import "BackgroundCleanupService.h"
#import "JSONKit.h"
#import "ASIHTTPRequest.h"
#implementation BackgroundCleanupService
/*
* The singleton instance. To get an instance, use
* the getInstance function.
*/
static BackgroundCleanupService *instance = NULL;
/**
* Singleton instance.
*/
+(BackgroundCleanupService *)getInstance {
#synchronized(self) {
if (instance == NULL) {
instance = [[self alloc] init];
}
}
return instance;
}
- (void)run {
NSURL* URL = [NSURL URLWithString:[NSString stringWithFormat:#"http://www.example.com/user/%#/endsession", #"SESSIONKEY"]];
__block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:URL];
[request setTimeOutSeconds:20]; // 20 second timeout
// Handle request response
[request setCompletionBlock:^{
NSDictionary *responseDictionary = [[request responseData] objectFromJSONData];
// Assume service succeeded if JSON key "success" returned
if([responseDictionary objectForKey:#"success"]) {
NSLog(#"Session ended");
}
else {
NSLog(#"Error ending session");
}
}];
// Handle request failure
[request setFailedBlock:^{
NSError *error = [request error];
NSLog(#"Service error: %#", error.localizedDescription);
}];
// Start the request synchronously since the background service
// is already running on a background thread
[request startSynchronous];
}
#end
may be helped

Fetching data using NsoperationQueue in sleep mode

I have added data fetching operations to NSOperationQueue .During the process, I just put the device to sleep mode and the process gets stopped. I surfed stack overflow an get some basic ideas.I need help to continue my fetching process without any interruption when device moves to sleep mode.Help appreciated !!
NSManagedObjectContext *managedObjectContext=((AppDelegate *)[[UIApplication sharedApplication] delegate]).managedObjectContext;
NSOperationQueue *downLoadQueue=((AppDelegate *)[[UIApplication sharedApplication] delegate]).downloadqueue;
//Fetch operation
if([fetchqueue count]>0)
{
Queue *queue=[fetchqueue objectAtIndex:0];
queue.status=#"INP";
[managedObjectContext performBlockAndWait:^{
NSError * error = nil;
if (![managedObjectContext save:&error]){
NSLog(#"Unresolved error while loading3");
}
}];
DownloadOperation *downloadOp=[[DownloadOperation alloc]init];
downloadOp.queue=queue;
[downLoadQueue addOperation:downloadOp];
}
}
The downLoadQueue started its execution. It is fetching data from server meanwhile the device goes to sleep and the execution stops. I don't know how to continue in this block applicationDidEnterBackground. How can I get my lengthy downloading process during sleep mode?
Also tried this one..But the operation was not resumed.
//code follows
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSOperationQueue *downLoadQueue;
downLoadQueue=((AppDelegate *)[[UIApplication sharedApplication] delegate]).downloadqueue;
[downLoadQueue waitUntilAllOperationsAreFinished];
});
}
Please suggest any ideas.
I think the only way to do this is background task. Take a look at apple docs about this. There are also a lot of answers on stackowerflow how to implemet such feature.
I think in your case you can try next code to execute NSOperations a in backgraund task:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
bgTask = [application beginBackgroundTaskWithName:#"MyTask" expirationHandler:^{
// Clean up any unfinished task business by marking where you
// stopped or ending the task outright.
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Start the long-running task and return immediately.
//Start your NSOperationQueue if it's not executing
//Lock current thread while operations are executing
[queue waitUntilAllOperationsAreFinished];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Do the work associated with the task, preferably in chunks.
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
});
}

Can we measure the data of M7/M8 while the iPhone is unawakened in the CLVisit delegate method?

Can I write the code in this method to get the M7's data and does it useful while I don't run the app?
-(void)locationManager:(CLLocationManager *)manager didVisit:(CLVisit*)visit
{
}
Yes, you can query CMMotionActivityManager from locationManager:didVisit:
Please note that visits are not reported to your app in real time, in my tests they are be delayed 20 to 60 minutes. That means starting activity monitoring with startActivityUpdatesToQueue:withHandler: makes no sense, as these updates won't tell you what happened during the visit.
However you still can fetch and analyze activity events that happened during the visit using queryActivityStartingFromDate:toDate:toQueue:withHandler:
Keep in mind that locationManager:didVisit: might and most likely will be called while your app is in background mode, thus you have about 10 seconds to query the CMMotionActivityManager and process the data. Since you have no control over the CMMotionActivityManager and there is no guarantee it will process your query in a timely fashion, you may want to invoke beginBackgroundTaskWithExpirationHandler: in your locationManager:didVisit: as well.
#property (nonatomic) UIBackgroundTaskIdentifier bgTask;
#property (nonatomic, strong) CMMotionActivityManager *motionActivityManager;
...
- (void)locationManager:(CLLocationManager *)manager didVisit:(CLVisit *)visit
{
if (visit.arrivalDate && visit.departureDate) {
// use strong self here, as we must end the background task explicitly
self.bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^ {
[self stopBackgroundTask];
}];
[self.motionActivityManager queryActivityStartingFromDate:visit.arrivalDate
toDate:visit.departureDate
toQueue:[NSOperationQueue currentQueue]
withHandler:^(NSArray *activities, NSError *error) {
// handle CMMotionActivity history here
[self stopBackgroundTask];
}];
}
}
- (void) stopBackgroundTask {
if (self.bgTask != UIBackgroundTaskInvalid) {
[[UIApplication sharedApplication] endBackgroundTask:self.bgTask];
self.bgTask = UIBackgroundTaskInvalid;
}
}

applicationDidEnterBackground is not waiting till method execution is completed

I would like to save data when app goes in background. I am doing cancelling NSOperation and saving data in applicationDidEnterBackground. But it does not complete execution.
How can I complete this before my app goes in background?
Code :
-(void)applicationDidEnterBackground:(UIApplication *)application
{
//FUNCTION_START
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
[self performSelectorOnMainThread:#selector(dispatchStateNotification:)
withObject:[NSNumber numberWithInt:666]
waitUntilDone:YES ];
// Write to core data
GWSCoreDataController *dataController = [GWSCoreDataController sharedManager];
NSError *error;
if (![dataController.managedObjectContext save:&error]) {
NSLog(#"Error while saving data to Core Data: %#", [error localizedDescription]);
}
// FUNCTION_END
}
-(void)dispatchStateNotification:(NSNumber *)value {
[[NSNotificationCenter defaultCenter] postNotificationName:APPLICATION_ENTERED_BACKGROUND_NOTIFICATION object:value];
}
You can start a background task, and do your cleanup stuff
- (void)applicationDidEnterBackground
{
UIBackgroundTaskIdentifier bgTaskId =
[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:bgTaskId];
}];
// Start cleanup
.......
[[UIApplication sharedApplication] endBackgroundTask: bgTaskId];
}
I had the same issue and had to put the save call in applicationWillResignActive instead - didEnterBackground just doesn't seem to have the complete CoreData to save with...
I have solved this problem for my requirement like below. Added one flag and run while loop till this flag will not become false. As soon as my task will get complete or app comes in foreground I have marked this flag as false.
// start the task asynchronously which is written into the block on new thread.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{
//this loop runs continuously while flag is YES.
while(appDidEnterBackground){
sleep(1);
}//end of while
//ends the background task.
[application endBackgroundTask: background_task];
background_task = UIBackgroundTaskInvalid;
});//end of dispatch queue

Continue executing NSOperation after enter to background

I need to run my long running task until it is finished and it does not matter if the application is gone to the background.
So I have subclass of NSOperation, and I added this NSOperation to NSOperationQueue. In the main function of the NSOperation I have:
- (void)main {
UIApplication *app = [UIApplication sharedApplication];
UIBackgroundTaskIdentifier taskId = 0;
taskId = [app beginBackgroundTaskWithExpirationHandler:^(void) {
[app endBackgroundTask:taskId];
}];
NSManagedObjectContext *searchContext = [NSManagedObjectContext createSearchTempContext];
[searchContext performBlockAndWait:^{
NSArray *array = //Get Array data from CoreData;
if (array.count > 0) {
//Long running task
}
if (taskId != UIBackgroundTaskInvalid) [app endBackgroundTask:taskId];
}];
Is it all I need to run in the background? Or something I missed?
UIBackgroundTaskIdentifier would only be able to give you some extra time to complete your task, it would allow your app to remain alive for maximum 10 mins, then it would go in suspended state, For an APp to run in background there are only few ways possible which are listed as follows
Play audio:
Receive location updates:
Perform finite-length tasks:
Process Newsstand Kit downloads:
Provide Voice-over-IP (VoIP) services:
If your app dosent use any of these then it will be suspended after 10 mins regardless of all your operations

Resources