SmartDriveDemo — App Overview - scopetech/Mobile-Public GitHub Wiki

SmartDriveDemo — App Overview & Component Reference

The SmartDriveDemo iOS app is the reference integration for the Scope mobile SDKs (SmartDrive / TripWrapper / SDApiManager / BLEFramework / Wellness). It demonstrates a universal integration — the same host works for smart-mode clients and for beacon / mHub clients — using the ScopeSDK façade. Use it as the worked example when integrating the SDKs into a new app.

Companion wiki pages (read alongside this one):


Table of Contents

  1. Architecture at a glance
  2. Frameworks, packages & required configuration
  3. Requirements & running the demo
  4. SDK integration layer
  5. App lifecycle — AppDelegate.swift
  6. Trip data & UI
  7. Beacon / mHub
  8. Diagnostics, logs & export
  9. Authentication — LoginViewModel.swift
  10. Wellness (optional module)
  11. Configuration & helpers
  12. Key flows
  13. Adopting this app as your integration template
  14. Where to start

1. Architecture at a glance

The app is layered so that exactly one file is app-specific per concern, and the SDK integration is a thin, portable seam.

flowchart TD
    UI["UI — View Controllers + XIBs"] --> VM["View Models<br/>(Login, Trips, TripStatus, Beacon, Diagnostics, Wellness)"]
    VM --> READ["TripManager (facade) · FormattingHelper · TripDetailsManager"]
    READ --> SCOPE["ScopeSDK (façade)"]
    SCOPE --> TW["TripWrapper_iOS<br/>(owns delegate, send, retry)"]
    TW --> SD["SCPSmartDrive<br/>(location, motion, trip detection)"]
    SCOPE --> HOST["AppScopeHost (ScopeHost)"]
    SCOPE --> BLE["BLEFrameworkProxy (beacon)"]
    HOST --> API["SDApiManager (backend / auth)"]
    BLE --> BLEF["BLEFramework<br/>(SBeacon / SCPBeaconManager)"]
Loading
  • App logic never talks to SCPSmartDrive for trip lifecycle — TripWrapper owns that. The app only reads SmartDrive for live display values (speed/distance) and supplies configuration values.
  • ScopeSDK.swift is copied verbatim from the SDK; AppScopeHost.swift is the only integration file you write. See the SDK Integration (recommended) page.

2. Frameworks, packages & required configuration

All Scope SDKs this app uses ship as Swift packages, pinned together by the Consolidated SPM. This section states only what the Demo does and links out — the authoritative installation steps, product list, and Required App Configuration live in the iOS Consolidated SPM documentation and are not repeated here.

Adding the package dependency

  1. Xcode → File → Add Package Dependencies…
  2. Paste the Consolidated SPM repository URL and choose ConsolidatedSPM-iOS — the meta-package that pins the whole matched set.
  3. Add the products the Demo uses to the app target: SCPSmartDrive, TripWrapper_iOS, SDApiManager, BLEFramework, Wellness_iOS.

A single repository provides all of them; see the Consolidated SPM documentation for the exact URL and version-rule guidance.

Required app configuration

The Demo's Info.plist and target capabilities already include everything the SDKs need (location, motion & fitness, background modes, notifications, and camera for Wellness). Don't re-derive these — the authoritative list is the Required App Configuration section of the Consolidated SPM documentation. Runtime permission timing is covered by Session & App Lifecycle Management, and the Diagnostics screen (§8) plus the health snapshot flag any missing entry at runtime.

Maintaining versions

  • Pin via ConsolidatedSPM-iOS. Bump it to the target release; it updates the whole matched set at once. Then commit Package.resolved — it records the exact versions the build was tested with.
  • SCPSmartDrive and TripWrapper are a matched pair — never update one without the other (ConsolidatedSPM does this for you).
  • SPM package version ≠ framework version. The package numbers and the framework versions shown in the health snapshot are different schemes; confirm both against the release's migration guide.
  • For a full list of releases and changelogs, see the ConsolidatedSPM-iOS Releases page.

3. Requirements & running the demo

