iOS Tutorial Background location tracking

iLocation library allows you to track the device location when your app is in the foreground.

Using the code posted in this tutorial you can mark your app as a special kind of app that requires background location updates and then your app will continue to run in the background and receive location updates until you call LocationManager.Stop or until the user kills the app.

Apple's documentation: https://developer.apple.com/library...n.html#//apple_ref/doc/uid/TP40009497-CH2-SW3

The first step is to mark the app for background execution:
B4X:
#PlistExtra: <key>UIBackgroundModes</key><array><string>location</string></array>
We also need to describe the reason for the location usage:
B4X:
#PlistExtra:<key>NSLocationAlwaysUsageDescription</key><string>Track your location in the background for better ad revenue.</string>
#PlistExtra:<key>NSLocationUsageDescription</key><string>Used to display the current navigation data.</string>
On iOS 7 the NSLocationUsageDescription string will be used. On iOS 8+ the NSLocationAlwaysUsageDescription string will be used. This replaces the standard NSLocationWhenInUseUsageDescription string. Make sure to update the strings as needed.

Two additional changes:
1. You need to call StartBackground sub instead of Location.Start
2. (optional) Call AllowPauseLocationAutomatically sub and set the activity sub. The OS will use this information to pause the location updates when the location is not expected to change.
You can read more about the activities types: https://developer.apple.com/library.../index.html#//apple_ref/c/tdef/CLActivityType

Edit: It is recommended to disable the AllowPauseLocationAutomatically as explained in post #10.

See the code in the attached project.
Note that the debugger will eventually disconnect when the app is in the background.

Edit: September 2018 - Added new required usage key:
B4X:
#PlistExtra:<key>NSLocationAlwaysAndWhenInUseUsageDescription</key><string>Track your location in the background for better ad revenue.</string>
 

Attachments

  • BackgroundLocation.zip
    2.6 KB · Views: 805
Last edited:

fbritop

Active Member
Licensed User
Longtime User
Thanks Erel,
The problem was when we use multiple instances of UIBackgroundModes:

B4X:
       '#PlistExtra: <key>UIBackgroundModes</key><array><string>location</string></array>
  
    #PlistExtra:<key>UIBackgroundModes</key><array><string>location</string><string>remote-notification</string><string>bluetooth-central</string></array>
 

franchsesko

Member
Licensed User
Longtime User
Hi,

I'm try to get the OBJC part to work but I get a runtime error when my StartBackground() method is called.
Would you have any idea why ?

This is the error
B4X:
Application_Start
Error occurred on line: 67 (Main)
Method not found: isSignificantLocationAvailable, target: <B4ILocationManager: 0x156552b0>
Stack Trace: (
  CoreFoundation       <redacted> + 150
  libobjc.A.dylib      objc_exception_throw + 38
  CoreFoundation       <redacted> + 0
  LocationManagerTest  +[B4I runDynamicMethod:method:throwErrorIfMissing:args:] + 420
  LocationManagerTest  -[B4INativeObject RunMethod::] + 158
  LocationManagerTest  -[b4i_main _startbackground::] + 1114
  LocationManagerTest  -[b4i_main _application_start:] + 3128
  CoreFoundation       <redacted> + 68
  CoreFoundation       <redacted> + 292
  LocationManagerTest  +[B4I runDynamicMethod:method:throwErrorIfMissing:args:] + 1784
 LocationManagerTest  -[B4IShell runMethod:] + 574
 LocationManagerTest  -[B4IShell raiseEventImpl:method:args::] + 2212
 LocationManagerTest  -[B4IShellBI raiseEvent:event:params:] + 1442
 LocationManagerTest  __33-[B4I raiseUIEvent:event:params:]_block_invoke + 74
 libdispatch.dylib    <redacted> + 10
 libdispatch.dylib    <redacted> + 22
 libdispatch.dylib    <redacted> + 1524
 CoreFoundation       <redacted> + 8
 CoreFoundation       <redacted> + 1574
 CoreFoundation       CFRunLoopRunSpecific + 520
 CoreFoundation       CFRunLoopRunInMode + 108
 GraphicsServices     GSEventRunModal + 160
 UIKit                UIApplicationMain + 144
 LocationManagerTest  main + 106
 libdyld.dylib        <redacted> + 2
)
Application_Active

and this is my code:
B4X:
#If OBJC

- (BOOL)isSignificantLocationAvailable {
    if([CLLocationManager significantLocationChangeMonitoringAvailable]) {
      return YES;
    }
    return NO;
}

#end if

Sub StartBackground(lm As LocationManager, MinimumDistance As Double)
    Dim no As NativeObject = lm
    Dim isSignLocAvailable As Boolean = no.RunMethod("isSignificantLocationAvailable", Null).AsBoolean
   
    no = no.GetField("manager")
    If App.OSVersion >= 8 Then
        no.RunMethod("requestAlwaysAuthorization", Null)
    End If
    If App.OSVersion >= 9 Then
        no.RunMethod("setAllowsBackgroundLocationUpdates:", Array(True))
    End If
    no.SetField("distanceFilter", MinimumDistance)

  If isSignLocAvailable = True Then
    no.RunMethod("startMonitoringSignificantLocationChanges",Null)
  Else
        no.RunMethod("startUpdatingLocation", Null)
  End If
