Initializing the SDK

The SDK needs to be initialized with some basic settings (such as the directory the SDK can use for data storage and logs), and with a TokenSigner that is provided by the host application. This must occur early in the lifecycle of the app.

Create a class to implement the TokenSigner (see example below):

import Common

class TokenSignerExample: TokenSigner {
    func sign(_ unsignedToken: String, resultHandler: @escaping ResultHandler<String>) {
        // signing logic
    }
}

Next, create a class to configure/initialize the SDK (see example below):

import Common

class SDKConfigExample {
    // Choose a directory that the SDK can access and will own.
    let sdkRootDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        .first!.appendingPathComponent("sdk", isDirectory: true)

    // The AppTokenSigner is a stub for the TokenSigner implementation developed within the host application.
    let appTokenSigner = AppTokenSigner()
    
    init() {
        try! initialize(Directories(root: sdkRootDirectory),
                        logLevel: .debug,
                        tokenSignerFactory: {self.appTokenSigner})
    }
}

NOTE: Classes can (and should) be renamed, as appropriate.

WARNING: Failing to initialize the SDK before using its functionality will result in unexpected behaviour.

Beginning Trip Detection Manager

To upload trip data, the current mobile device must be activated for data collection through the Portal. This is to ensure trip data can be analyzed properly on the IMS backend. Depending on the mode of activation the host application chooses and the design of the app, device activation may only need to be performed once for a device.

Identity

The host application shares the identity of its currently authenticated user by sharing an Identity object with the SDK. An Identity is constructed with the host application's API Key and the host application's unique user identifier, provided during the enrolment process.

UploadRoute

The host application can choose upload route for uploading trip files which can be passed during the initialization of the SDK.

Feature

The features: [Feature] parameter of TripDetectionManager's initializer specifies which features are used for Trip Detection and Trip Validation. A combination of Trip Detectors and Trip Validators can be passed into the initializer to meet the needs of the App. Simply declare all Feature components that are needed.

A standard setup would include at least one Trip Detector and one Trip Validator. For example, a phone only trip detection configuration would include [Feature.Geofence] and [Feature.PhoneOnlyValidation].

Trip Detection

Feature.Geofence: To use geofences for trip detection, pass in [Feature.Geofence].This feature provides trip detection using Core Motion and Core Location functionalities.

Feature.Bluetooth: To use Bluetooth devices for trip detection, pass in [Feature.Bluetooth].This feature uses Core Bluetooth functionality to have trip detection using a bluetooth device (i.e. wedge).

NOTE: If there is no Trip Detection Feature parameter passed in the configuration, Trip Detection functionality will be limited.

Trip Validation

Starting with the SDK 1.15.0, the host app can provide the trip validation features as in the example above during the initialization of SDK.

Feature.PhoneOnlyValidation: To use phone-only trip validation feature, pass in [Feature.PhoneOnlyValidation]. This feature allows a trip to be validated using the onboard sensors of the phone.

Feature.DeviceValidation: To use Bluetooth devices for trip validation feature, pass [Feature.DeviceValidation]. This feature allows a trip to be validated by the presence of an associated Bluetooth device during the trip.

NOTE: If there is no Trip Validation Feature parameter passed in the configuration, trips will be validated as soon as they are detected.

Complete Example

We can create a class that contains all of the trip detection methods listed above:

import Common
import Portal
import TripDetection
import TripDetectionUmbrella

class TripDetectionManagerExample {
    // The identity of the user.
    private let identity: Identity

    private let tripDetectionManager: TripDetectionManager?

    init() {
        identity = Identity(apiKey: "HOST-APP-API-KEY",
                            externalReferenceID: "HOST-APP-USER-IDENTIFIER")

        // The telemetry events to be recorded
        let telemetryEvents: Set<TelemetryEvent> = [.gps,
                                                    .speed,
                                                    .gyroscope(.oneHz),
                                                    .gravity(.oneHz),
                                                    .accelerometer(.oneHz),
                                                    .userAcceleration(.oneHz),
                                                    .magnetometer(.oneHz)]
        
        // Feature for phone trip detection
        let features: [Feature] = [Feature.Geofence, Feature.PhoneOnlyValidation]
        
        // Feature for device trip detetction
        // let features = [Feature.Bluetooth, Feature.DeviceValidation]
        
        // Feature for phone-only and device trip detection
        // let features: [Feature] = [Feature.Geofence, Feature.PhoneOnlyValidation, Feature.Bluetooth, Feature.DeviceValidation]

        // Initializes trip detection by way of the TripDetectionManager.
        tripDetectionManager = TripDetectionManager(identity: identity,
                                                    uploadRoute: uploadRouteType,
                                                    telemetryEvents: telemetryEvents,
                                                    features: features)
    }

