Skip to content

[SDTEST-2704] Add watchOS and visionOS support#247

Merged
ypopovych merged 17 commits into
mainfrom
sdtest-2704-watchos-visionos-support
May 14, 2026
Merged

[SDTEST-2704] Add watchOS and visionOS support#247
ypopovych merged 17 commits into
mainfrom
sdtest-2704-watchos-visionos-support

Conversation

@ypopovych

@ypopovych ypopovych commented May 13, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add watchOS 8+ and visionOS 1+ to the SDK's distribution: new xcframework slices (watchos, watchsimulator, xros, xrsimulator), Package.swift / podspec / xcodeproj platform entries, Makefile archive targets, and CI matrix coverage. Bumps swift-code-coverage to 2.1.0 to pick up the matching binary slices; aligns Mac Catalyst minimum to v14 with it.
  • Conditionalize SDK source for the new platforms: WatchKit on watchOS (no UIKit UIDevice there), visionOS in the iOS-style branches, WKApplication notifications on watchOS, drop unused CoreTelephony framework, fix the long-broken os(watchOS) import UIKit branch in PlatformUtils. Gate Kronos/NTPClock (CFHost-dependent) to non-watchOS; force DateClock there.
  • Move RunLoopWaiter from DatadogSDKTesting to EventsExporter and route DataUploader.upload(data:) / uploadWithResult(data:) through it so main-thread callers on watchOS don't deadlock the run loop. Fix a latent URLProtocol contract violation in ServerMock (was calling both didFailWithError and urlProtocolDidFinishLoading).
  • Sweep dead OS-version #available checks now that minimums are iOS 15 / macOS 11 / tvOS 15 / watchOS 8 / visionOS 1 — drops the iOS<13 JSONEncoder workaround, the iOS 11.x ISO8601DateFormatter fallback, the iOS<13.4 FileHandle.seekToEnd/readToEnd fallback, the NSScanner.scan…(into: &NSString) legacy paths, etc. Net −152 lines.
  • Re-enable integration UI tests on watchOS (XCUIApplication is available there); the only build-config blocker was INFOPLIST_KEY_WKWatchOnly / INFOPLIST_KEY_WKApplication on the test app.
  • Re-enable network integration tests on watchOS after the redirect-handling fix landed in URLSessionInstrumentation.
  • Refactor the EventsExporter test infra: introduce an HTTPClientType protocol seam and migrate DataUploaderTests / DataUploadWorkerTests to a new in-process MockHTTPClient, sidestepping the watchOS URLProtocol bypass and getting those suites green there.