End Sub
 

franchsesko

Member
Licensed User
Longtime User
Yeah, sorry, got it.
I was not invoking the method on the correct object, here's my own fix.

B4X:
Sub StartBackground(lm As LocationManager, MinimumDistance As Double)
    Dim no As NativeObject = lm
    Dim NativeMe As NativeObject = Me
    Dim isSignLocAvailable As Boolean = NativeMe.RunMethod("isSignificantLocationAvailable", Null).AsBoolean

    no = no.GetField("manager")
    If App.OSVersion >= 8 Then
        no.RunMethod("requestAlwaysAuthorization", Null)
    End If
    If App.OSVersion >= 9 Then
        no.RunMethod("setAllowsBackgroundLocationUpdates:", Array(True))
    End If
    no.SetField("distanceFilter", MinimumDistance)

  If isSignLocAvailable = True Then
    no.RunMethod("startMonitoringSignificantLocationChanges",Null)
  Else
        no.RunMethod("startUpdatingLocation", Null)
  End If
End Sub
 

b4xscripter

Member
Licensed User
Longtime User
Hi!
When I tried to publish my app in App Store, I got this issue:

*******
Your app declares support for location in the UIBackgroundModes key in your Info.plist file but does not have any features that require persistent location. Apps that declare support for location in the UIBackgroundModes key in your Info.plist file must have features that require persistent location.
*******


in my plist I have:


B4X:
	#PlistExtra:<key>NSLocationWhenInUseUsageDescription</key><string>Used to display the current navigation data.</string>
	#PlistExtra:<key>NSLocationUsageDescription</key><string>Used to display the current navigation data.</string>
	#PlistExtra:<key>NSLocationAlwaysUsageDescription</key><string>Track your location in the background for better ad revenue.</string>
	#PlistExtra:<key>NSLocationUsageDescription</key><string>Used to display the current navigation data.</string>
    #PlistExtra: <key>UIBackgroundModes</key><array><string>location</string></array>

and my code is similar to the example in this thread:

B4X:
Sub StartBackground(lm As LocationManager, MinimumDistance As Double)
	Dim no As NativeObject = lm
	no = no.GetField("manager")
	If App.OSVersion >= 8 Then
		no.RunMethod("requestAlwaysAuthorization", Null)
	End If
	If App.OSVersion >= 9 Then
		no.RunMethod("setAllowsBackgroundLocationUpdates:", Array(True))
	End If
	no.SetField("distanceFilter", MinimumDistance)
	no.RunMethod("startUpdatingLocation", Null)
End Sub

B4X:
Private Sub Application_Start (Nav As NavigationController)

	
	NavControl = Nav
	Page1.Initialize("Page1")
	Page1.RootPanel.LoadLayout("Page1")
	NavControl.ShowPage(Page1)
	
	App.RegisterUserNotifications(True, True, True) 'request permission for notifications
	App.ApplicationIconBadgeNumber = 0
	
	If App.LaunchOptions.IsInitialized Then
		Dim ln As Notification = App.LaunchOptions.Get("UIApplicationLaunchOptionsLocalNotificationKey")
		If ln.IsInitialized Then
			'hd.ToastMessageShow("Application was started from a notification: " & ln.AlertBody, True)
		End If
	End If
	
	fm.Initialize("fm")
	WKWebView3.Loadurl("http://85.88.153.23.nip.io:7095/promotions")
	Dim no As NativeObject = WKWebView3
	no.GetField("scrollView").SetField("bounces", False)
	
	
	
	LocManager.Initialize("LocManager")
	
	'in background....
	Dim no As NativeObject = LocManager
	no = no.GetField("manager")
	no.SetField("pausesLocationUpdatesAutomatically", False)
	StartBackground(LocManager, 0) 'replaces locManager.Start
	'in background....
	
	LocManager_AuthorizationStatusChanged(0)
	tn.Initialize("tn")


End Sub


Sub AllowPauseLocationAutomatically(lm As LocationManager, ActivityType As Int) 'ignore
	Dim no As NativeObject = lm
	no = no.GetField("manager")
	no.SetField("activityType", ActivityType)
	no.SetField("pausesLocationUpdatesAutomatically", True)
End Sub




Private Sub LocManager_LocationChanged (Location1 As Location)
	Dim job As HttpJob
	job.Initialize("gps", Me)
	job.PostString(serverLink & "/gps", "lat=" & Location1.Latitude & "&lon=" & Location1.Longitude & "&speed=" & Location1.Speed & "&altitude=" & Location1.Altitude & "&os=" & "ios"  & "&userId=" & userId)
	Dim job As HttpJob
	job.Initialize("getBeacons", Me)
	job.PostString(serverLink & "/getBeacons", "lat=" & Location1.Latitude & "&lon=" & Location1.Longitude & "&speed=" & Location1.Speed & "&altitude=" & Location1.Altitude )
