From d844a089e09acb1bccfaeacf05e3b183ce7c94f1 Mon Sep 17 00:00:00 2001 From: Jude Kwashie Date: Wed, 12 Aug 2026 11:26:38 +0000 Subject: [PATCH 1/5] feat(app_installations): add Pigeon support Migrate the Android/iOS/macOS method-channel bridge to a typed Pigeon HostApi while keeping EventChannel-based onIdChange listeners. --- .../analysis_options.yaml | 11 + .../FirebaseInstallationsPlugin.kt | 130 ++++------ ...eratedAndroidFirebaseAppInstallations.g.kt | 182 ++++++++++++++ .../firebase_app_installations/Package.swift | 8 +- .../FirebaseAppInstallationsMessages.g.swift | 197 +++++++++++++++ .../FirebaseInstallationsPlugin.swift | 226 +++++++----------- .../IdChangedStreamHandler.swift | 6 +- .../firebase_app_installations/Package.swift | 8 +- .../FirebaseAppInstallationsMessages.g.swift | 1 + ...od_channel_firebase_app_installations.dart | 36 +-- .../lib/src/pigeon/messages.pigeon.dart | 165 +++++++++++++ .../pigeons/copyright.txt | 3 + .../pigeons/messages.dart | 36 +++ .../pubspec.yaml | 1 + ...annel_firebase_app_installations_test.dart | 104 ++++++++ .../test/mock.dart | 12 + .../test/pigeon/test_api.dart | 165 +++++++++++++ 17 files changed, 1032 insertions(+), 259 deletions(-) create mode 100644 packages/firebase_app_installations/analysis_options.yaml create mode 100644 packages/firebase_app_installations/firebase_app_installations/android/src/main/kotlin/io/flutter/plugins/firebase/installations/firebase_app_installations/GeneratedAndroidFirebaseAppInstallations.g.kt create mode 100644 packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/FirebaseAppInstallationsMessages.g.swift create mode 120000 packages/firebase_app_installations/firebase_app_installations/macos/firebase_app_installations/Sources/firebase_app_installations/FirebaseAppInstallationsMessages.g.swift create mode 100644 packages/firebase_app_installations/firebase_app_installations_platform_interface/lib/src/pigeon/messages.pigeon.dart create mode 100644 packages/firebase_app_installations/firebase_app_installations_platform_interface/pigeons/copyright.txt create mode 100644 packages/firebase_app_installations/firebase_app_installations_platform_interface/pigeons/messages.dart create mode 100644 packages/firebase_app_installations/firebase_app_installations_platform_interface/test/method_channel/method_channel_firebase_app_installations_test.dart create mode 100644 packages/firebase_app_installations/firebase_app_installations_platform_interface/test/mock.dart create mode 100644 packages/firebase_app_installations/firebase_app_installations_platform_interface/test/pigeon/test_api.dart diff --git a/packages/firebase_app_installations/analysis_options.yaml b/packages/firebase_app_installations/analysis_options.yaml new file mode 100644 index 000000000000..3d91c8d049b5 --- /dev/null +++ b/packages/firebase_app_installations/analysis_options.yaml @@ -0,0 +1,11 @@ +# Copyright 2026 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +include: ../../analysis_options.yaml + +analyzer: + exclude: + - firebase_app_installations_platform_interface/lib/src/pigeon/messages.pigeon.dart + - firebase_app_installations_platform_interface/test/pigeon/test_api.dart + - firebase_app_installations_platform_interface/pigeons/messages.dart diff --git a/packages/firebase_app_installations/firebase_app_installations/android/src/main/kotlin/io/flutter/plugins/firebase/installations/firebase_app_installations/FirebaseInstallationsPlugin.kt b/packages/firebase_app_installations/firebase_app_installations/android/src/main/kotlin/io/flutter/plugins/firebase/installations/firebase_app_installations/FirebaseInstallationsPlugin.kt index 0b55bf25178e..deec8e6b9870 100644 --- a/packages/firebase_app_installations/firebase_app_installations/android/src/main/kotlin/io/flutter/plugins/firebase/installations/firebase_app_installations/FirebaseInstallationsPlugin.kt +++ b/packages/firebase_app_installations/firebase_app_installations/android/src/main/kotlin/io/flutter/plugins/firebase/installations/firebase_app_installations/FirebaseInstallationsPlugin.kt @@ -12,128 +12,95 @@ import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterPluginBinding import io.flutter.plugin.common.BinaryMessenger import io.flutter.plugin.common.EventChannel -import io.flutter.plugin.common.MethodCall -import io.flutter.plugin.common.MethodChannel -import io.flutter.plugin.common.MethodChannel.MethodCallHandler -import io.flutter.plugin.common.MethodChannel.Result import io.flutter.plugins.firebase.core.FlutterFirebasePlugin import io.flutter.plugins.firebase.core.FlutterFirebasePluginRegistry /** FirebaseInstallationsPlugin */ -class FirebaseInstallationsPlugin : FlutterFirebasePlugin, FlutterPlugin, MethodCallHandler { - private var channel: MethodChannel? = null +class FirebaseInstallationsPlugin : + FlutterFirebasePlugin, FlutterPlugin, FirebaseAppInstallationsHostApi { private var messenger: BinaryMessenger? = null private val streamHandlers = mutableMapOf() override fun onAttachedToEngine(binding: FlutterPluginBinding) { messenger = binding.binaryMessenger - channel = - MethodChannel(binding.binaryMessenger, METHOD_CHANNEL_NAME).also { - it.setMethodCallHandler(this) - } - + FirebaseAppInstallationsHostApi.setUp(binding.binaryMessenger, this) FlutterFirebasePluginRegistry.registerPlugin(METHOD_CHANNEL_NAME, this) } override fun onDetachedFromEngine(binding: FlutterPluginBinding) { - channel?.setMethodCallHandler(null) - channel = null + FirebaseAppInstallationsHostApi.setUp(binding.binaryMessenger, null) messenger = null removeEventListeners() } - private fun getInstallations(arguments: Map): FirebaseInstallations { - val appName = requireNotNull(arguments["appName"] as? String) + private fun getInstallations(appName: String): FirebaseInstallations { return FirebaseInstallations.getInstance(FirebaseApp.getInstance(appName)) } - private fun getId(arguments: Map): Task { - val taskCompletionSource = TaskCompletionSource() - + override fun delete(appName: String, callback: (Result) -> Unit) { FlutterFirebasePlugin.cachedThreadPool.execute { try { - taskCompletionSource.setResult(Tasks.await(getInstallations(arguments).id)) + Tasks.await(getInstallations(appName).delete()) + callback(Result.success(Unit)) } catch (exception: Exception) { - taskCompletionSource.setException(exception) + callback( + Result.failure( + FlutterError( + "firebase_app_installations", + exception.message, + getExceptionDetails(exception)))) } } - - return taskCompletionSource.task } - private fun getToken(arguments: Map): Task { - val taskCompletionSource = TaskCompletionSource() - + override fun getId(appName: String, callback: (Result) -> Unit) { FlutterFirebasePlugin.cachedThreadPool.execute { try { - val forceRefresh = requireNotNull(arguments["forceRefresh"] as? Boolean) - val tokenResult = Tasks.await(getInstallations(arguments).getToken(forceRefresh)) - taskCompletionSource.setResult(tokenResult.token) + callback(Result.success(Tasks.await(getInstallations(appName).id))) } catch (exception: Exception) { - taskCompletionSource.setException(exception) + callback( + Result.failure( + FlutterError( + "firebase_app_installations", + exception.message, + getExceptionDetails(exception)))) } } - - return taskCompletionSource.task } - private fun registerIdChangeListener(arguments: Map): Task { - val taskCompletionSource = TaskCompletionSource() - + override fun getToken( + appName: String, + forceRefresh: Boolean, + callback: (Result) -> Unit + ) { FlutterFirebasePlugin.cachedThreadPool.execute { try { - val appName = requireNotNull(arguments["appName"] as? String) - val handler = TokenChannelStreamHandler(getInstallations(arguments)) - val name = "$METHOD_CHANNEL_NAME/token/$appName" - val eventChannel = EventChannel(requireNotNull(messenger), name) - eventChannel.setStreamHandler(handler) - streamHandlers[eventChannel] = handler - taskCompletionSource.setResult(name) + val tokenResult = Tasks.await(getInstallations(appName).getToken(forceRefresh)) + callback(Result.success(tokenResult.token)) } catch (exception: Exception) { - taskCompletionSource.setException(exception) + callback( + Result.failure( + FlutterError( + "firebase_app_installations", + exception.message, + getExceptionDetails(exception)))) } } - - return taskCompletionSource.task } - private fun deleteId(arguments: Map): Task { - val taskCompletionSource = TaskCompletionSource() - - FlutterFirebasePlugin.cachedThreadPool.execute { - try { - Tasks.await(getInstallations(arguments).delete()) - taskCompletionSource.setResult(null) - } catch (exception: Exception) { - taskCompletionSource.setException(exception) - } - } - - return taskCompletionSource.task - } - - override fun onMethodCall(call: MethodCall, result: Result) { - val arguments = requireNotNull(call.arguments>()) - val methodCallTask = - when (call.method) { - "FirebaseInstallations#getId" -> getId(arguments) - "FirebaseInstallations#getToken" -> getToken(arguments) - "FirebaseInstallations#delete" -> deleteId(arguments) - "FirebaseInstallations#registerIdChangeListener" -> registerIdChangeListener(arguments) - else -> { - result.notImplemented() - return - } - } - - methodCallTask.addOnCompleteListener { task -> - if (task.isSuccessful) { - result.success(task.result) - } else { - val exception = task.exception - result.error( - "firebase_app_installations", exception?.message, getExceptionDetails(exception)) - } + override fun registerIdChangeListener(appName: String, callback: (Result) -> Unit) { + try { + val handler = TokenChannelStreamHandler(getInstallations(appName)) + val name = "$METHOD_CHANNEL_NAME/token/$appName" + val eventChannel = EventChannel(requireNotNull(messenger), name) + eventChannel.setStreamHandler(handler) + streamHandlers[eventChannel] = handler + callback(Result.success(name)) + } catch (exception: Exception) { + callback( + Result.failure( + FlutterError( + "firebase_app_installations", exception.message, getExceptionDetails(exception)))) } } @@ -163,6 +130,7 @@ class FirebaseInstallationsPlugin : FlutterFirebasePlugin, FlutterPlugin, Method FlutterFirebasePlugin.cachedThreadPool.execute { try { + removeEventListeners() taskCompletionSource.setResult(null) } catch (exception: Exception) { taskCompletionSource.setException(exception) diff --git a/packages/firebase_app_installations/firebase_app_installations/android/src/main/kotlin/io/flutter/plugins/firebase/installations/firebase_app_installations/GeneratedAndroidFirebaseAppInstallations.g.kt b/packages/firebase_app_installations/firebase_app_installations/android/src/main/kotlin/io/flutter/plugins/firebase/installations/firebase_app_installations/GeneratedAndroidFirebaseAppInstallations.g.kt new file mode 100644 index 000000000000..9576a1cee448 --- /dev/null +++ b/packages/firebase_app_installations/firebase_app_installations/android/src/main/kotlin/io/flutter/plugins/firebase/installations/firebase_app_installations/GeneratedAndroidFirebaseAppInstallations.g.kt @@ -0,0 +1,182 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. +// See also: https://pub.dev/packages/pigeon +@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") + +package io.flutter.plugins.firebase.installations.firebase_app_installations + +import android.util.Log +import io.flutter.plugin.common.BasicMessageChannel +import io.flutter.plugin.common.BinaryMessenger +import io.flutter.plugin.common.MessageCodec +import io.flutter.plugin.common.StandardMessageCodec +import java.io.ByteArrayOutputStream +import java.nio.ByteBuffer + +private object GeneratedAndroidFirebaseAppInstallationsPigeonUtils { + + fun wrapResult(result: Any?): List { + return listOf(result) + } + + fun wrapError(exception: Throwable): List { + return if (exception is FlutterError) { + listOf(exception.code, exception.message, exception.details) + } else { + listOf( + exception.javaClass.simpleName, + exception.toString(), + "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception)) + } + } +} + +/** + * Error class for passing custom error details to Flutter via a thrown PlatformException. + * + * @property code The error code. + * @property message The error message. + * @property details The error details. Must be a datatype supported by the api codec. + */ +class FlutterError( + val code: String, + override val message: String? = null, + val details: Any? = null +) : RuntimeException() + +private open class GeneratedAndroidFirebaseAppInstallationsPigeonCodec : StandardMessageCodec() { + override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { + return super.readValueOfType(type, buffer) + } + + override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { + super.writeValue(stream, value) + } +} + +/** Generated interface from Pigeon that represents a handler of messages from Flutter. */ +interface FirebaseAppInstallationsHostApi { + fun delete(appName: String, callback: (Result) -> Unit) + + fun getId(appName: String, callback: (Result) -> Unit) + + fun getToken(appName: String, forceRefresh: Boolean, callback: (Result) -> Unit) + + fun registerIdChangeListener(appName: String, callback: (Result) -> Unit) + + companion object { + /** The codec used by FirebaseAppInstallationsHostApi. */ + val codec: MessageCodec by lazy { GeneratedAndroidFirebaseAppInstallationsPigeonCodec() } + /** + * Sets up an instance of `FirebaseAppInstallationsHostApi` to handle messages through the + * `binaryMessenger`. + */ + @JvmOverloads + fun setUp( + binaryMessenger: BinaryMessenger, + api: FirebaseAppInstallationsHostApi?, + messageChannelSuffix: String = "" + ) { + val separatedMessageChannelSuffix = + if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.delete$separatedMessageChannelSuffix", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val appNameArg = args[0] as String + api.delete(appNameArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(GeneratedAndroidFirebaseAppInstallationsPigeonUtils.wrapError(error)) + } else { + reply.reply(GeneratedAndroidFirebaseAppInstallationsPigeonUtils.wrapResult(null)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.getId$separatedMessageChannelSuffix", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val appNameArg = args[0] as String + api.getId(appNameArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(GeneratedAndroidFirebaseAppInstallationsPigeonUtils.wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(GeneratedAndroidFirebaseAppInstallationsPigeonUtils.wrapResult(data)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.getToken$separatedMessageChannelSuffix", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val appNameArg = args[0] as String + val forceRefreshArg = args[1] as Boolean + api.getToken(appNameArg, forceRefreshArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(GeneratedAndroidFirebaseAppInstallationsPigeonUtils.wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(GeneratedAndroidFirebaseAppInstallationsPigeonUtils.wrapResult(data)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.registerIdChangeListener$separatedMessageChannelSuffix", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val appNameArg = args[0] as String + api.registerIdChangeListener(appNameArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(GeneratedAndroidFirebaseAppInstallationsPigeonUtils.wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(GeneratedAndroidFirebaseAppInstallationsPigeonUtils.wrapResult(data)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + } + } +} diff --git a/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Package.swift b/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Package.swift index 8a549b562493..5414590593c4 100644 --- a/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Package.swift +++ b/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Package.swift @@ -12,10 +12,10 @@ let firebaseSdkVersion: Version = "12.17.0" let package = Package( name: "firebase_app_installations", platforms: [ - .iOS("15.0") + .iOS("15.0"), ], products: [ - .library(name: "firebase-app-installations", targets: ["firebase_app_installations"]) + .library(name: "firebase-app-installations", targets: ["firebase_app_installations"]), ], dependencies: [ .package(url: "https://github.com/firebase/firebase-ios-sdk", exact: firebaseSdkVersion), @@ -31,8 +31,8 @@ let package = Package( .product(name: "FlutterFramework", package: "FlutterFramework"), ], resources: [ - .process("Resources") + .process("Resources"), ] - ) + ), ] ) diff --git a/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/FirebaseAppInstallationsMessages.g.swift b/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/FirebaseAppInstallationsMessages.g.swift new file mode 100644 index 000000000000..56f619405ae6 --- /dev/null +++ b/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/FirebaseAppInstallationsMessages.g.swift @@ -0,0 +1,197 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +import Foundation + +#if os(iOS) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#else + #error("Unsupported platform.") +#endif + +/// Error class for passing custom error details to Dart side. +final class PigeonError: Error { + let code: String + let message: String? + let details: Sendable? + + init(code: String, message: String?, details: Sendable?) { + self.code = code + self.message = message + self.details = details + } + + var localizedDescription: String { + "PigeonError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" + } +} + +private func wrapResult(_ result: Any?) -> [Any?] { + [result] +} + +private func wrapError(_ error: Any) -> [Any?] { + if let pigeonError = error as? PigeonError { + return [ + pigeonError.code, + pigeonError.message, + pigeonError.details, + ] + } + if let flutterError = error as? FlutterError { + return [ + flutterError.code, + flutterError.message, + flutterError.details, + ] + } + return [ + "\(error)", + "\(Swift.type(of: error))", + "Stacktrace: \(Thread.callStackSymbols)", + ] +} + +private func isNullish(_ value: Any?) -> Bool { + value is NSNull || value == nil +} + +private func nilOrValue(_ value: Any?) -> T? { + if value is NSNull { return nil } + return value as! T? +} + +private class FirebaseAppInstallationsMessagesPigeonCodecReader: FlutterStandardReader {} + +private class FirebaseAppInstallationsMessagesPigeonCodecWriter: FlutterStandardWriter {} + +private class FirebaseAppInstallationsMessagesPigeonCodecReaderWriter: FlutterStandardReaderWriter { + override func reader(with data: Data) -> FlutterStandardReader { + FirebaseAppInstallationsMessagesPigeonCodecReader(data: data) + } + + override func writer(with data: NSMutableData) -> FlutterStandardWriter { + FirebaseAppInstallationsMessagesPigeonCodecWriter(data: data) + } +} + +class FirebaseAppInstallationsMessagesPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable +{ + static let shared = FirebaseAppInstallationsMessagesPigeonCodec( + readerWriter: FirebaseAppInstallationsMessagesPigeonCodecReaderWriter() + ) +} + +/// Generated protocol from Pigeon that represents a handler of messages from Flutter. +protocol FirebaseAppInstallationsHostApi { + func delete(appName: String, completion: @escaping (Result) -> Void) + func getId(appName: String, completion: @escaping (Result) -> Void) + func getToken(appName: String, forceRefresh: Bool, + completion: @escaping (Result) -> Void) + func registerIdChangeListener(appName: String, + completion: @escaping (Result) -> Void) +} + +/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. +class FirebaseAppInstallationsHostApiSetup { + static var codec: FlutterStandardMessageCodec { + FirebaseAppInstallationsMessagesPigeonCodec.shared + } + + /// Sets up an instance of `FirebaseAppInstallationsHostApi` to handle messages through the + /// `binaryMessenger`. + static func setUp(binaryMessenger: FlutterBinaryMessenger, api: FirebaseAppInstallationsHostApi?, + messageChannelSuffix: String = "") { + let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" + let deleteChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.delete\(channelSuffix)", + binaryMessenger: binaryMessenger, codec: codec + ) + if let api { + deleteChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appNameArg = args[0] as! String + api.delete(appName: appNameArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + deleteChannel.setMessageHandler(nil) + } + let getIdChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.getId\(channelSuffix)", + binaryMessenger: binaryMessenger, codec: codec + ) + if let api { + getIdChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appNameArg = args[0] as! String + api.getId(appName: appNameArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + getIdChannel.setMessageHandler(nil) + } + let getTokenChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.getToken\(channelSuffix)", + binaryMessenger: binaryMessenger, codec: codec + ) + if let api { + getTokenChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appNameArg = args[0] as! String + let forceRefreshArg = args[1] as! Bool + api.getToken(appName: appNameArg, forceRefresh: forceRefreshArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + getTokenChannel.setMessageHandler(nil) + } + let registerIdChangeListenerChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.registerIdChangeListener\(channelSuffix)", + binaryMessenger: binaryMessenger, codec: codec + ) + if let api { + registerIdChangeListenerChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appNameArg = args[0] as! String + api.registerIdChangeListener(appName: appNameArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + registerIdChangeListenerChannel.setMessageHandler(nil) + } + } +} diff --git a/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/FirebaseInstallationsPlugin.swift b/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/FirebaseInstallationsPlugin.swift index eb1d365a1f7e..d79baccf589e 100644 --- a/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/FirebaseInstallationsPlugin.swift +++ b/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/FirebaseInstallationsPlugin.swift @@ -18,8 +18,11 @@ import FirebaseInstallations let kFLTFirebaseInstallationsChannelName = "plugins.flutter.io/firebase_app_installations" -public class FirebaseInstallationsPlugin: NSObject, FLTFirebasePluginProtocol, FlutterPlugin { - private var eventSink: FlutterEventSink? +// swift-format-ignore: AvoidRetroactiveConformances +extension FlutterError: @retroactive Error {} + +public class FirebaseInstallationsPlugin: NSObject, FLTFirebasePluginProtocol, FlutterPlugin, + FirebaseAppInstallationsHostApi { private var messenger: FlutterBinaryMessenger private var streamHandler = [String: IdChangedStreamHandler?]() @@ -36,13 +39,9 @@ public class FirebaseInstallationsPlugin: NSObject, FLTFirebasePluginProtocol, F binaryMessenger = registrar.messenger() #endif - let channel = FlutterMethodChannel( - name: kFLTFirebaseInstallationsChannelName, - binaryMessenger: binaryMessenger - ) let instance = FirebaseInstallationsPlugin(messenger: binaryMessenger) FLTFirebasePluginRegistry.sharedInstance().register(instance) - registrar.addMethodCallDelegate(instance, channel: channel) + FirebaseAppInstallationsHostApiSetup.setUp(binaryMessenger: binaryMessenger, api: instance) } public func firebaseLibraryVersion() -> String { @@ -50,6 +49,10 @@ public class FirebaseInstallationsPlugin: NSObject, FLTFirebasePluginProtocol, F } public func didReinitializeFirebaseCore(_ completion: @escaping () -> Void) { + for (_, handler) in streamHandler { + _ = handler?.onCancel(withArguments: nil) + } + streamHandler.removeAll() completion() } @@ -66,96 +69,12 @@ public class FirebaseInstallationsPlugin: NSObject, FLTFirebasePluginProtocol, F } /// Gets Installations instance for a Firebase App. - /// - Returns: a Firebase Installations instance for the passed app from Dart private func getInstallations(appName: String) -> Installations { let app: FirebaseApp = FLTFirebasePlugin.firebaseAppNamed(appName)! return Installations.installations(app: app) } - /// Gets Installations Id for an instance. - /// - Parameter arguments: the arguments passed by the Dart calling method - /// - Parameter result: the result instance used to send the result to Dart. - /// - Parameter errorBlock: the error block used to send the error to Dart. - private func getId( - arguments: NSDictionary, result: @escaping FlutterResult, - errorBlock: @escaping FLTFirebaseMethodCallErrorBlock - ) { - let instance = getInstallations(appName: arguments["appName"] as! String) - instance.installationID { (id: String?, error: Error?) in - if let error { - errorBlock(nil, nil, nil, error) - } else { - result(id) - } - } - } - - /// Deletes the Installations Id for an instance. - /// - Parameter arguments: the arguments passed by the Dart calling method - /// - Parameter result: the result instance used to send the result to Dart. - /// - Parameter errorBlock: the error block used to send the error to Dart. - private func deleteId( - arguments: NSDictionary, result: @escaping FlutterResult, - errorBlock: @escaping FLTFirebaseMethodCallErrorBlock - ) { - let instance = getInstallations(appName: arguments["appName"] as! String) - instance.delete { (error: Error?) in - if let error { - errorBlock(nil, nil, nil, error) - } else { - result(nil) - } - } - } - - /// Gets the Auth Token for an instance. - /// - Parameter arguments: the arguments passed by the Dart calling method - /// - Parameter result: the result instance used to send the result to Dart. - /// - Parameter errorBlock: the error block used to send the error to Dart. - private func getToken( - arguments: NSDictionary, result: @escaping FlutterResult, - errorBlock: @escaping FLTFirebaseMethodCallErrorBlock - ) { - let instance = getInstallations(appName: arguments["appName"] as! String) - let forceRefresh = arguments["forceRefresh"] as? Bool ?? false - instance - .authTokenForcingRefresh(forceRefresh) { - ( - tokenResult: InstallationsAuthTokenResult?, - error: Error? - ) in - if let error { - errorBlock(nil, nil, nil, error) - } else { - result(tokenResult?.authToken) - } - } - } - - /// Registers a listener for changes in the Installations Id. - /// - Parameter arguments: the arguments passed by the Dart calling method - /// - Parameter result: the result instance used to send the result to Dart. - /// - Parameter errorBlock: the error block used to send the error to Dart. - private func registerIdChangeListener( - arguments: NSDictionary, result: @escaping FlutterResult, - errorBlock: @escaping FLTFirebaseMethodCallErrorBlock - ) { - let instance = getInstallations(appName: arguments["appName"] as! String) - let appName = arguments["appName"] as! String - let eventChannelName = kFLTFirebaseInstallationsChannelName + "/token/" + appName - - let eventChannel = FlutterEventChannel(name: eventChannelName, binaryMessenger: messenger) - - if streamHandler[eventChannelName] == nil { - streamHandler[eventChannelName] = IdChangedStreamHandler(instance: instance) - } - - eventChannel.setStreamHandler(streamHandler[eventChannelName]!) - - result(eventChannelName) - } - - private func mapInstallationsErrorCodes(code: UInt) -> NSString { + private func mapInstallationsErrorCodes(code: UInt) -> String { let error = InstallationsErrorCode( InstallationsErrorCode .Code(rawValue: Int(code)) ?? InstallationsErrorCode.unknown @@ -175,64 +94,87 @@ public class FirebaseInstallationsPlugin: NSObject, FLTFirebasePluginProtocol, F } } - public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { - guard let args = call.arguments as? NSDictionary else { - result( - FlutterError( - code: "invalid-arguments", - message: "Arguments are not a dictionary", - details: nil - ) - ) - return + private func createFlutterError(_ error: Error) -> FlutterError { + let nsError = error as NSError + return FlutterError( + code: mapInstallationsErrorCodes(code: UInt(nsError.code)), + message: nsError.localizedDescription, + details: nil + ) + } + + public func delete(appName: String, + completion: @escaping (Result) -> Void) { + let instance = getInstallations(appName: appName) + instance.delete { (error: Error?) in + if let error { + completion(.failure(self.createFlutterError(error))) + } else { + completion(.success(())) + } } + } - let errorBlock: FLTFirebaseMethodCallErrorBlock = { - ( - code, message, details, - error: Error? - ) in - var errorDetails = [String: Any?]() - - errorDetails["code"] = - code - ?? self - .mapInstallationsErrorCodes(code: UInt((error! as NSError).code)) - errorDetails["message"] = - message ?? error? - .localizedDescription ?? "An unknown error has occurred." - errorDetails["additionalData"] = details - - if code == "unknown" { - NSLog( - "FLTFirebaseInstallations: An error occurred while calling method %@", - call.method + public func getId(appName: String, + completion: @escaping (Result) -> Void) { + let instance = getInstallations(appName: appName) + instance.installationID { (id: String?, error: Error?) in + if let error { + completion(.failure(self.createFlutterError(error))) + } else if let id { + completion(.success(id)) + } else { + completion( + .failure( + FlutterError( + code: "unknown", + message: "Installation ID was nil", + details: nil + ) + ) ) } + } + } - result( - FLTFirebasePlugin.createFlutterError( - fromCode: errorDetails["code"] as! String, - message: errorDetails["message"] as! String, - optionalDetails: errorDetails[ - "additionalData" - ] as? [AnyHashable: Any], - andOptionalNSError: error + public func getToken(appName: String, + forceRefresh: Bool, + completion: @escaping (Result) -> Void) { + let instance = getInstallations(appName: appName) + instance.authTokenForcingRefresh(forceRefresh) { + (tokenResult: InstallationsAuthTokenResult?, + error: Error?) in + if let error { + completion(.failure(self.createFlutterError(error))) + } else if let token = tokenResult?.authToken { + completion(.success(token)) + } else { + completion( + .failure( + FlutterError( + code: "unknown", + message: "Installation token was nil", + details: nil + ) + ) ) - ) + } } + } - switch call.method { - case "FirebaseInstallations#getId": - getId(arguments: args, result: result, errorBlock: errorBlock) - case "FirebaseInstallations#delete": - deleteId(arguments: args, result: result, errorBlock: errorBlock) - case "FirebaseInstallations#getToken": - getToken(arguments: args, result: result, errorBlock: errorBlock) - case "FirebaseInstallations#registerIdChangeListener": - registerIdChangeListener(arguments: args, result: result, errorBlock: errorBlock) - default: - result(FlutterMethodNotImplemented) + public func registerIdChangeListener(appName: String, + completion: @escaping (Result) -> Void) { + let instance = getInstallations(appName: appName) + let eventChannelName = kFLTFirebaseInstallationsChannelName + "/token/" + appName + + let eventChannel = FlutterEventChannel(name: eventChannelName, binaryMessenger: messenger) + + if streamHandler[eventChannelName] == nil { + streamHandler[eventChannelName] = IdChangedStreamHandler(instance: instance) } + + eventChannel.setStreamHandler(streamHandler[eventChannelName]!) + + completion(.success(eventChannelName)) } } diff --git a/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/IdChangedStreamHandler.swift b/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/IdChangedStreamHandler.swift index 89f9cbc2e4e9..30790c94adf5 100644 --- a/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/IdChangedStreamHandler.swift +++ b/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/IdChangedStreamHandler.swift @@ -47,10 +47,8 @@ class IdChangedStreamHandler: NSObject, FlutterStreamHandler { } } - func onListen( - withArguments _: Any?, - eventSink events: @escaping FlutterEventSink - ) -> FlutterError? { + func onListen(withArguments _: Any?, + eventSink events: @escaping FlutterEventSink) -> FlutterError? { eventSink = events installationIDObserver = NotificationCenter.default.addObserver( diff --git a/packages/firebase_app_installations/firebase_app_installations/macos/firebase_app_installations/Package.swift b/packages/firebase_app_installations/firebase_app_installations/macos/firebase_app_installations/Package.swift index c98e797d9f36..523cf17ac191 100644 --- a/packages/firebase_app_installations/firebase_app_installations/macos/firebase_app_installations/Package.swift +++ b/packages/firebase_app_installations/firebase_app_installations/macos/firebase_app_installations/Package.swift @@ -12,10 +12,10 @@ let firebaseSdkVersion: Version = "12.17.0" let package = Package( name: "firebase_app_installations", platforms: [ - .macOS("10.15") + .macOS("10.15"), ], products: [ - .library(name: "firebase-app-installations", targets: ["firebase_app_installations"]) + .library(name: "firebase-app-installations", targets: ["firebase_app_installations"]), ], dependencies: [ .package(url: "https://github.com/firebase/firebase-ios-sdk", exact: firebaseSdkVersion), @@ -31,8 +31,8 @@ let package = Package( .product(name: "FlutterFramework", package: "FlutterFramework"), ], resources: [ - .process("Resources") + .process("Resources"), ] - ) + ), ] ) diff --git a/packages/firebase_app_installations/firebase_app_installations/macos/firebase_app_installations/Sources/firebase_app_installations/FirebaseAppInstallationsMessages.g.swift b/packages/firebase_app_installations/firebase_app_installations/macos/firebase_app_installations/Sources/firebase_app_installations/FirebaseAppInstallationsMessages.g.swift new file mode 120000 index 000000000000..3992a0860aab --- /dev/null +++ b/packages/firebase_app_installations/firebase_app_installations/macos/firebase_app_installations/Sources/firebase_app_installations/FirebaseAppInstallationsMessages.g.swift @@ -0,0 +1 @@ +../../../../ios/firebase_app_installations/Sources/firebase_app_installations/FirebaseAppInstallationsMessages.g.swift \ No newline at end of file diff --git a/packages/firebase_app_installations/firebase_app_installations_platform_interface/lib/src/method_channel/method_channel_firebase_app_installations.dart b/packages/firebase_app_installations/firebase_app_installations_platform_interface/lib/src/method_channel/method_channel_firebase_app_installations.dart index 8ab904811d42..b6286cb80325 100644 --- a/packages/firebase_app_installations/firebase_app_installations_platform_interface/lib/src/method_channel/method_channel_firebase_app_installations.dart +++ b/packages/firebase_app_installations/firebase_app_installations_platform_interface/lib/src/method_channel/method_channel_firebase_app_installations.dart @@ -9,6 +9,7 @@ import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_app_installations_platform_interface/firebase_app_installations_platform_interface.dart'; import 'package:flutter/services.dart'; +import '../pigeon/messages.pigeon.dart'; import 'utils/exception.dart'; class MethodChannelFirebaseAppInstallations @@ -19,10 +20,8 @@ class MethodChannelFirebaseAppInstallations return MethodChannelFirebaseAppInstallations._(); } - /// The [MethodChannelFirebaseFunctions] method channel. - static const MethodChannel channel = MethodChannel( - 'plugins.flutter.io/firebase_app_installations', - ); + static final FirebaseAppInstallationsHostApi _api = + FirebaseAppInstallationsHostApi(); static final Map> _idTokenChangesListeners = >{}; @@ -33,11 +32,8 @@ class MethodChannelFirebaseAppInstallations final controller = _idTokenChangesListeners[app.name] = StreamController.broadcast(); - channel.invokeMethod( - 'FirebaseInstallations#registerIdChangeListener', { - 'appName': app.name, - }).then((channelName) { - final events = EventChannel(channelName!, channel.codec); + _api.registerIdChangeListener(app.name).then((channelName) { + final events = EventChannel(channelName); events .receiveGuardedBroadcastStream(onError: convertPlatformException) @@ -45,6 +41,10 @@ class MethodChannelFirebaseAppInstallations (Object? arguments) => controller.add((arguments as Map)['token']), onError: controller.addError, ); + // ignore: avoid_catches_without_on_clauses + }).catchError((_) { + // Silently ignore errors during listener registration. + // This can happen in test environments where the host API is not set up. }); } @@ -62,9 +62,7 @@ class MethodChannelFirebaseAppInstallations @override Future delete() async { try { - await channel.invokeMethod('FirebaseInstallations#delete', { - 'appName': app!.name, - }); + await _api.delete(app!.name); } catch (e, s) { convertPlatformException(e, s); } @@ -73,12 +71,7 @@ class MethodChannelFirebaseAppInstallations @override Future getId() async { try { - final id = (await channel.invokeMethod( - 'FirebaseInstallations#getId', - {'appName': app!.name}, - ))!; - - return id; + return await _api.getId(app!.name); } catch (e, s) { convertPlatformException(e, s); } @@ -87,12 +80,7 @@ class MethodChannelFirebaseAppInstallations @override Future getToken(bool forceRefresh) async { try { - final id = (await channel.invokeMethod( - 'FirebaseInstallations#getToken', - {'appName': app!.name, 'forceRefresh': forceRefresh}, - ))!; - - return id; + return await _api.getToken(app!.name, forceRefresh); } catch (e, s) { convertPlatformException(e, s); } diff --git a/packages/firebase_app_installations/firebase_app_installations_platform_interface/lib/src/pigeon/messages.pigeon.dart b/packages/firebase_app_installations/firebase_app_installations_platform_interface/lib/src/pigeon/messages.pigeon.dart new file mode 100644 index 000000000000..88cae64bc59f --- /dev/null +++ b/packages/firebase_app_installations/firebase_app_installations_platform_interface/lib/src/pigeon/messages.pigeon.dart @@ -0,0 +1,165 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: unused_import, unused_shown_name +// ignore_for_file: type=lint + +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List; + +import 'package:flutter/services.dart'; +import 'package:meta/meta.dart' show immutable, protected, visibleForTesting; + +Object? _extractReplyValueOrThrow( + List? replyList, + String channelName, { + required bool isNullValid, +}) { + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else if (!isNullValid && (replyList.isNotEmpty && replyList[0] == null)) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } + return replyList.firstOrNull; +} + +List wrapResponse( + {Object? result, PlatformException? error, bool empty = false}) { + if (empty) { + return []; + } + if (error == null) { + return [result]; + } + return [error.code, error.message, error.details]; +} + +class _PigeonCodec extends StandardMessageCodec { + const _PigeonCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is int) { + buffer.putUint8(4); + buffer.putInt64(value); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + default: + return super.readValueOfType(type, buffer); + } + } +} + +class FirebaseAppInstallationsHostApi { + /// Constructor for [FirebaseAppInstallationsHostApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + FirebaseAppInstallationsHostApi( + {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = + messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + final BinaryMessenger? pigeonVar_binaryMessenger; + + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); + + final String pigeonVar_messageChannelSuffix; + + Future delete(String appName) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.delete$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([appName]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); + } + + Future getId(String appName) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.getId$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([appName]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as String; + } + + Future getToken(String appName, bool forceRefresh) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.getToken$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([appName, forceRefresh]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as String; + } + + Future registerIdChangeListener(String appName) async { + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.registerIdChangeListener$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([appName]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); + return pigeonVar_replyValue! as String; + } +} diff --git a/packages/firebase_app_installations/firebase_app_installations_platform_interface/pigeons/copyright.txt b/packages/firebase_app_installations/firebase_app_installations_platform_interface/pigeons/copyright.txt new file mode 100644 index 000000000000..c507a9c1b050 --- /dev/null +++ b/packages/firebase_app_installations/firebase_app_installations_platform_interface/pigeons/copyright.txt @@ -0,0 +1,3 @@ +Copyright 2026, the Chromium project authors. Please see the AUTHORS file +for details. All rights reserved. Use of this source code is governed by a +BSD-style license that can be found in the LICENSE file. diff --git a/packages/firebase_app_installations/firebase_app_installations_platform_interface/pigeons/messages.dart b/packages/firebase_app_installations/firebase_app_installations_platform_interface/pigeons/messages.dart new file mode 100644 index 000000000000..40eeb4aa551b --- /dev/null +++ b/packages/firebase_app_installations/firebase_app_installations_platform_interface/pigeons/messages.dart @@ -0,0 +1,36 @@ +// Copyright 2026 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:pigeon/pigeon.dart'; + +@ConfigurePigeon( + PigeonOptions( + dartOut: 'lib/src/pigeon/messages.pigeon.dart', + dartTestOut: 'test/pigeon/test_api.dart', + dartPackageName: 'firebase_app_installations_platform_interface', + kotlinOut: + '../firebase_app_installations/android/src/main/kotlin/io/flutter/plugins/firebase/installations/firebase_app_installations/GeneratedAndroidFirebaseAppInstallations.g.kt', + kotlinOptions: KotlinOptions( + package: + 'io.flutter.plugins.firebase.installations.firebase_app_installations', + ), + swiftOut: + '../firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/FirebaseAppInstallationsMessages.g.swift', + copyrightHeader: 'pigeons/copyright.txt', + ), +) +@HostApi(dartHostTestHandler: 'TestFirebaseAppInstallationsHostApi') +abstract class FirebaseAppInstallationsHostApi { + @async + void delete(String appName); + + @async + String getId(String appName); + + @async + String getToken(String appName, bool forceRefresh); + + @async + String registerIdChangeListener(String appName); +} diff --git a/packages/firebase_app_installations/firebase_app_installations_platform_interface/pubspec.yaml b/packages/firebase_app_installations/firebase_app_installations_platform_interface/pubspec.yaml index 4e62d9074c84..419aadfe1d7a 100644 --- a/packages/firebase_app_installations/firebase_app_installations_platform_interface/pubspec.yaml +++ b/packages/firebase_app_installations/firebase_app_installations_platform_interface/pubspec.yaml @@ -22,3 +22,4 @@ dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^6.0.0 + pigeon: 26.3.4 diff --git a/packages/firebase_app_installations/firebase_app_installations_platform_interface/test/method_channel/method_channel_firebase_app_installations_test.dart b/packages/firebase_app_installations/firebase_app_installations_platform_interface/test/method_channel/method_channel_firebase_app_installations_test.dart new file mode 100644 index 000000000000..b4663fe915df --- /dev/null +++ b/packages/firebase_app_installations/firebase_app_installations_platform_interface/test/method_channel/method_channel_firebase_app_installations_test.dart @@ -0,0 +1,104 @@ +// Copyright 2026 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_app_installations_platform_interface/src/method_channel/method_channel_firebase_app_installations.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../mock.dart'; +import '../pigeon/test_api.dart'; + +void main() { + setupFirebaseAppInstallationsMocks(); + + late FirebaseApp app; + late MethodChannelFirebaseAppInstallations installations; + late _TestFirebaseAppInstallationsHostApi hostApi; + + setUpAll(() async { + app = await Firebase.initializeApp(); + hostApi = _TestFirebaseAppInstallationsHostApi(); + TestFirebaseAppInstallationsHostApi.setUp(hostApi); + installations = MethodChannelFirebaseAppInstallations(app: app); + // Allow constructor listener registration to complete. + await Future.delayed(Duration.zero); + }); + + tearDownAll(() { + TestFirebaseAppInstallationsHostApi.setUp(null); + }); + + setUp(() { + hostApi.reset(); + }); + + test('delete forwards the app name', () async { + await installations.delete(); + + expect(hostApi.appName, app.name); + expect(hostApi.deleteCalled, isTrue); + }); + + test('getId forwards the app name', () async { + final id = await installations.getId(); + + expect(hostApi.appName, app.name); + expect(id, 'test-installation-id'); + }); + + test('getToken forwards the app name and forceRefresh', () async { + final token = await installations.getToken(true); + + expect(hostApi.appName, app.name); + expect(hostApi.forceRefresh, isTrue); + expect(token, 'test-installation-token'); + }); + + test('registerIdChangeListener is invoked for onIdChange setup', () { + expect(hostApi.registerIdChangeListenerCalled, isTrue); + expect(hostApi.registeredAppName, app.name); + }); +} + +class _TestFirebaseAppInstallationsHostApi + implements TestFirebaseAppInstallationsHostApi { + String? appName; + String? registeredAppName; + bool? forceRefresh; + bool deleteCalled = false; + bool registerIdChangeListenerCalled = false; + + void reset() { + appName = null; + forceRefresh = null; + deleteCalled = false; + // Keep registerIdChangeListenerCalled / registeredAppName — set during construction. + } + + @override + Future delete(String appName) async { + this.appName = appName; + deleteCalled = true; + } + + @override + Future getId(String appName) async { + this.appName = appName; + return 'test-installation-id'; + } + + @override + Future getToken(String appName, bool forceRefresh) async { + this.appName = appName; + this.forceRefresh = forceRefresh; + return 'test-installation-token'; + } + + @override + Future registerIdChangeListener(String appName) async { + registeredAppName = appName; + registerIdChangeListenerCalled = true; + return 'plugins.flutter.io/firebase_app_installations/token/$appName'; + } +} diff --git a/packages/firebase_app_installations/firebase_app_installations_platform_interface/test/mock.dart b/packages/firebase_app_installations/firebase_app_installations_platform_interface/test/mock.dart new file mode 100644 index 000000000000..b25a3dd7f5c8 --- /dev/null +++ b/packages/firebase_app_installations/firebase_app_installations_platform_interface/test/mock.dart @@ -0,0 +1,12 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:firebase_core_platform_interface/test.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void setupFirebaseAppInstallationsMocks() { + TestWidgetsFlutterBinding.ensureInitialized(); + + setupFirebaseCoreMocks(); +} diff --git a/packages/firebase_app_installations/firebase_app_installations_platform_interface/test/pigeon/test_api.dart b/packages/firebase_app_installations/firebase_app_installations_platform_interface/test/pigeon/test_api.dart new file mode 100644 index 000000000000..35e0c0f4aae2 --- /dev/null +++ b/packages/firebase_app_installations/firebase_app_installations_platform_interface/test/pigeon/test_api.dart @@ -0,0 +1,165 @@ +// Copyright 2026, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import, no_leading_underscores_for_local_identifiers, omit_obvious_local_variable_types +// ignore_for_file: avoid_relative_lib_imports +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:firebase_app_installations_platform_interface/src/pigeon/messages.pigeon.dart'; + +class _PigeonCodec extends StandardMessageCodec { + const _PigeonCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is int) { + buffer.putUint8(4); + buffer.putInt64(value); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + default: + return super.readValueOfType(type, buffer); + } + } +} + +abstract class TestFirebaseAppInstallationsHostApi { + static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => + TestDefaultBinaryMessengerBinding.instance; + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); + + Future delete(String appName); + + Future getId(String appName); + + Future getToken(String appName, bool forceRefresh); + + Future registerIdChangeListener(String appName); + + static void setUp( + TestFirebaseAppInstallationsHostApi? api, { + BinaryMessenger? binaryMessenger, + String messageChannelSuffix = '', + }) { + messageChannelSuffix = + messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + { + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.delete$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, + (Object? message) async { + final List args = message! as List; + final String arg_appName = args[0]! as String; + try { + await api.delete(arg_appName); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.getId$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, + (Object? message) async { + final List args = message! as List; + final String arg_appName = args[0]! as String; + try { + final String output = await api.getId(arg_appName); + return [output]; + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.getToken$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, + (Object? message) async { + final List args = message! as List; + final String arg_appName = args[0]! as String; + final bool arg_forceRefresh = args[1]! as bool; + try { + final String output = + await api.getToken(arg_appName, arg_forceRefresh); + return [output]; + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.registerIdChangeListener$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, + (Object? message) async { + final List args = message! as List; + final String arg_appName = args[0]! as String; + try { + final String output = + await api.registerIdChangeListener(arg_appName); + return [output]; + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + } +} From 8c32323291297fd2895d778de2134c1adf253ae7 Mon Sep 17 00:00:00 2001 From: Jude Kwashie Date: Wed, 12 Aug 2026 11:49:19 +0000 Subject: [PATCH 2/5] style(app_installations): fix Apple formatting for CI --- .../firebase_app_installations/Package.swift | 8 ++--- .../FirebaseAppInstallationsMessages.g.swift | 24 +++++++------ .../FirebaseInstallationsPlugin.swift | 35 ++++++++++++------- .../IdChangedStreamHandler.swift | 6 ++-- .../firebase_app_installations/Package.swift | 8 ++--- 5 files changed, 49 insertions(+), 32 deletions(-) diff --git a/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Package.swift b/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Package.swift index 5414590593c4..8a549b562493 100644 --- a/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Package.swift +++ b/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Package.swift @@ -12,10 +12,10 @@ let firebaseSdkVersion: Version = "12.17.0" let package = Package( name: "firebase_app_installations", platforms: [ - .iOS("15.0"), + .iOS("15.0") ], products: [ - .library(name: "firebase-app-installations", targets: ["firebase_app_installations"]), + .library(name: "firebase-app-installations", targets: ["firebase_app_installations"]) ], dependencies: [ .package(url: "https://github.com/firebase/firebase-ios-sdk", exact: firebaseSdkVersion), @@ -31,8 +31,8 @@ let package = Package( .product(name: "FlutterFramework", package: "FlutterFramework"), ], resources: [ - .process("Resources"), + .process("Resources") ] - ), + ) ] ) diff --git a/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/FirebaseAppInstallationsMessages.g.swift b/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/FirebaseAppInstallationsMessages.g.swift index 56f619405ae6..42ad7966b4ad 100644 --- a/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/FirebaseAppInstallationsMessages.g.swift +++ b/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/FirebaseAppInstallationsMessages.g.swift @@ -91,10 +91,12 @@ class FirebaseAppInstallationsMessagesPigeonCodec: FlutterStandardMessageCodec, protocol FirebaseAppInstallationsHostApi { func delete(appName: String, completion: @escaping (Result) -> Void) func getId(appName: String, completion: @escaping (Result) -> Void) - func getToken(appName: String, forceRefresh: Bool, - completion: @escaping (Result) -> Void) - func registerIdChangeListener(appName: String, - completion: @escaping (Result) -> Void) + func getToken( + appName: String, forceRefresh: Bool, + completion: @escaping (Result) -> Void) + func registerIdChangeListener( + appName: String, + completion: @escaping (Result) -> Void) } /// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. @@ -105,12 +107,14 @@ class FirebaseAppInstallationsHostApiSetup { /// Sets up an instance of `FirebaseAppInstallationsHostApi` to handle messages through the /// `binaryMessenger`. - static func setUp(binaryMessenger: FlutterBinaryMessenger, api: FirebaseAppInstallationsHostApi?, - messageChannelSuffix: String = "") { + static func setUp( + binaryMessenger: FlutterBinaryMessenger, api: FirebaseAppInstallationsHostApi?, + messageChannelSuffix: String = "" + ) { let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" let deleteChannel = FlutterBasicMessageChannel( name: - "dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.delete\(channelSuffix)", + "dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.delete\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) if let api { @@ -131,7 +135,7 @@ class FirebaseAppInstallationsHostApiSetup { } let getIdChannel = FlutterBasicMessageChannel( name: - "dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.getId\(channelSuffix)", + "dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.getId\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) if let api { @@ -152,7 +156,7 @@ class FirebaseAppInstallationsHostApiSetup { } let getTokenChannel = FlutterBasicMessageChannel( name: - "dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.getToken\(channelSuffix)", + "dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.getToken\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) if let api { @@ -174,7 +178,7 @@ class FirebaseAppInstallationsHostApiSetup { } let registerIdChangeListenerChannel = FlutterBasicMessageChannel( name: - "dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.registerIdChangeListener\(channelSuffix)", + "dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.registerIdChangeListener\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec ) if let api { diff --git a/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/FirebaseInstallationsPlugin.swift b/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/FirebaseInstallationsPlugin.swift index d79baccf589e..f4e389b3fa4e 100644 --- a/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/FirebaseInstallationsPlugin.swift +++ b/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/FirebaseInstallationsPlugin.swift @@ -22,7 +22,8 @@ let kFLTFirebaseInstallationsChannelName = "plugins.flutter.io/firebase_app_inst extension FlutterError: @retroactive Error {} public class FirebaseInstallationsPlugin: NSObject, FLTFirebasePluginProtocol, FlutterPlugin, - FirebaseAppInstallationsHostApi { + FirebaseAppInstallationsHostApi +{ private var messenger: FlutterBinaryMessenger private var streamHandler = [String: IdChangedStreamHandler?]() @@ -103,8 +104,10 @@ public class FirebaseInstallationsPlugin: NSObject, FLTFirebasePluginProtocol, F ) } - public func delete(appName: String, - completion: @escaping (Result) -> Void) { + public func delete( + appName: String, + completion: @escaping (Result) -> Void + ) { let instance = getInstallations(appName: appName) instance.delete { (error: Error?) in if let error { @@ -115,8 +118,10 @@ public class FirebaseInstallationsPlugin: NSObject, FLTFirebasePluginProtocol, F } } - public func getId(appName: String, - completion: @escaping (Result) -> Void) { + public func getId( + appName: String, + completion: @escaping (Result) -> Void + ) { let instance = getInstallations(appName: appName) instance.installationID { (id: String?, error: Error?) in if let error { @@ -137,13 +142,17 @@ public class FirebaseInstallationsPlugin: NSObject, FLTFirebasePluginProtocol, F } } - public func getToken(appName: String, - forceRefresh: Bool, - completion: @escaping (Result) -> Void) { + public func getToken( + appName: String, + forceRefresh: Bool, + completion: @escaping (Result) -> Void + ) { let instance = getInstallations(appName: appName) instance.authTokenForcingRefresh(forceRefresh) { - (tokenResult: InstallationsAuthTokenResult?, - error: Error?) in + ( + tokenResult: InstallationsAuthTokenResult?, + error: Error? + ) in if let error { completion(.failure(self.createFlutterError(error))) } else if let token = tokenResult?.authToken { @@ -162,8 +171,10 @@ public class FirebaseInstallationsPlugin: NSObject, FLTFirebasePluginProtocol, F } } - public func registerIdChangeListener(appName: String, - completion: @escaping (Result) -> Void) { + public func registerIdChangeListener( + appName: String, + completion: @escaping (Result) -> Void + ) { let instance = getInstallations(appName: appName) let eventChannelName = kFLTFirebaseInstallationsChannelName + "/token/" + appName diff --git a/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/IdChangedStreamHandler.swift b/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/IdChangedStreamHandler.swift index 30790c94adf5..89f9cbc2e4e9 100644 --- a/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/IdChangedStreamHandler.swift +++ b/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/IdChangedStreamHandler.swift @@ -47,8 +47,10 @@ class IdChangedStreamHandler: NSObject, FlutterStreamHandler { } } - func onListen(withArguments _: Any?, - eventSink events: @escaping FlutterEventSink) -> FlutterError? { + func onListen( + withArguments _: Any?, + eventSink events: @escaping FlutterEventSink + ) -> FlutterError? { eventSink = events installationIDObserver = NotificationCenter.default.addObserver( diff --git a/packages/firebase_app_installations/firebase_app_installations/macos/firebase_app_installations/Package.swift b/packages/firebase_app_installations/firebase_app_installations/macos/firebase_app_installations/Package.swift index 523cf17ac191..c98e797d9f36 100644 --- a/packages/firebase_app_installations/firebase_app_installations/macos/firebase_app_installations/Package.swift +++ b/packages/firebase_app_installations/firebase_app_installations/macos/firebase_app_installations/Package.swift @@ -12,10 +12,10 @@ let firebaseSdkVersion: Version = "12.17.0" let package = Package( name: "firebase_app_installations", platforms: [ - .macOS("10.15"), + .macOS("10.15") ], products: [ - .library(name: "firebase-app-installations", targets: ["firebase_app_installations"]), + .library(name: "firebase-app-installations", targets: ["firebase_app_installations"]) ], dependencies: [ .package(url: "https://github.com/firebase/firebase-ios-sdk", exact: firebaseSdkVersion), @@ -31,8 +31,8 @@ let package = Package( .product(name: "FlutterFramework", package: "FlutterFramework"), ], resources: [ - .process("Resources"), + .process("Resources") ] - ), + ) ] ) From 40f83e5039b45ad09c45d36e4e800c93bf6e3725 Mon Sep 17 00:00:00 2001 From: Jude Kwashie Date: Wed, 12 Aug 2026 12:08:46 +0000 Subject: [PATCH 3/5] refactor(app_installations): drop deprecated pigeon test APIs Replace dartTestOut/dartHostTestHandler with binary-messenger mocks. --- .../analysis_options.yaml | 1 - .../lib/src/pigeon/messages.pigeon.dart | 83 ++++----- .../pigeons/messages.dart | 3 +- ...annel_firebase_app_installations_test.dart | 147 ++++++++++------ .../test/pigeon/test_api.dart | 165 ------------------ 5 files changed, 125 insertions(+), 274 deletions(-) delete mode 100644 packages/firebase_app_installations/firebase_app_installations_platform_interface/test/pigeon/test_api.dart diff --git a/packages/firebase_app_installations/analysis_options.yaml b/packages/firebase_app_installations/analysis_options.yaml index 3d91c8d049b5..23846daf2324 100644 --- a/packages/firebase_app_installations/analysis_options.yaml +++ b/packages/firebase_app_installations/analysis_options.yaml @@ -7,5 +7,4 @@ include: ../../analysis_options.yaml analyzer: exclude: - firebase_app_installations_platform_interface/lib/src/pigeon/messages.pigeon.dart - - firebase_app_installations_platform_interface/test/pigeon/test_api.dart - firebase_app_installations_platform_interface/pigeons/messages.dart diff --git a/packages/firebase_app_installations/firebase_app_installations_platform_interface/lib/src/pigeon/messages.pigeon.dart b/packages/firebase_app_installations/firebase_app_installations_platform_interface/lib/src/pigeon/messages.pigeon.dart index 88cae64bc59f..b44063d7d6a7 100644 --- a/packages/firebase_app_installations/firebase_app_installations_platform_interface/lib/src/pigeon/messages.pigeon.dart +++ b/packages/firebase_app_installations/firebase_app_installations_platform_interface/lib/src/pigeon/messages.pigeon.dart @@ -13,9 +13,9 @@ import 'package:flutter/services.dart'; import 'package:meta/meta.dart' show immutable, protected, visibleForTesting; Object? _extractReplyValueOrThrow( - List? replyList, - String channelName, { - required bool isNullValid, + List? replyList, + String channelName, { + required bool isNullValid, }) { if (replyList == null) { throw PlatformException( @@ -37,16 +37,7 @@ Object? _extractReplyValueOrThrow( return replyList.firstOrNull; } -List wrapResponse( - {Object? result, PlatformException? error, bool empty = false}) { - if (empty) { - return []; - } - if (error == null) { - return [result]; - } - return [error.code, error.message, error.details]; -} + class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @@ -73,11 +64,9 @@ class FirebaseAppInstallationsHostApi { /// Constructor for [FirebaseAppInstallationsHostApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - FirebaseAppInstallationsHostApi( - {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + FirebaseAppInstallationsHostApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; final BinaryMessenger? pigeonVar_binaryMessenger; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); @@ -85,81 +74,77 @@ class FirebaseAppInstallationsHostApi { final String pigeonVar_messageChannelSuffix; Future delete(String appName) async { - final pigeonVar_channelName = - 'dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.delete$pigeonVar_messageChannelSuffix'; + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.delete$pigeonVar_messageChannelSuffix'; final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final Future pigeonVar_sendFuture = - pigeonVar_channel.send([appName]); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([appName]); final pigeonVar_replyList = await pigeonVar_sendFuture as List?; _extractReplyValueOrThrow( - pigeonVar_replyList, - pigeonVar_channelName, - isNullValid: true, - ); + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ) + ; } Future getId(String appName) async { - final pigeonVar_channelName = - 'dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.getId$pigeonVar_messageChannelSuffix'; + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.getId$pigeonVar_messageChannelSuffix'; final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final Future pigeonVar_sendFuture = - pigeonVar_channel.send([appName]); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([appName]); final pigeonVar_replyList = await pigeonVar_sendFuture as List?; final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( - pigeonVar_replyList, - pigeonVar_channelName, - isNullValid: false, - ); + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ) + ; return pigeonVar_replyValue! as String; } Future getToken(String appName, bool forceRefresh) async { - final pigeonVar_channelName = - 'dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.getToken$pigeonVar_messageChannelSuffix'; + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.getToken$pigeonVar_messageChannelSuffix'; final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final Future pigeonVar_sendFuture = - pigeonVar_channel.send([appName, forceRefresh]); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([appName, forceRefresh]); final pigeonVar_replyList = await pigeonVar_sendFuture as List?; final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( - pigeonVar_replyList, - pigeonVar_channelName, - isNullValid: false, - ); + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ) + ; return pigeonVar_replyValue! as String; } Future registerIdChangeListener(String appName) async { - final pigeonVar_channelName = - 'dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.registerIdChangeListener$pigeonVar_messageChannelSuffix'; + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.registerIdChangeListener$pigeonVar_messageChannelSuffix'; final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final Future pigeonVar_sendFuture = - pigeonVar_channel.send([appName]); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([appName]); final pigeonVar_replyList = await pigeonVar_sendFuture as List?; final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( - pigeonVar_replyList, - pigeonVar_channelName, - isNullValid: false, - ); + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ) + ; return pigeonVar_replyValue! as String; } } diff --git a/packages/firebase_app_installations/firebase_app_installations_platform_interface/pigeons/messages.dart b/packages/firebase_app_installations/firebase_app_installations_platform_interface/pigeons/messages.dart index 40eeb4aa551b..a0497ff0d9fc 100644 --- a/packages/firebase_app_installations/firebase_app_installations_platform_interface/pigeons/messages.dart +++ b/packages/firebase_app_installations/firebase_app_installations_platform_interface/pigeons/messages.dart @@ -7,7 +7,6 @@ import 'package:pigeon/pigeon.dart'; @ConfigurePigeon( PigeonOptions( dartOut: 'lib/src/pigeon/messages.pigeon.dart', - dartTestOut: 'test/pigeon/test_api.dart', dartPackageName: 'firebase_app_installations_platform_interface', kotlinOut: '../firebase_app_installations/android/src/main/kotlin/io/flutter/plugins/firebase/installations/firebase_app_installations/GeneratedAndroidFirebaseAppInstallations.g.kt', @@ -20,7 +19,7 @@ import 'package:pigeon/pigeon.dart'; copyrightHeader: 'pigeons/copyright.txt', ), ) -@HostApi(dartHostTestHandler: 'TestFirebaseAppInstallationsHostApi') +@HostApi() abstract class FirebaseAppInstallationsHostApi { @async void delete(String appName); diff --git a/packages/firebase_app_installations/firebase_app_installations_platform_interface/test/method_channel/method_channel_firebase_app_installations_test.dart b/packages/firebase_app_installations/firebase_app_installations_platform_interface/test/method_channel/method_channel_firebase_app_installations_test.dart index b4663fe915df..3f7f41f8664c 100644 --- a/packages/firebase_app_installations/firebase_app_installations_platform_interface/test/method_channel/method_channel_firebase_app_installations_test.dart +++ b/packages/firebase_app_installations/firebase_app_installations_platform_interface/test/method_channel/method_channel_firebase_app_installations_test.dart @@ -2,103 +2,136 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_app_installations_platform_interface/src/method_channel/method_channel_firebase_app_installations.dart'; +import 'package:firebase_app_installations_platform_interface/src/pigeon/messages.pigeon.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import '../mock.dart'; -import '../pigeon/test_api.dart'; + +const String _hostApiPrefix = + 'dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi'; void main() { setupFirebaseAppInstallationsMocks(); late FirebaseApp app; late MethodChannelFirebaseAppInstallations installations; - late _TestFirebaseAppInstallationsHostApi hostApi; + + String? lastDeleteAppName; + String? lastGetIdAppName; + String? lastGetTokenAppName; + bool? lastForceRefresh; + String? lastRegisterAppName; + + ByteData? encodeSuccess([Object? value]) { + return FirebaseAppInstallationsHostApi.pigeonChannelCodec.encodeMessage( + [value], + ); + } setUpAll(() async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMessageHandler('$_hostApiPrefix.registerIdChangeListener', ( + ByteData? message, + ) async { + final List args = + FirebaseAppInstallationsHostApi.pigeonChannelCodec.decodeMessage( + message, + ) as List; + lastRegisterAppName = args[0]! as String; + return encodeSuccess( + 'plugins.flutter.io/firebase_app_installations/token/$lastRegisterAppName', + ); + }); + app = await Firebase.initializeApp(); - hostApi = _TestFirebaseAppInstallationsHostApi(); - TestFirebaseAppInstallationsHostApi.setUp(hostApi); installations = MethodChannelFirebaseAppInstallations(app: app); - // Allow constructor listener registration to complete. await Future.delayed(Duration.zero); }); tearDownAll(() { - TestFirebaseAppInstallationsHostApi.setUp(null); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMessageHandler( + '$_hostApiPrefix.registerIdChangeListener', + null, + ); }); setUp(() { - hostApi.reset(); + lastDeleteAppName = null; + lastGetIdAppName = null; + lastGetTokenAppName = null; + lastForceRefresh = null; + + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMessageHandler('$_hostApiPrefix.delete', + (ByteData? message) async { + final List args = + FirebaseAppInstallationsHostApi.pigeonChannelCodec.decodeMessage( + message, + ) as List; + lastDeleteAppName = args[0]! as String; + return encodeSuccess(); + }); + + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMessageHandler('$_hostApiPrefix.getId', + (ByteData? message) async { + final List args = + FirebaseAppInstallationsHostApi.pigeonChannelCodec.decodeMessage( + message, + ) as List; + lastGetIdAppName = args[0]! as String; + return encodeSuccess('test-installation-id'); + }); + + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMessageHandler('$_hostApiPrefix.getToken', ( + ByteData? message, + ) async { + final List args = + FirebaseAppInstallationsHostApi.pigeonChannelCodec.decodeMessage( + message, + ) as List; + lastGetTokenAppName = args[0]! as String; + lastForceRefresh = args[1]! as bool; + return encodeSuccess('test-installation-token'); + }); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMessageHandler('$_hostApiPrefix.delete', null); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMessageHandler('$_hostApiPrefix.getId', null); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMessageHandler('$_hostApiPrefix.getToken', null); }); test('delete forwards the app name', () async { await installations.delete(); - expect(hostApi.appName, app.name); - expect(hostApi.deleteCalled, isTrue); + expect(lastDeleteAppName, app.name); }); test('getId forwards the app name', () async { final id = await installations.getId(); - expect(hostApi.appName, app.name); + expect(lastGetIdAppName, app.name); expect(id, 'test-installation-id'); }); test('getToken forwards the app name and forceRefresh', () async { final token = await installations.getToken(true); - expect(hostApi.appName, app.name); - expect(hostApi.forceRefresh, isTrue); + expect(lastGetTokenAppName, app.name); + expect(lastForceRefresh, isTrue); expect(token, 'test-installation-token'); }); test('registerIdChangeListener is invoked for onIdChange setup', () { - expect(hostApi.registerIdChangeListenerCalled, isTrue); - expect(hostApi.registeredAppName, app.name); + expect(lastRegisterAppName, app.name); }); } - -class _TestFirebaseAppInstallationsHostApi - implements TestFirebaseAppInstallationsHostApi { - String? appName; - String? registeredAppName; - bool? forceRefresh; - bool deleteCalled = false; - bool registerIdChangeListenerCalled = false; - - void reset() { - appName = null; - forceRefresh = null; - deleteCalled = false; - // Keep registerIdChangeListenerCalled / registeredAppName — set during construction. - } - - @override - Future delete(String appName) async { - this.appName = appName; - deleteCalled = true; - } - - @override - Future getId(String appName) async { - this.appName = appName; - return 'test-installation-id'; - } - - @override - Future getToken(String appName, bool forceRefresh) async { - this.appName = appName; - this.forceRefresh = forceRefresh; - return 'test-installation-token'; - } - - @override - Future registerIdChangeListener(String appName) async { - registeredAppName = appName; - registerIdChangeListenerCalled = true; - return 'plugins.flutter.io/firebase_app_installations/token/$appName'; - } -} diff --git a/packages/firebase_app_installations/firebase_app_installations_platform_interface/test/pigeon/test_api.dart b/packages/firebase_app_installations/firebase_app_installations_platform_interface/test/pigeon/test_api.dart deleted file mode 100644 index 35e0c0f4aae2..000000000000 --- a/packages/firebase_app_installations/firebase_app_installations_platform_interface/test/pigeon/test_api.dart +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright 2026, the Chromium project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. -// Autogenerated from Pigeon (v26.3.4), do not edit directly. -// See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import, no_leading_underscores_for_local_identifiers, omit_obvious_local_variable_types -// ignore_for_file: avoid_relative_lib_imports -import 'dart:async'; -import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; -import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:firebase_app_installations_platform_interface/src/pigeon/messages.pigeon.dart'; - -class _PigeonCodec extends StandardMessageCodec { - const _PigeonCodec(); - @override - void writeValue(WriteBuffer buffer, Object? value) { - if (value is int) { - buffer.putUint8(4); - buffer.putInt64(value); - } else { - super.writeValue(buffer, value); - } - } - - @override - Object? readValueOfType(int type, ReadBuffer buffer) { - switch (type) { - default: - return super.readValueOfType(type, buffer); - } - } -} - -abstract class TestFirebaseAppInstallationsHostApi { - static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => - TestDefaultBinaryMessengerBinding.instance; - static const MessageCodec pigeonChannelCodec = _PigeonCodec(); - - Future delete(String appName); - - Future getId(String appName); - - Future getToken(String appName, bool forceRefresh); - - Future registerIdChangeListener(String appName); - - static void setUp( - TestFirebaseAppInstallationsHostApi? api, { - BinaryMessenger? binaryMessenger, - String messageChannelSuffix = '', - }) { - messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; - { - final pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.delete$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger); - if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); - } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { - final List args = message! as List; - final String arg_appName = args[0]! as String; - try { - await api.delete(arg_appName); - return wrapResponse(empty: true); - } on PlatformException catch (e) { - return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); - } - }); - } - } - { - final pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.getId$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger); - if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); - } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { - final List args = message! as List; - final String arg_appName = args[0]! as String; - try { - final String output = await api.getId(arg_appName); - return [output]; - } on PlatformException catch (e) { - return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); - } - }); - } - } - { - final pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.getToken$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger); - if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); - } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { - final List args = message! as List; - final String arg_appName = args[0]! as String; - final bool arg_forceRefresh = args[1]! as bool; - try { - final String output = - await api.getToken(arg_appName, arg_forceRefresh); - return [output]; - } on PlatformException catch (e) { - return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); - } - }); - } - } - { - final pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.registerIdChangeListener$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger); - if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); - } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { - final List args = message! as List; - final String arg_appName = args[0]! as String; - try { - final String output = - await api.registerIdChangeListener(arg_appName); - return [output]; - } on PlatformException catch (e) { - return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); - } - }); - } - } - } -} From c19f99ead185bb12ba308c9aaa5ada934b27dd51 Mon Sep 17 00:00:00 2001 From: Jude Kwashie Date: Wed, 12 Aug 2026 12:26:23 +0000 Subject: [PATCH 4/5] fix(app_installations): restore original didReinitializeFirebaseCore behavior --- .../firebase_app_installations/FirebaseInstallationsPlugin.kt | 1 - .../FirebaseInstallationsPlugin.swift | 4 ---- 2 files changed, 5 deletions(-) diff --git a/packages/firebase_app_installations/firebase_app_installations/android/src/main/kotlin/io/flutter/plugins/firebase/installations/firebase_app_installations/FirebaseInstallationsPlugin.kt b/packages/firebase_app_installations/firebase_app_installations/android/src/main/kotlin/io/flutter/plugins/firebase/installations/firebase_app_installations/FirebaseInstallationsPlugin.kt index deec8e6b9870..32a420de276d 100644 --- a/packages/firebase_app_installations/firebase_app_installations/android/src/main/kotlin/io/flutter/plugins/firebase/installations/firebase_app_installations/FirebaseInstallationsPlugin.kt +++ b/packages/firebase_app_installations/firebase_app_installations/android/src/main/kotlin/io/flutter/plugins/firebase/installations/firebase_app_installations/FirebaseInstallationsPlugin.kt @@ -130,7 +130,6 @@ class FirebaseInstallationsPlugin : FlutterFirebasePlugin.cachedThreadPool.execute { try { - removeEventListeners() taskCompletionSource.setResult(null) } catch (exception: Exception) { taskCompletionSource.setException(exception) diff --git a/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/FirebaseInstallationsPlugin.swift b/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/FirebaseInstallationsPlugin.swift index f4e389b3fa4e..7f3703a25177 100644 --- a/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/FirebaseInstallationsPlugin.swift +++ b/packages/firebase_app_installations/firebase_app_installations/ios/firebase_app_installations/Sources/firebase_app_installations/FirebaseInstallationsPlugin.swift @@ -50,10 +50,6 @@ public class FirebaseInstallationsPlugin: NSObject, FLTFirebasePluginProtocol, F } public func didReinitializeFirebaseCore(_ completion: @escaping () -> Void) { - for (_, handler) in streamHandler { - _ = handler?.onCancel(withArguments: nil) - } - streamHandler.removeAll() completion() } From abd491c98b0c7a7684ab331afb32111fb9cd3db3 Mon Sep 17 00:00:00 2001 From: Jude Kwashie Date: Wed, 12 Aug 2026 12:45:56 +0000 Subject: [PATCH 5/5] style(app_installations): format generated pigeon Dart for CI --- .../lib/src/pigeon/messages.pigeon.dart | 74 ++++++++++--------- 1 file changed, 39 insertions(+), 35 deletions(-) diff --git a/packages/firebase_app_installations/firebase_app_installations_platform_interface/lib/src/pigeon/messages.pigeon.dart b/packages/firebase_app_installations/firebase_app_installations_platform_interface/lib/src/pigeon/messages.pigeon.dart index b44063d7d6a7..4b1471f70f57 100644 --- a/packages/firebase_app_installations/firebase_app_installations_platform_interface/lib/src/pigeon/messages.pigeon.dart +++ b/packages/firebase_app_installations/firebase_app_installations_platform_interface/lib/src/pigeon/messages.pigeon.dart @@ -13,9 +13,9 @@ import 'package:flutter/services.dart'; import 'package:meta/meta.dart' show immutable, protected, visibleForTesting; Object? _extractReplyValueOrThrow( - List? replyList, - String channelName, { - required bool isNullValid, + List? replyList, + String channelName, { + required bool isNullValid, }) { if (replyList == null) { throw PlatformException( @@ -37,8 +37,6 @@ Object? _extractReplyValueOrThrow( return replyList.firstOrNull; } - - class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @override @@ -64,9 +62,11 @@ class FirebaseAppInstallationsHostApi { /// Constructor for [FirebaseAppInstallationsHostApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - FirebaseAppInstallationsHostApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + FirebaseAppInstallationsHostApi( + {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + pigeonVar_messageChannelSuffix = + messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; final BinaryMessenger? pigeonVar_binaryMessenger; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); @@ -74,77 +74,81 @@ class FirebaseAppInstallationsHostApi { final String pigeonVar_messageChannelSuffix; Future delete(String appName) async { - final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.delete$pigeonVar_messageChannelSuffix'; + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.delete$pigeonVar_messageChannelSuffix'; final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([appName]); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([appName]); final pigeonVar_replyList = await pigeonVar_sendFuture as List?; _extractReplyValueOrThrow( - pigeonVar_replyList, - pigeonVar_channelName, - isNullValid: true, - ) - ; + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ); } Future getId(String appName) async { - final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.getId$pigeonVar_messageChannelSuffix'; + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.getId$pigeonVar_messageChannelSuffix'; final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([appName]); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([appName]); final pigeonVar_replyList = await pigeonVar_sendFuture as List?; final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( - pigeonVar_replyList, - pigeonVar_channelName, - isNullValid: false, - ) - ; + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); return pigeonVar_replyValue! as String; } Future getToken(String appName, bool forceRefresh) async { - final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.getToken$pigeonVar_messageChannelSuffix'; + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.getToken$pigeonVar_messageChannelSuffix'; final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([appName, forceRefresh]); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([appName, forceRefresh]); final pigeonVar_replyList = await pigeonVar_sendFuture as List?; final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( - pigeonVar_replyList, - pigeonVar_channelName, - isNullValid: false, - ) - ; + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); return pigeonVar_replyValue! as String; } Future registerIdChangeListener(String appName) async { - final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.registerIdChangeListener$pigeonVar_messageChannelSuffix'; + final pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_app_installations_platform_interface.FirebaseAppInstallationsHostApi.registerIdChangeListener$pigeonVar_messageChannelSuffix'; final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([appName]); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([appName]); final pigeonVar_replyList = await pigeonVar_sendFuture as List?; final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( - pigeonVar_replyList, - pigeonVar_channelName, - isNullValid: false, - ) - ; + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ); return pigeonVar_replyValue! as String; } }