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
Related
From Apple watch extension, handlewatchkitextensionrequest is not calling async call and returns nill data to apple watch, below are code please help me out.
-(void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *))reply
{
__block UIBackgroundTaskIdentifier watchKitHandler;
watchKitHandler = [[UIApplication sharedApplication] beginBackgroundTaskWithName:#"backgroundTask"
expirationHandler:^{
watchKitHandler = UIBackgroundTaskInvalid;
}];
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
if ([[userInfo objectForKey:#"request"] isEqualToString:#"NearestFacility"]) {
[self NearestFacilityClicked];
if (self.ary_WatchKitNearestFacility.count>0) {
NSDictionary *response = #{#"response" : self.ary_WatchKitNearestFacility};
reply(response);
}
}
dispatch_semaphore_signal(sema);
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
dispatch_after(dispatch_time( DISPATCH_TIME_NOW, (int64_t)NSEC_PER_SEC * 1), dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[UIApplication sharedApplication] endBackgroundTask:watchKitHandler];
});
}
-(IBAction)NearestFacilityClicked
{
// ASIFormdatarequest call
[request startAsynchronous];
}
In watch os 2 you need to use session for transferring the data between watch & iPhone.
First you need to initialise the WCSession in viewdidload()
if (WCSession.isSupported()) {
let session = WCSession.defaultSession()
session.delegate = self
session.activateSession()
}
then to send the data you can use
let transfer = WCSession.defaultSession().transferUserInfo(applicationDict)
Then, on the receiving end, you'll need to implement session:didReceiveUserInfo: (Developer documentation). Note, according to Apple's "watchOS2 Transition Guide,"
To begin communication, both your Watch app and your iOS app must have an active WCSession object. Typically, each app creates, configures, and activates a session object at launch time and stores a reference to it in a central location. When you want to send data, you retrieve the session object and call its methods.
I am using AFNetwork (its base on NAFDownloadRequestOperation) and my task in downloading multiple zip files one by one from amazon bucket.
When app is in foreground every thing is working very well, but when app goes in background mode that time downloading is running for some time only and it will automatically stop. I read some blog about it in that I get that following method called before downloading will stop.
[self.operationUpdate setShouldExecuteAsBackgroundTaskWithExpirationHandler:^{
NSLog(#"downloading will stop");
}];
Problem in background mode downloading is automatically stop
What I want: If downloading is stop in background and when app again comes to foreground I need to resume downloading from that point.
I also use following code in AppDelegate but I am not understand how to resume previous downloading.
__block UIBackgroundTaskIdentifier backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:^(void) {
[application endBackgroundTask:backgroundTaskIdentifier];
NSLog(#"applicationWillResignActive");
[__SERVER_INSTANCE cancellAllDownloading];
// [[YourRestClient sharedClient] cancelAllHTTPOperations];
}];
If any one have any solution please let me know, Thanks in advance.
You should use AFDownloadRequestOperation
Your request will look like
AFDownloadRequestOperation *operation = [[AFDownloadRequestOperation alloc] initWithRequest:request targetPath:path shouldResume:YES];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Successfully downloaded file to %#", path);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
[operations addObject:operation];
After you restart your app, and generate the request having a same url, it will resume downloading. "shouldResume:YES" works.
So, on your background task you can recreate request to finish download
__block UIBackgroundTaskIdentifier backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:^(void) {
[application endBackgroundTask:backgroundTaskIdentifier];
NSLog(#"applicationWillResignActive");
[__SERVER_INSTANCE cancellAllDownloading];
// recreate here your request to finish fownload,
//or recreate in when app will enter foreground
}];
Hope this helps
The background downloading stops because after sometime because you might not have enable Background Fetch in Capabilities in xcode.
Please see attached screenshot
And Appdelegate You have write this code,
-(BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
return true;
}
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
completionHandler(UIBackgroundFetchResultNewData);
}
OR
- (void)applicationDidEnterBackground:(UIApplication *)application
{
UIApplication *app = [UIApplication sharedApplication];
UIBackgroundTaskIdentifier bgTask;
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
NSTimeInterval ti = [[UIApplication sharedApplication]backgroundTimeRemaining];
NSLog(#"backgroundTimeRemaining: %f", ti);
// just for debug
}];
}
Hope this will help!
I am working on an iOS application in which I am using Restful web service by NSURLConnection, when I call web service and after calling web service press home button then application goes in to background and it is not getting response in background. In my application response should get even when application will be in background state.
So please suggest me any suitable answer for this.
Should I use NSURLSession ?
NSURL *myURL = [NSURL URLWithString:#""]; // set your url here
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDownloadTask *getTask = [session downloadTaskWithURL:myURL completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
// do stuff with the result
}];
// don't forget to start your task
[getTask resume];
Here's a nice tutorial you might want to take:
http://www.raywenderlich.com/51127/nsurlsession-tutorial
Use UIBackgroundTaskIdentifier, something like this
(void)applicationDidEnterBackground:(UIApplication *)application {
//Start Background Service and get data every 10 second
[self runBackgroundTask:10];
}
(void)runBackgroundTask: (int) time{
UIApplication *app;
app = [UIApplication sharedApplication];
//check if application is in background mode
if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) {
//create UIBackgroundTaskIdentifier and create tackground task, which starts after time
__block UIBackgroundTaskIdentifier bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
dispatch_async(dispatch_get_main_queue(), ^{
NSTimer *refreshTimer = [NSTimer scheduledTimerWithTimeInterval:time target:self selector:#selector(doRefresh) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:refreshTimer forMode:NSDefaultRunLoopMode];
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
});
}
}
doRefresh - This is method where you give your web service
I'm starting a background task in -applicationDidEnterBackground that uploads data to my server, if the user has changed settings relevant to the push notifications. When the user changes a setting I set a static BOOL to YES and only send the changes when the app enters the background. I pass the a block ending the task to the method so when reaching connectionDidFinishLoading it calls it and ends the task.
It works most the times on the simulator, but doesn't work on the actual device.
Relevant code:
self.bgTask = [application beginBackgroundTaskWithExpirationHandler:^
{
[application endBackgroundTask:self.bgTask];
self.bgTask = UIBackgroundTaskInvalid;
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
{
[PushInfo checkDirty:^{
NSLog(#"push info sent");
[application endBackgroundTask:self.bgTask];
self.bgTask = UIBackgroundTaskInvalid;
}];
});
...
// in PushInfo.m :
typedef void (^VoidBlock)();
static BOOL dirty;
+ (void) checkDirty:(VoidBlock)endBlock
{
if(dirty)
{
PushInfo *pi = [[PushInfo alloc] init];
NSLog(#"sending pushinfo"); // This code is always reached
[pi setEndBlock:endBlock];
[pi updatePushInfo];
}
else
endBlock();
}
- (void) updatePushInfo
{
...
// Create a NSURLConnection to send the data
...
}
- (void) connectionDidFinishLoading:(NSURLConnection *)connection
{
...
NSLog(#"sent push info");
dirty = NO;
if(endBlock)
{
endBlock();
}
}
Am I missing anything ?
EDIT : even when it does send the information to the server on the simulator, the static variable is still YES for some reason...
Try moving your code to:
-(void) applicationWillResignActive:(UIApplication *)application
I believe having it in applicationDidEnterBackground is too late.
Checked the docs, and you are right, it should be fine in applicationDidEnterBackground.
Another suggestion, try moving beginBackgroundTaskWithExpirationHandler inside the dispatch_async block. It may have to be started in the same thread but didn't see this explicitly stated in the documentation.
ended using +sendSynchronousRequest:returningResponse:error:
Using iOS 4.0 I am trying to do a download an image in the background when the app is suspended. What I am doing is when I get the applicationDidEnterBackground delegate call, I initiate one asynchronous NSURLConnection and set the app delegate as the delegate for the connection. But none of the NSURLConnection delegates are getting called back. I have captured the network calls using Wireshark and I can see that my request succeeded and got the response too. But since none of the delegate methods are invoked I am unable to do anything with the data.
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSLog(#"Application entered background state.");
// UIBackgroundTaskIdentifier 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;
});
}];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:#"http://animal.discovery.com/mammals/leopard/pictures/leopard-picture.jpg"] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:15];
self.connection = [[[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO] autorelease];
[self.connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[self.connection start];
[request release];
NSLog(#"Sent download request....");
dispatch_async(dispatch_get_main_queue(), ^{
while([application backgroundTimeRemaining] > 1.0) {
//Do something..
}
[application endBackgroundTask:self->bgTask];
self->bgTask = UIBackgroundTaskInvalid;
NSLog(#"Ending background task....");
});
}
What should I do to complete a download asynchronously in background when the application goes to background?
I think it's because you're ending the background task before the download actually finishes. You should put the call to [application endBackgroundTask:self->bgTask]; in the NSURLConnectionDelegate method when the download finishes (both success and unsuccessfully), and not just after calling [self.connection start]