Amazon S3 Upload using NSURLSession in the background - ios

I've been doing a application which involves uploading videos to S3(comparatively large size) using AWS iOS SDK enabling background mode/fetch.
However I have observed that the background upload task continues for a while approx 3 mins and then it gets suspended/app gets killed.
MyApp[2548] has active assertions beyond permitted time:
{(
identifier: Background Content Fetching (66) process: MyApp[2548] permittedBackgroundDuration: 30.000000 reason: backgroundContentFetching owner pid:33 preventSuspend preventThrottleDownUI preventIdleSleep preventSuspendOnSleep
)}
The issue is very much explained in here https://trovepromo-tf.trove-st...
The answer given here "If you do not call the completionHandler within 30 seconds your app will crash in the background" seems right. So Ive implemented URLSessionDidFinishEventsForBackgroundURLSession: in my VC and
handleEventsForBackgroundURLSession:
in AppDelegate.
However the problem is still that the upload gets suspended after 3 minutes( I think OS does this)
My understanding is that URLSessionDidFinishEventsForBackgroundURLSession: delegate is called before this shutdown/suspension. Am I right about this?
Is there anyway I could continue the upload from inside the URLSessionDidFinishEventsForBackgroundURLSession:
somehow?
Or is there any automatic way that the upload process continues regardless of the fact that app has been woken up and put back to sleep.

Related

iOS why system kills the app using location in background

