I have an iOS application and a companion watchOS application. iOS app provides access token for watchOS app when watchOS app starts for the first time. After this watchOS app starts to get data updates from the server periodically by sending NSURLSessionDownloadTask requests.
My NSURLSession is configured following way:
NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:[[[[NSBundle mainBundle] bundleIdentifier] stringByAppendingString:#".backgroundsession"] stringByAppendingString:[NSUUID UUID].UUIDString]];
config.sharedContainerIdentifier = APP_GROUPS_ID;
self.urlSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
Then I send requests periodically following way:
NSURL *url = [NSURL URLWithString:API];
NSMutableURLRequest *request = [self buildRequestForURL:url andMethod:#"POST"];
NSString *body = [NSString stringWithFormat:<some request params>];
[request setHTTPBody:[body dataUsingEncoding:NSUTF8StringEncoding]];
[[self.urlSession downloadTaskWithRequest:request] resume];
watchOS app successfully receives updates for some time. It successfully receives updates when iOS app is in background mode or even when it is manually unloaded.
But watchOS app stops receiving updates when I lock iPhone screen (right after or after a couple of minutes).
Has anybody experienced the same issue with watchOS apps?
Take a look at This Apple Example - WatchBackgroundRefresh, and my answer to this question. Hopefully that can lead you to some working code.
Related
It's surprisingly difficult to find a definitive answer to this; couldn't find it mentioned in the Apple documentation and couldn't find a definite yes/no after searching past questions.
The question is simple - if the app requests a background fetch to be performed after N time, then the user terminates the app. Will the OS still launch the app into the background to perform the background fetch?
Okay, once again background modes cause confusion. No offense to the other people trying to help, but this is more complicated than it seems.
First of all:
This is out of date, as Sausage guessed in the comments. I know this for a fact, because the section about VoIP apps is still explaining the "old way" to do this, with a handler that gets called periodically. I investigated this a bit for this answer, so I suggest you go and read that. The important lesson for this case here is that iOS makes a distinction between an app being terminated by the user or by the system, plus it also plays a role whether the phone was rebooted or not.
So to sum this (and your question) up you basically want to know whether this part of the above, outdated documentation is still correct word for word:
In most cases, the system does not relaunch apps after they are force quit by the user. One exception is location apps, which in iOS 8 and later are relaunched after being force quit by the user. In other cases, though, the user must launch the app explicitly or reboot the device before the app can be launched automatically into the background by the system. When password protection is enabled on the device, the system does not launch an app in the background before the user first unlocks the device.
Apple: Understanding When Your App Gets Launched into the Background
I thoroughly investigated the rest of the docs, but did not find any definite answer, so it unfortunately boils down to what dan already suggested: Test it. My gut feeling is that the documentation is still correct in that regard, though (as said what's not is the VoIP stuff). I say that because the UI in the Settings app calls the feature "Background App Refresh", so users are probably supposed to understand that an app having this permission won't refresh when they "push" them out of background (i.e. home button -> swipe it out). For regular users, apps are either quit (not in the task manager at all), in the foreground (using them) or in background (they're in the task manager and another app is in foreground and/or the phone is locked).
To really test this you'd have to write an app and actually carry it around a bit (I assume at least two days) in each condition. First while it is in background (the OS should periodically let it fetch, as you probably know this can also be triggered in Xcode) and then while it is force-quit. The problem is to verify that it fetched stuff. I'd go with a logfile that can be shared via iTunes. I have typed up some code for this:
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
NSLog(#"We're awake! Booyah!");
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:config
delegate:nil
delegateQueue:[NSOperationQueue mainQueue]];
NSMutableURLRequest *request = [NSMutableURLRequest new];
request.HTTPMethod = #"GET";
request.URL = [NSURL URLWithString:#"https://www.google.com"];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request
completionHandler:^(NSData * _Nullable data,
NSURLResponse * _Nullable response,
NSError * _Nullable error) {
NSDate *now = [NSDate date];
NSString *toLog = [NSString stringWithFormat:#"%# - fetched\n",
[now description]];
[self updateTestDocumentWithString:toLog];
NSLog(#"Yay, done!");
completionHandler(UIBackgroundFetchResultNewData);
}];
[task resume];
}
- (void)updateTestDocumentWithString:(NSString *)toAppend {
NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *filePath = [[docDir stringByAppendingPathComponent:#"logfile.txt"] copy];
if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
if (![[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil]) {
NSLog(#"We're effed...");
return;
}
}
NSFileHandle *file = [NSFileHandle fileHandleForUpdatingAtPath:filePath];
if (!file) {
NSLog(#"We're effed again...");
return;
}
[file seekToEndOfFile];
// ensure this is never nil
[file writeData:[toAppend dataUsingEncoding:NSUTF8StringEncoding]];
[file closeFile];
}
This would go into the app delegate, and don't forget to add the Application supports iTunes file sharing boolean setting in your app's plist. I will leave this running on my development device for a bit and check the logfile, eventually reporting back here. Feel free to test it yourself, too.
EDIT:
https://devforums.apple.com/message/873265#873265 (login required)
Also keep in mind that if you kill your app from the app switcher
(i.e. swiping up to kill the app) then the OS will never relaunch the
app regardless of push notification or background fetch. In this case
the user has to manually relaunch the app once and then from that
point forward the background activities will be invoked. -pmarcos
That post was by an Apple employee so I think i can trust that this information is correct.
OLD answer:
According to this answer wrote by a top user: iOS background fetch: your app won't be woken up again.
Make sure you're not killing the app (i.e. by double tapping on the
home button and swiping up on your app for force the app to
terminate). If the app is killed, it will prevent background fetch
from working correctly.
It really doesn't make sense for it to be woken up...it kinda invalidates the user killing the app.
Having that said there are different ways a terminated/force quit app can be launched again:
Tapping on a notification.
Tapping on the app icon.
Using openUrl to open your app from another app.
If you use PushKit...then your app would be launched. Imagine if had a VOIP app e.g. Skype, WhatsApp and a friend was calling you but you had have force-quit the app, you wouldn't receive calls. For more see here.
Location updates either through use of region monitoring or the significant-change location service. See this answer and make sure to read this entire page from Apple docs.
Rebooting the device would also undo anything blocked through force-quit
Reading the Apple documentation here I found this text snippet which should explain your question:
The techniques offered by iOS fall into three categories:
Apps that start a short task in the foreground can ask for time to finish that task when the app moves to the background.
**Apps that initiate downloads in the foreground can hand off management of those downloads to the system, thereby allowing the app to be suspended or terminated while the download continues.**
Apps that need to run in the background to support specific types of tasks can declare their support for one or more background execution modes.
The second option is exactly about downloading the data, which can be delegated to the system even if the can be terminated.
Using NSUrlSession with a background configuration let's me download files even if the app gets terminated by iOS. Being curious, I tried to add an upload task and noticed that it won't continue, even not if the app is only suspended.
Apple talks about "Downloading in the background" but they don't explicitly state that uploading would not be possible.
Can somebody confirm that uploads and background session configuration don't work together?
They DO work together.
What you need to do is the following:
Have a NSURLSessionConfiguration with background configuration
NSURLSessionConfiguration *conf = [NSURLSessionConfiguration backgroundSessionConfiguration:#"backgroundSession"];
Setup a NSURLSession (#property NSURLSession *urlSession)
Obtain the path to the file (fullPath)
Create the NSURLRequest (request)
Create a NSURLSessionTask
NSURLSessionTask*task = [self.urlSession uploadTaskWithRequest:request fromFile:fullPath];
[task resume];
And the task will run in background. You can get the status from NSURLSession delegate methods.
Cheers
I use NSURLSession to download files in background. Here is how I start a new NSURLSessionDownloadTask:
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[self nextURL]];
_task = [[self downloadSession] downloadTaskWithRequest:request completionHandler:nil];
[_task resume];
After calling resume, I encountered the following error:
nsnetworkd[1557] <Error>: Couldn't add download <SSDownload: 0x16d63a20>: 958545229883046269 to download manager
After this, no delegate methods were called (it seems like the session was killed). The internet connection was ok (Safari was able to open websites).
It occurred only once on iOS 7.0.4. Can someone please explain what could this error mean? Is this an iOS 7.0.x bug or is there a good workaround?
I'm searching for a cross-platform chat service that supports mobile (Android and iOS), and Desktop. I've looked into a few sites that offer chat services, but none of them seem to be what I'm looking for in terms of features. So I've decided to try to create an iOS application that will connect to a service that we will use for the desktop version.
That was just a bit of background info, but here's the question:
I am creating a url request in order to send information to the server that will handle and store my url request like so:
-(IBAction)clickedSendButton:(id)sender
{
urlReq = [NSString stringWithFormat:#"%#/mobile/PrivateChatMsgSending/?sid=%#&skey=%#&rid=%#&msg=%#",domain,((ApplicationAppDelegate *) [UIApplication sharedApplication].delegate).loggedProfileID,((ApplicationAppDelegate *) [UIApplication sharedApplication].delegate).loggedSessionID,receipientProfileId,txtFldChat.text];
NSURL *url = [NSURL URLWithString:[urlReq stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSURLRequest *urlrequest = [NSURLRequest requestWithURL:url];
connection = [[[NSURLConnection alloc] initWithRequest:urlrequest delegate:self] autorelease];
[self ShowIndicatorView:#"Sending..."];
}
So would I be able to pass these parameters to my php script on the server side all while being compatible with my desktop version? Is there anything within my application that could potentially inhibit my user's from receiving messages from desktop users? What might be a better solution to creating a multi-platform messaging service?
My app requires upload of video files from users phone which will then be processed on server.
THe problem is the size of the file can go to 200 MB plus and the user won't keep the application open to wait for the file to upload. Since apple doesn't allow apps to run in background for more than a limited time. How can I ensure that my files are uploaded. I am using afnetworking to set up an upload task as given by ios 7 library.
Please if anyone can point me in the right direction or have any solution it would be greatly appreciated. I have banged my head on this for too long. Thanks.
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
[manager setTaskDidSendBodyDataBlock:^(NSURLSession *session,NSURLSessionTask *task ,int64_t bytesSent, int64_t totalBytesSent,int64_t totalBytesExpectedToSend){
CGFloat progress = ((CGFloat)totalBytesSent / (CGFloat)sensize);
NSLog(#"Uploading files %lld -- > %lld",totalBytesSent,totalBytesExpectedToSend);
[self.delegate showingProgress:progress forIndex:ind];
}];
dataTask = [manager uploadTaskWithStreamedRequest:request progress:nil completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
if (error) {
NSLog(#"Error: %#", error);
} else {
}
}];
My request is a normal multipart form request.
Use:
NSURLSessionConfiguration:backgroundSessionConfiguration:
instead of
NSURLSessionConfiguration:defaultSessionConfiguration
From the NSURLSessionConfiguration:backgroundSessionConfiguration: documentation:
Upload and download tasks in background sessions are performed by an external daemon instead of by the app itself. As a result, the transfers continue in the background even if the app is suspended, exits, or crashes.
So in your case, change:
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
to:
NSString *appID = [[NSBundle mainBundle] bundleIdentifier];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfiguration:appID];
Implementing application:handleEventsForBackgroundURLSession:completionHandler: on your app delegate will allow your app to be woken up (ie. un-suspended or un-terminated in background mode) when an upload has completed (whether it has completed successfully or not).
Don't get confused with Background Fetching. You don't need it. Background Fetching simply wakes you app up to periodically give your app the chance to fetch small amounts of content regularly. It may however, be useful for restarting failed "background-mode" uploads periodically.
You should use a background session configuration instead if a default session configuration. This ensures that your data transfer will continue in the background once the user has exited your app.
Of course, this is correct as long as the user has background fetching enabled for your app in the Settings app of the device.
Be sure to enable the Background fetch capability on your project settings:
(source: migueldiazrubio.com)
(source: migueldiazrubio.com)
Then implement the application:handleEventsForBackgroundURLSession:completionHandler: in your App Delegate to be informed when the data transfer ends and do whatever you need to do (UI update…) inside your app with the received/sent file. Don't forget to call the completionHandler to inform the system that you have ended your tasks. iOS then will take a screenshot of the active screen of your app and update the one in the iOS 7 multitasking screen.