12

I'm making a benchmark App for test purposes ONLY. I am not intending this to go to the App Store.

What I need is my NSTimer to continue running on the background using a UIBackgroundTaskIdentifier, save data to a Core Data db and finally push the data to a server (I'm using Parse), after a certain time interval, of course.

So basically, I haven´t found any questions which apply to my specific case. I set my NSTimer like so:

    UIBackgroundTaskIdentifier bgTask;
UIApplication  *app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
    [app endBackgroundTask:bgTask]; 
}];

self.timer = [NSTimer scheduledTimerWithTimeInterval:self.localInterval target:self selector:@selector(updateCoreData:) userInfo:nil repeats:YES];

the method updateCoreData simply calls the Core Data class and makes the necessary insertions.

I've read about VoIP and the Music playing part, but don't know exactly which one would apply best for my case, nor how to implement them.

LilDwarf
  • 543
  • 1
  • 4
  • 14
  • 1
    In your timer handler function you could use Great Central Dispatch to do the lengthy tasks. This way you don't block your event handler from being called. – guitarflow Feb 09 '12 at 23:29
  • Are you successful doing this? I have same task and I am trying so hard but yet not successful........Can you help me? – Minakshi Feb 28 '12 at 07:12
  • @Xyz Yes, I managed to make it. To make the NSTimers work at the background, the code I provided works. What happens is after 10 minutes or so of being inactive, the app shuts down. To prevent this, I used Location Services instead of VoIP, since it's just easier to implement. I turned the flag on on the Info.plist file, then I made a timer that started the location services, and stopped it right away every 9 minutes. It worked like a charm ;) If you need a sample code, let me know. – LilDwarf Mar 02 '12 at 18:01
  • Ya, please provide me code....I really need it....I am working on this more than a month... – Minakshi Mar 07 '12 at 11:10
  • @Xyz, check out my answer for the question ;) – LilDwarf Mar 08 '12 at 19:18
  • I know it's an old question, but still - Any chance of your app making to app store? I'm trying to avoid paying $299 for enterprise distribution... – selytch Mar 09 '13 at 05:58
  • @selytch Highly unlikely haha If an app has those flags to keep it running on the background, they have to be really well justified to get accepted on the app store. If you'd like I can share the project on github, just send me a reply or whatever – LilDwarf Mar 14 '13 at 03:33

2 Answers2

37

I figured it out by myself, for anyone else with a similar problem, what I did was to first turn on the location update flag on your Info.plist file. To do this, you must add the Key called "Required Background Modes" on Xcode 4, then as a value, select "App registers for location updates"

I have a timer declared like so in my .h file:

NSTimer *silenceTimer;
@property (nonatomic, retain) NSTimer *silenceTimer;

Then on the .m file, I declared it like so:

UIBackgroundTaskIdentifier bgTask;
UIApplication  *app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
    [app endBackgroundTask:bgTask]; 
}];
self.silenceTimer = [NSTimer scheduledTimerWithTimeInterval:300 target:self
selector:@selector(startLocationServices) userInfo:nil repeats:YES];

Finally, on the selector method, I made the following:

-(void)startLocationServices {
    [locationManager startUpdatingLocation];
    [locationManager stopUpdatingLocation];
}

This will create a timer that starts and immediately stops location services after 5 minutes. This will be enough for the app to stay alive indefinately, unless you kill the process.

