You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
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):
closure Initialise method is not marked as throw, and no error models exist #1 in static HealthStore.requestAuthorization(readTypes:writeTypes:completion:)
Queue: com.apple.HealthKit.HKHealthStoreImplementation.client...
So this fails inside HealthKit / Terra’s authorization path, before any call to getDaily or ACTIVITY_SUMMARY parsing.
In the TerraSample app, with the same SDK version, devId, and effectively the same TerraHealthManager, the authorization path completes successfully and I can fetch step count / daily data.
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.
TerraiOS
initConnectioncrash 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
TerraHealthManagerimplementation 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 duringTerraManager.initConnection(type: .APPLE_HEALTH, ...), even though the capabilities and entitlements are the same.Environment
TerraiOS v1.6.31TerraSampleClientApp(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")
}`
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
}`
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)
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):
Queue: com.apple.HealthKit.HKHealthStoreImplementation.client...
So this fails inside HealthKit / Terra’s authorization path, before any call to getDaily or ACTIVITY_SUMMARY parsing.
In the TerraSample app, with the same SDK version, devId, and effectively the same TerraHealthManager, the authorization path completes successfully and I can fetch step count / daily data.
What I have already verified
Also:
Expected behavior
- Initialize Terra.
- Request HealthKit authorization via TerraManager.initConnection(...).
- Either:
- Complete successfully, or
- Call the completion handler with success = false and an error.
Actual behavior
- Authorization UI appears as expected.
- User can grant permissions.
- Subsequent getDaily calls run successfully.
- 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.31where 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.