Migrating to the Scope SDKs (ConsolidatedSPM 1.1.1) — iOS - scopetech/Mobile-Public GitHub Wiki

Migrating to the Scope SDKs (ConsolidatedSPM 1.1.1) — iOS

Migration guide for integrator teams updating from earlier SDK versions. Companion: Flutter edition. Related wiki pages: iOS Session & App Lifecycle Management, iOS Diagnostic Logs and Export.

This release focuses on trip detection reliability on iOS 26 — trips that failed to end, merged together, were truncated mid-drive, or were incorrectly discarded are resolved. The update is designed to be low-effort: no integration redesign is required, and the few possible compile-time touch points are listed below with their one-line fixes.

Target versions

Package Version
ConsolidatedSPM-iOS 1.1.1 (recommended — pins all of the below)
SCPSmartDriveSPM-iOS 1.1.18
TripWrapperSPM-iOS 1.0.9
SDApiManagerSPM-iOS 1.0.7
BLEFrameworkSPM-iOS 1.0.5
BiosenseSignalSPM-iOS 1.0.0
WellnessSPM-iOS 1.0.1

[!IMPORTANT] SCPSmartDrive and TripWrapper are a matched pair — update them together. Updating the Consolidated package to 1.1.1 does this for you. After updating, commit your Package.resolved; it records the exact set your build was tested with.

Step 1 — Update the dependency

Bump ConsolidatedSPM-iOS to 1.1.1 (File → Packages → Update to Latest Package Versions, or edit the version rule), or bump the individual packages to the table above. Then: File → Packages → Reset Package Caches, and Clean Build Folder.

Step 2 — Build, and fix what the compiler flags

Three known source-compatibility items. All are mechanical; the compiler pinpoints each site.

2.1 APIManagerProtocol.isRealtimeSendingEnabled() (TripWrapper) — the protocol gained a member that selects the trip-end sending pipeline. If your build reports a missing conformance on your API manager / coordinator class, add:

/// Batch (PreTrips) sending — the pre-update behavior.
/// Return true only if adopting realtime sending (and pass
/// enableRealtimeSending: true to initialize — the two must agree).
func isRealtimeSendingEnabled() -> Bool {
    return false
}

2.2 CCTimeInterval is no longer Optional (SDApiManager nullability audit) — if you wrapped its initializer in if let / guard let, remove the binding:

// Before
guard let interval = CCTimeInterval(from: start, to: end) else { return }
// After
let interval = CCTimeInterval(from: start, to: end)

The same applies to any other SDApiManager type the compiler flags with "Initializer for conditional binding must have Optional type."

2.3 SCPSmartDriveManagerDelegate.didReceiveLog optionality — if you implement the SmartDrive log delegate directly, the description parameter is now non-optional:

// Before
func didReceiveLog(with group: LogGroup, groupInfo info: String?, description text: String!)
// After
func didReceiveLog(with group: LogGroup, groupInfo info: String?, description text: String)

No other public API changes. TripNotificationDelegate, TripConfigProvider, DeviceIDProvider, LoggingProtocol, TripWrapperDelegate, and all trip data accessors are unchanged — your integration code otherwise compiles as-is.

Step 3 — Behavior changes to review

These are deliberate and apply automatically; review whether any affect your product expectations.

  1. Long standstills end the trip. A stop longer than the trip-end threshold (default 300 s) now completes the trip even with the phone still inside the vehicle (e.g. a long railway crossing); driving on starts a new trip. The threshold remains configurable (autostopThresholdSeconds / trip-end threshold setting).
  2. Mode-of-transport skipping is distance-capped. Map-based public-transport filtering no longer discards trips longer than kSettingsMOTSkipMaxDistance (new default: 3000 m when unset). If you relied on unlimited MOT skipping, set the key explicitly.
  3. Trip end times are the actual stop moment, not the confirmation moment (~5 minutes later). Trip start/end notifications align with the recorded lifecycle.
  4. Notification guidance: if your TripNotificationDelegate skips scheduling the trip-end notification while the app is foreground — don't. App state at scheduling time says nothing about state ~5 minutes later when it fires. Always schedule; decide foreground presentation in your UNUserNotificationCenterDelegate's willPresent.

Step 4 — What you get automatically

  • The trip-reliability fixes themselves: correct trip completion under iOS 26's motion-activity misbehavior, no mid-drive truncation by recovery routines, no duplicate/overlapping portal trips, no loss of short or transit-corridor trips, serialized realtime event sending.
  • Health snapshots in your diagnostic logs. TripWrapper now emits a TRIPWRAPPER HEALTH SNAPSHOT block (through your LoggingProtocol) at initialization, trip start, trip end, and backgrounding (throttled) — versions, permission states with ⚠️ markers, SDK configuration, trip counts. Include it when contacting support; it answers the first ten triage questions.
    • Note: snapshots are multi-line log entries. Your log export (per the Diagnostic Logs and Export guide) handles this as-is; if your in-app log viewer renders one line per entry, set the cell to multi-line or expect to read snapshots in the exported file.
  • GPS startup diagnostics now log the location authorization status when no fix is available — permission problems announce themselves in the log header.

Step 5 — Verify

[!IMPORTANT] Fresh installs reset iOS location permission. After reinstalling during your own validation, confirm Location Always + Precise Location (and Background App Refresh) before test drives — automatic recording cannot function without them. The new health snapshot states all three with ⚠️ markers if wrong.

Drive-test checklist:

  • Trips start and end at the correct times in dense city traffic with frequent stops.
  • Consecutive trips separated by a parked stop longer than the threshold appear as separate trips.
  • No duplicate or overlapping trips on the portal from a single drive.
  • Trip start/end notifications correspond to real recorded trips.
  • Car trips on bus/tram corridors are recorded normally.
  • The first log lines after launch include the health snapshot with your app version and TripWrapper: 1.0.9 / SmartDrive: 1.1.11.

Optional — the simplified integration facade

This release's reference apps also introduce an optional simplified integration (ScopeSDK facade: one start, one authenticated, one stop, a single ScopeHost struct instead of multiple protocol conformances). It is not required for this migration — your existing protocol-based integration is fully supported and unchanged. When you next revisit integration code, see iOS Simplified SDK Integration for the two-file architecture, its principles, and the step-by-step; the working reference is the SmartDriveDemo feature/sdk_simplification branch.

Support

If anything behaves unexpectedly after updating, export the diagnostic log (it now begins with the health snapshot) and send it with your Package.resolved versions.