feat(ios): Add UIScene lifecycle support for background app refresh#1
Merged
feat(ios): Add UIScene lifecycle support for background app refresh#1
Conversation
Apple requires UIKit apps built with the latest SDK to adopt the UIScene lifecycle. In UIScene apps applicationDidEnterBackground is never called, which prevented background app refresh from being scheduled. Changes: - Add SceneLifecycleBridge that registers itself via an ObjC runtime check (responds(to:) / perform(_:with:)), so the plugin compiles on all Flutter versions >= 3.22.0, not only those that expose addSceneDelegate (introduced in Flutter 3.38.0). - sceneDidEnterBackground schedules background app refresh, mirroring what applicationDidEnterBackground does for non-UIScene apps. - Add FlutterForegroundTaskEarlyRegistration +load in the ObjC layer to register the BGTaskScheduler handler at binary load time, before Flutter initialises — required for background-launch scenarios. - Add isBgTaskRegistered guard to registerAppRefresh() to prevent a double-registration crash during the UIScene transition period. Based on Dev-hwang#378 by @Gibbo97, adapted to avoid bumping the minimum Flutter version.
638f188 to
b9dbad3
Compare
|
ppamorim
added a commit
that referenced
this pull request
Apr 18, 2026
The MethodChannel-based debugThreadId() always measured the platform
main thread (every FlutterEngine shares the same platform task runner on
Android/iOS), making it impossible to distinguish mergedEngine from
dedicatedEngine. This replaces the native handlers with an in-isolate
probe via package:universal_ffi/ffi.dart:
- Android: gettid() from libc.so
- iOS/macOS: pthread_mach_thread_np(pthread_self()) via DynamicLibrary.process()
- Other platforms: null
The probe initialization is async (universal_ffi requires it on Android)
and cached after first call. Platform detection is moved to
package:platform/platform.dart (already a direct dependency) instead of
dart:io. Removed universal_io; added universal_ffi to pubspec.yaml.
Stale native debugThreadId handlers removed from:
- MethodCallHandlerImpl.kt
- service/ForegroundTask.kt
- SwiftFlutterForegroundTaskPlugin.swift
- service/ForegroundTask.swift
Documentation updated:
- merged_platform_ui_thread_mitigation.md: new §2 "Known bugs" section
listing this as Bug #1 with root cause, symptom, and fix plan; all
downstream sections renumbered; risk table row marked resolved.
- threading_model.md: runtime verification section updated from
"proposed" to "implemented"; old MethodChannel history preserved.
- CHANGELOG.md: debugThreadId entry revised to reflect FFI semantics.
Tests updated: debugThreadId group now asserts the helper never hits
either MethodChannel, returns a positive tid on supported hosts, and
is stable across calls on the same isolate.
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.



Summary
UISceneDelegate(whereapplicationDidEnterBackgroundis never called).SceneLifecycleBridge, a small abstraction that registers itself with Flutter'saddSceneDelegatevia an ObjC runtime check (responds(to:)/perform(_:with:)), so the plugin compiles on all Flutter versions ≥ 3.22.0 — not only those that shipaddSceneDelegate(introduced in Flutter 3.38.0).FlutterForegroundTaskEarlyRegistration(+load) in the ObjC layer to register theBGTaskSchedulerhandler at binary load time, before Flutter initialises — required when iOS wakes the app in the background to service a refresh task.isBgTaskRegisteredguard to prevent a double-registration crash during the UIScene transition period.Based on Dev-hwang/flutter_foreground_task#378 by @Gibbo97, adapted to avoid bumping the minimum Flutter version.
Files changed
ios/Classes/SceneLifecycleBridge.swiftios/Classes/SwiftFlutterForegroundTaskPlugin.swiftregisterAppRefreshForBackgroundLaunch(), addsisBgTaskRegisteredguard.ios/Classes/FlutterForegroundTaskPlugin.mFlutterForegroundTaskEarlyRegistration +loadto register BGTask handler before Flutter init.Key design decision
The original PR (Dev-hwang#378) bumps
flutter: ">=3.38.0"and declaresFlutterSceneLifeCycleDelegateconformance. This PR avoids both:respondsToSelector:, so markingsceneDidEnterBackgroundas@objcis sufficient.addSceneDelegatecalled viaNSSelectorFromString+responds(to:)— silently no-ops on older Flutter, works normally on ≥ 3.38.0.This keeps the minimum Flutter version at
>=3.22.0.Test plan
sceneDidEnterBackgroundfires and schedules background refresh+loadearly registrationGeneratedPluginRegistrant.register(with:)is called from multiple locations