From 5aee1690c03d57a8cb282c57a8a50962c8680f85 Mon Sep 17 00:00:00 2001 From: Barba2k2 Date: Fri, 26 Dec 2025 12:25:45 -0300 Subject: [PATCH 1/3] fix: prevent iOS crash when rootViewController is nil during plugin registration --- example/pubspec.lock | 8 ++-- .../SwiftFlutterBarcodeScannerPlugin.swift | 40 +++++++++++-------- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index 6d82933..0ce2a21 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -140,10 +140,10 @@ packages: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.17.0" path: dependency: transitive description: @@ -264,10 +264,10 @@ packages: dependency: transitive description: name: test_api - sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" + sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 url: "https://pub.dev" source: hosted - version: "0.7.6" + version: "0.7.7" vector_math: dependency: transitive description: diff --git a/ios/Classes/SwiftFlutterBarcodeScannerPlugin.swift b/ios/Classes/SwiftFlutterBarcodeScannerPlugin.swift index 4087678..72cb241 100644 --- a/ios/Classes/SwiftFlutterBarcodeScannerPlugin.swift +++ b/ios/Classes/SwiftFlutterBarcodeScannerPlugin.swift @@ -47,8 +47,7 @@ let ONLY_BARCODE = [ AVMetadataObject.ObjectType.upce]; public class SwiftFlutterBarcodeScannerPlugin: NSObject, FlutterPlugin, ScanBarcodeDelegate,FlutterStreamHandler { - - public static var viewController = UIViewController() + public static var lineColor:String="" public static var cancelButtonText:String="" public static var isShowFlashIcon:Bool=false @@ -59,9 +58,18 @@ public class SwiftFlutterBarcodeScannerPlugin: NSObject, FlutterPlugin, ScanBarc public static var delayMillis:Int=0 public static var delayTimer: Timer? + /// Get the root view controller safely + public static var viewController: UIViewController? { + if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, + let rootVC = windowScene.windows.first?.rootViewController { + return rootVC + } + // Fallback for older iOS versions + return UIApplication.shared.delegate?.window??.rootViewController + } + public static func register(with registrar: FlutterPluginRegistrar) { - viewController = (UIApplication.shared.delegate?.window??.rootViewController)! let channel = FlutterMethodChannel(name: "flutter_barcode_scanner", binaryMessenger: registrar.messenger()) let instance = SwiftFlutterBarcodeScannerPlugin() registrar.addMethodCallDelegate(instance, channel: channel) @@ -155,30 +163,29 @@ public class SwiftFlutterBarcodeScannerPlugin: NSObject, FlutterPlugin, ScanBarc controller.modalPresentationStyle = .fullScreen } + guard let viewController = SwiftFlutterBarcodeScannerPlugin.viewController else { + result("-1") + return + } + if checkCameraAvailability(){ if checkForCameraPermission() { - SwiftFlutterBarcodeScannerPlugin.viewController.present(controller - , animated: true) { - - } + viewController.present(controller, animated: true) }else { AVCaptureDevice.requestAccess(for: .video) { success in DispatchQueue.main.async { if success { - SwiftFlutterBarcodeScannerPlugin.viewController.present(controller - , animated: true) { - - } + viewController.present(controller, animated: true) } else { let alert = UIAlertController(title: "Action needed", message: "Please grant camera permission to use barcode scanner", preferredStyle: .alert) - + alert.addAction(UIAlertAction(title: "Grant", style: .default, handler: { action in UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!) })) - + alert.addAction(UIAlertAction(title: "Cancel", style: .cancel)) - - SwiftFlutterBarcodeScannerPlugin.viewController.present(alert, animated: true) + + viewController.present(alert, animated: true) } } }} @@ -193,10 +200,11 @@ public class SwiftFlutterBarcodeScannerPlugin: NSObject, FlutterPlugin, ScanBarc /// Show common alert dialog func showAlertDialog(title:String,message:String){ + guard let viewController = SwiftFlutterBarcodeScannerPlugin.viewController else { return } let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert) let alertAction = UIAlertAction(title: "Ok", style: .default, handler: nil) alertController.addAction(alertAction) - SwiftFlutterBarcodeScannerPlugin.viewController.present(alertController, animated: true, completion: nil) + viewController.present(alertController, animated: true, completion: nil) } } From d198054c349ecff3255fd00f834b74e3a3dba917 Mon Sep 17 00:00:00 2001 From: Barba2k2 Date: Tue, 20 Jan 2026 09:10:28 -0300 Subject: [PATCH 2/3] refactor: improve viewController retrieval and add logging --- .../SwiftFlutterBarcodeScannerPlugin.swift | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/ios/Classes/SwiftFlutterBarcodeScannerPlugin.swift b/ios/Classes/SwiftFlutterBarcodeScannerPlugin.swift index 72cb241..b8f6c1d 100644 --- a/ios/Classes/SwiftFlutterBarcodeScannerPlugin.swift +++ b/ios/Classes/SwiftFlutterBarcodeScannerPlugin.swift @@ -60,11 +60,24 @@ public class SwiftFlutterBarcodeScannerPlugin: NSObject, FlutterPlugin, ScanBarc /// Get the root view controller safely public static var viewController: UIViewController? { - if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, - let rootVC = windowScene.windows.first?.rootViewController { - return rootVC + if #available(iOS 13.0, *) { + // Prefer the foreground active window scene + let windowScene = UIApplication.shared.connectedScenes + .compactMap { $0 as? UIWindowScene } + .first(where: { $0.activationState == .foregroundActive }) + ?? UIApplication.shared.connectedScenes + .compactMap { $0 as? UIWindowScene } + .first + + if let scene = windowScene { + // Prefer the key window, fall back to the first window + let window = scene.windows.first(where: { $0.isKeyWindow }) ?? scene.windows.first + if let rootVC = window?.rootViewController { + return rootVC + } + } } - // Fallback for older iOS versions + // Fallback for older iOS versions or if no suitable scene/window was found return UIApplication.shared.delegate?.window??.rootViewController } @@ -200,7 +213,10 @@ public class SwiftFlutterBarcodeScannerPlugin: NSObject, FlutterPlugin, ScanBarc /// Show common alert dialog func showAlertDialog(title:String,message:String){ - guard let viewController = SwiftFlutterBarcodeScannerPlugin.viewController else { return } + guard let viewController = SwiftFlutterBarcodeScannerPlugin.viewController else { + NSLog("SwiftFlutterBarcodeScannerPlugin: Unable to show alert '\(title)': root view controller is nil.") + return + } let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert) let alertAction = UIAlertAction(title: "Ok", style: .default, handler: nil) alertController.addAction(alertAction) From cdcb2c155c58275612ec82a551bb76619b606306 Mon Sep 17 00:00:00 2001 From: Barba2k2 Date: Tue, 20 Jan 2026 09:14:23 -0300 Subject: [PATCH 3/3] fix: use distinct error code and re-resolve viewController in async callbacks --- ios/Classes/SwiftFlutterBarcodeScannerPlugin.swift | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ios/Classes/SwiftFlutterBarcodeScannerPlugin.swift b/ios/Classes/SwiftFlutterBarcodeScannerPlugin.swift index b8f6c1d..7c9a578 100644 --- a/ios/Classes/SwiftFlutterBarcodeScannerPlugin.swift +++ b/ios/Classes/SwiftFlutterBarcodeScannerPlugin.swift @@ -177,7 +177,7 @@ public class SwiftFlutterBarcodeScannerPlugin: NSObject, FlutterPlugin, ScanBarc } guard let viewController = SwiftFlutterBarcodeScannerPlugin.viewController else { - result("-1") + result("-3") return } @@ -187,8 +187,12 @@ public class SwiftFlutterBarcodeScannerPlugin: NSObject, FlutterPlugin, ScanBarc }else { AVCaptureDevice.requestAccess(for: .video) { success in DispatchQueue.main.async { + guard let currentViewController = SwiftFlutterBarcodeScannerPlugin.viewController else { + return + } + if success { - viewController.present(controller, animated: true) + currentViewController.present(controller, animated: true) } else { let alert = UIAlertController(title: "Action needed", message: "Please grant camera permission to use barcode scanner", preferredStyle: .alert) @@ -198,7 +202,7 @@ public class SwiftFlutterBarcodeScannerPlugin: NSObject, FlutterPlugin, ScanBarc alert.addAction(UIAlertAction(title: "Cancel", style: .cancel)) - viewController.present(alert, animated: true) + currentViewController.present(alert, animated: true) } } }}