From 49c2952a25643d206f8639412872c4c79491a5b1 Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Wed, 22 Jul 2026 13:15:27 -0400 Subject: [PATCH 1/8] Drop the AndroidBluetooth dependency that breaks Apple builds --- Package.swift | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Package.swift b/Package.swift index 7a00432..3fda959 100644 --- a/Package.swift +++ b/Package.swift @@ -10,7 +10,13 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/PureSwift/GATT.git", branch: "master"), - .package(url: "https://github.com/PureSwift/AndroidBluetooth.git", branch: "master"), + // AndroidBluetooth is temporarily not declared. It requests the `AndroidManifest` product + // from `Android`, but that product moved to `swift-android-native` + // (PureSwift/Android#40), and SwiftPM validates the whole package graph even for + // dependencies conditional on `.android` — so simply declaring it breaks Apple-platform + // builds. Restore this together with the `AndroidBluetooth` target dependency below once + // PureSwift/AndroidBluetooth#4 is merged. The Swift sources still guard their use of it + // with `#if os(Android)`, so nothing else has to change. .package(url: "https://github.com/PureSwift/Bluetooth.git", from: "7.2.0"), // SwiftUI for Android. Apple platforms use the system SwiftUI instead, so this is only // linked for .android — see the conditional target dependencies below. @@ -64,11 +70,6 @@ let package = Package( package: "GATT", condition: .when(platforms: [.macOS, .iOS, .macCatalyst, .watchOS, .tvOS, .visionOS]) ), - .product( - name: "AndroidBluetooth", - package: "AndroidBluetooth", - condition: .when(platforms: [.android]) - ), .product( name: "AndroidSwiftUI", package: "AndroidSwiftUI", From c973acc1ebf000291ae99fceab4754a6a21d7e0a Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Wed, 22 Jul 2026 13:25:26 -0400 Subject: [PATCH 2/8] Remove the Skip gradle build phase from the Xcode project --- .../project.pbxproj | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/Darwin/BluetoothExplorer.xcodeproj/project.pbxproj b/Darwin/BluetoothExplorer.xcodeproj/project.pbxproj index 5fba681..219b15f 100644 --- a/Darwin/BluetoothExplorer.xcodeproj/project.pbxproj +++ b/Darwin/BluetoothExplorer.xcodeproj/project.pbxproj @@ -105,7 +105,6 @@ 499CD43A2AC5B799001AE8D8 /* Sources */, 499CD43C2AC5B799001AE8D8 /* Frameworks */, 499CD43E2AC5B799001AE8D8 /* Resources */, - 499CD4452AC5B869001AE8D8 /* Run skip gradle */, 499CD44A2AC5B9C6001AE8D8 /* Embed Frameworks */, ); buildRules = ( @@ -166,25 +165,6 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 499CD4452AC5B869001AE8D8 /* Run skip gradle */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = "Run skip gradle"; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = "/bin/sh -e"; - shellScript = "if [ \"${SKIP_ZERO}\" != \"\" ]; then\n echo \"note: skipping skip due to SKIP_ZERO\"\n exit 0\nelif [ \"${ENABLE_PREVIEWS}\" = \"YES\" ]; then\n echo \"note: skipping skip due to ENABLE_PREVIEWS\"\n exit 0\nelif [ \"${ACTION}\" = \"install\" ]; then\n echo \"note: skipping skip due to archive install\"\n exit 0\nelif [ \"${SKIP_ACTION}\" = \"none\" ]; then\n echo \"note: skipping skip due to SKIP_ACTION none\"\n exit 0\nelse\n SKIP_ACTION=\"${SKIP_ACTION:-launch}\"\nfi\nPATH=${BUILD_ROOT}/Release:${BUILD_ROOT}/Debug:${BUILD_ROOT}/../../SourcePackages/artifacts/skip/skip/skip.artifactbundle/macos:${PATH}:${HOMEBREW_PREFIX:-/opt/homebrew}/bin\necho \"note: running gradle build with: $(which skip) gradle -p ${PWD}/../Android ${SKIP_ACTION:-launch}${CONFIGURATION:-Debug}\"\nskip gradle -p ../Android ${SKIP_ACTION:-launch}${CONFIGURATION:-Debug}\n"; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ From d5475c7e25d0b8374229aae4f28ab33e350cd068 Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Wed, 22 Jul 2026 13:34:31 -0400 Subject: [PATCH 3/8] Use a WasmKit fork that does not force warnings-as-errors --- Package.swift | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index 3fda959..fc5f88f 100644 --- a/Package.swift +++ b/Package.swift @@ -21,7 +21,16 @@ let package = Package( // SwiftUI for Android. Apple platforms use the system SwiftUI instead, so this is only // linked for .android — see the conditional target dependencies below. .package(url: "https://github.com/PureSwift/AndroidSwiftUI.git", branch: "master"), - .package(url: "https://github.com/swiftwasm/WasmKit.git", .upToNextMinor(from: "0.3.1")) + // Fork of WasmKit 0.3.1 with one change: upstream declares + // `.treatAllWarnings(as: .error)` for Apple platforms, which collides with the + // `-suppress-warnings` Xcode passes to package dependencies and makes the app fail to build + // in Xcode ("Conflicting options '-warnings-as-errors' and '-suppress-warnings'"). That + // setting only reaches package targets from the command line, so it cannot be overridden + // from the xcconfig or the project. Track upstream and drop the fork once it is fixed there. + .package( + url: "https://github.com/MillerTechnologyPeru/WasmKit.git", + revision: "ba06b7c64b5bc692c19c301ba2ef843c8d0f37c2" + ) ], targets: [ .target( From 7d6da6323406b713efa1fec1a543007910acc484 Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Wed, 22 Jul 2026 13:34:31 -0400 Subject: [PATCH 4/8] Drop the CI workarounds that are no longer needed --- .github/workflows/ci.yml | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 35b122c..2e9d818 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,9 +1,3 @@ -# NOTE: every job here fails on a clean checkout until PureSwift/AndroidBluetooth#4 is merged. -# AndroidBluetooth requests the `AndroidManifest` product from `Android`, but it moved to -# `swift-android-native` (PureSwift/Android#40), and SwiftPM validates the whole package graph even -# for dependencies that are conditional on `.android`: -# error: product 'AndroidManifest' required by package 'androidbluetooth' ... not found -# That is a one-line manifest fix upstream, not a problem with this repo. name: CI on: @@ -41,11 +35,6 @@ jobs: with: xcode-version: latest-stable - # WasmKit declares `.treatAllWarnings(as: .error)` for Apple platforms, which conflicts with - # the `-suppress-warnings` Xcode passes to package dependencies: - # error: Conflicting options '-warnings-as-errors' and '-suppress-warnings' - # The setting only reaches package targets from the command line, so it cannot live in the - # xcconfig or the project. - name: Archive working-directory: Darwin run: | @@ -56,7 +45,6 @@ jobs: -archivePath "$RUNNER_TEMP/BluetoothExplorer.xcarchive" \ -skipPackagePluginValidation \ -skipMacroValidation \ - SWIFT_SUPPRESS_WARNINGS=NO \ CODE_SIGNING_ALLOWED=NO - name: Verify archive contents @@ -72,26 +60,3 @@ jobs: name: ios-archive path: ${{ runner.temp }}/BluetoothExplorer.xcarchive retention-days: 14 - - android-package: - name: Android Swift Package - runs-on: macos-15 - timeout-minutes: 60 - steps: - - uses: actions/checkout@v4 - - uses: maxim-lobanov/setup-xcode@v1 - with: - xcode-version: latest-stable - - - name: Install Swift Android SDK - run: | - brew install skiptools/skip/skip || (brew update && brew install skiptools/skip/skip) - skip android sdk install --version 6.3.3 - - # Only the plugin engine is built here. The full app does not yet cross-compile for Android: - # AndroidBluetooth's `@JavaImplementation` macro generates invalid code for its scan callback, - # and WasmKit's SystemExtras target does not compile against the Android sysroot - # (`st_mode` is UInt32 there while swift-system's CInterop.Mode is UInt16). Neither is - # reachable from this target. See Documentation/AndroidSwiftUIMigration.md. - - name: Build plugin engine for Android - run: swift build --swift-sdk swift-6.3.3-RELEASE_android --target BluetoothExplorerPluginEngine From 0d7be0f7e3645c0796557d375c99f23a7e94e251 Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Wed, 22 Jul 2026 13:34:48 -0400 Subject: [PATCH 5/8] Record that iOS builds and runs without workarounds --- Documentation/AndroidSwiftUIMigration.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Documentation/AndroidSwiftUIMigration.md b/Documentation/AndroidSwiftUIMigration.md index dc0a87c..4ced352 100644 --- a/Documentation/AndroidSwiftUIMigration.md +++ b/Documentation/AndroidSwiftUIMigration.md @@ -88,7 +88,19 @@ device or emulator. Notable limitations, each documented in the source: ## Platform status -- **Apple platforms** — the package resolves and every target builds with no Skip involvement. +- **Apple platforms** — working end to end. A clean checkout resolves, builds, tests and archives + with **no mirrors, no environment variables and no extra xcodebuild flags**, and the app runs in + the simulator: the device list populates from `MockCentral`, and all 13 bundled plugins install + into `Documents/Plugins/` on first launch and appear in the Plugins tab. + + Two things were needed to get there beyond removing Skip: + - The `AndroidBluetooth` package dependency is temporarily not declared. SwiftPM validates the + whole graph even for `.android`-conditional dependencies, so its `AndroidManifest` bug broke + Apple builds. Restore it with PureSwift/AndroidBluetooth#4. + - WasmKit is taken from a fork that drops `.treatAllWarnings(as: .error)`. Upstream's setting + collides with the `-suppress-warnings` Xcode passes to package dependencies, which made the app + unbuildable in Xcode; the override only works from the command line, so it could not be fixed + from the xcconfig or project. - **Android** — the dependency wiring is in place, but the Android app build has not been verified end to end. It additionally needs the two PureSwift PRs above, and the Kotlin JNI peers under `Sources/BluetoothExplorer/Skip/` (`ScanCallback.kt`, `BluetoothGattCallback.kt`) rehomed into the From 9c81c3eee23d92b65b6461f025d564e3fd9ca882 Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Wed, 22 Jul 2026 14:29:16 -0400 Subject: [PATCH 6/8] Pin CI to a runner image and Xcode with Swift 6.3 --- .github/workflows/ci.yml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2e9d818..1ac917a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,13 +13,16 @@ concurrency: jobs: test: name: Test - runs-on: macos-15 + runs-on: macos-26 timeout-minutes: 45 steps: - uses: actions/checkout@v4 + # Pinned, not latest-stable: WasmKit 0.3.x declares swift-tools-version 6.3, so it needs + # Swift 6.3+. Xcode 26.5 (the macos-26 default) ships Swift 6.2.x and fails with + # "package 'wasmkit' is using Swift tools version 6.3.0 but the installed version is 6.2.4". - uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: latest-stable + xcode-version: '26.6' - name: Build run: swift build --build-tests - name: Test @@ -27,13 +30,16 @@ jobs: ios-archive: name: iOS Archive - runs-on: macos-15 + runs-on: macos-26 timeout-minutes: 60 steps: - uses: actions/checkout@v4 + # Pinned, not latest-stable: WasmKit 0.3.x declares swift-tools-version 6.3, so it needs + # Swift 6.3+. Xcode 26.5 (the macos-26 default) ships Swift 6.2.x and fails with + # "package 'wasmkit' is using Swift tools version 6.3.0 but the installed version is 6.2.4". - uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: latest-stable + xcode-version: '26.6' - name: Archive working-directory: Darwin From f0ad8361b1ed714198f6e7f861ecc2b45c53642e Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Wed, 22 Jul 2026 14:31:48 -0400 Subject: [PATCH 7/8] Stop ignoring Package.resolved so the graph is reproducible --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index eb5ceef..f5c416d 100644 --- a/.gitignore +++ b/.gitignore @@ -39,7 +39,10 @@ playground.xcworkspace # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. # Packages/ # Package.pins -*Package.resolved +# NOTE: Package.resolved is deliberately committed. This is an app, not a library, and several +# dependencies track moving branches (GATT master, AndroidSwiftUI master), so without it every +# clean checkout — including CI — resolves a different graph than the one that was tested. +Darwin/**/Package.resolved .build/ .swiftpm From fdaaa46cab2b186952dd285970a46a927fee5994 Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Wed, 22 Jul 2026 14:31:48 -0400 Subject: [PATCH 8/8] Commit the resolved dependency graph --- Package.resolved | 176 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 Package.resolved diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 0000000..51a2888 --- /dev/null +++ b/Package.resolved @@ -0,0 +1,176 @@ +{ + "originHash" : "8b32b28011bb149a00e00863969b6c918972017fd6cea9b44aace44913d40d43", + "pins" : [ + { + "identity" : "android", + "kind" : "remoteSourceControl", + "location" : "https://github.com/PureSwift/Android.git", + "state" : { + "branch" : "master", + "revision" : "815159f37488510d3deac61da6025022dc378fab" + } + }, + { + "identity" : "androidswiftui", + "kind" : "remoteSourceControl", + "location" : "https://github.com/PureSwift/AndroidSwiftUI.git", + "state" : { + "branch" : "master", + "revision" : "ff646f4886d441264c1d05bbb90ffee01ab3aa82" + } + }, + { + "identity" : "bluetooth", + "kind" : "remoteSourceControl", + "location" : "https://github.com/PureSwift/Bluetooth.git", + "state" : { + "revision" : "7cd6513aeaf0bc669c06bb346bcd5c9cc7c51cc3", + "version" : "7.6.0" + } + }, + { + "identity" : "gatt", + "kind" : "remoteSourceControl", + "location" : "https://github.com/PureSwift/GATT.git", + "state" : { + "branch" : "master", + "revision" : "ba4b36aed4a901ccf583e7365b661bc02727ea5a" + } + }, + { + "identity" : "javalang", + "kind" : "remoteSourceControl", + "location" : "https://github.com/PureSwift/JavaLang.git", + "state" : { + "branch" : "master", + "revision" : "1b7320afd8d40a669422ee61d5335ae6d647dce5" + } + }, + { + "identity" : "kotlin", + "kind" : "remoteSourceControl", + "location" : "https://github.com/PureSwift/Kotlin.git", + "state" : { + "branch" : "master", + "revision" : "88a9bb94d156ef95d177bc6a65580dfacdda4b13" + } + }, + { + "identity" : "opencombine", + "kind" : "remoteSourceControl", + "location" : "https://github.com/PureSwift/OpenCombine.git", + "state" : { + "branch" : "feature/android", + "revision" : "5ea4600557999c7d160a3d211684a565fcb31bf8" + } + }, + { + "identity" : "swift-android-native", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swift-android-sdk/swift-android-native.git", + "state" : { + "branch" : "main", + "revision" : "5f71c1584b3cbe3842d335ebb4d7b545205d9945" + } + }, + { + "identity" : "swift-argument-parser", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-argument-parser", + "state" : { + "revision" : "6a52f3251125d74daf04fcbd5e6f08a75d074382", + "version" : "1.8.2" + } + }, + { + "identity" : "swift-atomics", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-atomics.git", + "state" : { + "revision" : "0442cb5a3f98ab802acb777929fdb446bda11a34", + "version" : "1.3.1" + } + }, + { + "identity" : "swift-collections", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-collections", + "state" : { + "revision" : "a0cb0954ecb21e4e31b0070e6ed5674e8556685a", + "version" : "1.6.0" + } + }, + { + "identity" : "swift-java", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftlang/swift-java.git", + "state" : { + "branch" : "main", + "revision" : "d3092ee208d0704a1349e2d87ab8599306fc98f1" + } + }, + { + "identity" : "swift-java-jni-core", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftlang/swift-java-jni-core", + "state" : { + "branch" : "main", + "revision" : "b440a0be98381550cf189c903782e817619ffa05" + } + }, + { + "identity" : "swift-log", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-log", + "state" : { + "revision" : "a878e7f8f46cfc0e1125e565b5c08e7d5272dc9a", + "version" : "1.14.0" + } + }, + { + "identity" : "swift-nio", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-nio", + "state" : { + "revision" : "0b18836bd8b0162e7e17a995a3fbee20ed8f3b2b", + "version" : "2.101.3" + } + }, + { + "identity" : "swift-subprocess", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftlang/swift-subprocess.git", + "state" : { + "revision" : "13d087685b95d64d6aac9b94500d347bbe84c39b", + "version" : "0.4.0" + } + }, + { + "identity" : "swift-syntax", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftlang/swift-syntax", + "state" : { + "revision" : "79e4b74a295b6eb74a8b585e3a39d29e70c1dbd1", + "version" : "603.0.2" + } + }, + { + "identity" : "swift-system", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-system", + "state" : { + "revision" : "b5544ba79a70a0cb3563e75bf26dc198d6b40ed3", + "version" : "1.7.4" + } + }, + { + "identity" : "wasmkit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/MillerTechnologyPeru/WasmKit.git", + "state" : { + "revision" : "ba06b7c64b5bc692c19c301ba2ef843c8d0f37c2" + } + } + ], + "version" : 3 +}