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

Skip to content

[iOS]: Swift plugins crash when adopting UISceneDelegate #168228

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
gaaclarke opened this issue May 2, 2025 · 9 comments
Open

[iOS]: Swift plugins crash when adopting UISceneDelegate #168228

gaaclarke opened this issue May 2, 2025 · 9 comments
Labels
team-ios Owned by iOS platform team

Comments

@gaaclarke
Copy link
Member

gaaclarke commented May 2, 2025

When following Apple's best practices of adopting UISceneDelegate, if the project uses any swift plugins, the project will crash at launch when plugins are registered because the objc bridge classes for swift FlutterPlugin subclasses will be nil.

I ran into this crash while trying to implement #167267, but I've also seen users in the wild running into this crash as well ( https://developer.apple.com/forums/thread/748671 ).

reproduction steps

  1. flutter create -t plugin --platform=ios swiftplugin
  2. cd swiftplugin
  3. Add the following to example/ios/Runner/Info.plist (Note that the value of the fields don't need to be valid because startup will crash before it is used).
    	<key>UIApplicationSceneManifest</key>
    	<dict>
    		<key>UIApplicationSupportsMultipleScenes</key>
    		<false/>
    		<key>UISceneConfigurations</key>
    		<dict>
    			<key>UIWindowSceneSessionRoleApplication</key>
    			<array>
    				<dict>
    					<key>UISceneClassName</key>
    					<string>UIWindowScene</string>
    					<key>UISceneDelegateClassName</key>
    					<string>$(PRODUCT_MODULE_NAME).AppDelegate</string>
    					<key>UISceneConfigurationName</key>
    					<string>flutter</string>
    					<key>UISceneStoryboardFile</key>
    					<string>Main</string>
    				</dict>
    			</array>
    		</dict>
    	</dict>
  4. cd example; flutter run

crash

* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
    frame #0: 0x0000000194f85b08 libswiftCore.dylib`swift_getObjectType + 36
    frame #1: 0x000000010329935c plugintest`static PlugintestPlugin.register(registrar=__C.FlutterPluginRegistrar @ 0x000000016ce08b88) at PlugintestPlugin.swift:0
  * frame #3: 0x000000010325d834 Runner.debug.dylib`+[GeneratedPluginRegistrant registerWithRegistry:](self=GeneratedPluginRegistrant, _cmd="registerWithRegistry:", registry=0x0000600000c0daa0) at GeneratedPluginRegistrant.m:25:3
    frame #4: 0x000000010325d8c4 Runner.debug.dylib`AppDelegate.application(application=0x0000000103f05a80, launchOptions=nil) at AppDelegate.swift:10:31
    frame #6: 0x0000000185b2e30c UIKitCore`-[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 312
    frame #7: 0x0000000185b2f804 UIKitCore`-[UIApplication _callInitializationDelegatesWithActions:forCanvas:payload:fromOriginatingProcess:] + 2936
    frame #8: 0x0000000185b34984 UIKitCore`-[UIApplication _runWithMainScene:transitionContext:completion:] + 976
    frame #9: 0x00000001851405ec UIKitCore`-[_UISceneLifecycleMultiplexer completeApplicationLaunchWithFBSScene:transitionContext:] + 148
    frame #10: 0x0000000185b316b0 UIKitCore`-[UIApplication _compellApplicationLaunchToCompleteUnconditionally] + 44
    frame #11: 0x0000000185b319b8 UIKitCore`-[UIApplication _run] + 760
    frame #12: 0x0000000185b35bd4 UIKitCore`UIApplicationMain + 124
    frame #13: 0x0000000184f0a334 UIKitCore`UIKit.UIApplicationMain(Swift.Int32, Swift.Optional<Swift.UnsafeMutablePointer<Swift.UnsafeMutablePointer<Swift.Int8>>>, Swift.Optional<Swift.String>, Swift.Optional<Swift.String>) -> Swift.Int32 + 100
    frame #16: 0x000000010325de10 Runner.debug.dylib`main at <compiler-generated>:0
    frame #17: 0x0000000103009410 dyld_sim`start_sim + 20
    frame #18: 0x00000001030d6b4c dyld`start + 6000

Inspecting the type in the debugger after the crash looks like this:

(lldb) po PlugintestPlugin.self
note: error: No module map file in /Users/aaclarke/Library/Developer/Xcode/DerivedData/Runner-akumchbkelcnzlcblbxzpccssdcp/Build/Products/Debug-iphonesimulator/Flutter.framework
error: Error [IRForTarget]: Couldn't resolve the class for an Objective-C static method call

doctor

[!] Flutter (Channel [user-branch], 3.32.0-1.0.pre.382, on macOS 15.4.1 24E263 darwin-arm64, locale en) [551ms]
    ! Flutter version 3.32.0-1.0.pre.382 on channel [user-branch] at /Users/aaclarke/dev/flutter
      Currently on an unknown channel. Run `flutter channel` to switch to an official channel.
      If that doesn't fix the issue, reinstall Flutter by following instructions at https://flutter.dev/setup.
    ! Upstream repository unknown source is not a standard remote.
      Set environment variable "FLUTTER_GIT_URL" to unknown source to dismiss this error.
    • Framework revision 448e3036e0 (28 minutes ago), 2025-05-02 09:38:10 -0700
    • Engine revision 9ffc2d9ef3
    • Dart version 3.9.0 (build 3.9.0-76.0.dev)
    • DevTools version 2.46.0-dev.0
    • If those were intentional, you can disregard the above warnings; however it is recommended to use "git" directly to perform update checks and upgrades.

[!] Android toolchain - develop for Android devices (Android SDK version 35.0.0-rc4) [1,093ms]
    • Android SDK at /Users/aaclarke/Library/Android/sdk
    • Emulator version 35.2.10.0 (build_id 12414864) (CL:N/A)
    • Platform android-35, build-tools 35.0.0-rc4
    • Java binary at: path/to/jdk/bin/java
      This JDK is specified in your Flutter configuration.
      To change the current JDK, run: `flutter config --jdk-dir="path/to/jdk"`.
    ✗ Cannot execute path/to/jdk/bin/java to determine the version

[!] Xcode - develop for iOS and macOS (Xcode 16.3) [1,107ms]
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 16E140
    ! CocoaPods 1.15.2 out of date (1.16.2 is recommended).
        CocoaPods is a package manager for iOS or macOS platform code.
        Without CocoaPods, plugins will not work on iOS or macOS.
        For more info, see https://flutter.dev/to/platform-plugins
      To update CocoaPods, see https://guides.cocoapods.org/using/getting-started.html#updating-cocoapods

[✓] Chrome - develop for the web [7ms]
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2024.2) [7ms]
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 21.0.3+-79915917-b509.11)

[✓] VS Code (version 1.99.3) [4ms]
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.110.0

[✓] Connected device (4 available) [6.3s]
    • iPhone (5) (wireless) (mobile) • 00008110-001524D40A10401E            • ios            • iOS 18.4.1 22E252
    • iPhone 16 Pro (mobile)         • 9DA578E6-E85A-44FA-9673-D01C31F3B779 • ios            • com.apple.CoreSimulator.SimRuntime.iOS-18-2 (simulator)
    • macOS (desktop)                • macos                                • darwin-arm64   • macOS 15.4.1 24E263 darwin-arm64
    • Chrome (web)                   • chrome                               • web-javascript • Google Chrome 135.0.7049.116

[✓] Network resources [152ms]
    • All expected network resources are available.
@gaaclarke gaaclarke added the team-ios Owned by iOS platform team label May 2, 2025
@gaaclarke
Copy link
Member Author

gaaclarke commented May 2, 2025

@stuartmorgan-g is this a failure that we've run into in other circumstances with swift plugins where we are able to compile fine but at runtime the bridge classes are nil?

@gaaclarke
Copy link
Member Author

As an experiment I moved the +[GeneratedPluginRegistrant registerWithRegistry:] call later in the app startup with a "dispatch after" and the crash still happened. The theory was maybe that was too early in app startup and the bridge wasn't initialized yet, but that doesn't seem to be the case.

@gaaclarke
Copy link
Member Author

gaaclarke commented May 2, 2025

Jenn question if when it prints something like

No module map file in /Users/aaclarke/Library/Developer/Xcode/DerivedData/Runner-gnymzorfhvfvixdrpycicrjdhpnl/Build/Products/Debug-iphonesimulator/Flutter.framework

is there a module map there? There is (albeit in a Modules subdirectory).

$ cat /Users/aaclarke/Library/Developer/Xcode/DerivedData/Runner-gnymzorfhvfvixdrpycicrjdhpnl/Build/Products/Debug-iphonesimulator/Flutter.framework/Modules/module.modulemap
framework module Flutter {
  umbrella header "Flutter.h"

  export *
  module * { export * }
}

@gaaclarke
Copy link
Member Author

Here is a similar crash where the user claimed it only happened in testflight: https://stackoverflow.com/questions/77206289/flutter-runs-locally-but-testflight-build-crashes-on-swift-getobjecttype

I tried adding the linker flags -ObjC and -all_load to the example target to see if the objc bridge code was being dropped by the linker, but that didn't fix it.

@gaaclarke
Copy link
Member Author

I tried switching to swift package manager and it doesn't resolve the crash.

@gaaclarke
Copy link
Member Author

So in the reproduction above, the crash happens when attempting to use the SwiftpluginPlugin class in the swiftplugin package.