End Sub


Why does Apple ask me that if I really use the app in background?

I am a bit confused...

Thank you for your help!
 
D

Deleted member 103

Guest
On iOS 7 the NSLocationUsageDescription string will be used. On iOS 8+ the NSLocationAlwaysUsageDescription string will be used. This replaces the standard NSLocationWhenInUseUsageDescription string. Make sure to update the strings as needed.
If I understood that correctly, this means from iOS 8+ only the string "NSLocationAlwaysUsageDescription" is needed, right?
 

Starchild

Active Member
Licensed User
Longtime User
With distanceFilter set to 0, should I expect continuous Location events? (every second or so)

I am only getting 3 or 4 initial events when the App first starts then no more.
 

Starchild

Active Member
Licensed User
Longtime User
Are you testing it in release mode?

Yes. Relese mode.

I was wondering what to expect when the distanceFilter was set to 0.
It didn't seem to generate gps events. I think I still need to move location.

Interesting:
After leaving my Ipad sit overnight, it has now generated hundreds of gps events.
They now seem to be about 1 every 5 seconds (in forebround). Although when the Ipad is closed the
frequency significantly slows down. Maybe 1 every 30 seconds or so (in Background).

Just looking for what are the factors affecting the gps events, both in foreground and background operation, so I can develop a reliable and expected performance APP. Would appreciate any applicable info relating to this.

I am running the demo from the top post.
NB: my Ipad is IOS 10.3.1
 

sdesan

Member
Licensed User
Longtime User
I tested the code and works fine. I have inserted a simple msgbox to notify when i'm near a specific GPS point
B4X:
p1.Initialize2("+42.0559340","+13.9242163") 'target 
p3.Initialize2(Location1.Latitude,Location1.Longitude)
Dim Distance As Float = p1.DistanceTo(p3)
i also write in log same notification, but when the application is in background the log is writed but msgbox, obviusly, in not showed so i need to know how to move from backgroud to foreground the app before to show any message.
Thank you
 

sdesan

Member
Licensed User
Longtime User
Thank you Erel
Must i do something like this ? https://www.b4x.com/android/forum/t...cal-notification-on-ios-like-whatsapp.100257/ or there is a better way to show local notification? And what it's best way for understanding if app is in foreground or in background?
I have inserted
B4X:
App.RegisterUserNotifications(True, True, True)
        Dim n As Notification
        n.Initialize(DateTime.Now + 5000)
        n.AlertBody = "Position found"
        If App.OSVersion >= 8.2 Then
            Dim no As NativeObject = n
            no.SetField("Open the app to read more information", "Position found")
        End If
        n.Register
and it works very well.
Thank you Erel
 
Last edited:

sdesan

Member
Licensed User
Longtime User
Just a little update and a question: i'll like to play a custom sound on notification so i made a little change based on https://www.b4x.com/android/forum/t...e-for-local-or-scheduled-notifications.54940/

B4X:
Sub LocManager_LocationChanged (Location1 As Location)
    'Check position
    p1.Initialize2("+42.0559340","+13.9242163") 
    p3.Initialize2(Location1.Latitude,Location1.Longitude)
    Dim Distance As Float = p1.DistanceTo(p3)
    'Log("Distance is "&Distance&" meters")
    If Distance < 1000 Then
        label4.Text=NumberFormat(Distance,2,2)  & " metri"
    Else
        label4.Text="Too far..."
    End If
    ControllaPosizione(Distance)
End Sub
Sub ControllaPosizione(distanza As Float)
    If distanza<=20 
            'Sending notification
            App.RegisterUserNotifications(True, True, True)
            Dim n As Notification
            n.Initialize(DateTime.Now)   
            Dim no As NativeObject = n
            n.PlaySound = True
            n.AlertBody = "My messagei."
            n.Register
            If App.OSVersion >= 8.2 Then   
                no.SetField("alertTitle", "Check point")
                'Custom sound
                no.SetField("soundName", "ring2.wav")
            End If
            MostraTesto1.RootPanel.LoadLayout("ShowInfo")
            NavControl.ShowPage(MostraTesto1)
            myPhone.Vibrate
            mp1.Initialize(File.DirAssets,"ring.mp3","MPlayer")
            mp1.play
            distanza=0
    End If
End Sub
Unfortunately i hear only the default notification sound. In post 54940 i can read
You should run this code before you register the notification (the code itself is correct).
but i don't understand where i have to move/put the right code
Furthermore when i install the app on the phone the first time appears correctly a request to use gps location, but for notification the request for allow or not the request appears only first time that there is a notification but if the smartphone it's not at hand i don't receive any notification except the one 'do you want to allow notifications'?
So it's possible to request directly at installation 'do you want to allow notifications'? changing/adding a row somewhere, my be in Project Attributes?
 
Top