Skip to content

TerraiOS initConnection crash in ClientApp app, but same code works in TerraSample (iOS 26.2.1, v1.6.31) #11

Description

@gokulgovind-hit

TerraiOS initConnection crash in ClientApp, but same code works in TerraSample (iOS 26.2.1, v1.6.31)

Summary

I have two apps in the same repo using the Terra iOS SDK (TerraiOS v1.6.31):

  • TerraSample (minimal sample app)
  • ClientApp (main production app, target: ClientApp)

Both use effectively the same TerraHealthManager implementation and run on the same device, iOS version, Xcode version, and Terra SDK version.

In TerraSample, the Terra connection + HealthKit authorization + daily data fetch work without issues.

In ClientApp, the app crashes inside the Terra / HealthKit authorization path during TerraManager.initConnection(type: .APPLE_HEALTH, ...), even though the capabilities and entitlements are the same.


Environment

  • Device: iPhone (real device)
  • iOS: 26.2.1
  • Xcode: 26.0.1
  • Terra iOS SDK: TerraiOS v1.6.31
  • Targets:
    • Working: TerraSample
    • Crashing: ClientApp (mobile-ios/ClientApp)

Relevant code

TerraHealthManager (ClientApp)

`final class TerraHealthManager: @unchecked Sendable {
static let shared = TerraHealthManager()
private let devId = "
private var terraManager: TerraManager?
private let queue = DispatchQueue(label: "ClientApp.terra.manager")

// Check if a Terra connection exists
var isTerraUserConnected: Bool {
    guard let terraManager else { return false }
    return terraManager.getUserId(type: .APPLE_HEALTH) != nil
}

// Public entry point
func ensureTerraReady() async throws {
    try await initialiseSDKIfNeeded()
    try await ensureUserConnection()
}

private func initialiseSDKIfNeeded() async throws {
    if terraManager != nil { return }

    try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, Error>) in
        queue.async {
            if self.terraManager != nil {
                continuation.resume()
                return
            }
            let referenceId = UUID().uuidString
            Terra.instance(devId: self.devId, referenceId: referenceId) { manager, error in
                if let _ = error {
                    continuation.resume(throwing: TerraHealthError.sdkNotInitialised)
                    return
                }
                guard let manager else {
                    continuation.resume(throwing: TerraHealthError.sdkNotInitialised)
                    return
                }
                self.terraManager = manager
                continuation.resume()
            }
        }
    }
}

private func ensureUserConnection() async throws {
    guard let terraManager else {
        throw TerraHealthError.sdkNotInitialised
    }
    if terraManager.getUserId(type: .APPLE_HEALTH) != nil {
        return
    }

    // Fetch Terra token from our backend (this works; not the cause of crash)
    let token = try await TerraAPI.fetchToken()

    try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, Error>) in
        let activityPermissions: Set<CustomPermissions> = [.ACTIVITY_SUMMARY]
        terraManager.initConnection(
            type: .APPLE_HEALTH,
            token: token,
            customReadTypes: activityPermissions,
            schedulerOn: false
        ) { success, error in
            if let error {
                continuation.resume(throwing: TerraHealthError.initConnectionFailed(error.localizedDescription))
                return
            }
            guard success else {
                continuation.resume(throwing: TerraHealthError.initConnectionFailed("Unsuccessful status"))
                return
            }
            continuation.resume()
        }
    }
}

}`

This logic (same devId, same initConnection call) has been copied into the TerraSample project and works there.

SwiftUI entry point (ClientApp)

`private func requestTerraPermission() {
isLoading = true
errorMessage = nil
isConnected = false
activitySummaryText = nil

Task {
    do {
        try await TerraHealthManager.shared.ensureTerraReady()
        await MainActor.run {
            isLoading = false
            isConnected = TerraHealthManager.shared.isTerraUserConnected
            if isConnected {
                loadActivitySummary()
            }
        }
    } catch {
        await MainActor.run {
            isLoading = false
            errorMessage = error.localizedDescription
        }
    }
}

}`

The TerraSample app uses the same pattern: .onAppear { Task { try await TerraHealthManager.shared.ensureTerraReady() } }.

Entitlements and Info.plist

Both targets now have matching HealthKit entitlements.

TerraSample entitlements:

