import Flutter import Security import UIKit @main @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { GeneratedPluginRegistrant.register(with: self) registerDurableAuthStorageChannel() registerMobileContextChannel() return super.application(application, didFinishLaunchingWithOptions: launchOptions) } private func registerMobileContextChannel() { guard let controller = window?.rootViewController as? FlutterViewController else { return } let channel = FlutterMethodChannel( name: "com.org.yumiparty/mobile_context", binaryMessenger: controller.binaryMessenger ) channel.setMethodCallHandler { call, result in switch call.method { case "getTimeZoneIdentifier": result(TimeZone.current.identifier) default: result(FlutterMethodNotImplemented) } } } private func registerDurableAuthStorageChannel() { guard let controller = window?.rootViewController as? FlutterViewController else { return } let channel = FlutterMethodChannel( name: "com.org.yumiparty/durable_auth_storage", binaryMessenger: controller.binaryMessenger ) channel.setMethodCallHandler { [weak self] call, result in guard let self, let args = call.arguments as? [String: Any], let key = args["key"] as? String else { result(FlutterError(code: "bad_args", message: "Missing key", details: nil)) return } switch call.method { case "write": guard let value = args["value"] as? String else { result(FlutterError(code: "bad_args", message: "Missing value", details: nil)) return } self.writeKeychainValue(value, key: key) result(nil) case "read": result(self.readKeychainValue(key: key)) case "delete": self.deleteKeychainValue(key: key) result(nil) default: result(FlutterMethodNotImplemented) } } } private func keychainQuery(key: String) -> [String: Any] { [ kSecClass as String: kSecClassGenericPassword, kSecAttrService as String: "com.org.yumiparty.durable_auth_storage", kSecAttrAccount as String: key ] } private func writeKeychainValue(_ value: String, key: String) { deleteKeychainValue(key: key) guard let data = value.data(using: .utf8) else { return } var query = keychainQuery(key: key) query[kSecValueData as String] = data query[kSecAttrAccessible as String] = kSecAttrAccessibleAfterFirstUnlock SecItemAdd(query as CFDictionary, nil) } private func readKeychainValue(key: String) -> String? { var query = keychainQuery(key: key) query[kSecReturnData as String] = true query[kSecMatchLimit as String] = kSecMatchLimitOne var result: AnyObject? let status = SecItemCopyMatching(query as CFDictionary, &result) guard status == errSecSuccess, let data = result as? Data else { return nil } return String(data: data, encoding: .utf8) } private func deleteKeychainValue(key: String) { SecItemDelete(keychainQuery(key: key) as CFDictionary) } }