Here is the situation:
I am picking a large video using imagepicker. Obviously the picker will take a bit of time to compress the video. So to ease user experience I have enabled background task for it.
Now here comes the issue:
If user choose a video and and tap the home button, application goes to background and continue compressing video for next 600 secs. And the background task expires. In the expiration handler I have stopped my background task.
Now if the user is resuming app after the background expiration
- (void)applicationDidBecomeActive:(UIApplication *)application
is not being invoked. Can anyone explain me why this happens?
When the background tasks expires, your app will really be closed! So it's not becoming active again, it's launching.
You should handle stuff in your expiration handler or/and when your background task ends successfully. Both situations, you need to set the background_task as invalidated.
If your app goes to background while converting the video, and then user open it again BEFORE the task end or the background task expires, then you should see the app calling applicationDidBecomeActive.
I assume you know it, but maybe you are missing the multitask properties in your Info.plist file, so your app isn't accepting background tasks the way you expect.
-(void) applicationDidBecomeActive:(UIApplication)application
This method Only called when the app's sate is changed from inactive state to active state.
Is it possible to know whether video picking finished? if it's possible then just store it. and when the user comes again to the app. just fire the functionality you required.
Could you try to add log statement to method applicationDidFinishLaunching? May be the app terminates or crashes before a user opens it.
Also, I think correct way is to save current parsing context when app receives signal like applicationDidFinishLaunching and when app starts resume parsing. Because a user can close the app manually.
Related
I am trying to run some code during the applicationWillResignActive when the user opens the task switcher and it has worked fine until I began using bluetooth in my app.
When bluetooth tries to connect to a device it shows an alert window asking if the user wants to pair the device. This alert is enough to trigger the applicationWillResignActive method and then runs my code for when the app is being navigated away from (task switcher). This causes a large problem since the code I intend to run when switching away, turns off some much needed functionality within the actual app. So once they press "pair" or "cancel" on that alert, all of my app stops functioning as it should because the app has lost focus.
I have tried to detect the state of the application during this time with this... NSUInteger state = [[UIApplication sharedApplication] applicationState]; thinking of course that it would be considered active when the alert pops up and inactive when in the task switcher. However, this was not the case it shows up as active for both use cases.
Update #1
The question...
How can I differentiate in the application between the app causing a system level inactive focus state like running code to connect to bluetooth, versus the user causing the system level inactive focus like double tapping the home button? All in the efforts to distinguish what is causing the applicationWillResignActive method to fire.
Update #2
The intention of this functionality is to set a flag in NSUserDefaults when bluetooth connects to the device. This flag is being "observed" and used to trigger the changing of view controllers to a page related to this new BT connection. When the user double presses the home button and moves to task switcher I turn off BT and switch to iBeacon so I can notify of events. All is well with this current implementation all bar 1 use case.
If the user hasn't yet connected to the BT device and it connects for the first time and that pairing alert comes up it fires the applicationWillResignActive method just the same as double tapping the home button does. In this method the code then checks for that NSUserDefaults flag to see if it switched on (which by this time it is because the BT has already reached the CBCentralManager's didConnectPeripheral method and turned it on) and if it's on, it turns off BT and switched to scanning for iBeacon. Because the app is still open this obviously causes problems. The app is running so the user see's the BT connect, the new view slide in, the pairing alert come up, then the new view slide right back out and iBeacon starts sending notifications intended for when the user is in the task switcher.
I already have this exact functionality happening in the applicationWillEnterBackground method so that's not the answer. I need to have a way of saying "the app is running right now and we've received an alert instead of double tapping home, so please don't turn off BT and turn on iBeacon yet"
Two possible solutions:
1. The answer may lie in this statement:
When bluetooth tries to connect to a device it shows an alert window asking if the user wants to pair the device.
Your app must do something to cause this alert to appear. You could set a Date field to the current time in your AppDelegate when this happens, and then when you get a call to applicationWillResignActive you can compare that timestamp to the current time, and if it is < 1 second or so, you have a pretty good clue that the bluetooth dialog went up.
Of course, this is not foolproof. As #danh notes in his comment, the design of iOS makes this really difficult. You won't know for sure if the bluetooth dialog went up, or if the user or OS just happened to bring something else to the foreground at the same time. What's more, it's always possible that even if the bluetooth dialog comes up, the user might decide at that very moment to go check his or her email or start browsing Facebook. In that case, it is both true that the bluetooth dialog is what sent your app to the background, AND the user navigated away from the app. Unfortunately, iOS doesn't really give you a way to differentiate the two.
2. You might use a background task to handle your cleanup logic.
You can request up to 180 seconds of background running time after the call to applicationWillResignActive, so you could defer your cleanup tasks until say 175 seconds have passed since your app is resigned to the background. If the user doesn't come back within 3 minutes, it's probably time to do this cleanup anyway. My blog post here shows the basics of setting up a background task. It is specifically targeted to extending beacon ranging time, but you can put whatever logic you want inside the background code block like this:
- (void)extendBackgroundRunningTime {
if (_backgroundTask != UIBackgroundTaskInvalid) {
// if we are in here, that means the background task is already running.
// don't restart it.
return;
}
NSLog(#"Attempting to extend background running time");
__block Boolean self_terminate = YES;
_backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithName:#"DummyTask" expirationHandler:^{
NSLog(#"Background task expired by iOS");
if (self_terminate) {
[[UIApplication sharedApplication] endBackgroundTask:_backgroundTask];
_backgroundTask = UIBackgroundTaskInvalid;
}
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(#"Background task started. Waiting 175 seconds before cleanup.");
[NSThread sleepForTimeInterval:175];
//TODO: perform cleanup code if app is not in the foreground by now
});
}
In my iOS app I have a task that is executed perodically with a timer (the task is checking my database every 10 seconds):
var timer: dispatch_source_t!;
timer = Dispatch.timerAsync(interval: intervalTime){
//my task here
}
if a speciifc parameter in my database is set then I stop the timer and process the results (including updating the UI) in the main thread:
dispatch_async(dispatch_get_main_queue()) {
self.processResults();
}
Now what I'm trying to do is to make this work when the user switches to another app. When my app goes to the background the timer stops working and when I go back to my app it starts working again. I wanted to know if it is possible to pass this task with the timer to the background thread when the user switches to another app so that my app still checks the database every 10 seconds and if the parameter in the database is set the user gets a notification (local notification). I would like to avoid using Apple's remote notification if possible... Is this possible?
I don't have enough experience with app programming so I wanted to get some idea to see if I'm approaching this problem the right away. Any input, suggestion or sample code is very appreciated. Thanks!
I have implemented NSURLSession for downloading fairly large files from our servers. Now as long as I'm working in foreground or background and go back to the app the transactions are working and getting finished.
But if I force-quit the app using the the multitasking screen and re-open the app again. the downloading process is not getting finished although as I understood from the docs, it should, here what the docs state:
If an iOS app is terminated by the system and relaunched, the app can use the same identifier to create a new configuration object and session and retrieve the status of transfers that were in progress at the time of termination. This behavior applies only for normal termination of the app by the system. If the user terminates the app from the multitasking screen, the system cancels all of the session’s background transfers. In addition, the system does not automatically relaunch apps that were force quit by the user. The user must explicitly relaunch the app before transfers can begin again.
Meaning if I relaunch the app the transaction before the force-quit should started again, or are they? Is there an additional operation I need to commit in order this to work?
UPDATE: I stumbled on this project:
https://github.com/Heikowi/HWIFileDownload#force-quit
That states:
Force Quit
After the app has been killed by the user, downloads do not continue in the background. On iOS 7 (and later) resume data is passed back.
Meaning there is a way to receive resume data even if the application was killed by the user in the background. Only the project is written in Objective-C and I can't understand what are they doing to achieve this.
After a force-quit the:
NSURLSessionTaskDelegate - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
delegate method will be called when the app is restarted. If the download task can be resumed the error object will contain the resume data:
[error.userInfo objectForKey:NSURLSessionDownloadTaskResumeData].
Using this data you can resume the download process by creating a NSURLSessionDownloadTask with:
(NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData*)resumeData.
More info about that can be found in Life Cycle of a URL Session with Custom Delegates, step 13.
I think that after your application did force-quit you should start all over again (.
If the user terminates your app, the system cancels any pending tasks.
and
When all of the tasks associated with a background session are complete, the system relaunches a terminated app (assuming that the sessionSendsLaunchEvents property was set to YES and that the user did not force quit the app)
https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/BackgroundExecution/BackgroundExecution.html
-> use URLSession background Sessions download doesn't stop at all....you don't have to explicitly code for resuming the download or something..
https://developer.apple.com/reference/foundation/urlsession
check for background session in this link...if you're not able get the still...comment me and i would help in detail.
I'm making an app that receives constant updates (potentially hundreds of times a day) and, to make for a better user experience, it would be nice to have these downloaded in the background.
Looking at Apple's[1] documentation I need to set the background mode to "Background fetch". Exploring deeper you can read about the application:performFetchWithCompletionHandler[2] function which states that:
When this method is called, your app has up to 30 seconds of wall-clock time to perform the download operation and call the specified completion handler block... If your app takes a long time to call the completion handler, it may be given fewer future opportunities to fetch data in the future.
The problem is our downloads will take longer than 30 seconds to download, and as such would rather not face the wrath of Apple sending updates fewer and farther between, thus exacerbating the issue!
So, am I able to do this somehow?
Also, I have created a crude experiment whereby I create a NSTimer:scheduledTimerWithTimeInterval to run every minute which logs to the console. This successfully works both on the iPhone in simulation (has been running for 30 mins plus) and also when I place it on a phone (a week plus)... why would this be!?
It may be hard to do because of the Apple 30s obligation. They decided so to eventually prevent big download to happen not to drain battery and data plan.
You must be sure you really need to download that much data (as it takes this long) in background, plus hundred times a day!
I mean, when your app goes foreground after a (long) background period, it may not be updated and it's normal case. So you need to do the update when the app goes foreground; only one update is needed. Otherwise, you should step back and rethink the update process.
Found a solution:
You can bypass Apple's application:performFetchWithCompletionHandler function and set your own timer. To do this make sure you do the following:
Have selected Background fetch under Your app settings > "Capabilities" > "Background Modes".
In AppDelegate.m, within application:didFinishLaunchingWithOptions, add the following code:
[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
// Nothing
}];
You can now add a NSTimer in your AppDelegate and it will continue to run whilst in the background. E.g.,
_timerBg = [NSTimer scheduledTimerWithTimeInterval:1800
target:self
selector:#selector(bgFunction)
userInfo:nil
repeats:YES];
I have an iOS application where I need to persist a set of data after the application HARD CLOSES (when the user double clicks the Home button and slides the application upwards). Then, when the application comes back to the foreground, I need to fetch that data and do something with it. I'm just not sure where I need to put that logic for Application Hard Close and Resuming the Application.
In your AppDelegate
When your app is going to be closed, but still in Multitasking menu the following method is getting called
-(void)applicationWillResignActive:(UIApplication*)application
If after 3 minutes user doesn re-open your app this method is going to be called
-(void)applicationDidEnterBackground:(UIApplication*)application
If user re-opens your app from multitasking menu the following method is getting called
-(void)applicationWillEnterForeground:(UIApplication*)application
If user is going to close your app from multitasking menu this method is getting called(you will have limited time to perform some logic here)
-(void)applicationWillTerminate:(UIApplication*)application
When user presses home twice applicationWillResignActive and applicationDidEnterBackground is called. You can save data here.
When user opens app, applicationWillEnterForeground is called, you get data which you save and process.
When a user hard-closes the application, the UIViewController's Delegate's method called applicationWillTerminate. Here I can catch and save the model data before it's all destroyed.
Then when a user launches the application again, there are many choices like didFinishLaunchingWithOptions where I can grab the data stored to disk.
Your app no longer gets an applicationWillTerminate call ever. You are simply silently killed while in the background. You have to write your app to save state in the applicationDidEnterBackground handler, as nmh describes. That's your only option.