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

Migrating to the Scope SDKs (ConsolidatedSPM 1.1.1) — Flutter

Flutter analogue of the iOS migration guide, for teams integrating the SmartDrive / SDApiManager / TripWrapper SDKs in a Flutter app. Related wiki pages: Flutter Session & App Lifecycle Management, Flutter Diagnostic Logs and Export.

The SDKs are native iOS frameworks; in a Flutter app they live in ios/Runner. This migration therefore touches only the native side — your Dart code, the platform channel contract, and the Dart↔native call flow are unchanged. If your Runner follows the lifecycle guide, this is a dependency bump plus at most three one-line Swift fixes.

Target versions

Same as the iOS guide — update ios/Runner's package dependencies:

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 — Consolidated 1.1.1 updates them together. Commit the updated Package.resolved under ios/.

Step 1 — Update the native dependency

Open ios/Runner.xcworkspace (or the Runner project) in Xcode, bump ConsolidatedSPM-iOS to 1.1.1, then File → Packages → Reset Package Caches and Clean Build Folder. Build the Runner once from Xcode before returning to flutter build — Swift compile issues are far easier to fix there.

Step 2 — Fix what the Swift compiler flags (Runner only)

Three known items, all in ios/Runner Swift code (typically your AppDelegate or the coordinator class it uses); each is mechanical:

  1. APIManagerProtocol.isRealtimeSendingEnabled() — if your conformance is flagged as incomplete, add:
    func isRealtimeSendingEnabled() -> Bool { return false }  // batch mode = previous behavior
    
  2. CCTimeInterval is no longer Optional — remove if let / guard let around its initializer where the compiler flags "must have Optional type."
  3. didReceiveLog optionality — if the Runner implements the SmartDrive log delegate: description text: String! → description text: String.

Nothing changes in Dart. The channel methods from the lifecycle guide (isAuthorized, login, logout, refresh, enterBackground) and the log-export flow are source- and behavior-compatible.

Step 3 — Behavior changes to review

Identical to the iOS guide, summarized — see it for detail:

  1. Stops longer than the trip-end threshold (default 300 s) end the trip, including long in-vehicle standstills; driving on starts a new trip.
  2. Map-based mode-of-transport skipping is capped at kSettingsMOTSkipMaxDistance (new default 3000 m) — set explicitly if you relied on unlimited skipping.
  3. Trip end times are the actual stop moment; notifications align with the recorded lifecycle.
  4. If your native TripNotificationDelegate skips scheduling while foreground, stop doing that — always schedule; decide foreground presentation in userNotificationCenter(_:willPresent:).

Step 4 — What you get automatically

  • The full set of iOS 26 trip-reliability fixes (see the iOS guide).
  • Health snapshots in the diagnostic log: TRIPWRAPPER HEALTH SNAPSHOT blocks at initialization, trip start/end, and backgrounding — versions, permission states (with ⚠️ markers), SDK configuration, trip counts. They flow through the existing export path from the Flutter Diagnostic Logs and Export guide unchanged. If you render logs in a Dart UI one line per entry, note snapshots are multi-line — show them expandable, or read them in the exported file.
  • GPS startup diagnostics include the location authorization status — a wrong-permission device announces itself in the first screen of the log.

Step 5 — Verify

[!IMPORTANT] Fresh installs reset iOS location permission — and Flutter development cycles reinstall often. After any reinstall, confirm Location Always + Precise Location + Background App Refresh before test drives. The health snapshot flags all three.

Then run the drive-test checklist from the iOS guide (trip splits at long stops, no duplicates on the portal, notifications matching real trips, corridor trips recorded) plus the two Flutter-specific checks from the lifecycle guide: a background autostart drive with the app killed (arming lives in native didFinishLaunchingWithOptions, not the channel), and a screen-off drive (no re-login, recording continues).

Note on the lifecycle guide's demo references

The lifecycle guide says to copy initializeFeatures / configureSmartDrive from the SmartDriveDemo AppDelegate unchanged. The demo has since adopted the optional simplified facade (ScopeSDK + AppScopeHost); both integration styles remain fully supported. For this migration, keep your existing Runner code — only apply the Step 2 fixes. If you later modernize, follow iOS Simplified SDK Integration (section 5 covers the Runner specifics — the two files to copy live on the SmartDriveDemo feature/sdk_simplification branch); the channel contract is unaffected either way.