    func activateDevice() {
        deviceService.activate { [weak self] result in
            guard self != nil else {
                return
            }
            switch result {
            case .failure(let error):
                log.e("Error activating device: \(error)")
            case .success:
                log.i("Device activation successful")
            }
        }
    }

    func enableTripDetection() {
        // Enable Trip Detection
       tripDetectionManager.enable { (state, date) in
            switch state {
            case .potentialTrip:
                log.d("Potential trip at \(date)!")
            case .confirmedTrip:
                log.d("Confirmed trip at \(date)!")
            case .endedTrip:
                log.d("Trip ended at \(date)!")
            @unknown default:
                log.e("Error fetching trip states with date!")
            }
        }
    }

    func disableTripDetection() {
        tripDetectionManager?.disable()
    }
}

Configure Bluetooth Device Provider

  • Starting with SDK 1.19.0, host app has the capabilities to configure the device providers

  • Host app can use the new Devices and IMSBluetooth frameworks to configure the bluetooth usage for TripDetection. Both frameworks are required to be included in the project if the trip detection variant is using bluetooth and devices to detect trips.

Concrete Example

The Devices module provides APIs as listed below:

  • scanForDevices: To scan bluetooth devices

  • associateDevice: To associate an IMS device

  • disassociateDevice: To disassociate an IMS device

  • clearAllAssociatedDevices: To clear all associated devices

  • associatedDevices: To retrieve already associated devices

Below is the snapshot for understanding the configuration required to utilize the new modules:

import Combine
import Common
import Devices
import IMSInterfaces
import IMSBluetooth
import Primitives

class BluetoothManager {
    static let shared = BluetoothManager()
    private let bluetoothDeviceProvider: IMSDeviceProvidable

    let supportedDevicesPrefixes = [
        "IMS-"
    ]

    init() {
        let pattern = "^(" + supportedDevicesPrefixes.joined(separator: "|") + ")"
        do {
            // Create a regex object
            bluetoothDeviceProvider = BluetoothDeviceProvider(wedgeRegex: try NSRegularExpression(pattern: pattern, options: []),
                                                              tdwdServerEnv: .dev)
        } catch {
            fatalError("Failed to create regex: \(error)")
        }
        IMSDeviceManager.configure(providers: [bluetoothDeviceProvider])
    }
    
    func scanForDevices() {
        IMSDeviceManager.scanForDevices(withTimeout: 20.0)
            .sink { completion in
                switch completion {
                case .finished:
                    break
                case .failure(let error):
                    print("Scan error: \(error)")
                }
            } receiveValue: {
                self.devices.append($0)
            }
            .store(in: &cancellables)
    }
    
    func associateDevice(deviceId: String, deviceName: String, toVehichleId vehicleId: String?) {
        let bluetoothDevice = IMSDevice(deviceId: deviceId, deviceName: deviceName)
        IMSDeviceManager.associateDevice(bluetoothDevice, toVehichleId: vehicleId)
    }
    
    func disassociateDevice(_ device: IMSDevice) {
        IMSDeviceManager.disassociateDevice(device)
    }
    
    func clearAllAssociatedDevices() {
        IMSDeviceManager.clearAllAssociatedDevices()
    }
    
    func associatedDevices() -> [IMSDevice] {
        IMSDeviceManager.associatedDevices.compactMap { device in
            // use the device
        }
    }
}

SDK initialization feedback

Starting with the SDK 1.10, it is possible to add a handler to be notified of SDK status changes. Setting a handler:

tripDetectionManager.setStatusHandler { status in
    print("Trip detection status \(status)")
}

Removing the current handler:

tripDetectionManager.setStatusHandler(nil)

These can be added to the Trip Detection Manager class.

NOTE: The list of statuses will be expanded in future releases.

Last updated