Audience: external developers integrating the Scope SDKs. This app is a working reference — the parts integrations most often get wrong are shown running end to end with inline comments.

Requirements

  • Xcode with CocoaPods installed. CocoaPods supplies ZIPFoundation (used only for log export); the SDKs come via SPM (§2).
  • A real device — automotive trip detection does not work in the Simulator.
  • Location = Always + Precise and Motion & Fitness granted, or trips won't record in the background (§2 and the Required App Configuration).
  • Access to the SSO-protected scopetech GitHub org to resolve the SPM dependencies (PAT + SSO — see Consolidated SPM §2).

Run it

  1. pod install (pulls ZIPFoundation for log export).
  2. Open SmartDriveDemo.xcworkspace — not the .xcodeproj.
  3. Build & run on a real device.
  4. Log in, then choose on the login screen: SMART (no BLE) or BEACON (BLE devices), and Real-time or Post-trip event sending. Both are stored in UserDefaults and restored next launch.

No backend URL to hardcode — the base URL is returned with the account after login and cached. The credentials shown on the login screen are placeholders; use your own test account.


4. SDK integration layer

File Role
ScopeSDK.swift The façade — start / authenticated / stop, trip callbacks (onTripStarted/Ended/Updated), performBackgroundRefresh, isHealthy. Owns TripWrapper.initialize(...) and the internal HostAdapter that bridges the four TripWrapper protocols. Copied verbatim — do not edit.
AppScopeHost.swift The app's integration surface: ScopeHost (backend answers + onBeaconTripStart), ScopeConfiguration.app (mode / realtime / autoSend + configureSmartDrive/stopSmartDrive closures), and the LoggingProtocol adapter mapping TripLogGroup → LogGroup. The only file you write.
BLEFrameworkProxy.swift Bridges BLEFramework (BLEManager, SCPBeaconManager, SBeacon) to SDApiManager. Also exposes onBeaconTripStart() → SCPBeaconManager.shared.onTripStart() — the beacon snapshot for beacon/mHub trips. Living here keeps the BLEFramework import out of AppScopeHost (which would clash with TripWrapper_iOS.APIResponse).

Beacon / mHub note. In beacon mode, ScopeSDK invokes ScopeHost.onBeaconTripStart() at trip start; AppScopeHost forwards it to BLEFrameworkProxy.shared.onBeaconTripStart(), which snapshots the in-range beacon so the trip resolves a device ID and can be sent. Smart-mode apps use the protocol's default no-op. See the SDK Integration (recommended) page (§2.4 and the pitfalls) for detail.


5. App lifecycle — AppDelegate.swift