I have an app that uses the location updates when it is in the foreground as well as in the background. Using the CoreLocation framework, I have implemented the app so that location updates are sent to the server after every 5 minutes, using this code as a reference.
This works fine in foreground, but when the app goes to the background, it is getting killed by the OS after 30 minutes to an hour. I want the app to get updates for at least 8 hours, even in the background.
Also, the app is using the about 10% of the battery per hour. Is this related to the app being killed in the background? If so, then how can I resolve the battery problem? Otherwise, can anyone tell me what the issue is?
Below is the crash log for the device:
Exception Type: 00000020
Exception Codes: 0x000000008badf00d
Exception Note: SIMULATED (this is NOT a crash)
Highlighted by Thread: 2
Application Specific Information:
<BKNewProcess: 0x17e74840; com.app.app; pid: 560; hostpid: -1> has active assertions beyond permitted time:
{(
<BKProcessAssertion: 0x17d78740> id: 560-C9E81E97-90D9-4F95-871E-3DC53372F302 name: Called by UIKit, from <redacted> process: <BKNewProcess: 0x17e74840; com.app.example; pid: 560; hostpid: -1> permittedBackgroundDuration: 180.000000 reason: finishTask owner pid:560 preventSuspend preventIdleSleep preventSuspendOnSleep ,
<BKProcessAssertion: 0x17e6a870> id: 560-BD7B29FC-DABC-42FF-AF17-B277BDB1C59D name: Called by UIKit, from <redacted> process: <BKNewProcess: 0x17e74840; com.app.example; pid: 560; hostpid: -1> permittedBackgroundDuration: 180.000000 reason: finishTask owner pid:560 preventSuspend preventIdleSleep preventSuspendOnSleep
)}
For the background task I use the following function:
func backgroundTask(){
var application=UIApplication.sharedApplication()
var background_task: UIBackgroundTaskIdentifier?
background_task = application.beginBackgroundTaskWithExpirationHandler({() -> Void in
application.endBackgroundTask(background_task!)
background_task = UIBackgroundTaskInvalid
})
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {() -> Void in
//run the app without startUpdatingLocation. backgroundTimeRemaining decremented from 600.00
self.locationManager.startUpdatingLocation()
while (true) {
//backgroundTimeRemaining time does not go down.
print("Background time Remaining: \(UIApplication.sharedApplication().backgroundTimeRemaining)")
NSThread.sleepForTimeInterval(1)
break
//wait for 1 sec
}
application.endBackgroundTask(background_task!)
background_task = UIBackgroundTaskInvalid
})
}
When your app enters in background state switch to significant location updates and your app will receive location update continuously. you can call startMonitoringSignificantLocationChanges on CLLocationManger's object i think. And you not need to establish background task also i think.
Check the Apple Documentation, It states,
If you start this service and your app is subsequently terminated, the system automatically relaunches the app into the background if a new event arrives. In such a case, the options dictionary passed to the application:willFinishLaunchingWithOptions: and application:didFinishLaunchingWithOptions: methods of your app delegate contains the key UIApplicationLaunchOptionsLocationKey to indicate that your app was launched because of a location event. Upon relaunch, you must still configure a location manager object and call this method to continue receiving location events. When you restart location services, the current event is delivered to your delegate immediately. In addition, the location property of your location manager object is populated with the most recent location object even before you start location services
So, it will solve your problem i think and it will solve issue of battery also.
Second thing (for battery consumption), You should not set DesiredAccuracy to kCLLocationAccuracyBest when want to update location in background for long time. You can set kCLLocationAccuracyThreeKilometers as DesiredAccuracy and you can set setDistanceFilter to very big digit like 99999 when enter in background.
You can refer this so post and this so post.
Hope this will help :)
Do you have any crash log.If application not terminated by exception of some hidden bug you should suspicious of memory pressure.I think this article will lead you to find reason of sudden termination
https://www.raywenderlich.com/23704/demystifying-ios-application-crash-logs
As #Lion said use Significant Location Change when entering in background. I had the same issue using SLC so you will have it too. When the app was entering the background it was killed by the system due to memory warnings. So what I did it was to create a singleton for CoreLocation which will receive all the delegates calls of CoreLocation.
In order to restart you service in background in case it's getting killed:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if ([launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey]) {
//NSLog(#"Restarting SCL");
LocationService *loc = [LocationService sharedInstance];
[loc setManagedObjectContext:self.managedObjectContext];
}
LocationService is my singleton.
Also implement this function in AppDelegate to handle Memory Warnings Notifications:
-(void)applicationDidReceiveMemoryWarning:(UIApplication *)application
{
//Sending notification to every controller to free some memory
[[NSNotificationCenter defaultCenter] postNotificationName:#"freeMemory" object:nil userInfo:nil];
SDImageCache *imageCache = [SDImageCache sharedImageCache];
[imageCache clearMemory];
[imageCache clearDisk];
//NSLog(#"Received memory warning!");
}
For example clear the image cache.
Also I you are are using MapView make sure to nullify whatever you don't use since is very expensive view (and buggy with leaks).
As other note, you need to register to receive background location updates. What may not be clear is that this is not the same thing as "running in the background all the time doing whatever you feel like." It means that at various times, at the system's discretion, you will be sent location data, you must deal with that location data as quickly as you can and then return. You will be called again when the system chooses to. There may be other location services running. The system tries to optimize this by coalescing all the different location clients.
Background tasks, like the one you're trying to use, are for a completely different problem. They're to request "a little extra time" to get something done when the user exits the app. Something like clean up your database or the like. If you let them keep running, you'll get killed. Looking at your current code, it doesn't look like it actually does anything though, since it looks like it breaks out of the loop after one call. But any code that calls NSThread.sleepForTimeInterval(1) is almost guaranteed to be incorrect in iOS. There are almost no reasons ever to make that call. But you shouldn't need a background task to manage location updates.
I am posting this answer because Everyone is said that it is OS default behavior, We can not change it....bla....bla.
Recently, I was working with the same requirement. After 2-3 week hard work, I did it. For other users, I create a helper class for it. My app will never be killed by OS until the location tracking running.
Use HSLocationManager for infinite location tracking in the active and inactive state.
Refer my app which is available in the app store(App will never kill by OS if location tracking is running)
Location manager that allows getting background location updates every
n seconds with desired location accuracy.
Advantage:
OS will never kill our app if the location manager is currently
running.
Give periodically location update when it required(range is between 2 -
170 seconds (limited by max allowed background task time))
Customizable location accuracy and time period.
Low memory consumption(Singleton class)

Frequent time interval background fetch made

I have developed two different applications
App1 - With Healthkit enabled.
App2 - ionic application
App1 task : Read data from healthkit which is store in the server.
App2 task : Retrieve the stored data from server and display.
I started App2 from App1 using openURLScheme. So App1 running on the background mode and also It should be continued more than 3 mins to an hour.
I tried following Scenario:
bgTask = self.applicationUI!.beginBackgroundTaskWithName("MyTask", expirationHandler: { () -> Void in
self.applicationUI!.endBackgroundTask(self.bgTask!)
self.bgTask = UIBackgroundTaskInvalid;
})
self.bgTask = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler({ () -> Void in
self.monitorInBackground()
})
I get error : permittedBackgroundDuration: 180.000000 reason: finishTask
extent background process for next 3 minute (After IOS 7 introduce. before IOS 7, the process execution time was 10 minute).
Note :
I hope it can be feasible using APNS silent notification. But I expected better solution other than the APNS.
Apple has a good section on background execution in their documentation.
The two ways of doing this are a silent notification (as you suggest) and background fetch.
The "pro" of using a silent notification is that you can control when it happens fairly precisely as long as your user is online. (Which they probably have to be to access the server anyway.) But, yes, it adds a lot of complexity.
Background fetch works nicely, but you don't get much control over when it happens. This may or may not be a problem, depending on what your app does.
Other options that might work include background audio, location updates and VoIP, but they might get you rejected.
Just running a background task won't work -- that's designed for finishing off tasks rather than keeping them running for a long time.

iOS Newsstand download failed when shut down the app

I'm having difficulty in the download using NewsstandKit when the app shut down.
My newsstand app download does starts in Background Mode, and exit to the background or push a download notification, the download issue everything is OK, but i shut down the app then the task is downloading,the downloading task failed.
Does it has to do some thing with the Server? or others?
You need to resume your downloads when the App Launches again.
Something to the tune of this would work
// Inside App Delegate Did Finish Launching
NKLibrary *nkLib = [NKLibrary sharedLibrary];
for(NKAssetDownload *asset in [nkLib downloadingAssets]){
[asset downloadWithDelegate:newsstandDownloadDelegate];
}
If you read the documentation you will find the following paragraph detailing how you should handle app termination:
While assets are being downloaded, the application could be suspended
or even terminated entirely if, for instance, there is insufficient
memory or the user force-quits the application. The application
(assuming it has the newsstand-content property) is later relaunched
into the background to handle the completion of the download or any
authentication challenges or errors related to it. The procedure in
this case is the following:
When the application is launched, it can use the
UIApplicationLaunchOptionsNewsstandDownloadsKey key to access an array
in the launchOptions dictionary passed into the
application:didFinishLaunchingWithOptions:. If there is an array, it
contains the identifiers that caused the launch; these could be
identifiers for downloads that have finished downloading that or could
not complete downloading. It iterates through the array of
NKAssetDownload objects from the downloadingAssets property of the
NKLibrary object and calls downloadWithDelegate: on each of them. If
it wants, the application can use the asset identifiers obtained in
the previous step to check which asset downloads caused the relaunch.
The NSURLConnectionDownloadDelegate object handles the asset downloads
as it does normally. The Newsstand Kit framework expects all calls of
its methods to be made on the main thread; the NSURLConnectionDelegate
Protocol methods are also invoked on the main thread.

ios: inapp purchase content stops downloading from Apple server after 95 percent download

I am using IAP in my app and hosting content on apple server. After purchase of product, downloading starts normally. I am tracking downloading through progress bar. But when download reaches 95 percent transaction gets removed and it doesn't get finish callback:
2013-11-14 11:54:06.365 InAppPurchaseDemo[10365:60b] progress ::0.950000
2013-11-14 11:54:07.826 InAppPurchaseDemo[10365:60b] downloadContentIdentifier com.mycompanyName.myapp.product1
2013-11-14 11:54:09.411 InAppPurchaseDemo[10365:60b] send Notification
2013-11-14 11:54:09.519 InAppPurchaseDemo[10365:60b] transaction removed
My application is live on app store and some of my users are facing this problem and on some user devices it is working fine.
I am testing IAP on device FYI.
I don't know what part of code I should post for this so not posted it yet.
Are you sure it is not actually finished? When I output the progress of my hosted content download it also doesn't go past 95%. However, it does finish and I get notified that is complete.
Are you perhaps testing it in the simulator? It doesn't work there. You must use a real device.

Not calling didBecomeActive if background task expires

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.

Resources