3

Overview

My compagny is asking me to release an application that can check location of the device every two hours. The app would send these location data through a TCP/IP socket to my server and then receive information accordingly to these very data (straight away, and through the same TCP/IP socket). So I'm not trying to make my application running in background mode continuously (which, actually, seems to be a hot topic in iOS, and it's also not suitable for my project).

Question

In order to be reliable, what is the best practice to achieve this? So, I would like to know:

  • Since my app is suspended (= inactive), does Apple allow to open a socket to send location when it's woken up by didUpdateToLocation?
  • How long do I have to perform my send/receive task via my socket?
  • Should I create a real background task with beginBackgroundTaskWithExpirationHandler and use the 10 minutes allowed by Cocoa to perform my send/receive task?
  • It is possible (and allowed by Apple) to ask for a 10 mins background task every 2 hours, without human interaction (ie. the user should re-open the app, etc)?

What I achieved/found so far

  • I added the location key in my Info.plist to be able to run the didUpdateToLocation handler when my app is inactive.
  • I am able to send and received data through a socket I have opened when my application was in foreground (= active).
  • I tried to check the backgroundTimeRemaining when didUpdateToLocation is called. I got a very large result number, which seems to be normal because, at this point, the applicationState is not in UIApplicationStateBackground but in UIApplicationStateActive.

These points are not very clear in the official documentation, and I did not found topics related to my specific case.

Thanks for your help.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • 1
    `beginBackgroundTaskWithExpirationHandler` does NOT create a background task, it marks the current thread as something that wants to continue to run if the app is backgrounded. – progrmr Jul 31 '13 at 15:20
  • 1
    @user1382094: Please remember to mark the correct answer as the accepted answer to your question, in case it helped you. Thanks! – veducm Jan 22 '14 at 11:50

1 Answers1

5

According to Apple's documentation, you can achieve these by using a very similar approach to the one you described. What I would do is something similar to what is explained in this post by mindsizzlers:

  • As a recommendation, turn on significant location updates when the app enters in background, so you save battery. You can do this when the app goes to background:

    - (void) applicationDidEnterBackground:(UIApplication *) application
    {
        // Create the location manager if you do not already have one.
        if (nil == locationManager)
            locationManager = [[CLLocationManager alloc] init];
    
        locationManager.delegate = self;
        [locationManager startMonitoringSignificantLocationChanges];
    }
    

    Then, the system will wake your app when location changes, as explained in the documentation.

    If you leave this service running and your app is subsequently suspended or terminated, the service automatically wakes up your app when new location data arrives. At wake-up time, your app is put into the background and given a small amount of time to process the location data. Because your app is in the background, it should do minimal work and avoid any tasks (such as querying the network) that might prevent it from returning before the allocated time expires. If it does not, your app may be terminated.

    In order to avoid the action of sending the new location to the server from being highly unreliable (it may work sometimes) you should tell iOS in advance that you are doing a background task that should be allowed to run to completion.

  • Change your location manager delegate (didUpdateToLocation) to handle background location updates.

    - (void) locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation
    fromLocation:(CLLocation *)oldLocation
    {
        if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) {
            // Send the new location to your server in a background task
            // bgTask is defined as an instance variable of type UIBackgroundTaskIdentifier
            bgTask = [[UIApplication sharedApplication] 
            beginBackgroundTaskWithExpirationHandler:
            ^{
                [[UIApplication sharedApplication] endBackgroundTask:bgTask];
            }];
    
            // Make a SYNCHRONOUS call to send the new location to our server
    
            // Close the task
            if (bgTask != UIBackgroundTaskInvalid) {
                [[UIApplication sharedApplication] endBackgroundTask:bgTask];
                bgTask = UIBackgroundTaskInvalid;
            }
    
        } else {
                // Handle location updates in the normal way
        }   
    }
    

This way, you will not need to have the timer running. Since you will be waken up automatically and sending the updated location to your server every time it changes significantly.

Of course, if you want to make sure this happens in a specific interval, you can still go with the approach of setting a timer to start receiving location updates and as soon as you get it, you send it to the server. Take a look to this post talking about Background Modes in iOS (section: Receiving Location Updates) and this other questions to see how to do this in detail.

Community
  • 1
  • 1
veducm
  • 5,933
  • 2
  • 34
  • 40