import Flutter import UIKit import UserNotifications // Mirrors LockInBroMobile/Services/NotificationService.swift: pure native APNs, // no Firebase. The hex device token is forwarded to Dart over a MethodChannel // (`blindmaster/apns`) which then POSTs it to /register_apns_token. @main @objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate, UNUserNotificationCenterDelegate { private var apnsChannel: FlutterMethodChannel? private var pendingToken: String? override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { UNUserNotificationCenter.current().delegate = self // Trigger APNs registration on every launch so iOS calls // didRegisterForRemoteNotificationsWithDeviceToken with a fresh token. application.registerForRemoteNotifications() return super.application(application, didFinishLaunchingWithOptions: launchOptions) } func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) { GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry) let registrar = engineBridge.pluginRegistry.registrar(forPlugin: "BlindMasterApns") let channel = FlutterMethodChannel( name: "blindmaster/apns", binaryMessenger: registrar.messenger() ) channel.setMethodCallHandler { [weak self] call, result in switch call.method { case "requestPermission": UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in if let error = error { print("[APNs] permission error: \(error)") } DispatchQueue.main.async { UIApplication.shared.registerForRemoteNotifications() } result(granted) } case "getToken": result(self?.pendingToken) default: result(FlutterMethodNotImplemented) } } apnsChannel = channel // If iOS already delivered the token before Dart attached the channel, // flush it now so the upload still happens. if let token = pendingToken { channel.invokeMethod("onToken", arguments: token) } } override func application( _ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data ) { let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined() pendingToken = token apnsChannel?.invokeMethod("onToken", arguments: token) super.application(application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken) } override func application( _ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error ) { print("[APNs] registration failed: \(error)") super.application(application, didFailToRegisterForRemoteNotificationsWithError: error) } // Show banners while the app is in the foreground (matches LockInBroMobile). override func userNotificationCenter( _ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void ) { completionHandler([.banner, .sound, .badge]) } }