AppDelegate owns launch/auth/background wiring and the SmartDrive values (not the mechanics). Key responsibilities:

  • appType (smart vs beacon) and flags isMOTEnabled, isRealtimeTripSendingEnabled — the single source that ScopeConfiguration.app reads.
  • initializeFeatures() — configures the API, then arms ScopeSDK.start(configuration:host:)
    • authenticated(); wireScopeSDKCallbacks() connects onTripStarted/Ended/Updated.
  • configureSmartDrive() / stopSmartDrive() — values only: permission asks, autostart, thresholds, plus configureMProfiler(), configureBLEFramework(), configureMOT(). These are the closures ScopeConfiguration invokes. Keep them internal (they're called from AppScopeHost.swift).
  • Background tasks — registerBackgroundFetchHandler(), scheduleBackgroundFetch(), handleAppRefreshTask(), and SCPSmartDriveBackgroundFetchDelegate; forwards to ScopeSDK.performBackgroundRefresh().
  • TripWrapperDelegate — tripStarted/tripEnded/tripUpdated for UI refresh/analytics (never sends trips from here).
  • UserDefaults helpers getTripEndThreshold() / setTripEndThreshold(value:).

Lifecycle rules (arm in didFinishLaunching guarded by isAuthorized(), log out in exactly one place, permission timing) follow the Session & App Lifecycle Management page.


6. Trip data & UI

File Role
TripManager.swift Thin facade over TripWrapper — getLocalTrips(), getTargetTrips(), getCurrentTrip(), retryFailedTrips(), sendCompletedTrips(), and convertToTrip(from:) to bridge TripRecord → Trip. Does not implement SCPSmartDriveManagerDelegate and does not send trips. Calls retryFailedTrips() on didBecomeActive.
TripNotificationManager.swift Implements TripNotificationDelegate only — showTripStartNotification(), scheduleTripEndNotification(), cancelTripEndNotification(), showPendingTripNotification(). Background-only: skips notifications when the app is active; de-dupes per tripId. Schedules the trip-end local push getTripEndThreshold() − 2 seconds out.
TripsViewModel.swift Backs the Trips screen with three modes (logs, serverTrips, localTrips). Local trips come through TripManager (→ TripWrapper); server trips via SDApiManager.getTripsFor(...) + an assembled unfinished trip from getMZoneEvents(...), parsed by TripDetailsManager.
TripStatusViewModel.swift Live dashboard values. A 1 s timer reads SCPSmartDriveManager for current speed/distance/duration (display only), gated by TripWrapper.shared.getCurrentTrip(); pauses in background. Publishes via TripStatusDelegate.
TripDetailsManager.swift Parses backend trip JSON and MZone events into the app's models. createTrip(data:isLightMode:), createTrips(...) (assembles unfinished trips from events), filterEventsByThreshold(...), validateTrip(trip:). Defines the app data models below.

Data models (in TripDetailsManager.swift)

  • Trip — app display model: tripId, distance, duration, start/endLocation, start/endDate, numberOfExceptions, exceptionType, maxSpeed, events, finishedTrip, isBusiness.
  • TripEvent / TripEventType (braking, acceleration, cornering, speeding, distraction start/end) — each with a title and image; fromMZoneEventType(...) maps server strings.
  • TripStats, TripExceptionType — scoring/summary types.
  • TripRecord (from TripWrapper_iOS) is the SDK's value type; TripManager.convertToTrip bridges it to Trip.

7. Beacon / mHub

File Role
BeaconViewModel.swift Scans (SBeaconManager.startScan), lists paired (SCPBeacon) and unpaired (UnpairedSBeacon) beacons via SCPBeaconManager.getMixedBeacons(), refreshing every 1 s. Pairing installs the beacon to the selected vehicle (SDApiManager.installBeacon) and creates the "My Car" activity/beacon in SCPBeaconManager.
BeaconViewController.swift Two-section table (paired / unpaired); pull-to-refresh; tap an unpaired beacon to pair. Shows in-range state from beacon.isInRange.

Beacon trip recording is driven by the SDK, not these screens — pairing here is what makes onBeaconTripStart (§4) resolve a device ID at trip start.


8. Diagnostics, logs & export

An in-app health dashboard that surfaces the same signals as the SDK's health snapshot. It builds DiagnosticItems grouped into DiagnosticCategory (Permissions, SDK, Configuration, Network, Storage) with per-item DiagnosticStatus (ok / warning / critical) and a recommendation. Checks include: authorization, location & motion & notification permissions, TripWrapper init / delegate / protocol wiring, network (via NWPathMonitor), MProfiler config, MOT state, realtime sending, beacon config, storage space, and SDK versions. getOverallHealth() rolls them up.

For capturing and exporting the underlying logs (and reading multi-line health snapshots), see iOS Diagnostic Logs & Export.

Log capture & export

Beyond the in-app health dashboard, the demo captures and exports diagnostic logs:

File Role
SmartDriveHelper.swift logHealthCheck — authorization, permissions, recorder state, trip counts (the same signals as the SDK health snapshot).
LogEventManager.swift Core Data log store with retention; captures crashes and server errors.
ReportManager.swift Builds the per-day ZIP bundle with an identifying header.
ReportComposer.swift Shares the ZIP via email / the share sheet.
TripsViewController.swift Hosts the Export logs button (exportLogs).

ZIP bundling uses ZIPFoundation (via CocoaPods). Full capture / format / export guidance: iOS Diagnostic Logs & Export.


9. Authentication — LoginViewModel.swift

Wraps SDApiManager auth: isAuthenticated(), login(username:password:), fetchAccountDetails(), and validateCredentials(...) (→ ValidationResult). On success the app calls ScopeSDK.shared.authenticated() so recording begins post-auth (see the lifecycle page).


10. Wellness (optional module)

File Role
WellnessScanViewController.swift Camera-based vitals scan via Wellness_iOS (WellnessHealth): create/start/stop session, render camera + face ROI, show heart rate / breathing rate / oxygen saturation, progress, and validity guidance. Independent of trip recording.
CircularProgressBar.swift Reusable CAShapeLayer ring used by the wellness progress display.

11. Configuration & helpers

File Role
MOTSettings.swift Mode-of-transport config: MOTSettings (enabled, skip list ["Air","Water","Rail","Public"], map keys) and MOTConfig (airplane / railway / water / offroad thresholds) with createDefault() factories. Consumed by AppDelegate.configureMOT().
FormattingHelper.swift App-wide formatting: distance, duration (h m s and HH:MM:SS), speed (m/s → km/h), dates, and the server date parsers (toDateFromServerStringWithTimezone). Adds Float.format(f:), Double.formatted(decimals:), and Date.add/subtract.

12. Key flows

Launch → record → send. didFinishLaunching → initializeFeatures() → configureAPI() → ScopeSDK.start(.app, AppScopeHost()) → after login ScopeSDK.authenticated() → configureSmartDrive() runs, TripWrapper.initialize(...) runs once → SmartDrive detects a drive → TripWrapper records and (auto-send / realtime per config) uploads via AppScopeHost.postTripEventBatch → TripWrapperDelegate refreshes the UI; TripNotificationManager posts background notifications.

Beacon / mHub trip. Pair a beacon (§7) → on the next drive ScopeSDK calls onBeaconTripStart → BLEFrameworkProxy snapshots the in-range beacon → the trip is tagged with the beacon's device ID and uploaded.

Diagnostics. DiagnosticsViewModel.refresh() recomputes all checks; the same health signals appear in exported logs (see the Diagnostic Logs page).


Typical integration sequence (order matters)

The order the demo follows — and the order your app should follow:

  1. Configure the API base URL — AppDelegate.configureAPI, once per launch.
  2. Log in — SDApiManager.login. No SDK feature works without an authenticated session.
  3. Fetch the account — SDApiManager.getAccountWithComplete; selects the vehicle and sets the user ID, unit serial number, and base URL.
  4. Request & verify permissions — Always + Precise location, Motion, notifications.
  5. Arm recording — ScopeSDK.start(configuration:host:) + ScopeSDK.authenticated(), which runs configureSmartDrive() and initializes TripWrapper once.

Arm only after the account is fetched (step 3). Trips are tagged with the selected vehicle's unit serial number; starting to record before it's set means trips can't be matched to a vehicle and log headers come out empty. See Session & App Lifecycle Management.

13. Adopting this app as your integration template

  1. Copy ScopeSDK.swift verbatim.
  2. Write AppScopeHost.swift — backend answers via your API layer, config values from your app, the logging adapter for your LogGroup. Beacon/mHub apps add onBeaconTripStart() (route through your BLE proxy).
  3. Keep TripManager as a thin read facade and TripNotificationManager as TripNotificationDelegate-only.
  4. Wire AppDelegate per the lifecycle page; put SmartDrive values in configureSmartDrive().
  5. Use DiagnosticsViewModel / exported logs (Diagnostic Logs & Export) to verify the integration.

Full step-by-step: iOS SDK Integration (recommended.


14. Where to start

  1. Follow §3 — Requirements & running the demo and record a trip on a real device.
  2. Read Session & App Lifecycle Management — the most common source of integration bugs.
  3. Trace AppDelegate arming: configureAPI → login → account fetch → ScopeSDK.start / authenticated.
  4. Open the diagnostics files (§8) and export a log bundle to see exactly what support receives.

Support & licensing

  • Support: [email protected]
  • The SDKs are proprietary and require licensing; this app is provided for integration reference only.

Questions? Contact the Scope Mobile SDK team.

⚠️ **GitHub.com Fallback** ⚠️