Skip to content

fix(ios): enable community builds — dynamic DEVELOPMENT_TEAM, APP_GROUP_IDENTIFIER, and stripped dev entitlements - #7641

Draft
formed2forge wants to merge 6 commits into
BasedHardware:mainfrom
formed2forge:fix/ios-community-build
Draft

fix(ios): enable community builds — dynamic DEVELOPMENT_TEAM, APP_GROUP_IDENTIFIER, and stripped dev entitlements#7641
formed2forge wants to merge 6 commits into
BasedHardware:mainfrom
formed2forge:fix/ios-community-build

Conversation

@formed2forge

@formed2forge formed2forge commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Problem

Community developers with a paid Apple Developer account cannot build the dev flavor of the Omi iOS app out of the box. There are three blockers:

  1. Hardcoded team IDproject.pbxproj has the BasedHardware team ID (9536L8KLMP) in all 9 dev-flavor build configurations. Xcode rejects signing for any other team.
  2. Bundle ID override in devDebug.xcconfigAPP_BUNDLE_IDENTIFIER=com.friend-app-with-wearable.ios12.development is set here, but Custom.xcconfig (written by setup.sh) sets a machine-specific bundle at higher priority. The stale duplicate in devDebug.xcconfig causes confusion and overrides the dynamic value in some tool flows.
  3. Unprovisionable capabilities in dev entitlementsRunnerDebug/Profile/Release-dev.entitlements request push notifications (aps-environment), associated domains (h.omi.me, try.omi.me), HotspotConfiguration, and Wi-Fi info. Community developers cannot provision these against their own team without modifying files, causing code-signing to fail with "entitlements do not match."

Solution

Commit 1 — Dynamic DEVELOPMENT_TEAM and APP_GROUP_IDENTIFIER

  • setup.sh: adds detect_apple_team_id() which auto-discovers the developer's team ID from their local provisioning profiles (with APPLE_DEVELOPMENT_TEAM env var override and interactive fallback). Writes both DEVELOPMENT_TEAM and APP_GROUP_IDENTIFIER (machine-specific, hostname-derived) to Custom.xcconfig.
  • project.pbxproj: all 9 dev-flavor DEVELOPMENT_TEAM entries changed from literal 9536L8KLMP to $(DEVELOPMENT_TEAM), resolved at build time from Custom.xcconfig.
  • devDebug.xcconfig: removes APP_BUNDLE_IDENTIFIER and GOOGLE_REVERSE_CLIENT_ID duplicates — both are already set correctly via Custom.xcconfig at higher xcconfig priority.
  • BatteryWidget.entitlements, Runner.entitlements: use $(APP_GROUP_IDENTIFIER) instead of the hardcoded group.com.friend-app-with-wearable.ios12.
  • BatteryWidget-Info.plist, Runner/Info.plist: expose $(APP_GROUP_IDENTIFIER) via AppGroupIdentifier key so Swift code reads it at runtime.
  • SharedDefaults.swift, AppDelegate.swift: read app group ID from Info.plist with fallback to the base identifier, instead of hardcoded string.

Commit 2 — Strip unprovisionable capabilities from dev-flavor entitlements

Removes from RunnerDebug/Profile/Release-dev.entitlements:

  • aps-environment (push notifications — requires explicit portal provisioning)
  • com.apple.developer.associated-domains (h.omi.me / try.omi.me — requires domain ownership)
  • com.apple.developer.networking.HotspotConfiguration
  • com.apple.developer.networking.wifi-info

Replaces hardcoded group.com.friend-app-with-wearable.ios12 with $(APP_GROUP_IDENTIFIER).

These capabilities remain in the prod-flavor and release entitlements where BasedHardware provisions them.

Testing

Tested on macOS with a personal Apple Developer account using bash setup.sh ios. The app builds and runs on a physical device with automatic signing, no manual Xcode project edits required.

Notes

  • Custom.xcconfig is gitignored — it is machine-generated by setup.sh and must never be committed.
  • The APP_GROUP_IDENTIFIER is derived from the machine hostname, giving each developer a unique app group that they can provision under their own team.
  • Existing BasedHardware team members are unaffected: setup.sh will detect their team ID from their existing provisioning profiles and write it to Custom.xcconfig as before.

Update