Test plan

  • xcodebuild build for iOS / iOS-sim / tvOS / tvOS-sim / macOS / Mac Catalyst / watchOS / watchOS-sim / visionOS / visionOS-sim
  • EventsExporter unit tests on iOS sim (96 / 0 failures)
  • EventsExporter unit tests on watchOS sim (95 / 1 skipped — HTTPClientTests.testWhenRequestIsNotDelivered requires URLSession+URLProtocol error propagation that watchOS doesn't deliver; equivalent assertion is covered at the DataUploader layer via MockHTTPClient)
  • DatadogSDKTesting unit tests on iOS sim
  • Integration tests on watchOS sim (make tests/integration/watchOSsim — UI + unit + Swift Testing all green)
  • Integration tests on iOS sim (regression — green)
  • CI matrix run on the new watchOSsim / visionOSsim entries (covered by the workflow changes here)

Known limitations on watchOS, documented in code

  • KSCrash 2.5.1 hardcodes KSCRASH_HAS_SIGNAL = 0 and KSCRASH_HAS_MACH = 0 on watchOS (upstream KSSystemCapabilities.h, comment: "WatchOS signal is broken as of 3.1"). Swift array-bounds traps emit SIGILL, which can't be captured with the only available monitors (.nsException, .cppException). Integration XCCrash / STCrash tests are gated with .disabled(if: XcodeTestRunner.isWatchOSChildSDK, …) and a doc-pointer to the upstream constant.
  • One HTTPClientTests failure-path test is skipped — see test-plan note above.
  • Real-device support for Spawn-driven features (TIA, code coverage post-processing, dSYM symbolication) inherits the existing iOS/tvOS-device gap; gated through the same targetEnvironment(simulator) || os(macOS) pattern.

🤖 Generated with Claude Code

ypopovych and others added 5 commits May 13, 2026 13:37
Adds watchos/watchsimulator/xros/xrsimulator slices to the SDK
distribution (Package.swift, podspec, xcodeproj), so consumers can
link DatadogSDKTesting from watchOS 8+ and visionOS 1+ targets.

- Conditionalize UIKit/WatchKit imports and the iOS-only orientation /
  appearance helpers to the new platforms. Replace the dead
  `#if os(watchOS) import UIKit` branch (UIKit is a no-op subset on
  watchOS) with proper WatchKit + visionOS UIKit branches.
- Bump swift-code-coverage to 2.1.0 to pick up the new xcframework
  slices; align Mac Catalyst minimum to v14 accordingly. Drop the dead
  `xcode16_*` cases from XcodeVersion mapping (2.1.0 only exposes
  `.xcode26`).
- Gate Kronos and `NTPClock` to non-watchOS platforms — Kronos depends
  on `CFHost*` which doesn't exist on watchOS. Force `DateClock` there.
- Use `WKApplication` notification names on watchOS in DDTestMonitor.
  Skip `Spawn.output("hostname")` for `developerMachineHostName` on
  real-device watchOS/visionOS.
- Move `RunLoopWaiter` from `DatadogSDKTesting` into `EventsExporter`
  (now `public`) and switch `DataUploader.upload(data:)` /
  `uploadWithResult(data:)` to use it instead of a bare
  `DispatchSemaphore`. Main-thread callers on watchOS no longer
  deadlock the run loop while waiting for URLSession to deliver.
- Fix a latent `URLProtocol` contract violation in `ServerMock`: it was
  calling both `didFailWithError` and `urlProtocolDidFinishLoading`;
  watchOS doesn't tolerate that and silently drops the error callback.
- Bump `WATCHOS_DEPLOYMENT_TARGET = 8.0`, `XROS_DEPLOYMENT_TARGET = 1.0`,
  `TARGETED_DEVICE_FAMILY = 1,2,3,4,6,7`. Drop unused CoreTelephony
  framework dependency entirely (no source references it).
- Add `INFOPLIST_KEY_WKApplication[sdk=watch*] = YES` and
  `INFOPLIST_KEY_WKWatchOnly[sdk=watch*] = YES` to the
  IntegrationTests-UITests app so it installs on the watchOS simulator.
- Makefile + CI workflows: new archive targets for the four watchOS /
  visionOS SDKs, expanded xcframework / dSYMs deps, and matrix entries
  for `watchOSsim` / `visionOSsim` in unit and integration tests.

Skipped on watchOS with documented reasons:
- 7 EventsExporter tests that drive URLProtocol-based mocks
  synchronously: watchOS' URLSession bypasses `URLProtocol` mocks on
  sync calls (verified empirically — `canInit` is called but
  `startLoading` is not, and the request reaches the real network).
- IntegrationTests crash tests (XCCrash / STCrash): KSCrash 2.5.1
  explicitly disables SIGNAL and MACH monitors on watchOS
  (`KSCRASH_HAS_SIGNAL = 0`, `KSCRASH_HAS_MACH = 0`), so SIGILL from
  Swift array-bounds traps cannot be captured.
- IntegrationTests networkIntegration tests: redirect handling
  produces 2 info spans on watchOS instead of 1; needs separate
  investigation.

Verified: SDK builds for all six platforms (iOS, tvOS, macOS,
Catalyst, watchOS, visionOS — device and simulator where applicable).
EventsExporter unit tests pass on watchOS sim (95 tests, 7 skipped).
IntegrationTests pass on watchOS sim (UI + smoke). iOS regressions
green.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Now that the minimum deployment targets are iOS 15, macOS 11, tvOS 15,
watchOS 8, and visionOS 1, drop the `#available` checks and legacy
fallbacks that are unreachable under those minimums.

- JSONUtils: drop iOS 13 / macOS 10.15 gate on `.withoutEscapingSlashes`.
- EncodableValue: remove the SR-6163 primitive-serialization workaround
  (the array-strip trick for iOS <13's `JSONEncoder`).
- DateFormatting: remove the legacy `DateFormatter` fallback for the
  iOS 11.0/11.1 `.withFractionalSeconds` crash.
- File.swift: drop `#if compiler(>=5.2)` + iOS 13.4 gate, remove
  `legacyAppend` / `legacyRead` (`seekToEndOfFile` / `readDataToEndOfFile`).
  Both `append` and `read` use the modern `seekToEnd` / `readToEnd` API
  directly.
- PlatformUtils: drop `OSX 10.14` gate in `getAppearance` and the
  `iOS 13` gate in `getOrientation` (`statusBarOrientation` fallback).
- URLSessionInstrumentation: drop the iOS 13 / macOS 10.15 gate around
  `injectIntoNSURLSessionCreateTaskMethods`. Simplify the formerly-noisy
  `macOS 12 / iOS 15 / tvOS 15 / watchOS 8 / visionOS 1` checks to just
  `#available(macOS 12.0, *)` since macOS is the only platform where it
  is still conditional. Strip the redundant `visionOS 1.0` from the
  still-conditional iOS 16 / macOS 13 / watchOS 9 / tvOS 16 checks.
- InstrumentationUtils: drop the iOS 14 / macOS 11 / tvOS 14 /
  watchOS 7 / visionOS 1 gate around `os_log`.
- StderrCapture: drop iOS 13 gate and the legacy `NSScanner.scan…(into:
  &NSString)` branches in `logTimedErrOutput` and `logUIStep`.
- DDNetworkInstrumentation: drop the iOS 13 gate around
  `withUnsafeCurrentTask`.

Net: -152 lines. Verified builds for iOS/macOS/tvOS/watchOS/visionOS
simulators; EventsExporter unit tests green on iOS (96/96) and watchOS
(95 + 7 skipped).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Network instrumentation now handles URLSession redirect / async-await
paths correctly on watchOS, so the previously-disabled
`networkIntegration` integration tests can run there.

- Drop dead `#available(iOS 13 / macOS 10.15 / watchOS 6 / tvOS 13)`
  gate around `injectIntoNSURLSessionCreateTaskMethods` and
  `#available(iOS 14 / macOS 11 / tvOS 14)` around `os_log` in
  InstrumentationUtils — all below the current deployment minimums.
- Simplify the formerly-all-platform `#available(macOS 12 / iOS 15 /
  tvOS 15 / watchOS 8)` checks to `#available(macOS 12.0, *)` (the
  only platform still conditional under the new minimums). Same for
  `AsyncTaskDelegate`'s class-level annotation.
- Add `visionOS` to `InstrumentationUtils`'s `UIKit` import branch.
- Re-enable `XCNetworkIntegration` and `STNetworkIntegration` runner
  tests on watchOS by removing the
  `.disabled(if: XcodeTestRunner.isWatchOSChildSDK)` traits.

Verified passing on watchOS sim and iOS sim integration runs.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Replaces the `URLProtocol`-based test mock for `DataUploader` /
`DataUploadWorker` tests with an in-process `HTTPClientType` mock. The
old `ServerMock` + `URLSessionConfiguration.protocolClasses` setup is
incompatible with watchOS' URL-loading machinery for sync-via-runloop
callers: watchOS calls `URLProtocol.canInit` (returns `true`), then
silently bypasses the protocol and routes the request to the real
network. The behavior is independent of which thread / queue dispatches
`.resume()`, of the run-loop wait strategy, and of using
`URLProtocol.registerClass`. Switching the tests to mock at the
`HTTPClient` level instead of `URLProtocol` sidesteps the issue
entirely while keeping the production code path unchanged.

- `Sources/EventsExporter/Upload/HTTPClient.swift`: add `HTTPClientType`
  protocol with the two existing entry points (`send`, `sendWithResult`).
  Concrete `HTTPClient` conforms.
- `Sources/EventsExporter/Upload/DataUploader.swift`: retype `httpClient`
  from concrete `HTTPClient` to `any HTTPClientType`.
- `Tests/EventsExporter/Helpers/MockHTTPClient.swift` (new): in-process
  mock that records requests and synchronously invokes the caller's
  completion handler with the configured delivery. Exposes the same
  `waitFor` / `waitAndReturnRequests` / `waitAndAssertNoRequestsSent`
  API surface that `ServerMock` provided.
- `Tests/EventsExporter/Upload/DataUploaderTests.swift` and
  `DataUploadWorkerTests.swift`: migrate every `ServerMock` +
  `HTTPClient(session:)` instantiation to `MockHTTPClient`. Drop the
  per-class watchOS skip — both suites now run cleanly there.
- `Tests/EventsExporter/Upload/HTTPClientTests.swift`: unchanged. These
  tests specifically validate `HTTPClient`'s `URLSession` mapping, so
  they have to keep using `URLProtocol`. The one failure-path case
  remains skipped on watchOS; the equivalent assertion at the
  `DataUploader` layer is now covered there via
  `MockHTTPClient(delivery: .failure(...))`.

Results: watchOS EventsExporter goes from 95 tests / 7 skipped to
95 tests / 1 skipped, 0 failures. iOS regression: 96 / 0 / 0.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@ypopovych
ypopovych requested review from a team as code owners May 13, 2026 14:22
@ypopovych
ypopovych requested a review from anmarchenko May 13, 2026 15:10
ypopovych and others added 12 commits May 13, 2026 16:32
Previously `RunLoopWaiter.wait()` on the main thread busy-polled with
`CFRunLoopRunInMode(.defaultMode, 0, true)` — fine for latency but
hot on CPU and pointlessly tight when callers tend to take several
ms. Switch to a 0.05s timeout per iteration and post a wake-up event
from `signal()` so the run loop returns immediately rather than waiting
out the timeout:

- `signal()` enqueues a no-op block on the main run loop (any common
  mode) and calls `CFRunLoopWakeUp`. Any `wait()` blocked in
  `CFRunLoopRunInMode` exits, re-checks `_locked`, and returns.
- The 0.05s safety-net timeout covers the race where `signal()` runs
  before `wait()` enters its blocking state, and the case where the
  run loop has no other sources installed (which would otherwise make
  `CFRunLoopRunInMode` return with `kCFRunLoopRunFinished`).

Verified: EventsExporter unit tests on watchOS sim (95 / 1 skipped /
0 failures) and iOS sim (96 / 0 / 0). DatadogSDKTesting unit tests on
iOS sim (356 / 0 / 0) — `waitForAsync` also exercises this code path.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
`testGivenDataToUpload_whenUploadFinishesAndNeedsToBeRetried_thenDataIsPreserved`
intermittently failed with `API violation - multiple calls made to
-[XCTestExpectation fulfill] for "Upload has started"`.

With `needsRetry: true` the batch stays on disk, so the worker
re-uploads on every tick (every ~50ms under `UploadPerformanceMock.veryQuick`).
If a second tick fires before `wait(for:timeout:)` returns after the
first fulfillment, XCTest throws on the duplicate fulfill — pure
scheduling-jitter race.

Set `assertForOverFulfill = false` on the expectation: the test only
cares that an upload starts at least once. Verified by running the
test 5x in isolation and the full suite 3x — all green.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Both URLProtocol-based `HTTPClientTests` cases used a 1s
`waitForExpectations` timeout. The actual round-trip is milliseconds,
but on a busy simulator (parallel test execution, CI load) the
URLSession + URLProtocol setup occasionally blows past 1s, causing
`testWhenRequestIsNotDelivered_itReturnsHTTPRequestDeliveryError` to
spuriously time out:

  Asynchronous wait failed: Exceeded timeout of 1 seconds, with
  unfulfilled expectations: \"receive response\".

Bump both wait timeouts to 5s — generous enough to ride out jitter,
still tight enough to fail fast on a real hang. Verified with 5
consecutive runs of `HTTPClientTests` on iOS sim, all green.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
`SwiftTestingTraitTests` and `SwiftTestingTaggedSuiteTests` both use
the shared `ObserverTesterTrait`. Swift Testing runs `@Suite` types in
parallel by default, so both can reach the trait's `provideScope`
post-checks concurrently. The original ordering was:

  1. `if session.modules.first?.value.duration > 0 { await session.stop() }`
  2. `let statuses = try #require(session.modules[...].suites[...])`

`SessionManager.stop()` clears `_session` and ends the underlying
session — so when one suite wins the race and calls stop() first, the
sibling's step 2 reads `nil` and the whole suite fails with `Expectation
failed: session.modules[…]?.suites[…] → nil`, followed by knock-on
`unknownSuite` / `moduleNotFound` errors and a process restart.

Move the snapshot read above the conditional `session.stop()` so the
data is captured before any sibling has a chance to tear the session
down. Verified by running both suites 5x in a row on tvOS sim — all
green.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
When the test bundle crashes and XCTest relaunches it, Swift Testing
does NOT re-run \`prepare(for:)\` for tests that already completed in
the previous launch (\"summary will include totals from previous
launches\"). Our \`SwiftTestingSuiteProvider.Registry\` is process-local,
so it ends up empty for any suite whose tests are all carried over.
The suite-level \`provideScope\` then calls \`registry.count(for:)\` /
\`registry.tests(for:)\` / \`registry.suites(for:)\`, which previously
threw \`unknownSuite\` / \`moduleNotFound\`, killing the whole suite at
its \`@Suite\` line:

    ✘ Suite SwiftTestingTraitTests recorded an issue at
      SwiftTestingTraitTests.swift:14:2: Caught error:
      unknownSuite(name: \"SwiftTestingTraitTests\", ...)

The downstream code already copes with empty suites (\`testsCount: 0\`,
empty \`_left\`, etc.), so make Registry return empty sets / zero
counts instead of throwing. Also register the bare \`module → suite\`
slot from \`Registry.register\` when the info is a suite, so suites
that are scoped without any individual test calling \`register\` still
show up in \`suites(for:)\`.

Verified by stress-running the previously-flaky suite pair on tvOS 5x
in a row (all green) and re-running the full DatadogSDKTesting / iOS,
EventsExporter / watchOS test suites — no regressions (356 / 0 and 95
/ 1-skipped / 0 respectively).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
\`unknownSuite\` and \`moduleNotFound\` were only ever thrown by the
\`Registry\` lookups that are now non-throwing, and no other code in
\`Sources/\`, \`Tests/\`, or \`IntegrationTests/\` references them.
Drop them; leave \`moduleAlreadyEnded\` in place since \`State.didEnded\`
still throws it.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
The unit-test job ran all five platforms sequentially in each Xcode
job (1 job per Xcode × 5 sequential platforms × 2 schemes = ~30 wall
minutes total). Split it into one job per (Xcode × platform), so
15 jobs run in parallel and each one tests both schemes on a single
platform.

- Makefile: keep the existing `tests/unit/%` pattern (% = scheme,
  runs all platforms — preserved for local use) and add five explicit
  per-platform targets `tests/unit/{macOS,iOSsim,tvOSsim,watchOSsim,
  visionOSsim}` that run both schemes on one platform. Make's
  explicit-target rule wins over the pattern.
- `.github/workflows/unitTests.yml`: replace the single-axis matrix
  with a `xcode × platform` matrix. Each job runs
  `make tests/unit/${platform}`. Job names render as
  "Xcode_26.x / iOSsim" etc. Artifact names are scoped per
  (xcode, platform) to stay unique.

Verified via `make -n tests/unit/iOSsim` and `make -n tests/unit/EventsExporter`
— new per-platform target runs the two schemes once; old per-scheme
pattern still hits all five platforms.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Two follow-ups on the previous parallelization commit:

- Collapse the five explicit per-platform Makefile targets into a single
  `tests/unit/%` pattern target that picks the right `SIMULATOR` value
  via `$(filter $*,…)`, exactly mirroring how `tests/integration/%` is
  already written. Update the no-arg `tests/unit` aggregate to fan out
  to the five platforms (instead of two schemes).

- Move simulator names into per-Xcode entries in the workflow matrix
  (`matrix.env.iossim` / `tvossim` / `watchossim` / `visionossim`).
  This mirrors `integrationTests.yml` and lets future Xcode versions
  pick different simulator names (e.g. when Apple drops a device from
  the Xcode-bundled simulator runtime) without touching the platforms
  axis.

Verified `make -n tests/unit/iOSsim`, `make -n tests/unit/macOS`, and
`make -n tests/unit` each emit the expected xcodebuild invocations.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Local development sometimes wants to run a single scheme across every
platform without invoking the full matrix. Add explicit per-scheme
targets that shadow the `tests/unit/%` platform-pattern:

  make tests/unit/EventsExporter     # all 5 platforms × EventsExporter
  make tests/unit/DatadogSDKTesting  # all 5 platforms × DatadogSDKTesting

Implemented via a small `xctest_unit_all_platforms` macro so adding a
new unit-test scheme is a one-liner. Per-platform target
(`tests/unit/iOSsim`, …) and aggregate (`tests/unit`) behaviour is
unchanged.

Verified with `make -n` for all four target shapes.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Two fixes for the residual tvOS flakes after a crash-induced bundle
relaunch:

1. `Registry.register(test:)` — the suite-branch was
   \`_tests[test.module, default: [:]][test.suite, default: []]\` as a
   bare expression, which is a *read* in Swift's subscript-with-default
   form, not a write. The dictionary was never mutated for
   \`isSuite=true\`, so on a relaunch where no individual test of a
   suite re-runs \`prepare(for:)\`, \`registry.registeredTests[module]?[suite]\`
   stayed nil and the trait failed at
   \`#require(tests[ddModule]?[ddSuite])\`.
   Replace with an explicit assignment that runs only when the slot is
   missing — subscript-with-default on the LHS of an assignment does
   mutate, creating the outer module slot if needed.

2. `ObserverTesterTrait` — the shared `DatadogSwiftTestingTrait.sharedSuiteProvider`
   was only set up in `prepare(for:)`. On a relaunch where Swift
   Testing skips `prepare(for:)` for a suite whose tests all completed
   in the previous launch but still invokes the suite-level
   \`provideScope\`, the provider stayed nil and \`#require(provider …)\`
   threw — which Swift Testing then escalated to a fatal
   "Recording issues for suites is not supported" and crashed the
   bundle into another relaunch loop. Extract the setup into a private
   \`ensureSuiteProvider()\` helper and call it from both \`prepare\` and
   \`provideScope\`.

Verified with 5/5 consecutive runs of the trait suites on tvOS sim and
a full \`DatadogSDKTesting\` iOS regression (356 / 0).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Restore the end-of-suite \`session.stop()\` call but make it race-safe
across parallel @suite siblings that share \`ObserverTesterTrait\`.

\`Mocks.SessionManager.stop()\` clears \`_session\` and the observer
fires \`SessionAndModuleObserver.didFinish(session:)\`, which nils both
the manager's stored session and
\`DatadogSwiftTestingTrait.sharedSuiteProvider\`. Swift Testing runs
\`@Suite\` types in parallel, so two suites racing into the post-checks
could leave one of them reading \`nil\` and crashing with the fatal
"Recording issues for suites is not supported".

New \`PostCheckGate\` actor:

- \`session(for:)\` reads \`provider.session.session\` and stashes the
  first non-nil reference seen. Siblings that arrive after a stop
  receive the stashed \`Mocks.Session\` — the underlying class is still
  alive even though the manager's pointer is cleared.
- \`stopOnce(via:)\` guards the stop call with an atomic flag so it runs
  exactly once across all sibling suites.

Verified with 8/8 consecutive runs of the trait suites on tvOS sim
(`SwiftTestingTraitTests` + `SwiftTestingTaggedSuiteTests`) and a full
\`DatadogSDKTesting\` iOS regression (356 / 0).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Xcode 26.2 / Testing library 1501 (tvOS sim) failed to compile

    let session = try await #require(Self.gate.session(for: suiteProvider))

with "async call in a function that does not support concurrency". The
`#require` macro expansion on that toolchain doesn't reliably keep the
inner expression in an async context, so the `await` is lost inside the
synthesized closure.

Extract the `await` to the caller so the macro only deals with the
optional unwrap:

    let resolvedSession = await Self.gate.session(for: suiteProvider)
    let session = try #require(resolvedSession)

Build now succeeds on tvOS sim under Xcode 26.2.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@ypopovych
ypopovych merged commit 5ecc0b8 into main May 14, 2026
29 of 31 checks passed
@ypopovych
ypopovych deleted the sdtest-2704-watchos-visionos-support branch May 14, 2026 09:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants