-
Notifications
You must be signed in to change notification settings - Fork 69
fix(ios): resolve UIScene lifecycle crash in plugin registration #158
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -3,18 +3,27 @@ import UIKit | |||||||||||||||||||||||||||||||
| import JitsiMeetSDK | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| public class JitsiMeetPlugin: NSObject, FlutterPlugin, FlutterStreamHandler { | ||||||||||||||||||||||||||||||||
| var flutterViewController: UIViewController | ||||||||||||||||||||||||||||||||
| var jitsiMeetViewController: JitsiMeetViewController? | ||||||||||||||||||||||||||||||||
| var eventSink: FlutterEventSink? | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| init(flutterViewController: UIViewController) { | ||||||||||||||||||||||||||||||||
| self.flutterViewController = flutterViewController | ||||||||||||||||||||||||||||||||
| private var rootViewController: UIViewController? { | ||||||||||||||||||||||||||||||||
| if #available(iOS 15.0, *) { | ||||||||||||||||||||||||||||||||
| return UIApplication.shared.connectedScenes | ||||||||||||||||||||||||||||||||
| .compactMap { $0 as? UIWindowScene } | ||||||||||||||||||||||||||||||||
| .first { $0.activationState == .foregroundActive }? | ||||||||||||||||||||||||||||||||
| .keyWindow?.rootViewController | ||||||||||||||||||||||||||||||||
| } else if #available(iOS 13.0, *) { | ||||||||||||||||||||||||||||||||
| return UIApplication.shared.connectedScenes | ||||||||||||||||||||||||||||||||
| .compactMap { $0 as? UIWindowScene } | ||||||||||||||||||||||||||||||||
| .flatMap { $0.windows } | ||||||||||||||||||||||||||||||||
| .first { $0.isKeyWindow }?.rootViewController | ||||||||||||||||||||||||||||||||
|
Comment on lines
+16
to
+19
|
||||||||||||||||||||||||||||||||
| return UIApplication.shared.connectedScenes | |
| .compactMap { $0 as? UIWindowScene } | |
| .flatMap { $0.windows } | |
| .first { $0.isKeyWindow }?.rootViewController | |
| let windows = UIApplication.shared.connectedScenes | |
| .compactMap { $0 as? UIWindowScene } | |
| .flatMap { $0.windows } | |
| if let keyWindow = windows.first(where: { $0.isKeyWindow }) { | |
| return keyWindow.rootViewController | |
| } | |
| if let firstWindow = windows.first { | |
| return firstWindow.rootViewController | |
| } |
Copilot
AI
Feb 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Force-unwrapping eventSink with the ! operator can cause a crash if eventSink is nil. This can happen if the join method is called before the event channel listener has been set up via onListen. Consider using optional chaining or a guard statement to check if eventSink is available before initializing JitsiMeetViewController, and return an appropriate error if it's nil.
| jitsiMeetViewController = JitsiMeetViewController.init(options: options, eventSink: eventSink!) | |
| guard let eventSink = eventSink else { | |
| result(FlutterError(code: "NO_EVENT_SINK", message: "Event sink not available. Ensure the event channel listener is attached before joining.", details: nil)) | |
| return | |
| } | |
| jitsiMeetViewController = JitsiMeetViewController.init(options: options, eventSink: eventSink) |
Copilot
AI
Feb 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Force-unwrapping jitsiMeetViewController with ! can cause a crash if the initialization of JitsiMeetViewController fails. While this is unlikely with the current API, it's safer to use optional chaining or guard statement to handle potential nil values. This also applies to line 123.
Copilot
AI
Feb 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The success message includes string interpolation of room which is an optional String. If room is nil, this will print "Successfully joined meeting nil". While this doesn't cause a crash, it creates an unclear message. Consider using nil coalescing operator to provide a more meaningful message, or unwrap room safely before interpolation.
| result("Successfully joined meeting \(room)") | |
| let successMessage: String | |
| if let roomName = room { | |
| successMessage = "Successfully joined meeting \(roomName)" | |
| } else { | |
| successMessage = "Successfully joined meeting" | |
| } | |
| result(successMessage) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The iOS 15.0+ implementation filters for scenes with activationState equal to foregroundActive. This means if the join method is called when the app is in a different state (like foregroundInactive during a transition), rootViewController will be nil and the join will fail with NO_VIEW_CONTROLLER error. Consider checking for foregroundInactive as well, or removing the activation state filter and just taking the first available scene, with a fallback to check other scenes if needed.