Repository navigation
iOS Consolidated SPM documentation
This document describes how the ScopeTechnology framework ConsolidatedSPM-iOS works and how to integrate it into your application.
ConsolidatedSPM-iOS is a Swift Package that provides unified access to multiple framework dependencies:
- BLEFramework - Bluetooth Low Energy functionality
- SCPSmartDrive - SmartDrive integration
- SDApiManager - API management
- BiosenseSignal - Biosense signal processing
- Wellness_iOS - Wellness features
- TripWrapper_iOS - Trip lifecycle management (detection, recording, upload, and retry)
Recording modes. Trips are recorded in one of two modes: Smart (no extra hardware — the phone detects driving) or Beacon / mHub (a Scope BLE device in the vehicle). Which mode you build for affects the permissions and configuration below.
- Quick start
- Prerequisites & authentication
- Installation
- Required App Configuration
- Usage in Code
- Coexistence with CocoaPods
- Updating the Package
- Troubleshooting
- Framework Documentation
- Integration Guides
The happy path from zero to a recorded trip. Each step links to its detailed section; skim it if you're experienced, follow the links if you're new.
- Get org access + authenticate GitHub in Xcode so the private package resolves — §2.
-
Add the package
ConsolidatedSPM-iOSand its products to your target — §3. - Declare permissions & capabilities (Info.plist keys, Background Modes, BGTask identifiers) — §4.
- Choose your integration style — for new apps, the ScopeSDK façade (recommended) — §5.2.
- Arm recording after login + account fetch so the vehicle's unit serial is set before a trip starts — §5.3.
- Record a trip on a real device (automotive detection does not work in the Simulator) and verify it in the diagnostic log.
For a complete, running example of all of the above, see the SmartDriveDemo — App Overview and the SmartDriveDemo-iOS repository.
You need a GitHub account with access to the scopetech organization — contact your
Scope Technology representative to be added as a collaborator. The ConsolidatedSPM-iOS
repository is private, so you must configure GitHub authentication in Xcode before
adding the package (Step 3), or the URL will fail to resolve.
- Sign in to GitHub and go to Settings → Developer settings → Personal access tokens → Tokens (classic).
- Click Generate new token (classic).
- Set a name (e.g.,
Xcode SPM access) and an expiration. - Enable at least the following scope:
-
repo— required for read access to private repositories.
-
- Click Generate token and copy the value immediately. GitHub will not show it again.
If the scopetech organization enforces SAML SSO, your PAT must be authorized separately:
- After creating the token, find it in the tokens list.
- Next to
scopetech, click Authorize and complete the SSO flow.
Without this step, the token will appear valid but every package fetch will fail with a 403.
- In Xcode, open Settings → Accounts.
- Click the + button at the bottom-left and choose GitHub.
- Enter your GitHub username and paste the PAT as the password.
- Click Sign In.
You should now see your GitHub account listed under Accounts. Xcode will use this credential when resolving Swift packages from github.com/scopetech/....
If you prefer SSH (recommended for CI runners and headless build agents):
- Generate an SSH key pair if you don't have one:
ssh-keygen -t ed25519 -C "your_email@example.com". - Add the public key to your GitHub account under Settings → SSH and GPG keys.
- If your org enforces SSO, click Configure SSO next to the new key and authorize
scopetech. - Test access:
ssh -T git@github.com.
When using SSH, choose the SSH URL in installation Step 3 below.
Step 1. Open your project in Xcode. If your project uses CocoaPods, open the .xcworkspace file (not .xcodeproj) so the package is visible to all workspace targets.
Step 2. From the menu, choose File → Add Package Dependencies…
Step 3. In the search bar (top right of the dialog), enter the repository URL:
HTTPS (recommended for individual developers):
https://github.com/scopetech/ConsolidatedSPM-iOS.git
SSH (recommended for CI):
git@github.com:scopetech/ConsolidatedSPM-iOS.git
For the differences between HTTPS and SSH remotes, see the GitHub documentation on remote repositories.
Step 4. Configure the dependency rule:
-
Recommended: select Up to Next Major Version with starting version
1.0.0. - This will pull bug fixes and new features automatically. The version will update across any
1.x.xrelease, but not to2.0.0.
Step 5. Choose the target(s) the package products should be added to, then click Add Package. Wait for Xcode to finish resolving and downloading the dependency.
- In the Project Navigator (left sidebar), select your project (the blue icon at the top).
- In the project editor, select the project (not a target) from the left section.
- Open the Package Dependencies tab.
- You should see
ConsolidatedSPM-iOSlisted with its resolved version. - Open the General tab for each target you intend to use the SDK in, and confirm the relevant package products appear under Frameworks, Libraries, and Embedded Content. If a target is missing the dependency, add it manually here.
If the package failed to resolve, see Troubleshooting.
The frameworks in this package use Bluetooth, location, motion, notifications, and background execution. Your app must declare the corresponding usage descriptions, capabilities, and background task identifiers — otherwise the SDK will fail at runtime even if the build succeeds. Some of these are Info.plist keys, some are runtime prompts, and one (Background App Refresh) is a device setting the user controls — see the table below.
| Capability | Set / requested via | Needed for | If missing or off |
|---|---|---|---|
| Location — Always + Precise | Info.plist keys (§4.1) + runtime prompt, escalated to Always (§4.4) | All background trip recording | No background trips |
| Motion & Fitness |
NSMotionUsageDescription + runtime prompt |
Trip detection (smart mode), driving events | Trips not detected reliably |
| Bluetooth |
NSBluetooth… keys (§4.1) + runtime prompt |
Beacon mode only | No beacon trips |
| Notifications | Runtime prompt (§4.6) | Trip start / end alerts | No trip notifications |
| Background Modes | Signing & Capabilities (§4.2) | Background execution | SDK fails at runtime |
| BGTaskScheduler identifiers | Info.plist (§4.3) | Scheduling periodic tasks | Scheduling fails (error 3) |
| Background App Refresh | Device setting — not a prompt (§4.5) | Periodic tasks actually running (retry, upload, periodic check) | Periodic background work never runs |
Add the following keys to your Info.plist. Use copy that is appropriate for your app — these strings are shown to end users in iOS permission prompts.
<key>NSBluetoothAlwaysUsageDescription</key>
<string>This app uses Bluetooth to detect your vehicle.</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>This app needs to collect and display the data sent from the device.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app uses your location to record trips.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>This app records trips automatically in the background.</string>
<key>NSMotionUsageDescription</key>
<string>This app uses motion data to detect driving.</string>
Note: The two
NSBluetooth…keys are only required if you use beacon mode. Smart (no-BLE) mode does not need Bluetooth.
In the target's Signing & Capabilities tab, add the Background Modes capability and enable:
- bluetooth-central
- fetch
- location
- remote-notification
The SDK schedules background tasks using BGTaskScheduler. Every identifier the SDK schedules must be declared in your Info.plist under BGTaskSchedulerPermittedIdentifiers, otherwise scheduling fails with error 3 (notPermitted).
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>com.scope.aio.periodicCheck</string>
</array>
The Info.plist keys in §4.1 only allow the prompts to appear — the SDK still needs the user to grant the right level of access:
- Always authorization is required for background trip recording. With only When In Use, trips will not record while the app is backgrounded. Request When In Use first, then escalate to Always.
- Precise / Full Accuracy is required. If the user turns Precise Location off (reduced accuracy, iOS 14+), trips may not record.
If either is missing, prompt the user to enable Always and Precise Location in iOS Settings. You can read the current state with CLLocationManager.authorizationStatus and accuracyAuthorization.
Background App Refresh is a device setting (Settings → General → Background App Refresh, and per app), not an Info.plist permission and not an in-app prompt. It controls whether iOS runs your app's background fetch and BGTaskScheduler work — the fetch mode (§4.2) and the identifiers in §4.3.
- Trip detection still works with it off. Location-triggered wake-ups (significant location change, region monitoring, Always location) relaunch the app even when terminated, independent of this setting — so a trip can still start.
-
Periodic background work needs it on. The SDK's periodic task (
com.scope.aio.periodicCheck) and the retry/upload/finalization it drives only run when Background App Refresh is enabled. With it off, pending uploads may not retry and periodic checks will not fire — background reliability degrades.
There is no API to request it, so detect its status and guide the user:
switch UIApplication.shared.backgroundRefreshStatus {
case .available:
break // good
case .denied:
// User has turned it off — prompt them to enable
// Settings → General → Background App Refresh
break
case .restricted:
// Disabled by parental controls / MDM — the user cannot change it
break
@unknown default:
break
}Recommendation: treat Background App Refresh as required for dependable background trip recording and upload. Surface a message when it is
deniedso the user can re-enable it.
Trip start / end notifications are delivered as local notifications, which require runtime authorization (there is no Info.plist key). Request it once during setup:
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, _ in
// ...
}If not granted, trip recording still works — only the start/end notifications are suppressed.
You can import all frameworks with a single import statement:
import ConsolidatedSPMAlternatively, import individual frameworks if you only need a subset:
import BLEFramework
import SCPSmartDrive
import SDApiManager
import BiosenseSignal
import Wellness_iOS
import TripWrapper_iOSTrip recording can be integrated three ways. New apps should use the ScopeSDK façade — the others exist for existing integrations and lower-level control.
| Style | What it is | Use it when |
|---|---|---|
| ScopeSDK façade ✅ recommended / latest | Two files (ScopeSDK.swift copied verbatim + one app-written AppScopeHost.swift). Owns TripWrapper init and the protocol wiring for you. |
New integrations. Least code, hardest to get wrong. |
| TripWrapper coordinator | You implement TripWrapper's protocols (API bridge, device ID, config, logging) on your own coordinator class and call TripWrapper.initialize(...). This is the layer the façade automates. |
You need lower-level control, or you already integrated this way. |
| Direct SCPSmartDrive (legacy) | Your app implements SCPSmartDriveManagerDelegate and drives trip sending/retry itself. |
Legacy only — migrate off this. |
"Simplified" = the ScopeSDK façade. It is the newest and recommended path; the coordinator and direct-delegate paths are the older, more manual alternatives it replaces. Full walkthrough: iOS Simplified SDK Integration. Adopting TripWrapper from a direct SCPSmartDrive integration: iOS TripWrapper Migration Guide.
Recording is automatic once the SDK is configured and armed — you do not call a "record trip" method. Trips start when driving is detected (smart mode) or a paired beacon is in range (beacon/mHub mode), and are uploaded automatically. The essentials:
-
Authenticate first. No SDK feature works without a logged-in session (
SDApiManager.login), and trips are tagged with the selected vehicle's unit serial number, which is set during account fetch (SDApiManager.getAccountWithComplete). -
Arm after account fetch. Configure SmartDrive for your mode (smart vs beacon) and initialize the trip layer. With the façade this is
ScopeSDK.shared.start(configuration:host:)thenScopeSDK.shared.authenticated(). Arming before the unit serial is set means trips cannot be matched to a vehicle. - Grant permissions — Always + Precise location and Motion (§4.4); beacon mode also needs Bluetooth.
-
Stop on logout —
ScopeSDK.shared.stop()(façade) so recording doesn't continue for an unauthenticated user.
For the exact configuration setters (mode, autostart, end-trip timer, MOT, distracted driving, realtime sending) and how to read the live trip (status, distance, duration, speed), see the iOS SmartDrive framework page. For the end-to-end wiring, see the SmartDriveDemo — App Overview.
Each non-trip framework has its own initialization API — see the per-framework docs in Section 9 for API credentials, subscribing to events, and delegates.
If your app already uses CocoaPods, watch for the following:
-
Duplicate symbols. If the SPM and a Pod both ship the same dependency (for example
SSKeychain,ZIPFoundation, orDTError), the linker will fail with duplicate symbol errors. Pin the dependency to a single source — either remove it from yourPodfile, or exclude it from the SPM target — but do not include both. - Same dependency, different versions. Even when symbols don't collide outright, pulling a dependency in via both SPM and CocoaPods can cause subtle runtime issues. Prefer a single source of truth.
-
use_frameworks!vs static linkage. If yourPodfileuses static linkage, confirm your SPM products are compatible with that mode. Mixed linkage is supported but can surface module-map conflicts.
If you hit duplicate symbol errors, see Troubleshooting.
For a full list of releases and changelogs, see the ConsolidatedSPM-iOS Releases page.
- In Xcode, choose File → Packages → Update to Latest Package Versions.
- Xcode fetches the latest version compatible with your dependency rule.
- Build to confirm everything still works.
- Open Project → Package Dependencies.
- Right-click
ConsolidatedSPM-iOSand choose Update Package.
- Open Project → Package Dependencies.
- The version is shown in the Resolved Version column.
| Symptom | Likely cause | Fix |
|---|---|---|
xcodebuild: error: Could not resolve package dependencies with 401 / 403 |
PAT missing, expired, or not SSO-authorized | Re-check Section 2. Confirm the token has repo scope and is SSO-authorized for scopetech. |
| "Authentication failed" prompt in Xcode | Wrong credentials in Xcode → Settings → Accounts | Remove the GitHub account and re-add it with a fresh PAT. |
Repository not found |
Your GitHub user is not a collaborator on the scopetech repos |
Contact your Scope Technology representative to be added. |
| Resolution hangs forever | Stale package cache |
rm -rf ~/Library/Caches/org.swift.swiftpm and File → Packages → Reset Package Caches. |
| Symptom | Likely cause | Fix |
|---|---|---|
| Duplicate symbol errors at link time | Same dependency provided by both SPM and a Pod | See Section 6. Remove the duplicate from one source. |
No such module 'ConsolidatedSPM' after a successful resolve |
The package isn't added to that target | In the target's General → Frameworks, Libraries, and Embedded Content, add the relevant package product. |
Cannot find type 'X' in scope after import ConsolidatedSPM
|
Umbrella import doesn't re-export the inner module | Import the framework directly (e.g., import SCPSmartDrive). |
| Build hangs indefinitely |
XCBBuildService is stuck |
killall -9 XCBBuildService and rebuild. |
| Archive succeeds, debug build fails (or vice versa) | Stale build artifacts |
Product → Clean Build Folder (⇧⌘K), then rebuild. If the issue persists, delete ~/Library/Developer/Xcode/DerivedData. |
| Symptom | Likely cause | Fix |
|---|---|---|
| Permission prompts never appear | Missing Info.plist usage descriptions |
See Section 4.1. |
| Background tasks never run | Background Modes capability not enabled, or BGTaskScheduler identifier not registered | See Sections 4.2 and 4.3. |
| BLE features don't activate |
NSBluetoothAlwaysUsageDescription missing or user denied permission |
Verify the key is present and prompt the user to re-enable Bluetooth in iOS Settings. |
- BLEFramework — iOS BLE framework
- SCPSmartDrive — iOS SmartDrive framework
- SDApiManager — iOS SDApiManager Framework
- BiosenseSignal — the signal-processing framework that powers Wellness features
- Wellness_iOS — iOS Wellness Framework
- TripWrapper_iOS — SDK Reference · Migration Guide · Demo app
Beyond installation and per-framework APIs, these guides cover how to build your app correctly around the SDKs. They apply across frameworks, for both native iOS and Flutter.
Demo app — a complete worked integration (smart and beacon / mHub) and a per-component reference:
Session & app lifecycle — keep users logged in and recording across screen-off, background, and termination:
Diagnostic logs & export — capture and export logs for support:
Migration & integration guides:
- iOS Simplified SDK Integration — the recommended (ScopeSDK façade) integration style; evergreen.
- iOS TripWrapper Migration Guide — adopting TripWrapper from a direct SCPSmartDrive integration.
Release notes:
Earlier versioned migration guides:
Consolidated SDK
SmartDrive
PortalApiBase
PortalApiClient
Wellness
SBeacon
- Overview
- Installation
- Initialization
- Beacon Scanning
- Beacon Pairing
- Trip Management
- Driving Behavior
- Calibration
- Event Handling
- Data Models
- API Reference
Consolidated SPM
SDApiManager
CCFrameworkOpen
SmartDrive
SBeacon