iOS SDK
The MOTIONTAG Mobility & Location Analytics SDK collects an iPhone's raw sensor data in a battery efficient way. This data is then transmitted to the MotionTag back-end system (ISO 27001 certified). In the backend, the sensor events are processed and a partial journey is constructed. Journeys consist either solely of tracks or tracks plus stays. Tracks describe a movement from an origin to a destination with a certain mode of transport. Stays symbolize a stationary behaviour with a particular purpose for the visit.
The use cases for the SDK are manifold. In the mobility sector it can be used to get detailed mobility data for planning purposes. The collected data enables to compare how the transport network is being used. This way the effectiveness of the current infrastructure and the passenger flow management is measured and the design of new mobility services. By implementing and using the SDK you can make use of these findings to improve timetables and routes, expand transport supply and attract more passengers.
If you integrate the MOTIONTAG Tracker SDK inside your own application, you can either download user journeys via a provided dump interface on the admin dashboard, or we tailor a customized solution to your needs.
1. Installation
You can integrate the MOTIONTAG Tracker SDK into your Xcode project using SPM, CocoaPods, or Flutter;
On Swift Package Manager (SPM), add the SDK by entering the package link SPM URL and either use Exact version: x.x.x OR the master branch in the dependency rules.
On CocoaPods, simply add the following line to your Podfile:
pod 'MotionTagSDK'
You can also integrate a specific version:
pod 'MotionTagSDK', '~> x.x.x'
On Flutter, the SDK can be found on pub.dev URL. You can find an example implementation and further documentation on MOTIONTAG Flutter URL.
2. Changelog
You can find a summary of the changes to the latest SDK version in our changelog page.
3. Native sample app
A native iOS sample app can be found on our GitHub page: https://github.com/MOTIONTAG/motiontag-sample-app-ios This sample app showcases the best practices in integrating the SDK into native iOS apps.
4. Required App Settings
Capabilities -> Background Modes:
- Location Updates
Add to Info.plist:
- "Privacy - Motion Usage Description" string
- "Privacy - Location Always & When in Use Description" string
- "Privacy - When in Use Description" string
The minimum deployment target is 12.3.
5. Authorizations
The SDK requires two authorizations:
- Location (Always)
- Motion
Both would be need to be obtained by your app before calling the start
method the first time.
Location
On iOS > 13.5, the "Always" authorization can be obtained by obtaining the "While Using" authorization, and elevating it to "Always" right away as demonstrated in our sample app.
Motion
The "Motion authorization" is not essential, but it is recommended for improved battery usage. Unfortunately, iOS provides no such way for requesting "Motion authorization" - it is automatically requested when you start gathering motion data the first time. This is also demonstrated in the sample app.
6. SDK user authentication
The SDK must be configured at runtime with a user-specific token.
Tokens can be generated on your backend, or manually with the form below (only accessible when signed in). They are signed JWTs (see jwt.io). Users are identified by distinct UUIDs – the creation and management of the user UUIDs is up to you. MOTIONTAG creates a user entry in its database when data from the SDK for a new user UUID arrives for the first time.
To generate the JWTs on your backend, encode and sign a payload like the example below with the shared secret, which is accessible in the admin dashboard under "Authentication tokens". Include these claims:
iss
claim: the tenant key that you have received from MOTIONTAGsub
claim: the user UUIDexp
claim: expiry integer timestamp (optional)
Example payload:
{ "iss": "my-tenant-key", "sub": "aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb" }
my-tenant-key.motion-tag.de
) and
an account to sign in. Contact
MOTIONTAG Support
to request a custom domain setup and an account. If you
already have both, visit your custom domain and sign in to see
a customized version of this documentation.
To create tokens in your backend, use the shared secret available under Authentication tokens
7. Interface
The MotionTag protocol is the main entry point of the SDK.
The singleton instance to this protocol can be obtained by:
let motionTag: MotionTag = MotionTagCore.sharedInstance
and interacting with it through the following public methods and properties
Method | Description |
---|---|
initialize(using delegate: MotionTagDelegate, launchOption launchOptions: [AnyHashable : Any]?) |
Initializes the SDK with the MotionTagDelegate implementation and the launchOptions from didFinishLaunchingWithOptions . |
start() |
Starts tracking. It is recommended to set a valid user token prior to calling this function. The provided MotionTagDelegate implementation will be called to inform about SDK state changes or relevant tracking events. |
stop() |
Stops tracking. |
processBackgroundSessionEvents(with identifier: String, completionHandler: @escaping () -> Void) |
Must be called from the handleEventsForBackgroundURLSession method of the UIApplicationDelegate . |
func clearData() -> Int |
Deletes the tracked data. If you're planning to switch users, you need to stop tracking first. The function returns the number of data entries deleted. Please note that this function does not clear a previously specified userToken ; the app should update it after switching to a new user. |
It also contains the following public properties:
Property | Description |
---|---|
isTrackingActive: Bool { get } |
Indicates whether the SDK is currently tracking or not. |
userToken: String? { get set } |
Updates the user token (JWT). The SDK expects a valid user token prior to executing the start() function. The token is persisted by the SDK in the keychain. |
wifiOnlyDataTransfer: Bool { get set } |
Updates the setting that controls whether the data transmission only occurs when connected to a Wifi network or not. Its default value is false. Can be safely updated even after the tracking has been started. The setting will be persisted internally. |
hasRequiredPermissions: Bool { get } |
True when Location Services are Enabled AND app level location permission is set to Always and precise (iOS 14+). Otherwise false. |
version: String { get } |
MOTIONTAG SDK version string. |
The MotionTagDelegate
has both optional and required methods:
Method | Description |
---|---|
trackingStatusChanged(_ isTracking: Bool) |
Called upon a change in the tracking state, whether directly toggled by the user, or due to changes in location authorizations. Also called on the SDK initialization. |
locationAuthorizationStatusDidChange(_ status: CLAuthorizationStatus, precise: Bool) |
Called when the user changes the location authorization status of the app. Also called on the SDK initialization. The precise parameter is status of the iOS 14 "Precise Location" authorization. This parameter will be true on versions before 14. |
motionActivityAuthorized(_ authorized: Bool) |
Optional Called once when the SDK starts tracking, reflecting the "Motion & Fitness" authorization status. |
didTrackLocation(_ location: CLLocation) |
Optional Called whenever the SDK tracks a new location. |
dataUploadWithTracked(from startDate: Date ,to endDate: Date, didCompleteWithError error: Error?) |
Optional Called after the SDK uploads an event batch to the backend. If the transmission has been successful, error will be nil , otherwise it will contain the type and reason of the error. In case of an invalid token, error.localizedDescription will be Unauthorized. The parameters startDate and endDate are the timestamps of the earliest and latest events in the batch. |
8. Using the SDK
8.1 Initialization
A basic initialization of the SDK, directly from the AppDelegate would look like:
import UIKit
import MotionTagSDK
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
let motionTag: MotionTag = MotionTagCore.sharedInstance
func application(_: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
motionTag.initialize(using: self, launchOption: launchOptions)
return true
}
func application(_ application: UIApplication, processBackgroundSessionEventsWith identifier: String, completionHandler: @escaping () -> Void) {
motionTag.processBackgroundSessionEvents(with: identifier, completionHandler: completionHandler)
}
}
extension AppDelegate: MotionTagDelegate {
func trackingStatusChanged(_ isTracking: Bool) {
print("MotionTag SDK trackingStatusChanged: \(isTracking)")
}
func locationAuthorizationStatusDidChange(_ status: CLAuthorizationStatus, precise: Bool) {
print("MotionTag SDK CLAuthorizationStatus: \(status.rawValue) precise: \(precise)")
}
// optional methods:
func motionActivityAuthorized(_ authorized: Bool) {
print("MotionTag SDK motionActivityAuthorized: \(authorized)")
}
func didTrackLocation(_ location: CLLocation) {
print("MotionTag SDK didTrackLocation - CLLocation: \(location)")
}
func dataUploadWithTracked(from startDate: Date, to endDate: Date, didCompleteWithError error: Error?) {
let errorText = error == nil ? "successfully completed" : error.debugDescription
print("MotionTag SDK dataUploadWithTracked - startDate: \(startDate), endDate: \(endDate)", errorText)
}
}
It is crucial that the initialization happens as early as possible in the didFinishLaunchingWithOptions
delegate
call of the app.
The reason is that the app may be started from the background by the system.
By initializing the tracking library early, you guarantee that all location managers are set up to retrieve new incoming
locations.
Also, the SDK's processBackgroundSessionEventsWith
method needs to be called from your AppDelegate on the func application(_ application: UIApplication, handleEventsForBackgroundURLSession ...
method as shown above.
If you're using FirebaseAnalytics, you need to call
its handleEvents(forBackgroundURLSession ...
. here as well.
Moreover addFirebaseAppDelegateProxyEnabled
to your App's Info.plist
and set its value to NO
. Please
see FIRAnalytics(AppDelegate)
.
This goes for similar frameworks which swizzle the processBackgroundSessionEventsWith
method by default.
8.2 Start tracking
To start tracking, first a valid user token (JWT) needs to be set. Otherwise, the events would not get transmitted to the server.
motionTag.userToken = "A_VALID_JWT"
The token does not need to be set on every initialization, as the SDK keeps it in the keychain. This property can be used to retrieve the token when necessary. It is initially nil.
After that, the start()
method should be called to activate tracking:
motionTag.start()
8.3 Stop tracking
To stop tracking, just call the stop
method:
motionTag.stop()
8.4 Tracking state
The SDK preserves the tracking state across app and device restarts, therefore we recommend only calling the start
and stop
functions when the user manually enables or disables tracking in the App UI.
IMPORTANT: The token is user-specific and should not be used on multiple devices at the same time.
9. Essential integration recap
Initialize Early: Call SDK initialization in
AppDelegate
'sdidFinishLaunchingWithOptions
the first thing.Handling of background sessions Call the SDK's
processBackgroundSessionEventsWith
method from your AppDelegate'sapplication(_ application: UIApplication, handleEventsForBackgroundURLSession ...
method. If you're using the Firebase SDK, addFirebaseAppDelegateProxyEnabled
to your App'sInfo.plist
and set its value toNO
. Then you would need to call Firebase'shandleEvents(forBackgroundURLSession ...
right after the above call to the MOTIONTAG SDK.Set User Token: Obtain a valid JWT for your user and set it on
motiontag.userToken
before starting tracking. Don't persist the user token as it is persisted in the keychain. Once set, there is no need to set it again.Start/Stop Tracking: Don't persist the tracking state and instead rely on the SDK delegate
trackingStatusChanged
to update your toggle button's state. Call the SDK'sstart()
andstop()
methods ONLY based on user intent, e.g. don't call them in the initialization stage or from anywhere else in your app.Expiring User Tokens: If you're using JWT tokens with an expiration date, you would need to monitor upload errors using the
dataUploadWithTracked(from:to:didCompleteWithError:)
delegate method and detect token expiry byerror?.localizedDescription == "Unauthorized"
in the completion handler and implement your token refresh logic. Then simply set the token viamotionTag.userToken
and the SDK should automatically retry failed uploads. Do NOT try to proactively refresh tokens.
10. License
The SDK is licensed under the MOTIONTAG SDK Test License.
11. Open Source Software Licenses
The SDK relies on Open Source Software components. See their respective Open Source Licenses.