Skip to main content

iOS SDK: Getting started

This guide takes an app from installing the Cartrack BLE Lock SDK for iOS to locking and unlocking a vehicle, in the order the calls are made. It describes version 3.0.4; where an earlier version behaves differently, it says so.

Before you start​

  • iOS 15.0 or later, on an iPhone (3.0.1 supports iOS 11.0). The simulator has no Bluetooth: there, connect() reports BleError.bluetoohUnsupported (3).
  • NSBluetoothAlwaysUsageDescription in the app's Info.plist. The SDK creates its Bluetooth manager on the first BleService.getTerminal(terminalID:) call, and iOS stops an app that uses Bluetooth without that key. That first call also shows the Bluetooth permission prompt, and iOS shows its own alert when Bluetooth is off.
  • The app on screen when connecting, or from 3.0.4 bluetooth-central in the app's UIBackgroundModes to connect from the background too. On screen the SDK looks for every Bluetooth device nearby. In the background iOS reports only devices that advertise a service the app names, so there the SDK looks for the terminal's MiKey service. Up to 3.0.2, a connection started in the background never found the terminal.
  • The terminal's key: bluetooth_key from the Fleet API (GET /mikey/{registration}), 64 hexadecimal characters.

Install​

The SDK package is provided by Cartrack on request. If you do not have it yet, see Request SDK access.

  1. Add the CartrackBleLockSDK.xcframework folder, in Binary Framework in the package, to your Xcode project.
  2. In Project Settings → Frameworks, Libraries, and Embedded Content, set CartrackBleLockSDK.xcframework to Embed & Sign.
  3. Add import CartrackBleLockSDK to the files that use the SDK.

The package also holds the demo app, an Xcode project that makes every call below.

Get the terminal​

let terminal = BleService.getTerminal(terminalID: "CARPOOL01") // the Bluetooth name the terminal advertises
terminal.delegate = self // conforms to BleTerminalDelegate

The name is trimmed and upper-cased. The same name always returns the same BleTerminal, which has a single delegate: setting it replaces the previous one. Every callback comes on the main thread, and calls made on another thread are moved to it. In 3.0.1, a connect() made on another thread never completed.

Save the key​

terminal.saveAuthKey(authKey: bluetoothKey)

The answer comes straight away in bleTerminalDidSavedKey(terminal:error:). The SDK keeps one key for the app, not one per terminal: saving replaces it.

  • terminal.hasKey says whether a key is saved, and terminal.authKey returns it as lowercase hexadecimal.
  • Anything but pairs of hexadecimal digits fails with saveAuthKeyFailed (4); spaces around the key are trimmed. A save that fails keeps the key saved before. In 3.0.1, other characters were skipped, which shifted the key.
  • The key can be read while the phone is locked, once the phone was unlocked after starting, and it is never restored onto another phone from a backup: on a new phone, fetch the key from the Fleet API and save it again. A key saved by 3.0.1 moves to this protection the first time it is read.
  • A key of the wrong length is accepted here and fails at connect time with BleError.keyInvalid (6).
  • terminal.removeAuthKey() deletes the key, without a callback. Call it when the rental ends.

Connect​

terminal.connect(timeout: 20) // seconds, 10 by default

bleTerminalDidConnect(terminal:error:) comes once the terminal has accepted the key and the SDK has read the lock state into terminal.lockState. terminal.connectionState is already .connected once the key is accepted.

The timeout applies to each step in turn: the scan, the Bluetooth link, the authentication, then the lock state read, so an attempt can take up to four times as long. If the lock state read gets no answer, the connection is reported anyway, with lockState .unknown. connect() while connected reports the connection again.

A failed attempt reports one of:

  • keyNotFound (5) or keyInvalid (6), straight away, when no valid key is saved;
  • bluetoohUnauthorized (1), also from 3.0.4 when the Bluetooth prompt is still unanswered at the timeout, bluetoothDisabled (2) or bluetoohUnsupported (3);
  • terminalNotFound (7) when the terminal did not advertise during the attempt;
  • ctgError(45, 22) when the link or the authentication timed out;
  • ctgError(56, 21) when the terminal refused the key.

Send actions​

terminal.sendAction(.unlock)
terminal.getVehicleStats()
CallAnswers inUpdates
sendAction(.lock), sendAction(.unlock)bleTerminalDidAction(terminal:action:error:)lockState
sendAction(.lockState)bleTerminalDidAction(terminal:action:error:)lockState
sendAction(.ignitionState)bleTerminalDidAction(terminal:action:error:)ignitionState
sendAction(.headlight), .horn, .unlockNoKeyFobbleTerminalDidAction(terminal:action:error:)nothing
getVehicleStats()bleTerminalDidGetVehicleStats(terminal:vehicleStats:error:)nothing

Before answering .lock and .unlock, the SDK reads the lock state. It reports success when the terminal accepted the command, whatever the lock state reads, because the vehicle's lock state input can lag. When not connected, the answer is notConnected (8), straight away.

Each command gets exactly one answer. When the terminal says nothing within the timeout passed to connect(timeout:), the answer is ctgError(actionCode, 22), with the command's action code. In 3.0.1, a command the terminal did not answer never called back.

Handle every callback​

CallbackWhen
bleTerminalDidSavedKey(terminal:error:)Right after saveAuthKey(authKey:).
bleTerminalDidConnect(terminal:error:)Once per connect(): success once the lock state is read (or its read timed out), or the error that ended the attempt. Also with ctgError(45, 23) when the terminal refuses the keep-alive the SDK sends every 7 seconds; bleTerminalDisconnected follows.
bleTerminalDidAction(terminal:action:error:)Once per sendAction(_:).
bleTerminalDidGetVehicleStats(terminal:vehicleStats:error:)Once per getVehicleStats().
bleTerminalSignalUpdate(rssi:strength:)Every second while connected: .strong above -60 dBm, .good above -90 dBm, .weak below.
bleTerminalDisconnected(terminal:)When a connection reported by bleTerminalDidConnect ends, including after disconnect(), and once when disconnect() cancels an attempt. Never for an attempt that failed: that one reports through bleTerminalDidConnect.

Handle errors​

Every error is a BleError. Codes 1 to 8 come from the phone and the SDK; see BleError.

Terminal errors come as BleError.ctgError(actionCode, errorCode, description). errorCode is the CtgError code, 21 to 28, and actionCode is the command it answered:

actionCodeCommand
32Ignition state
33Vehicle stats
45Connect
56Authentication
57Lock
58Unlock
59Headlight
60Horn
61Unlock without key fob
63Lock state

For the other errors, actionCode is -1.

Disconnect​

terminal.disconnect()
terminal.removeAuthKey() // when the rental ends

When connected, disconnect() sends the disconnect command and drops the link, also when the terminal refuses or does not answer, and bleTerminalDisconnected(terminal:) comes once the link is down. During an attempt, it cancels the attempt and reports bleTerminalDisconnected(terminal:) once. With nothing connected, it does nothing and reports nothing.

If something does not behave as described here, see Troubleshooting.

API reference​