At the crash you can see the swiftplugin.framework is loaded:

(lldb) image list | grep swiftplugin
[  0] 75260E8D-37F9-3F0B-BC5C-BEF63D27EF59 0x000000010108c000 /Users/aaclarke/Library/Developer/Xcode/DerivedData/Runner-gnymzorfhvfvixdrpycicrjdhpnl/Build/Products/Debug-iphonesimulator/Runner.app/Frameworks/swiftplugin.framework/swiftplugin 

I expect the symbols for the objc classes to exist in that binary, they do appear.

nm /Users/aaclarke/Library/Developer/Xcode/DerivedData/Runner-gnymzorfhvfvixdrpycicrjdhpnl/Build/Products/Debug-iphonesimulator/swiftplugin/swiftplugin.framework/swiftplugin | grep SwiftpluginPlugin
0000000000001484 T _$s11swiftplugin17SwiftpluginPluginC6handle_6resultySo17FlutterMethodCallC_yypSgctF
0000000000001748 t _$s11swiftplugin17SwiftpluginPluginC6handle_6resultySo17FlutterMethodCallC_yypSgctFTo
0000000000003ee4 S _$s11swiftplugin17SwiftpluginPluginC6handle_6resultySo17FlutterMethodCallC_yypSgctFTq
00000000000011e8 T _$s11swiftplugin17SwiftpluginPluginC8register4withySo07FlutterC9Registrar_p_tFZ
0000000000001434 t _$s11swiftplugin17SwiftpluginPluginC8register4withySo07FlutterC9Registrar_p_tFZTo
0000000000001410 T _$s11swiftplugin17SwiftpluginPluginCACycfC
00000000000018f8 T _$s11swiftplugin17SwiftpluginPluginCACycfc
0000000000001974 t _$s11swiftplugin17SwiftpluginPluginCACycfcTo
0000000000003eec s _$s11swiftplugin17SwiftpluginPluginCMF
00000000000013ec T _$s11swiftplugin17SwiftpluginPluginCMa
000000000000c8b8 s _$s11swiftplugin17SwiftpluginPluginCMf
0000000000003eb0 S _$s11swiftplugin17SwiftpluginPluginCMn
000000000000c8d0 S _$s11swiftplugin17SwiftpluginPluginCN
0000000000001994 T _$s11swiftplugin17SwiftpluginPluginCfD
000000000000c8d0 S _OBJC_CLASS_$__TtC11swiftplugin17SwiftpluginPlugin
000000000000c940 D _OBJC_METACLASS_$__TtC11swiftplugin17SwiftpluginPlugin
0000000000002c70 s __CLASS_METHODS__TtC11swiftplugin17SwiftpluginPlugin
000000000000c870 s __DATA__TtC11swiftplugin17SwiftpluginPlugin
0000000000002c88 s __INSTANCE_METHODS__TtC11swiftplugin17SwiftpluginPlugin
000000000000c100 s __METACLASS_DATA__TtC11swiftplugin17SwiftpluginPlugin
000000000000c0f0 s __PROTOCOLS__TtC11swiftplugin17SwiftpluginPlugin
000000000000c148 s __PROTOCOLS__TtC11swiftplugin17SwiftpluginPlugin.1
0000000000003e8c s _symbolic _____ 11swiftplugin17SwiftpluginPluginC

I am able to resolve the class with NSClassFromString, however :

(lldb) p (Class)NSClassFromString(@"swiftplugin.SwiftpluginPlugin")
(Class) swiftplugin.SwiftpluginPlugin

That means we know the symbols is present in the binaries and it can be loaded, but for some reason [SwiftpluginPlugin class] is not working. This makes it sound like the magic that appends the module name is not working. That may be the module map that is referenced in the error message?

@gaaclarke
Copy link
Member Author

Editing the GeneratedPluginRegistrant to do NSLog(@"%@", [SwiftpluginPlugin class]); works, it prints out swiftplugin.SwiftpluginPlugin, correctly mapping the name to module+name. This is weird because it didn't work when invoked from lldb. This also means my theory that we don't have the information to map from name to module+name is incorrect.

@gaaclarke
Copy link
Member Author

Looking at the stacktrace again. It appears we are loading the register function fine. I got on a bit of a rabbit chase because po [SwiftpluginPlugin class] didn't work in lldb.

@gaaclarke
Copy link
Member Author

Okay, I think I've track down the root of the problem. SwiftpluginPlugin.register is called with a nil registrar. I don't know for the life of me why all of the messages I got from lldb didn't clearly state that. The plist item is rearranging the startup somehow so that that was previously already initialized, now it isn't.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
team-ios Owned by iOS platform team
Projects
None yet
Development

No branches or pull requests

1 participant