LilDwarf
  • 543
  • 1
  • 4
  • 14
  • Thanks for your nice information.Can you tell how I can get the location update every 15 mins. I will be really thankful. – Cyril Apr 13 '12 at 10:29
  • @Cyril, yes, you can use the same code, but instead of 300 in the timeinterval, put 900 (900 seconds = 15 min). This should execute the startLocationServices() method every 15 minutes, which activates the locationManager. For your case, i recommend removing the line [locationManager stopUpdatingLocation] and replacing it after you execute your code from the locationManagerDelegate – LilDwarf Jun 19 '12 at 18:33
  • Wow, this is a horrific abuse of the location services exemption for background processes. I'm surprised it even works, I would have thought that stopping location services would suspend your app. In any case, this is something that should rightly get your app rejected from the store. – Lily Ballard Aug 20 '12 at 06:51
  • @KevinBallard That's exactly why I mentioned at the very start of the post and I quote "I'm making a benchmark App for test purposes ONLY. I am not intending this to go to the App Store." Good to know you read the post before commenting – LilDwarf Aug 20 '12 at 22:45
  • @LilDwarf: Sorry, I came here through a link someone else pasted in a similar question (that had no non-app-store caveat). – Lily Ballard Aug 20 '12 at 22:51
  • It is true with this trick that the background task never expires and keeps the app running. But the side affect is the phone never sleeps. Try pinging your phone's IP and you will see it stays awake constantly even when the screen is off. Can this trick be modified to allow the phone's CPU to sleep as normal and maintain good battery life in our non-AppStore test apps? – malhal Jan 31 '13 at 04:35
  • 1
    money! best and most concise solution for this I have seen yet – Tom DeMille Feb 21 '13 at 19:57
  • @indiekiduk I' not sure what you mean, for example if you'd like to maintain the phone's CPU to sleep as normal, you should not use this trick at all, since the purpose of it is to never let the CPU sleep. – LilDwarf Mar 14 '13 at 03:35
  • @LilDwarf i'm using your solution for my app. but we are using the location data for user's checkin. will this get rejected by apple? – name-it Mar 19 '13 at 14:34
  • 1
    @Rajapandian Oh... not exactly. If it is well justified, it can get submitted. You can go ahead and submit it and if it gets rejected you can state your reasons for doing whatever you did wrong (if it involves this particular solution). – LilDwarf Mar 20 '13 at 04:44
  • @LilDwarf app close after around 30 to 40 hours. you have any idea why app closes? and also i'm getting memory leak when ever timer updates the location. The leak is `CLLocationInternal` from the CLLocation library. http://stackoverflow.com/questions/15806915/memory-leak-from-cllocationmanagercllocationinternal?noredirect=1#comment22482501_15806915 – name-it Apr 04 '13 at 10:53
  • @LilDwarf will it work on iOS 8 and above because timer are getting stopped in 180 sec though – hardikdevios Jun 21 '15 at 19:09
  • @hardik.shah I haven't tested it... the solution was made way back for iOS 5 I believe... it should still work, I haven't read the docs for any changes made on background tasks or timers, I'll test it out these days and make an update if necessary. – LilDwarf Jun 22 '15 at 21:11
  • @LilDwarf thanks bro b3cuase in my case it works in development mode but not on production but still waiting to try again with you4 feedback – hardikdevios Jun 22 '15 at 21:29
  • Has anyone got an app through the app store using this code though? – Stu P. Jan 25 '16 at 18:30
2

XCode 6.3.1, for iOS 8.3

There are big diffrences that I've encountered between the apps. behavior while plugged in to your Mac running XCode using debugger and the same apps. behavior while unplugged.

At current iOS version, 8.3, at most your app is allotted is 180 seconds to run in the background for finite long running task with an expiration handler.

No matter what you try, with your phone unplugged your app will be killed by the system in 180 seconds or earlier. I've done a significant amounts of tests and tricks to confirm this. See my post...

My NSTimer Post

Here is a neat way to tell how much time is left for your app to run before termination, keep in mind that you are not guaranteed this time.

NSTimeInterval backgroundTimeRemaining = [[UIApplication sharedApplication] backgroundTimeRemaining];

if (backgroundTimeRemaining == DBL_MAX)
{
    NSLog(@"background time remaining = undetermined");
}
else
{
    NSLog(@"background time remaining = %0.2f sec.", backgroundTimeRemaining);
}

Hope this helps your investigation. Cheers!

Community
  • 1
  • 1
serge-k
  • 3,394
  • 2
  • 24
  • 55
  • mate you are absolutely correct now we will get is 180 sec so do your work in 180 sec after that there is no way you get extra time in background , its true its working in background more then 180 sec in xcode debugger but not in production – hardikdevios Jun 28 '15 at 13:33