chore(example): fix example app builds on current toolchains (iOS 27, Apple Silicon, Flutter 3.47) - #1180
Merged
Merged
Conversation
hiroshihorie
marked this pull request as ready for review
August 26, 2026 16:47
hiroshihorie
requested review from
cloudwebrtc and
xianshijing-lk
as code owners
August 26, 2026 16:47
hiroshihorie
force-pushed
the
hiroshi/fix-example-builds
branch
from
August 26, 2026 16:59
a4567e2 to
bb19a19
Compare
hiroshihorie
added a commit
that referenced
this pull request
Aug 26, 2026
`Build` has been failing on `main` and therefore on every PR, regardless of what the PR changes — visible on #1180, `max/fix-ios-audio-session-before-recording` and `sync-upstream-2.11.0`, all of which show `Build ✗` with `Changeset Check ✓`. Two jobs are responsible. ## Dart Format Three files under `scripts/` are not formatted, so `dart format . --set-exit-if-changed` exits 1. Straight from the CI log: ``` Formatted scripts/check_version.dart Formatted scripts/create_change.dart Formatted scripts/create_version.dart Formatted 227 files (3 changed) in 0.72 seconds. ##[error]Process completed with exit code 1 ``` Formatting only, no behaviour change. ## Dart Analyze ``` lib/src/token_source/caching.dart:138:9 • unawaited_return_in_try_block lib/src/token_source/caching.dart:144:7 • unawaited_return_in_try_block ``` `CachingTokenSource.fetch` returned `resultFuture` from inside its `try`, so the surrounding `catch` could not observe a failure from that future and `finally` removed the in-flight entry before it settled. Both returns now `await`. Behaviour is unchanged — the completer is completed on the line immediately before each return, so the awaited future is already resolved — but errors now route through the existing `catch`, and the in-flight map is cleared only once the request settles. `AGENTS.md` calls out un-awaited async state updates as a recurring bug class here, so this is a real latent issue rather than a lint silence. Includes a `patch type="fixed"` changeset. ## The analysis_options churn, and a coverage hole it was hiding `flutter analyze` kept leaving `analysis_options.yaml` modified. The cause is `AnalysisOptionsMigration` (`flutter_tools/lib/src/migrations/analysis_options_migration.dart`, invoked from `project.dart:429`), which appends seven patterns — `build/**`, `android/**`, `ios/**`, `web/**`, `windows/**`, `macos/**`, `linux/**` — on every `flutter analyze` / `pub get` / `run`. It is unconditional; there is no feature flag or config to disable it, unlike the UIScene migrator's `enable-uiscene-migration`. Committing the patterns makes it a no-op. But one of them matters: **this package keeps real Dart source in `web/`** (the E2EE worker), not just assets, so `web/**` silently drops it from analysis. That was already happening in CI, since `flutter analyze` runs the migrator before it analyzes. Passing the path explicitly does **not** work — `dart analyze web/` still honours the root exclude. I confirmed this by appending a deliberate type error to `web/e2ee.logger.dart`: it reported `No issues found!`. Giving `web/` its own `analysis_options.yaml` creates a separate context that is genuinely analyzed; the same canary then correctly reports `return_of_invalid_type`. That file mirrors the root's error overrides and its formatter settings (`page_width: 120`, `trailing_commas: preserve`) so `dart format` output is unaffected, and `build.yaml` gains a `dart analyze web/` step. ## Verified locally ``` dart format . --set-exit-if-changed PASS (227 files, 0 changed) flutter analyze PASS (No issues found) dart analyze web/ PASS (and fails on an injected error) flutter test PASS (+398) dart run scripts/check_version.dart PASS import_sorter --exit-if-changed PASS ``` ## Not included Pinning the Flutter version in `.github/actions/setup-flutter/action.yml`, which currently passes only `channel: stable` and so tracks whatever the newest stable release is. That is a genuine fragility — `unawaited_return_in_try_block` arrived this way — but it is a separate decision from unbreaking CI, and worth its own discussion. Draft because #1180 should confirm this actually turns `Build` green once rebased on it.
Bumps the iOS floor for the example app and its broadcast extension: - Podfile `platform :ios` 13.0 -> 15.0, and the post_install override that forces IPHONEOS_DEPLOYMENT_TARGET on every pod 12.1 -> 15.0 - Runner and LiveKit Broadcast Extension targets 14.0 -> 15.0 Also sorts Info.plist keys into alphabetical order, which Xcode does on save (no semantic change).
`flutter analyze` in example/ was walking build output and the native platform folders. Adds the standard excludes (build, android, ios, web, windows, macos, linux) alongside the existing generated-code patterns.
iOS 27 terminates apps that have not adopted the UIScene lifecycle, and `flutter run` tool-exits on the resulting device log line, so the example app could no longer be launched on an iOS 27 device. - Add `UIApplicationSceneManifest` to Info.plist using the stock `FlutterSceneDelegate`, so no custom scene delegate is needed. - Conform AppDelegate to `FlutterImplicitEngineDelegate` and move `GeneratedPluginRegistrant.register` into `didInitializeImplicitFlutterEngine`. Under UIScene the app delegate's `window` is nil, so the old `guard let controller = window?...` returned early and skipped plugin registration entirely. See https://flutter.dev/to/uiscene-migration
Flutter 3.47 requires a macOS 12.0 floor; the example was still at 10.15. Updates the Podfile `platform :osx` and MACOSX_DEPLOYMENT_TARGET in all three build configurations.
No Flutter template uses `use_modular_headers!` on any platform; it was a local addition to both example Podfiles. Verified unnecessary by building each platform with Swift Package Manager disabled, so every plugin resolves through CocoaPods (including flutter_webrtc + WebRTC-SDK, and permission_handler_apple on iOS): both `pod install` and the Xcode build succeed without it.
The Podfile's post_install forced `EXCLUDED_ARCHS[sdk=iphonesimulator*] = 'arm64'` on every pod. That is a 2020 workaround for Intel Macs, and on Apple Silicon it is backwards: it made `flutter run`/`flutter build` print The following target(s) do not support arm64 architecture, which is a requirement for Apple Silicon iOS 26+ simulators on every invocation, listing every plugin. After removing it, `flutter build ios --simulator --debug` succeeds, the warning is gone, and the built binary reports `x86_64 arm64`.
- Project-level IPHONEOS_DEPLOYMENT_TARGET 12.1 -> 15.0. Every target already overrode this to 15.0, so it was dead weight, and any new target would have silently inherited 12.1. - Drop the three empty `EXCLUDED_ARCHS = ""` entries, left over from the same era as the simulator arm64 workaround.
With Swift Package Manager on (the default since Flutter 3.41), every plugin resolves as a Swift Package and `Flutter` is the only remaining pod, so `pod install` removes the `[CP] Embed Pods Frameworks` and `[CP] Copy Pods Resources` build phases: there is nothing left to embed or copy. These phases are generated and owned by CocoaPods, so `pod install` re-adds them for anyone building with SPM disabled. Committing the generated state keeps iOS consistent with macOS, which already carried only `[CP] Check Pods Manifest.lock`.
`flutter run -d <android>` failed outright: Flutter 3.47 enforces minimum AGP and Kotlin Gradle Plugin versions that the example was below, and they surface one at a time. Android Gradle Plugin 8.9.1 -> 8.11.1 (Flutter minimum) Kotlin Gradle Plugin 2.1.0 -> 2.2.20 (Flutter minimum) Also picks up the two AGP 9 opt-outs that Flutter's own migrator writes into gradle.properties (`android.builtInKotlin`, `android.newDsl`). These are the enforced minimums, not the versions `flutter create` now generates (AGP 9.1.0 / Kotlin 2.4.0 / Gradle 9.3.1). Moving to those is a larger toolchain jump and is left as separate work. Gradle stays at 8.14, which only warns. Verified by running the example on a Pixel 10 Pro emulator.
Two divergences from what `flutter create` generates, both surfaced as build warnings: - ndkVersion was pinned to 27.0.12077973 while connectivity_plus, device_info_plus, flutter_background, flutter_webrtc and jni all require 28.2.13676358. Use `flutter.ndkVersion` (the template's own reference, currently 28.2.13676358) so this tracks the SDK instead of drifting again. - Java source/target compatibility was VERSION_1_8, which now warns "source/target value 8 is obsolete and will be removed in a future release". The template uses VERSION_17. kotlinOptions.jvmTarget moves to 17 alongside it; leaving it at 1_8 would fail with inconsistent JVM-target compatibility. Both warnings are gone from the build output and the APK still builds.
hiroshihorie
force-pushed
the
hiroshi/fix-example-builds
branch
from
August 26, 2026 18:07
bb19a19 to
e982293
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The example app no longer launched on iOS 27 and no longer built for Android at all. This gets it running again on every platform, and clears out config that had drifted from what
flutter creategenerates.Scoped entirely to
example/— no changes to the published package, hence no changeset.The actual fix
iOS 27 terminates apps that have not adopted the UIScene lifecycle, and
flutter runtool-exits on the resulting device log line, so the app could not be launched on an iOS 27 device at all. Flutter's auto-migrator declined to help (AppDelegate does not match original template).The old
AppDelegatealso had a latent bug: under UIScene the app delegate'swindowisnil, soguard let controller = window?.rootViewControllerreturned early and skipped plugin registration entirely. The migration guide calls out that exact pattern.Android was separately broken — Flutter 3.47 enforces AGP and Kotlin minimums the example was below, surfaced one at a time (AGP 8.9.1 → 8.11.1, Kotlin 2.1.0 → 2.2.20).
Cleanups
EXCLUDED_ARCHS[sdk=iphonesimulator*] = 'arm64'— a 2020 Intel-Mac workaround that, on Apple Silicon, made every build printThe following target(s) do not support arm64 architectureand list every plugin.use_modular_headers!— in both Podfiles, in no Flutter template. Verified unnecessary by building each platform with SwiftPM disabled so every plugin resolves through CocoaPods (includingflutter_webrtc+WebRTC-SDK, andpermission_handler_appleon iOS).IPHONEOS_DEPLOYMENT_TARGET = 12.1(every target already overrode to 15.0) and three emptyEXCLUDED_ARCHS = "".[CP] Embed Pods Frameworks/[CP] Copy Pods Resources— with SwiftPM on,Flutteris the only pod left andpod installremoves them. Brings iOS in line with macOS.ndkVersionwas pinned to27.0.12077973while five plugins require28.2.13676358; now usesflutter.ndkVersion. Java targetVERSION_1_8→VERSION_17(thesource/target value 8 is obsoletewarning).Verified
Deliberately not included
flutter createnow generates). It builds and runs, but buys nothing functional today, and does not unlock built-in Kotlin —device_info_plus 12.3.0blocks that regardless. Flutter still shipsandroid.builtInKotlin=falsein its own template. Left for when the ecosystem catches up. Note Flutter already warns that AGP 8.11.1, Kotlin 2.2.20 and Gradle 8.14 will "soon be dropped", so this is a when, not an if.RunnerTeststarget. Neither platform has one, which is why the Podfiles cannot match the template. Not worth adding to an example app.Unrelated issue found
flutter buildrunsrsync -av --delete <repo>/ → example/build/<platform>/SourcePackages/client-sdk-flutter— the destination is inside the source, becauselivekit_clientis apath:dependency on the repo root. From a cleanbuild/it is one bounded ~737 MB copy, but each build re-copies the previous copies. It reached 30 GB and later 43 GB during this work, nesting 9 levels deep. Workaround isflutter cleanbetween platform switches; the real fix is upstream in Flutter, and it will hit any plugin repo whose example depends on its parent by path.