Rebased onto current main and reconciled with newer work on the underlying local fix/ios-community-build-rebuild branch (certificate-fingerprint-aware team detection, non-interactive TTY handling, GOOGLE_REVERSE_CLIENT_ID fallback fix) — verified end-to-end this session on both an iOS simulator and a physical device via the gold-standard local-dev setup investigation (see #11730/#11652/#11782).

Failure-Class: none

@formed2forge
formed2forge marked this pull request as draft June 4, 2026 12:48
@greptile-apps

greptile-apps Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR makes the Omi iOS dev flavor buildable by any community developer with a paid Apple account by dynamically resolving the signing team ID, app group identifier, and bundle identifier at build time — and stripping capabilities from dev entitlements that only BasedHardware can provision.

  • setup.sh gains detect_apple_team_id() which auto-discovers the developer's team from local provisioning profiles (env-var override → profile scan → keychain fallback → interactive prompt), then writes DEVELOPMENT_TEAM, APP_GROUP_IDENTIFIER, and APP_BUNDLE_IDENTIFIER to Custom.xcconfig.
  • project.pbxproj replaces the nine hardcoded 9536L8KLMP entries with $(DEVELOPMENT_TEAM) for all dev-flavor configurations; devDebug.xcconfig removes stale duplicate keys now superseded by Custom.xcconfig.
  • Dev entitlements (RunnerDebug/Profile/Release-dev.entitlements) drop push notifications, associated domains, HotspotConfiguration, and Wi-Fi info, while Runner.entitlements and BatteryWidget.entitlements switch their hardcoded app group string to $(APP_GROUP_IDENTIFIER); SharedDefaults.swift and AppDelegate.swift read the app group ID from Info.plist at runtime with a fallback.

Confidence Score: 3/5

The production entitlement gap in Runner.entitlements and BatteryWidget.entitlements is a concrete present defect on the production build path; a one-line default in Base.xcconfig would close it.

The production entitlement gap means any CI build that does not run setup.sh ios will embed an empty app group string, either breaking code-signing or silently making the battery widget unusable. The setup.sh fallback and validation issues are real but affect the developer setup experience rather than the shipped binary.

Runner.entitlements and BatteryWidget.entitlements both reference an undefined variable for prod builds; setup.sh multi-account fallback and unvalidated interactive team ID prompt also deserve a closer look.

Important Files Changed

Filename Overview
app/setup.sh Adds detect_apple_team_id() with a 4-step auto-detection strategy; step 3 silently picks the first team with a valid cert (wrong on multi-account machines), and step 4 interactive fallback accepts unchecked input that could produce a malformed Team ID in Custom.xcconfig.
app/ios/Runner/Runner.entitlements Switches app group from hardcoded group.com.friend-app-with-wearable.ios12 to $(APP_GROUP_IDENTIFIER), but no committed xcconfig defines that variable for production builds, leaving it empty and breaking the widget shared UserDefaults.
app/ios/BatteryWidget/BatteryWidget.entitlements Same $(APP_GROUP_IDENTIFIER) substitution as Runner.entitlements — production builds will have an empty app group unless CI/CD defines the variable.
app/ios/Runner.xcodeproj/project.pbxproj Replaces the 9 hardcoded DEVELOPMENT_TEAM = 9536L8KLMP entries with $(DEVELOPMENT_TEAM) for dev-flavor build configurations; prod configurations are untouched, which is the correct scope.
app/ios/Runner/RunnerDebug-dev.entitlements Strips push notifications, associated domains, HotspotConfiguration, and Wi-Fi info capabilities that community developers cannot provision; retained capabilities are all provisionable with automatic signing.
app/ios/Flutter/AppFrameworkInfo.plist Removes MinimumOSVersion key; Flutter toolchain regenerates this file at build time so practical impact is low, but the PR description claims it adds the key — opposite of what the diff shows.
app/ios/Flutter/devDebug.xcconfig Removes duplicate APP_BUNDLE_IDENTIFIER and GOOGLE_REVERSE_CLIENT_ID that were already set at higher priority in Custom.xcconfig; reduces confusion without functional change.
app/ios/Runner/Info.plist Adds AppGroupIdentifier key populated via xcconfig substitution; missing trailing newline after the closing plist tag.

Sequence Diagram

sequenceDiagram
    participant Dev as Developer
    participant setup as setup.sh
    participant xcconfig as Custom.xcconfig
    participant xcode as Xcode Build
    participant plist as Info.plist
    participant swift as Swift Runtime

    Dev->>setup: bash setup.sh ios
    setup->>setup: generate_device_suffix() hostname
    setup->>setup: detect_apple_team_id()
    Note over setup: 1. APPLE_DEVELOPMENT_TEAM env var 2. Scan provisioning profiles 3. Keychain cert fallback 4. Interactive prompt
    setup->>xcconfig: APP_BUNDLE_IDENTIFIER
    setup->>xcconfig: APP_GROUP_IDENTIFIER
    setup->>xcconfig: DEVELOPMENT_TEAM

    Dev->>xcode: flutter run --flavor dev
    xcode->>xcconfig: reads devDebug.xcconfig and Custom.xcconfig
    xcode->>xcode: expands DEVELOPMENT_TEAM in project.pbxproj
    xcode->>xcode: expands APP_GROUP_IDENTIFIER in entitlements
    xcode->>plist: writes AppGroupIdentifier value

    xcode->>swift: App launches
    swift->>plist: Bundle.main.object forInfoDictionaryKey AppGroupIdentifier
    plist-->>swift: resolved group identifier
    swift->>swift: UserDefaults suiteName groupId
Loading

Reviews (1): Last reviewed commit: "fix(ios): strip unprovisionable capabili..." | Re-trigger Greptile

Comment thread app/ios/Runner/Runner.entitlements
Comment on lines 21 to 23
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>13.0</string>
</dict>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 PR description contradicts the actual diff

The PR description states AppFrameworkInfo.plist: "add missing MinimumOSVersion key", but the diff removes the MinimumOSVersion / 13.0 entry. Flutter's toolchain regenerates this file on every build from its own SDK templates, so the runtime impact is minimal — but the description is inverted, which makes the intent hard to review.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment thread app/setup.sh Outdated
Comment on lines +98 to +113
# 3. Fallback: grab first team ID that has a valid signing cert in the keychain
if [ -z "$team_id" ] && [ -d "$profiles_dir" ]; then
while IFS= read -r -d '' profile; do
local plist candidate
plist=$(security cms -D -i "$profile" 2>/dev/null) || continue
candidate=$(echo "$plist" | xmllint --xpath \
"string(//key[text()='TeamIdentifier']/following-sibling::array[1]/string[1])" \
- 2>/dev/null)
if [ -n "$candidate" ]; then
if security find-identity -v -p codesigning 2>/dev/null | grep -q "$candidate"; then
team_id="$candidate"
break
fi
fi
done < <(find "$profiles_dir" -name '*.mobileprovision' -print0 2>/dev/null)
fi

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Step 3 fallback silently picks the wrong team on multi-account machines

When no provisioning profile matches the machine's bundle pattern (step 2), the code falls back to the first team ID that has a codesigning cert in the keychain — regardless of which account the developer intends to use. Developers with two or more Apple accounts (e.g., personal + employer) will have multiple valid certs, and the script will silently select one based on directory enumeration order. There is no disambiguation prompt before the wrong team ID is written to Custom.xcconfig.

Comment thread app/setup.sh Outdated
Comment on lines +115 to +122
# 4. Last resort: prompt the user
if [ -z "$team_id" ]; then
echo "⚠️ Could not auto-detect your Apple Development Team ID." >&2
echo " Find it at: https://developer.apple.com/account -> Membership" >&2
echo " or run: APPLE_DEVELOPMENT_TEAM=XXXXXXXXXX bash setup.sh ios" >&2
read -rp " Enter your Team ID (10 characters): " team_id
team_id=$(echo "${team_id}" | tr '[:lower:]' '[:upper:]' | tr -d ' ')
fi

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Interactive team ID fallback has no format validation

The prompt says "10 characters" but team_id is accepted as entered (after uppercasing and stripping spaces). An Apple Team ID must be exactly 10 uppercase alphanumeric characters. Entering fewer characters, a non-alphanumeric value, or an empty string would write a malformed value to Custom.xcconfig, producing confusing Xcode errors instead of a clear failure at setup time.

Comment thread app/ios/Runner/Info.plist Outdated
Comment on lines 189 to 192
<key>AppGroupIdentifier</key>
<string>$(APP_GROUP_IDENTIFIER)</string>
</dict>
</plist> No newline at end of file

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The file is missing a trailing newline after the closing </plist> tag. This can cause noisy diffs and some plist tooling warnings.

Suggested change
<key>AppGroupIdentifier</key>
<string>$(APP_GROUP_IDENTIFIER)</string>
</dict>
</plist>
<key>AppGroupIdentifier</key>
<string>$(APP_GROUP_IDENTIFIER)</string>
</dict>
</plist>

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@formed2forge
formed2forge force-pushed the fix/ios-community-build branch 3 times, most recently from d89c131 to b246990 Compare June 9, 2026 20:21
@Git-on-my-level

Copy link
Copy Markdown
Collaborator

Thanks for this @formed2forge — really solid work on the core problem here. Enabling community iOS builds by replacing hardcoded team IDs and entitlements with xcconfig-driven variables is the right architecture, and the detect_apple_team_id() multi-strategy detection is well thought out.

A few things to address before this is ready:

1. Scope — this PR mixes two concerns. Commits 1-5 implement the community-build fix (dynamic DEVELOPMENT_TEAM/APP_GROUP_IDENTIFIER, stripped dev entitlements). Commits 6-9 add a separate personal-configs overlay workflow (.personal_configs.example/, setup-personal.sh, Firebase credential templates). These are genuinely useful but unrelated to the signing fix. Would you be open to splitting the personal-configs work into its own PR? It would make both easier to review and merge independently.

2. Custom.xcconfig deletion needs maintainer confirmation. This file was previously tracked with GOOGLE_REVERSE_CLIENT_ID and APP_BUNDLE_IDENTIFIER defaults. The PR removes it from tracking (correctly, since it's machine-generated by setup.sh), but we should confirm no CI pipeline or other build flow depends on the tracked version. The Base.xcconfig fallback you added for APP_GROUP_IDENTIFIER is a good safety net — but is there an equivalent for GOOGLE_REVERSE_CLIENT_ID that dev builds might need?

3. The entitlement stripping is the right call. Push notifications, associated domains, HotspotConfiguration, and wifi-info are genuinely unprovisionable for community developers. Keeping them in prod-flavor entitlements while stripping from dev is exactly the right boundary.

4. detect_apple_team_id() feedback. The Greptile bot already flagged the multi-account disambiguation and input validation — good to see commit 50903c5a addressed both. One additional note: step 3's fallback scans provisioning profiles for team IDs with valid certs, which is reasonable. The interactive menu for multiple accounts is a nice touch.

5. AppFrameworkInfo.plist. The PR description mentions adding a MinimumOSVersion key to this file, but the final diff doesn't touch it — looks like it was restored to match main in a later commit. Worth updating the description to avoid confusion.

Looking forward to v2 once these are addressed. The community-build direction is valuable and this is a strong foundation.

@Git-on-my-level Git-on-my-level added needs-context needs more context to implement flutter flutter work labels Jun 26, 2026
@undivisible undivisible added human Human-authored pull request app mobile labels Aug 10, 2026
@Git-on-my-level

Copy link
Copy Markdown
Collaborator

Thanks for this work, @formed2forge. Building on @Git-on-my-level's June 26 review, I did a focused pass on the signing/config mechanics.

Verified: GOOGLE_REVERSE_CLIENT_ID is still covered. scripts/generate_ios_custom_config.sh still writes it to Custom.xcconfig at setup time, so removing the duplicate from devDebug.xcconfig doesn't lose it — the xcconfig resolution chain stays intact.

Runtime app-group resolution is well done. SharedDefaults.swift and AppDelegate.swift both read AppGroupIdentifier from Bundle.main.infoDictionary with a fallback to the original hardcoded string, so the widget extension and main app stay in sync even if someone builds without running setup.sh first.

Status: the head commit (4ea1709) hasn't changed since June 10, so the scope-split request is still pending. Splitting the personal-configs overlay (setup-personal.sh, .personal_configs.example/) into its own PR would let the signing fix move forward independently.

Minor: setup-personal.sh uses sed -i '' (BSD/macOS syntax). Fine for iOS-only tooling, but worth a comment if you want to guard Linux contributors.

Leaving for human maintainer review — the scope-split decision and Custom.xcconfig tracking change are product/process calls already flagged.


by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with need human response.

formed2forge added a commit to formed2forge/omi that referenced this pull request Aug 14, 2026
…the app delegate

Second half of the UIScene migration for BasedHardware#11568. Under the UIScene lifecycle the
app delegate owns no window at launch, so every binaryMessenger derived from
window?.rootViewController is nil in didFinishLaunching — and registering plugins
against the app delegate as the registry is itself fatal.

Measured on iPhone 17 Pro / iOS 27.0 with a file-backed probe: the log line before
GeneratedPluginRegistrant.register(with: self) wrote, the line after it never did,
and the process died. That is why this is not merely a nil-messenger problem.

Moves into didInitializeImplicitFlutterEngine(_:), sourcing the registry from
engineBridge.pluginRegistry and the messenger from
engineBridge.applicationRegistrar.messenger():

  - GeneratedPluginRegistrant registration
  - OmiPhoneCallsPlugin (now via registry.registrar(forPlugin:))
  - Watch/WatchRecorder, BLE, Ray-Ban Meta and phone-mic Pigeon APIs
  - notifyOnKill, apple_reminders, apple_health, speech, environment,
    audioSession, battery_widget channels and WifiNetworkPlugin

No window?.rootViewController reference remains in the file, and all nine
force unwraps of it are gone — each was an independent crash.

Left alone deliberately: SwiftFlutterForegroundTaskPlugin's registrant callback
and the top-level registerPlugins(registry:), which both receive a registry from
their caller and are already scene-independent.

Incidental fix: a launch carrying a deep link previously hit an early
`return true` in didFinishLaunching and skipped ALL channel setup. Registration no
longer shares a code path with link handling.

Still outstanding for BasedHardware#11568, not in this commit: applicationWillEnterForeground
never fires under scenes (measured, 0 of 4 foregrounds, while the notification
fires 4 of 4), so OmiBleManager.reconnectStalePeripherals() silently stops
running. That is a behavioural change and deserves its own commit.

Verification: `flutter build ios --flavor dev --debug` compiles with zero Swift
diagnostics. It cannot be signed on this branch — upstream/main hardcodes team
9536L8KLMP and unsuffixed bundle ids, which is what BasedHardware#7641 fixes — so runtime
verification is done with those signing changes applied on top.

Failure-Class: none

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qmdS9crg5qFhan7PCbWvZ
@formed2forge

Copy link
Copy Markdown
Contributor Author

Pushed a reconciliation commit: this PR's branch had diverged from ongoing local work on the same fix (certificate-fingerprint-aware team detection instead of a name-substring match, non-interactive-TTY handling so a CI/automation run fails fast instead of hanging on read, and a GOOGLE_REVERSE_CLIENT_ID fallback fix for community Firebase configs that don't carry that key). Merged your existing commits in — nothing here was dropped, just superseded where the local branch had a more complete version of the same function (verified: neither side had unique project.pbxproj content beyond the DEVELOPMENT_TEAM parameterization already present on both).

Also added Failure-Class: none to the PR body — this predates that convention and had no declaration, which was blocking any future push to this branch under the repo's current pre-push gate.

Verified end-to-end this session (gold-standard local-dev setup investigation, see #11730/#11652/#11782): setup.sh ios builds and signs correctly with this branch's DEVELOPMENT_TEAM/APP_GROUP_IDENTIFIER parameterization, both on an iOS simulator and a physical device.

formed2forge added a commit to formed2forge/omi that referenced this pull request Aug 18, 2026
…the app delegate

Second half of the UIScene migration for BasedHardware#11568. Under the UIScene lifecycle the
app delegate owns no window at launch, so every binaryMessenger derived from
window?.rootViewController is nil in didFinishLaunching — and registering plugins
against the app delegate as the registry is itself fatal.

Measured on iPhone 17 Pro / iOS 27.0 with a file-backed probe: the log line before
GeneratedPluginRegistrant.register(with: self) wrote, the line after it never did,
and the process died. That is why this is not merely a nil-messenger problem.

Moves into didInitializeImplicitFlutterEngine(_:), sourcing the registry from
engineBridge.pluginRegistry and the messenger from
engineBridge.applicationRegistrar.messenger():

  - GeneratedPluginRegistrant registration
  - OmiPhoneCallsPlugin (now via registry.registrar(forPlugin:))
  - Watch/WatchRecorder, BLE, Ray-Ban Meta and phone-mic Pigeon APIs
  - notifyOnKill, apple_reminders, apple_health, speech, environment,
    audioSession, battery_widget channels and WifiNetworkPlugin

No window?.rootViewController reference remains in the file, and all nine
force unwraps of it are gone — each was an independent crash.

Left alone deliberately: SwiftFlutterForegroundTaskPlugin's registrant callback
and the top-level registerPlugins(registry:), which both receive a registry from
their caller and are already scene-independent.

Incidental fix: a launch carrying a deep link previously hit an early
`return true` in didFinishLaunching and skipped ALL channel setup. Registration no
longer shares a code path with link handling.

Still outstanding for BasedHardware#11568, not in this commit: applicationWillEnterForeground
never fires under scenes (measured, 0 of 4 foregrounds, while the notification
fires 4 of 4), so OmiBleManager.reconnectStalePeripherals() silently stops
running. That is a behavioural change and deserves its own commit.

Verification: `flutter build ios --flavor dev --debug` compiles with zero Swift
diagnostics. It cannot be signed on this branch — upstream/main hardcodes team
9536L8KLMP and unsuffixed bundle ids, which is what BasedHardware#7641 fixes — so runtime
verification is done with those signing changes applied on top.

Failure-Class: none

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qmdS9crg5qFhan7PCbWvZ
@formed2forge

Copy link
Copy Markdown
Contributor Author

@Git-on-my-level — split done. Pushed a commit removing the personal-configs overlay (.personal_configs.example/, app/setup-personal.sh, the .gitignore entry) from this PR; it's now its own PR: #11789, carrying the same content (rebased onto current main, no functional changes from what was already reviewed here).

This PR is now scoped to just the community-build signing fix — dynamic DEVELOPMENT_TEAM/APP_GROUP_IDENTIFIER, stripped dev entitlements, and the certificate-fingerprint-aware team detection from the reconciliation commit pushed earlier today.

On your other two points from the June 26 review:

  • Custom.xcconfig tracking removal — confirmed safe in your Aug 12 follow-up (GOOGLE_REVERSE_CLIENT_ID still resolves via generate_ios_custom_config.sh).
  • AppFrameworkInfo.plist description mismatch — that file isn't touched by the current diff; happy to fix the PR description if it's still misleading, let me know.

formed2forge added a commit to formed2forge/omi that referenced this pull request Aug 18, 2026
Addresses review feedback on BasedHardware#11793:

- check_ios_prerequisites() silently passed when xcodebuild is on PATH
  but unusable (license not accepted, components missing) — xcodebuild
  -version then prints nothing matching, the version guard short-circuits,
  and a broken Xcode install sailed through the exact gate meant to
  catch it. Named explicitly now, with a remedy.
- select_ios_device()'s comment cited detect_apple_team_id as
  precedent; that function doesn't exist in setup.sh on main (it's from
  the separate, unmerged BasedHardware#7641). Removed the stale reference.
- The CocoaPods outdated-version remedy always said `sudo gem install
  cocoapods`, wrong for a Homebrew-installed CocoaPods (gem-installing
  over a brew-managed one doesn't actually update what's on PATH). Now
  names both.

New test confirms the broken-Xcode gap was real: fails (rc=0, silent
pass) against the pre-fix check_ios_prerequisites(), passes against the
fix.

Failure-Class: none
kodjima33 pushed a commit that referenced this pull request Aug 18, 2026
…eam (#11817)

A contributor with no Apple developer account couldn't run the app on
the iOS simulator, even though the simulator is normally the
account-free on-ramp — the watch companion target requires a team even
for a simulator destination:

    Error (Xcode): No Account for Team "9536L8KLMP". Add a new account...
    Error (Xcode): No profiles for '...development.watchapp' were found

Root-caused with `-showBuildSettings` and the real build log's tool
invocations, not guessed. Two hypotheses were tested and ruled out
first:

- A missing watchOS simulator runtime looked promising (this machine
  had none installed) but was disproven with a clean rebuild after
  installing the runtime and pairing a watch simulator to the test
  device — the watch target's resolved SDKROOT was still the *device*
  WatchOS SDK, confirmed both via `-showBuildSettings` and the
  `ExecuteExternalTool ... -isysroot .../WatchOS.platform/...` lines in
  the actual build log.
- An `Base.xcconfig`-level `CODE_SIGNING_ALLOWED[sdk=*simulator*] = NO`
  override (an earlier attempt) never matched anything, for the same
  reason: the watch target's own resolved SDK isn't a simulator SDK
  even when the overall scheme destination is a simulator, so an
  SDK-qualified condition can't distinguish this build from a genuine
  device build.

Since Runner and the widget already build for the simulator without a
signing identity, and the watch companion target is `SKIP_INSTALL =
YES` (it is only ever embedded in Runner.app, never independently
installed), disabling signing specifically for its dev-flavor
configurations is safe: it does not touch Runner's or the widget's
signing at all, and does not affect prod/beta, where the watch app may
still need real signing for distribution.

Verified live and reproducibly: a fully clean `xcodebuild` (cleared
DerivedData) using the exact invocation `flutter run` produces
succeeded, and the real `flutter run --flavor dev -d <simulator>`
reached `Launching lib/main.dart on iPhone 17 Pro in debug mode...`
with the same team (9536L8KLMP) this machine has no account for.

No automated test: this is an Xcode project-settings fix with no
Linux-CI-reachable seam (`Dart Analyze & Tests` runs on Ubuntu, where
Xcode does not exist), consistent with how #7641's signing changes
were verified — manually, on macOS, with a real build.

Failure-Class: none

Fixes #11776.
kodjima33 pushed a commit that referenced this pull request Aug 18, 2026
…1793)

* fix(app): pin an iOS build destination and validate prerequisites

Two related gaps in setup.sh, both hit before any app code matters:

1. run_build_ios() never passed -d to `flutter run`, so Flutter picked
   a destination itself. On a machine with no iOS simulator runtime
   installed and only a wirelessly-paired phone visible, it silently
   built for macOS desktop instead — after a full pod install
   --repo-update and build_runner pass — and failed with an unrelated
   "No macOS desktop project configured" error (#11775).

2. setup.sh prints a prerequisite list (Xcode v16.4, CocoaPods
   v1.16.2, Flutter v3.44.5) but validated almost none of it — one
   `command -v` check in the whole script. A missing or outdated tool
   surfaced as a confusing downstream failure instead of a named error,
   unlike the dev harness's own `Cannot start; missing prerequisites:`
   pattern, which names each gap with a remedy.

select_ios_device() enumerates iOS-platform destinations from `flutter
devices --machine`, returns the one candidate directly, prompts when
there are several (failing fast without a TTY, same reasoning as
detect_apple_team_id's prompt), and fails with a named error instead
of a silent fallback when there are none. check_ios_prerequisites()
validates Flutter/Xcode/CocoaPods/jq against the versions the script
already documents, listing every gap at once with its remedy.

Verified live, not just in the new shell tests: ran the real `bash
setup.sh ios` three ways — non-interactively with two real devices
connected (correctly failed fast rather than hang), through a real pty
feeding the interactive prompt (correctly built and reached `Launching
lib/main.dart on iPhone 17 Pro`, the device actually chosen), and with
a stubbed toolchain reporting only macOS as a destination (correctly
failed with the named "no iOS device or simulator found" error instead
of the original silent-fallback bug).

Failure-Class: none

Fixes #11775.

* fix(app): close review gaps in the iOS prerequisite check

Addresses review feedback on #11793:

- check_ios_prerequisites() silently passed when xcodebuild is on PATH
  but unusable (license not accepted, components missing) — xcodebuild
  -version then prints nothing matching, the version guard short-circuits,
  and a broken Xcode install sailed through the exact gate meant to
  catch it. Named explicitly now, with a remedy.
- select_ios_device()'s comment cited detect_apple_team_id as
  precedent; that function doesn't exist in setup.sh on main (it's from
  the separate, unmerged #7641). Removed the stale reference.
- The CocoaPods outdated-version remedy always said `sudo gem install
  cocoapods`, wrong for a Homebrew-installed CocoaPods (gem-installing
  over a brew-managed one doesn't actually update what's on PATH). Now
  names both.

New test confirms the broken-Xcode gap was real: fails (rc=0, silent
pass) against the pre-fix check_ios_prerequisites(), passes against the
fix.

Failure-Class: none
kodjima33 pushed a commit that referenced this pull request Aug 18, 2026
…11789)

Introduces a .personal_configs/ convention at the repo root for
contributors to store machine-local Firebase credentials and dev env
config without committing them. Run app/setup-personal.sh after
setup.sh to copy them into place.

Split out of #7641 at maintainer request (community-build signing fix
and this overlay are unrelated concerns and easier to review apart) —
carries the same content as that PR's commits 1295dc3/b246990fa9,
rebased onto current main.

Failure-Class: none
@formed2forge

Copy link
Copy Markdown
Contributor Author

Scope split done (personal-configs moved to #11789), verified on real hardware. Ready for maintainer sign-off.

@formed2forge

Copy link
Copy Markdown
Contributor Author

All CI checks pass, scope split done (personal configs moved to a separate PR), and the change has been hardware-verified. Pinging for maintainer sign-off.

cursor Bot pushed a commit to formed2forge/omi that referenced this pull request Sep 2, 2026
…the app delegate

Second half of the UIScene migration for BasedHardware#11568. Under the UIScene lifecycle the
app delegate owns no window at launch, so every binaryMessenger derived from
window?.rootViewController is nil in didFinishLaunching — and registering plugins
against the app delegate as the registry is itself fatal.

Measured on iPhone 17 Pro / iOS 27.0 with a file-backed probe: the log line before
GeneratedPluginRegistrant.register(with: self) wrote, the line after it never did,
and the process died. That is why this is not merely a nil-messenger problem.

Moves into didInitializeImplicitFlutterEngine(_:), sourcing the registry from
engineBridge.pluginRegistry and the messenger from
engineBridge.applicationRegistrar.messenger():

  - GeneratedPluginRegistrant registration
  - OmiPhoneCallsPlugin (now via registry.registrar(forPlugin:))
  - Watch/WatchRecorder, BLE, Ray-Ban Meta and phone-mic Pigeon APIs
  - notifyOnKill, apple_reminders, apple_health, speech, environment,
    audioSession, battery_widget channels and WifiNetworkPlugin

No window?.rootViewController reference remains in the file, and all nine
force unwraps of it are gone — each was an independent crash.

Left alone deliberately: SwiftFlutterForegroundTaskPlugin's registrant callback
and the top-level registerPlugins(registry:), which both receive a registry from
their caller and are already scene-independent.

Incidental fix: a launch carrying a deep link previously hit an early
`return true` in didFinishLaunching and skipped ALL channel setup. Registration no
longer shares a code path with link handling.

Still outstanding for BasedHardware#11568, not in this commit: applicationWillEnterForeground
never fires under scenes (measured, 0 of 4 foregrounds, while the notification
fires 4 of 4), so OmiBleManager.reconnectStalePeripherals() silently stops
running. That is a behavioural change and deserves its own commit.

Verification: `flutter build ios --flavor dev --debug` compiles with zero Swift
diagnostics. It cannot be signed on this branch — upstream/main hardcodes team
9536L8KLMP and unsuffixed bundle ids, which is what BasedHardware#7641 fixes — so runtime
verification is done with those signing changes applied on top.

Failure-Class: none

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qmdS9crg5qFhan7PCbWvZ
@cursor
cursor Bot force-pushed the fix/ios-community-build branch from a47ca67 to 1a037ae Compare September 2, 2026 03:13
@Git-on-my-level

Copy link
Copy Markdown
Collaborator

Follow-up review on the new head (1a037ae), covering the outstanding items from the June 26 maintainer review and the earlier automated pass, plus what's failing CI.

Prior asks — resolved. The scope split is done: the personal-configs overlay is gone from the diff (now #11789) and what remains is a cohesive iOS community-build change. The GOOGLE_REVERSE_CLIENT_ID chain is stronger than when last reviewed: Base.xcconfig now carries a documented default, generate_ios_custom_config.sh only writes the key when the plist actually has one (an empty assignment would override the base default, since Custom.xcconfig is included last), and google_reverse_client_id_test.sh asserts the resolved value rather than the mechanism.

Verified on this head:

  • project.pbxproj — exactly the nine -dev build configurations (Runner / BatteryWidget / omiWatchApp x Debug/Profile/Release) now use $(DEVELOPMENT_TEAM); all 24 production/standard configs keep the literal team. Correct blast radius.
  • Base.xcconfig — the APP_GROUP_IDENTIFIER default means production entitlements (Runner.entitlements, BatteryWidget.entitlements) resolve to the same group.com.friend-app-with-wearable.ios12 as before, which also closes the earlier "no committed xcconfig defines this variable" concern. Dev builds override it with the hostname-suffixed group written in generate_ios_custom_config(), matching the suffixed bundle id.
  • setup.sh detect_apple_team_id() — env override -> profile AppID match -> fingerprint-filtered fallback -> validated prompt. Matching profile-embedded certificates against held signing identities is the right authority model, and the non-TTY path fails fast instead of hanging on read (covered by the new test).
  • SharedDefaults.swift / AppDelegate.swift — both read AppGroupIdentifier from the bundle with the old literal as fallback, so building without running setup.sh behaves exactly as before; BatteryWidget-Info.plist injects the variable for the extension side.
  • Dev entitlements (RunnerDebug/Profile/Release-dev.entitlements) — dropping aps-environment, associated-domains, HotspotConfiguration and wifi-info is right: none are provisionable by a community team and these configs don't ship.
  • Ran the new shell tests on Linux: both pass, and the candidate-filter section self-skips without plutil instead of false-passing — good precedent, relevant below.

The failing Dart Analyze & Tests check is a test-portability issue, not a regression in this PR. dart analyze and the ratchet passed. The new test.sh loop then runs every test/shell/*_test.sh, which newly includes the pre-existing ios_dev_ats_config_test.sh (unchanged here, already on main). That test and the generator it exercises require macOS-only /usr/libexec/PlistBuddy, absent on the Ubuntu runner — it fails, and because test.sh runs under set -e, flutter test never actually executes. Suggested fix, mirroring the plutil skip in detect_apple_team_id_test.sh: skip the ATS test when /usr/libexec/PlistBuddy is unavailable, so Linux CI exercises the portable tests while macOS developers keep the ATS coverage.

Once that lands, this looks ready to come out of draft. Leaving the final sign-off to @Git-on-my-level — this changes repo-wide iOS signing defaults (team resolution, production entitlement resolution, untracking Custom.xcconfig), the class of change his June review reserved as a maintainer call.

by AI on behalf of David

@Git-on-my-level Git-on-my-level added needs-maintainer-review Needs a human maintainer to sign off before merge and removed needs-context needs more context to implement labels Sep 2, 2026
cursor Bot pushed a commit to formed2forge/omi that referenced this pull request Sep 2, 2026
…the app delegate

Second half of the UIScene migration for BasedHardware#11568. Under the UIScene lifecycle the
app delegate owns no window at launch, so every binaryMessenger derived from
window?.rootViewController is nil in didFinishLaunching — and registering plugins
against the app delegate as the registry is itself fatal.

Measured on iPhone 17 Pro / iOS 27.0 with a file-backed probe: the log line before
GeneratedPluginRegistrant.register(with: self) wrote, the line after it never did,
and the process died. That is why this is not merely a nil-messenger problem.

Moves into didInitializeImplicitFlutterEngine(_:), sourcing the registry from
engineBridge.pluginRegistry and the messenger from
engineBridge.applicationRegistrar.messenger():

  - GeneratedPluginRegistrant registration
  - OmiPhoneCallsPlugin (now via registry.registrar(forPlugin:))
  - Watch/WatchRecorder, BLE, Ray-Ban Meta and phone-mic Pigeon APIs
  - notifyOnKill, apple_reminders, apple_health, speech, environment,
    audioSession, battery_widget channels and WifiNetworkPlugin

No window?.rootViewController reference remains in the file, and all nine
force unwraps of it are gone — each was an independent crash.

Left alone deliberately: SwiftFlutterForegroundTaskPlugin's registrant callback
and the top-level registerPlugins(registry:), which both receive a registry from
their caller and are already scene-independent.

Incidental fix: a launch carrying a deep link previously hit an early
`return true` in didFinishLaunching and skipped ALL channel setup. Registration no
longer shares a code path with link handling.

Still outstanding for BasedHardware#11568, not in this commit: applicationWillEnterForeground
never fires under scenes (measured, 0 of 4 foregrounds, while the notification
fires 4 of 4), so OmiBleManager.reconnectStalePeripherals() silently stops
running. That is a behavioural change and deserves its own commit.

Verification: `flutter build ios --flavor dev --debug` compiles with zero Swift
diagnostics. It cannot be signed on this branch — upstream/main hardcodes team
9536L8KLMP and unsuffixed bundle ids, which is what BasedHardware#7641 fixes — so runtime
verification is done with those signing changes applied on top.

Failure-Class: none

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qmdS9crg5qFhan7PCbWvZ
formed2forge and others added 6 commits September 2, 2026 15:31
…up, dev entitlements

Community developers cannot build the iOS app: signing credentials are
hardcoded to BasedHardware's Apple team, and the dev-flavor entitlements
request capabilities a personal team cannot provision.

Three blockers, three fixes:

1. DEVELOPMENT_TEAM. setup.sh gains detect_apple_team_id(), which resolves the
   team from APPLE_DEVELOPMENT_TEAM, then a provisioning profile matching this
   machine's bundle ID, then any profile with a valid signing cert in the
   keychain, then an interactive prompt. The resolved value is written to
   Custom.xcconfig, and the nine dev-flavor build configurations in
   project.pbxproj read $(DEVELOPMENT_TEAM) instead of a literal team.
   Prod/beta/raybanDat configurations are deliberately left on the literal team
   — community builders cannot sign those anyway.

2. App group. The widget shares state with the app through an app group whose
   name must track the (per-machine suffixed) bundle ID. Base.xcconfig carries
   the unsuffixed default; setup.sh appends a suffixed APP_GROUP_IDENTIFIER for
   dev builds only, so prod/beta keep today's literal group. The Runner and
   BatteryWidget entitlements, both Info.plists, SharedDefaults.swift, and
   AppDelegate.swift all read it indirectly, each with a fallback to the
   original literal so a build without setup.sh still works.

3. Dev entitlements. RunnerDebug/Profile/Release-dev drop aps-environment,
   associated-domains, HotspotConfiguration, and wifi-info — the capabilities a
   free or personal Apple team cannot provision.

project.pbxproj is edited surgically: exactly nine DEVELOPMENT_TEAM lines
change and nothing else. Patching or 3-way merging this file does not work —
Xcode regenerates object IDs, so a merge silently adopts one whole side and
reverts unrelated upstream additions. An earlier attempt at this change did
exactly that, dropping ~670 lines of upstream Swift sources.

Verification (run locally on macOS 27, Aug 11 2026):
- detect_apple_team_id: APPLE_DEVELOPMENT_TEAM override returns the given team;
  with no override and no discoverable profiles it fails fast rather than
  hanging (see the following commit's regression test).
- End-to-end generate_ios_custom_config with a stub GoogleService-Info.plist:
    dev  -> APP_BUNDLE_IDENTIFIER=...ios12-mycomputer
            APP_GROUP_IDENTIFIER=group....ios12-mycomputer
            DEVELOPMENT_TEAM=98SC8JJDRG
    beta -> APP_BUNDLE_IDENTIFIER=...ios12.beta, no APP_GROUP_IDENTIFIER line,
            so it inherits the unsuffixed group from Base.xcconfig
- plutil -lint passes on project.pbxproj and all six touched plists/entitlements.
- bash -n passes on setup.sh.
- pbxproj audit: 9 dev-flavor configs now dynamic, 24 non-dev still literal,
  9 + 24 == 33 == the pre-change count, so no config was added or lost.

Not exercised: a full `flutter build ios`. This machine has no provisioning
profiles and no Apple team configured, so signing cannot be attempted here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
detect_apple_team_id() ends in an interactive prompt. In any non-interactive
context — CI, nested automation, a script whose stdin is an open-but-idle pipe —
`read` never sees EOF, so setup.sh blocked forever at the prompt instead of
failing with a usable message. Reproduced on this machine, which has zero
provisioning profiles and therefore always reaches that branch: the call sat
past a 120s deadline with no output.

Both `read` sites now require a TTY. Without one, the function prints what to
set (APPLE_DEVELOPMENT_TEAM) and returns non-zero, so setup.sh fails fast under
its own `set -e`.

Adds app/test/shell/ as the home for hermetic shell tests of setup.sh helpers,
discovered by app/test.sh (which mobile-app-checks.yml already runs, so these
execute in PR CI). The test drives the real function through two seams — $HOME,
which is where the profile scan looks, and stdin — rather than asserting on
source text, so it is behavioral coverage and not a static tripwire. It carries
its own deadline instead of depending on GNU timeout(1) being installed.

Verification:
- With the guard removed, the test reports "hung waiting for input with no TTY"
  and exits 1. With the guard, both cases pass. Re-ran after restoring to
  confirm setup.sh was left byte-identical.
- bash -n passes on the test, setup.sh, and test.sh.
- The discovery loop in test.sh finds and runs the test.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`detect_apple_team_id`'s fallback decided which teams to offer by collecting every
team named anywhere in `security find-identity -v -p codesigning` output. That is
wrong in both directions.

It accepted teams that cannot build: the list includes "Developer ID Application"
certificates, which sign Mac distribution builds and cannot sign an iOS
development build. And it matched as an unanchored substring, so a team ID
composed only of hex characters could match inside the 40-char SHA-1 that begins
every identity line.

More importantly, a certificate's common name is not authoritative about its
team. Apple keeps the original personal-team identifier in the common name when a
developer joins a paid team, so a certificate reading
"Apple Development: NAME (PERSONALTEAM)" can be issued under a different team
entirely — the real one is the OU, readable only by decoding the certificate.
Observed on the machine this was developed on: an identity whose common name says
(LW4P2T66Q4) but whose OU is 98SC8JJDRG, and which signs successfully for
98SC8JJDRG. A name-based check rejects exactly the team such a developer can use.

The fallback now asks the question Xcode asks when it picks a profile: does the
profile embed a certificate whose private key is on this machine? Fingerprints of
held iOS development identities are compared against the SHA-1 of each embedded
certificate, extracted with `plutil -extract DeveloperCertificates.<n> raw` and
`openssl x509`. The profile's own TeamIdentifier is authoritative for the team, so
that is what gets offered, and no inference is made from certificate names. The
identity list is still filtered to "Apple Development" and the legacy
"iPhone Developer" spelling, so a machine holding only a Mac Developer ID
certificate offers nothing.

Verified against the real profiles on this machine: the embedded certificates of
the installed profile have fingerprints 18309B14…, DBF7EBAE… and 8E1514B1…, of
which DBF7EBAE… and 8E1514B1… are held, so team 98SC8JJDRG is correctly offered.
Detection returns 98SC8JJDRG both through step 2's bundle match and — with the
bundle pattern forced not to match, so step 3 is reached — through this fallback.

Tests (app/test/shell/detect_apple_team_id_test.sh) generate real certificates
with openssl so a profile's embedded certificate and the stubbed identity list
agree on a genuine fingerprint:

- the profile's team is offered when we hold its embedded certificate, even though
  that certificate's common name names a different team
- a team whose embedded certificate we do not hold is not offered
- a machine holding only a Developer ID Application certificate offers nothing

Verification (macOS 27.0, 2026-08-13): `bash -n` clean; all 5 cases in the file
pass. Against the previous name-matching implementation, case 1 fails
("expected PAIDTEAM01 …, got ''") — the regression this fixes. Cases 2 and 3 pass
either way; they guard this implementation against being too permissive rather
than covering the old defect.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qmdS9crg5qFhan7PCbWvZ
Removing the hardcoded GOOGLE_REVERSE_CLIENT_ID from devDebug.xcconfig left
community builds with no value at all. Runner/Info.plist emits it as a
CFBundleURLSchemes entry, so the build shipped an empty URL scheme and the Google
Sign-In redirect had nowhere to land.

The chain failed in two places at once:

  setup/prebuilt/GoogleService-Info-Local.plist   no REVERSED_CLIENT_ID key
  generate_ios_custom_config.sh                   extracted empty, wrote
                                                  "GOOGLE_REVERSE_CLIENT_ID="
  Base.xcconfig                                   no default to fall back to
  devDebug.xcconfig                               includes Custom.xcconfig LAST,
                                                  so the empty value won
  Runner/Info.plist:88                            <string>$(GOOGLE_REVERSE_CLIENT_ID)</string>

Both halves are fixed, because either alone is insufficient: Base.xcconfig now
carries the default the June 26 review asked for, and the generator no longer
writes the key when the plist has no value — an empty assignment in
Custom.xcconfig overrides the default rather than deferring to it, since that
file is included afterwards. A plist that does carry REVERSED_CLIENT_ID still
overrides, so nobody silently builds against the checked-in fallback.

How this was missed twice: the PR review on Jun 26 asked to "verify
GOOGLE_REVERSE_CLIENT_ID has an equivalent fallback in Base.xcconfig", and both a
maintainer review comment (Aug 12) and my own earlier answer concluded it was
covered because the generator still writes the key. It does — it just writes
nothing useful for the community Firebase config. Running setup.sh's real config
path surfaced it immediately: the generated Custom.xcconfig read
"GOOGLE_REVERSE_CLIENT_ID=" with nothing after the equals sign. Asserting the
mechanism is not the same as asserting the value.

Tests (app/test/shell/google_reverse_client_id_test.sh) resolve the key the way
Xcode does — later includes win — and assert the resolved value, not the
mechanism:

- the community config resolves to a non-empty value
- the generator writes no empty assignment for a plist without the key
- a plist carrying the key still overrides the default
- Base.xcconfig carries a default at all

Verification (macOS 27.0, 2026-08-13): bash -n clean on the generator; all 4
cases pass. Reverting either half fails 2 cases — removing the Base default
fails "resolved to EMPTY" and "no default to fall back to"; restoring the
unconditional write fails "resolved to EMPTY" and "wrote an empty assignment".
Also confirmed by hand that a plist carrying REVERSED_CLIENT_ID produces that
value in Custom.xcconfig.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qmdS9crg5qFhan7PCbWvZ
detect_apple_team_id's real certificate-matching path shells out to
`plutil -extract ... raw`, which is macOS-only. CI runs shell tests on
an Ubuntu runner, so every candidate certificate silently failed to
decode there and the function always reported "no match" — the two
sub-tests expecting rejection passed for the wrong reason (a broken
matcher rejects everything), while the one sub-test expecting a real
match failed, which is what surfaced this in CI.

Skip the whole candidate-filtering section when plutil is unavailable
instead of asserting on a matcher that can't run. Verified both paths:
unchanged pass with plutil on PATH, clean skip (exit 0) with a PATH
that omits it.

Failure-Class: none
@cursor
cursor Bot force-pushed the fix/ios-community-build branch from 1a037ae to 1240a53 Compare September 2, 2026 15:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app flutter flutter work human Human-authored pull request ios mobile needs-maintainer-review Needs a human maintainer to sign off before merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants