Thanks to visit codestin.com
Credit goes to github.com

Skip to content

fix(ios): state the isolation of every plugin source the iOS app compiles - #2811

Merged
datlechin merged 3 commits into
mainfrom
fix/ios-shared-source-isolation
Sep 12, 2026
Merged

fix(ios): state the isolation of every plugin source the iOS app compiles#2811
datlechin merged 3 commits into
mainfrom
fix/ios-shared-source-isolation

Conversation

@datlechin

Copy link
Copy Markdown
Member

main is red on iOS Tests, and has been three times running for the same reason, one symbol at a time.

The defect

The iOS app target sets SWIFT_DEFAULT_ACTOR_ISOLATION: MainActor (TableProMobile/project.yml:146). It also compiles 20 source files that live under Plugins/ and belong to a macOS plugin target, which sets nothing of the kind.

So one file has two default isolations: nonisolated in the plugin, @MainActor in the app. Every declaration in it that is not marked nonisolated is main-actor on iOS alone, and the first nonisolated caller that touches it fails:

main actor-isolated property 'hasRangeTypes' can not be referenced from a nonisolated context
main actor-isolated property 'hasMultirangeTypes' can not be referenced from a nonisolated context
main actor-isolated property 'hasProcedureKind' can not be referenced from a nonisolated context

Nothing in the macOS build can see it, because the plugin compiles the same file correctly. The iOS job is the only thing that catches it, it runs only when a change touches an iOS path, and it reports one symbol at a time, so fixing the named symbol uncovers the next one. That is what happened: quoteLiteral was fixed, which exposed hasRangeTypes, which would have exposed FreeTDSErrorState.

The fix

Two declarations were left, and both are here:

  • PostgreSQLCapabilities (the three failing properties), read from the already-nonisolated PostgreSQLObjectQueries
  • FreeTDSErrorState, constructed at FreeTDSConnection.swift:25 by a nonisolated private let, so it is the next error rather than a hypothetical one

What stops it coming back

scripts/ci/check-ios-shared-isolation.py reads the shared file list out of TableProMobile/project.yml rather than repeating it, so a file added to the iOS target is covered the moment it is added. It reports every top-level declaration that states no isolation of its own, runs in Repo Hygiene on Ubuntu in under a second, and needs no Xcode.

Proven both ways: it passes on this branch, and re-removing nonisolated from PostgreSQLCapabilities makes it name that exact line and exit 1.

The invariant is written up in CLAUDE.md beside the others, including the related rule that a shared file must not reach into plugin-only code at all.

Also here

A SwiftLint error that arrived with #2809 and fails swiftlint lint --strict on main: a blank line before the closing brace of ObjectCopyRowCopier.

Verification

  • Four independent audits of all 20 shared files agree: 60 top-level declarations, every one now explicitly isolated, and not one of them touches UI, AppKit, UIKit, SwiftUI, Observation or any @MainActor type.
  • PostgreSQLDriver and MSSQLDriver plugins both build.
  • verify.sh build: PASS.
  • swiftlint lint --strict: clean, which it is not on main.
  • actionlint on the changed workflow: clean.

The iOS target itself cannot be built on this machine: the vendored oracle-nio checkout fails first with unknown attribute 'usableFromInlinenonisolated' from its @TaskLocal macro, which predates this branch and which CI's toolchain does not hit. CI is the check that matters, and the static guard is what makes the claim checkable without it.

https://claude.ai/code/session_01GdkP9Kj2oVdzM5acwU69A3

@datlechin
datlechin merged commit 35bb136 into main Sep 12, 2026
4 of 7 checks passed
@datlechin
datlechin deleted the fix/ios-shared-source-isolation branch September 12, 2026 17:14
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.

1 participant