<!-- TerraSample/TerraSample/TerraSample.entitlements --> <dict> <key>com.apple.developer.healthkit</key> <true/> <key>com.apple.developer.healthkit.access</key> <array> <string>health-records</string> </array> <key>com.apple.developer.healthkit.background-delivery</key> <true/> </dict>

ClientApp entitlements:

<!-- mobile-ios/ClientApp/ClientApp.entitlements --> <dict> <key>com.apple.developer.applesignin</key> <array> <string>Default</string> </array> <key>com.apple.developer.healthkit</key> <true/> <key>com.apple.developer.healthkit.access</key> <array> <string>health-records</string> </array> <key>com.apple.developer.healthkit.background-delivery</key> <true/> </dict>

Both Info.plist files contain the same Terra background task identifier:

<!-- TerraSample/TerraSample/Info.plist --> <dict> <key>BGTaskSchedulerPermittedIdentifiers</key> <array> <string>co.tryterra.data.post.request</string> </array> </dict>

<!-- mobile-ios/ClientApp/Info.plist --> <dict> <key>BGTaskSchedulerPermittedIdentifiers</key> <array> <string>co.tryterra.data.post.request</string> </array> <!-- ...other app keys (fonts, Google Sign-In, etc.)... --> </dict>

Crash details (ClientApp)

Image

When running the ClientApp target and opening the Terra connect screen:
As soon as terraManager.initConnection(type: .APPLE_HEALTH, ...) executes, the app crashes.
The Xcode thread view shows the crash on a HealthKit queue:
Key frames (from the screenshot stack trace):

Note: There is no Swift error thrown from my code path; instead it’s a low‑level crash due to _dispatch_assert_queue_fail. The console message indicates a queue assertion failure in the HealthKit client implementation.

What I have already verified

  • Same code: TerraHealthManager logic is effectively identical in TerraSample and ClientApp.
  • Same dev ID: "".
  • Same Terra iOS SDK version: TerraiOS v1.6.31.
  • Same iOS version: 26.2.1.
  • Same Xcode version: 26.0.1.
  • Same HealthKit capabilities / entitlements: both have com.apple.developer.healthkit, com.apple.developer.healthkit.access with health-records, and com.apple.developer.healthkit.background-delivery.
  • Dynamic token path works in practice (I can see the JSON response decoding correctly to TerraTokenResponse); ignoring token issues, the crash happens during HealthKit authorization, not while calling my backend.
    Also:
  • I have updated TerraSample to not only connect but also fetch daily step count using Terra; that app continues to work without any crash.

Expected behavior

  • TerraHealthManager.shared.ensureTerraReady() should behave the same way in both targets:
    - Initialize Terra.
    - Request HealthKit authorization via TerraManager.initConnection(...).
    - Either:
    - Complete successfully, or
    - Call the completion handler with success = false and an error.
  • It should not crash due to an internal dispatch queue assertion inside HealthKit / Terra.

Actual behavior

  • TerraSample (working):
    - Authorization UI appears as expected.
    - User can grant permissions.
    - Subsequent getDaily calls run successfully.
  • ClientApp (crashing):
    - When calling TerraHealthManager.shared.ensureTerraReady() from SwiftUI .onAppear using Task { ... }, the app crashes during TerraManager.initConnection with _dispatch_assert_queue_fail on a HealthKit client queue.
    - No Swift error is thrown back to my completion handler; the process just terminates.

Questions for Terra team

  • Are there any target‑specific or project‑specific requirements (beyond HealthKit entitlements and BGTaskSchedulerPermittedIdentifiers) that could cause TerraManager.initConnection → AppleHealth.init → HealthStore.requestAuthorization to assert on a specific dispatch queue in one target but not another, even when:
    - Code is identical,
    - SDK version is identical,
    - Entitlements are identical,
    - Call site is the same pattern (SwiftUI + Task + await ensureTerraReady())?

  • Is there any known issue in TerraiOS v1.6.31 where initConnection/HealthKit authorization is sensitive to being called from a particular queue context in multi‑module apps?

  • Is there a way to enable additional diagnostic logging in TerraiOS (or recommended flags) so I can capture more detailed logs about:
    The precise queue expectations around HealthKit authorization, and
    Why _dispatch_assert_queue_fail is being triggered for the ClientApp target but not for TerraSample?

Any guidance or suggested workarounds (e.g., forcing initConnection to be called on a specific queue / main thread, additional configuration needed, or known fixes in a newer TerraiOS version) would be greatly appreciated.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions