From 3fd96aaed933ad6d6b4607006283d15d9c30695c Mon Sep 17 00:00:00 2001 From: Jude Kwashie Date: Fri, 4 Jul 2025 09:45:56 +0000 Subject: [PATCH 1/6] feat(remote_config): add support for Pigeon. Update iOS to Swift and Android to Swift --- .../analysis_options.yaml | 10 + .../android/build.gradle | 12 + .../FirebaseRemoteConfigPlugin.java | 368 ------------- .../FlutterFirebaseAppRegistrar.java | 21 - .../FirebaseRemoteConfigPlugin.kt | 369 +++++++++++++ .../FlutterFirebaseAppRegistrar.kt | 18 + .../GeneratedAndroidFirebaseRemoteConfig.g.kt | 338 ++++++++++++ .../FirebaseRemoteConfigMessages.g.swift | 422 +++++++++++++++ .../windows/messages.g.cpp | 510 ++++++++++++++++++ .../windows/messages.g.h | 152 ++++++ ...method_channel_firebase_remote_config.dart | 70 +-- .../lib/src/pigeon/messages.pigeon.dart | 393 ++++++++++++++ .../pigeons/copyright.txt | 3 + .../pigeons/messages.dart | 63 +++ .../pubspec.yaml | 1 + .../test/pigeon/test_api.dart | 377 +++++++++++++ 16 files changed, 2691 insertions(+), 436 deletions(-) create mode 100644 packages/firebase_remote_config/analysis_options.yaml delete mode 100644 packages/firebase_remote_config/firebase_remote_config/android/src/main/java/io/flutter/plugins/firebase/firebaseremoteconfig/FirebaseRemoteConfigPlugin.java delete mode 100644 packages/firebase_remote_config/firebase_remote_config/android/src/main/java/io/flutter/plugins/firebase/firebaseremoteconfig/FlutterFirebaseAppRegistrar.java create mode 100644 packages/firebase_remote_config/firebase_remote_config/android/src/main/kotlin/io/flutter/plugins/firebase/firebaseremoteconfig/FirebaseRemoteConfigPlugin.kt create mode 100644 packages/firebase_remote_config/firebase_remote_config/android/src/main/kotlin/io/flutter/plugins/firebase/firebaseremoteconfig/FlutterFirebaseAppRegistrar.kt create mode 100644 packages/firebase_remote_config/firebase_remote_config/android/src/main/kotlin/io/flutter/plugins/firebase/firebaseremoteconfig/GeneratedAndroidFirebaseRemoteConfig.g.kt create mode 100644 packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigMessages.g.swift create mode 100644 packages/firebase_remote_config/firebase_remote_config/windows/messages.g.cpp create mode 100644 packages/firebase_remote_config/firebase_remote_config/windows/messages.g.h create mode 100644 packages/firebase_remote_config/firebase_remote_config_platform_interface/lib/src/pigeon/messages.pigeon.dart create mode 100644 packages/firebase_remote_config/firebase_remote_config_platform_interface/pigeons/copyright.txt create mode 100644 packages/firebase_remote_config/firebase_remote_config_platform_interface/pigeons/messages.dart create mode 100644 packages/firebase_remote_config/firebase_remote_config_platform_interface/test/pigeon/test_api.dart diff --git a/packages/firebase_remote_config/analysis_options.yaml b/packages/firebase_remote_config/analysis_options.yaml new file mode 100644 index 000000000000..75a21d21f70b --- /dev/null +++ b/packages/firebase_remote_config/analysis_options.yaml @@ -0,0 +1,10 @@ +# Copyright 2025 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# in the LICENSE file. + +include: ../../analysis_options.yaml + +analyzer: + exclude: + - firebase_remote_config_platform_interface/lib/src/pigeon/messages.pigeon.dart + - firebase_remote_config_platform_interface/test/pigeon/test_api.dart diff --git a/packages/firebase_remote_config/firebase_remote_config/android/build.gradle b/packages/firebase_remote_config/firebase_remote_config/android/build.gradle index 17c4c4ec04ee..41fc6f44f3d2 100644 --- a/packages/firebase_remote_config/firebase_remote_config/android/build.gradle +++ b/packages/firebase_remote_config/firebase_remote_config/android/build.gradle @@ -5,6 +5,7 @@ apply plugin: 'com.android.library' apply from: file("local-config.gradle") buildscript { + ext.kotlin_version = "1.8.22" repositories { google() mavenCentral() @@ -18,6 +19,8 @@ rootProject.allprojects { } } +apply plugin: 'kotlin-android' + def firebaseCoreProject = findProject(':firebase_core') if (firebaseCoreProject == null) { throw new GradleException('Could not find the firebase_core FlutterFire plugin, have you added it as a dependency in your pubspec?') @@ -44,11 +47,20 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } + kotlinOptions { + jvmTarget = JavaVersion.VERSION_17 + } + compileOptions { sourceCompatibility project.ext.javaVersion targetCompatibility project.ext.javaVersion } + sourceSets { + main.java.srcDirs += "src/main/kotlin" + test.java.srcDirs += "src/test/kotlin" + } + buildFeatures { buildConfig = true } diff --git a/packages/firebase_remote_config/firebase_remote_config/android/src/main/java/io/flutter/plugins/firebase/firebaseremoteconfig/FirebaseRemoteConfigPlugin.java b/packages/firebase_remote_config/firebase_remote_config/android/src/main/java/io/flutter/plugins/firebase/firebaseremoteconfig/FirebaseRemoteConfigPlugin.java deleted file mode 100644 index cd3e58fd8bee..000000000000 --- a/packages/firebase_remote_config/firebase_remote_config/android/src/main/java/io/flutter/plugins/firebase/firebaseremoteconfig/FirebaseRemoteConfigPlugin.java +++ /dev/null @@ -1,368 +0,0 @@ -// Copyright 2019 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. - -package io.flutter.plugins.firebase.firebaseremoteconfig; - -import static io.flutter.plugins.firebase.core.FlutterFirebasePluginRegistry.registerPlugin; - -import android.os.Handler; -import android.os.Looper; -import androidx.annotation.NonNull; -import com.google.android.gms.tasks.Task; -import com.google.android.gms.tasks.TaskCompletionSource; -import com.google.android.gms.tasks.Tasks; -import com.google.firebase.FirebaseApp; -import com.google.firebase.remoteconfig.ConfigUpdate; -import com.google.firebase.remoteconfig.ConfigUpdateListener; -import com.google.firebase.remoteconfig.ConfigUpdateListenerRegistration; -import com.google.firebase.remoteconfig.CustomSignals; -import com.google.firebase.remoteconfig.FirebaseRemoteConfig; -import com.google.firebase.remoteconfig.FirebaseRemoteConfigClientException; -import com.google.firebase.remoteconfig.FirebaseRemoteConfigException; -import com.google.firebase.remoteconfig.FirebaseRemoteConfigFetchThrottledException; -import com.google.firebase.remoteconfig.FirebaseRemoteConfigServerException; -import com.google.firebase.remoteconfig.FirebaseRemoteConfigSettings; -import com.google.firebase.remoteconfig.FirebaseRemoteConfigValue; -import io.flutter.Log; -import io.flutter.embedding.engine.plugins.FlutterPlugin; -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.plugins.firebase.core.FlutterFirebasePlugin; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; - -/** FirebaseRemoteConfigPlugin */ -public class FirebaseRemoteConfigPlugin - implements FlutterFirebasePlugin, - MethodChannel.MethodCallHandler, - FlutterPlugin, - EventChannel.StreamHandler { - - static final String TAG = "FRCPlugin"; - static final String METHOD_CHANNEL = "plugins.flutter.io/firebase_remote_config"; - static final String EVENT_CHANNEL = "plugins.flutter.io/firebase_remote_config_updated"; - - private MethodChannel channel; - - private final Map listenersMap = new HashMap<>(); - private EventChannel eventChannel; - private final Handler mainThreadHandler = new Handler(Looper.getMainLooper()); - - @Override - public void onAttachedToEngine(FlutterPluginBinding binding) { - setupChannel(binding.getBinaryMessenger()); - } - - @Override - public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { - tearDownChannel(); - } - - @Override - public Task> getPluginConstantsForFirebaseApp(final FirebaseApp firebaseApp) { - TaskCompletionSource> taskCompletionSource = new TaskCompletionSource<>(); - - cachedThreadPool.execute( - () -> { - try { - FirebaseRemoteConfig remoteConfig = FirebaseRemoteConfig.getInstance(firebaseApp); - Map configProperties = getConfigProperties(remoteConfig); - Map configValues = new HashMap<>(configProperties); - configValues.put("parameters", parseParameters(remoteConfig.getAll())); - - taskCompletionSource.setResult(configValues); - } catch (Exception e) { - taskCompletionSource.setException(e); - } - }); - - return taskCompletionSource.getTask(); - } - - private Map getConfigProperties(FirebaseRemoteConfig remoteConfig) { - Map configProperties = new HashMap<>(); - configProperties.put( - "fetchTimeout", remoteConfig.getInfo().getConfigSettings().getFetchTimeoutInSeconds()); - configProperties.put( - "minimumFetchInterval", - remoteConfig.getInfo().getConfigSettings().getMinimumFetchIntervalInSeconds()); - configProperties.put("lastFetchTime", remoteConfig.getInfo().getFetchTimeMillis()); - configProperties.put( - "lastFetchStatus", mapLastFetchStatus(remoteConfig.getInfo().getLastFetchStatus())); - Log.d(TAG, "Sending fetchTimeout: " + configProperties.get("fetchTimeout")); - return configProperties; - } - - @Override - public Task didReinitializeFirebaseCore() { - TaskCompletionSource taskCompletionSource = new TaskCompletionSource<>(); - - cachedThreadPool.execute( - () -> { - try { - removeEventListeners(); - taskCompletionSource.setResult(null); - } catch (Exception e) { - taskCompletionSource.setException(e); - } - }); - - return taskCompletionSource.getTask(); - } - - private void setupChannel(BinaryMessenger messenger) { - registerPlugin(METHOD_CHANNEL, this); - channel = new MethodChannel(messenger, METHOD_CHANNEL); - channel.setMethodCallHandler(this); - - eventChannel = new EventChannel(messenger, EVENT_CHANNEL); - eventChannel.setStreamHandler(this); - } - - private void tearDownChannel() { - channel.setMethodCallHandler(null); - channel = null; - eventChannel.setStreamHandler(null); - eventChannel = null; - removeEventListeners(); - } - - private FirebaseRemoteConfig getRemoteConfig(Map arguments) { - String appName = (String) Objects.requireNonNull(arguments.get("appName")); - FirebaseApp app = FirebaseApp.getInstance(appName); - return FirebaseRemoteConfig.getInstance(app); - } - - private Task setCustomSignals( - FirebaseRemoteConfig remoteConfig, Map customSignalsArguments) { - TaskCompletionSource taskCompletionSource = new TaskCompletionSource<>(); - cachedThreadPool.execute( - () -> { - try { - CustomSignals.Builder customSignals = new CustomSignals.Builder(); - for (Map.Entry entry : customSignalsArguments.entrySet()) { - Object value = entry.getValue(); - if (value instanceof String) { - customSignals.put(entry.getKey(), (String) value); - } else if (value instanceof Long) { - customSignals.put(entry.getKey(), (Long) value); - } else if (value instanceof Integer) { - customSignals.put(entry.getKey(), ((Integer) value).longValue()); - } else if (value instanceof Double) { - customSignals.put(entry.getKey(), (Double) value); - } else if (value == null) { - customSignals.put(entry.getKey(), null); - } - } - Tasks.await(remoteConfig.setCustomSignals(customSignals.build())); - taskCompletionSource.setResult(null); - } catch (Exception e) { - taskCompletionSource.setException(e); - } - }); - return taskCompletionSource.getTask(); - } - - @Override - public void onMethodCall(MethodCall call, @NonNull final MethodChannel.Result result) { - Task methodCallTask; - FirebaseRemoteConfig remoteConfig = getRemoteConfig(call.arguments()); - - switch (call.method) { - case "RemoteConfig#ensureInitialized": - { - methodCallTask = Tasks.whenAll(remoteConfig.ensureInitialized()); - break; - } - case "RemoteConfig#activate": - { - methodCallTask = remoteConfig.activate(); - break; - } - case "RemoteConfig#getAll": - { - methodCallTask = Tasks.forResult(parseParameters(remoteConfig.getAll())); - break; - } - case "RemoteConfig#fetch": - { - methodCallTask = remoteConfig.fetch(); - break; - } - case "RemoteConfig#fetchAndActivate": - { - methodCallTask = remoteConfig.fetchAndActivate(); - break; - } - case "RemoteConfig#setConfigSettings": - { - int fetchTimeout = Objects.requireNonNull(call.argument("fetchTimeout")); - int minimumFetchInterval = Objects.requireNonNull(call.argument("minimumFetchInterval")); - FirebaseRemoteConfigSettings settings = - new FirebaseRemoteConfigSettings.Builder() - .setFetchTimeoutInSeconds(fetchTimeout) - .setMinimumFetchIntervalInSeconds(minimumFetchInterval) - .build(); - methodCallTask = remoteConfig.setConfigSettingsAsync(settings); - break; - } - case "RemoteConfig#setDefaults": - { - Map defaults = Objects.requireNonNull(call.argument("defaults")); - methodCallTask = remoteConfig.setDefaultsAsync(defaults); - break; - } - case "RemoteConfig#getProperties": - { - Map configProperties = getConfigProperties(remoteConfig); - methodCallTask = Tasks.forResult(configProperties); - break; - } - case "RemoteConfig#setCustomSignals": - { - Map customSignals = - Objects.requireNonNull(call.argument("customSignals")); - methodCallTask = setCustomSignals(remoteConfig, customSignals); - break; - } - default: - { - result.notImplemented(); - return; - } - } - - methodCallTask.addOnCompleteListener( - task -> { - if (task.isSuccessful()) { - result.success(task.getResult()); - } else { - Exception exception = task.getException(); - Map details = new HashMap<>(); - if (exception instanceof FirebaseRemoteConfigFetchThrottledException) { - details.put("code", "throttled"); - details.put("message", "frequency of requests exceeds throttled limits"); - } else if (exception instanceof FirebaseRemoteConfigClientException) { - details.put("code", "internal"); - details.put("message", "internal remote config fetch error"); - } else if (exception instanceof FirebaseRemoteConfigServerException) { - details.put("code", "remote-config-server-error"); - details.put("message", exception.getMessage()); - - Throwable cause = exception.getCause(); - if (cause != null) { - String causeMessage = cause.getMessage(); - if (causeMessage != null && causeMessage.contains("Forbidden")) { - // Specific error code for 403 status code to indicate the request was forbidden. - details.put("code", "forbidden"); - } - } - } else { - details.put("code", "unknown"); - details.put("message", "unknown remote config error"); - } - result.error( - "firebase_remote_config", - exception != null ? exception.getMessage() : null, - details); - } - }); - } - - private Map parseParameters(Map parameters) { - Map parsedParameters = new HashMap<>(); - for (String key : parameters.keySet()) { - parsedParameters.put( - key, createRemoteConfigValueMap(Objects.requireNonNull(parameters.get(key)))); - } - return parsedParameters; - } - - private Map createRemoteConfigValueMap( - FirebaseRemoteConfigValue remoteConfigValue) { - Map valueMap = new HashMap<>(); - valueMap.put("value", remoteConfigValue.asByteArray()); - valueMap.put("source", mapValueSource(remoteConfigValue.getSource())); - return valueMap; - } - - private String mapLastFetchStatus(int status) { - switch (status) { - case FirebaseRemoteConfig.LAST_FETCH_STATUS_SUCCESS: - return "success"; - case FirebaseRemoteConfig.LAST_FETCH_STATUS_THROTTLED: - return "throttled"; - case FirebaseRemoteConfig.LAST_FETCH_STATUS_NO_FETCH_YET: - return "noFetchYet"; - case FirebaseRemoteConfig.LAST_FETCH_STATUS_FAILURE: - default: - return "failure"; - } - } - - private String mapValueSource(int source) { - switch (source) { - case FirebaseRemoteConfig.VALUE_SOURCE_DEFAULT: - return "default"; - case FirebaseRemoteConfig.VALUE_SOURCE_REMOTE: - return "remote"; - case FirebaseRemoteConfig.VALUE_SOURCE_STATIC: - default: - return "static"; - } - } - - @SuppressWarnings("unchecked") - @Override - public void onListen(Object arguments, EventChannel.EventSink events) { - Map argumentsMap = (Map) arguments; - FirebaseRemoteConfig remoteConfig = getRemoteConfig(argumentsMap); - String appName = (String) Objects.requireNonNull(argumentsMap.get("appName")); - - listenersMap.put( - appName, - remoteConfig.addOnConfigUpdateListener( - new ConfigUpdateListener() { - @Override - public void onUpdate(@NonNull ConfigUpdate configUpdate) { - ArrayList updatedKeys = new ArrayList<>(configUpdate.getUpdatedKeys()); - mainThreadHandler.post(() -> events.success(updatedKeys)); - } - - @Override - public void onError(@NonNull FirebaseRemoteConfigException error) { - events.error("firebase_remote_config", error.getMessage(), null); - } - })); - } - - @SuppressWarnings("unchecked") - @Override - public void onCancel(Object arguments) { - // arguments will be null on hot restart, so we will clean up listeners in didReinitializeFirebaseCore() - Map argumentsMap = (Map) arguments; - if (argumentsMap == null) { - return; - } - String appName = (String) Objects.requireNonNull(argumentsMap.get("appName")); - - ConfigUpdateListenerRegistration listener = listenersMap.get(appName); - if (listener != null) { - listener.remove(); - listenersMap.remove(appName); - } - } - - /** Remove all registered listeners. */ - private void removeEventListeners() { - for (ConfigUpdateListenerRegistration listener : listenersMap.values()) { - listener.remove(); - } - listenersMap.clear(); - } -} diff --git a/packages/firebase_remote_config/firebase_remote_config/android/src/main/java/io/flutter/plugins/firebase/firebaseremoteconfig/FlutterFirebaseAppRegistrar.java b/packages/firebase_remote_config/firebase_remote_config/android/src/main/java/io/flutter/plugins/firebase/firebaseremoteconfig/FlutterFirebaseAppRegistrar.java deleted file mode 100644 index 7c8b3e594da7..000000000000 --- a/packages/firebase_remote_config/firebase_remote_config/android/src/main/java/io/flutter/plugins/firebase/firebaseremoteconfig/FlutterFirebaseAppRegistrar.java +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2019 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. - -package io.flutter.plugins.firebase.firebaseremoteconfig; - -import androidx.annotation.Keep; -import com.google.firebase.components.Component; -import com.google.firebase.components.ComponentRegistrar; -import com.google.firebase.platforminfo.LibraryVersionComponent; -import java.util.Collections; -import java.util.List; - -@Keep -public class FlutterFirebaseAppRegistrar implements ComponentRegistrar { - @Override - public List> getComponents() { - return Collections.>singletonList( - LibraryVersionComponent.create(BuildConfig.LIBRARY_NAME, BuildConfig.LIBRARY_VERSION)); - } -} diff --git a/packages/firebase_remote_config/firebase_remote_config/android/src/main/kotlin/io/flutter/plugins/firebase/firebaseremoteconfig/FirebaseRemoteConfigPlugin.kt b/packages/firebase_remote_config/firebase_remote_config/android/src/main/kotlin/io/flutter/plugins/firebase/firebaseremoteconfig/FirebaseRemoteConfigPlugin.kt new file mode 100644 index 000000000000..4a3f4ebea8ae --- /dev/null +++ b/packages/firebase_remote_config/firebase_remote_config/android/src/main/kotlin/io/flutter/plugins/firebase/firebaseremoteconfig/FirebaseRemoteConfigPlugin.kt @@ -0,0 +1,369 @@ +// Copyright 2025 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. +package io.flutter.plugins.firebase.firebaseremoteconfig + +import android.os.Handler +import android.os.Looper +import com.google.android.gms.tasks.Task +import com.google.android.gms.tasks.TaskCompletionSource +import com.google.android.gms.tasks.Tasks +import com.google.firebase.FirebaseApp +import com.google.firebase.remoteconfig.ConfigUpdate +import com.google.firebase.remoteconfig.ConfigUpdateListener +import com.google.firebase.remoteconfig.ConfigUpdateListenerRegistration +import com.google.firebase.remoteconfig.CustomSignals +import com.google.firebase.remoteconfig.FirebaseRemoteConfig +import com.google.firebase.remoteconfig.FirebaseRemoteConfigClientException +import com.google.firebase.remoteconfig.FirebaseRemoteConfigException +import com.google.firebase.remoteconfig.FirebaseRemoteConfigFetchThrottledException +import com.google.firebase.remoteconfig.FirebaseRemoteConfigServerException +import com.google.firebase.remoteconfig.FirebaseRemoteConfigSettings +import com.google.firebase.remoteconfig.FirebaseRemoteConfigValue +import io.flutter.Log +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.EventChannel.EventSink +import io.flutter.plugins.firebase.core.FlutterFirebasePlugin +import io.flutter.plugins.firebase.core.FlutterFirebasePluginRegistry +import java.util.Objects + +/** FirebaseRemoteConfigPlugin */ +class FirebaseRemoteConfigPlugin + + : FlutterFirebasePlugin, FlutterPlugin, + EventChannel.StreamHandler, FirebaseRemoteConfigHostApi { + + private val listenersMap: MutableMap = HashMap() + private var eventChannel: EventChannel? = null + private val mainThreadHandler = Handler(Looper.getMainLooper()) + private var messenger: BinaryMessenger? = null + + override fun onAttachedToEngine(binding: FlutterPluginBinding) { + setupChannel(binding.binaryMessenger) + } + + override fun onDetachedFromEngine(binding: FlutterPluginBinding) { + tearDownChannel() + } + + override fun getPluginConstantsForFirebaseApp(firebaseApp: FirebaseApp): Task> { + val taskCompletionSource = TaskCompletionSource>() + + FlutterFirebasePlugin.cachedThreadPool.execute { + try { + val remoteConfig = FirebaseRemoteConfig.getInstance(firebaseApp) + val configProperties = getConfigProperties(remoteConfig) + val configValues: MutableMap = + HashMap(configProperties) + configValues["parameters"] = parseParameters(remoteConfig.all) + + taskCompletionSource.setResult(configValues) + } catch (e: Exception) { + taskCompletionSource.setException(e) + } + } + + return taskCompletionSource.task + } + + private fun getConfigProperties(remoteConfig: FirebaseRemoteConfig): Map { + val configProperties: MutableMap = HashMap() + configProperties["fetchTimeout"] = remoteConfig.info.configSettings.fetchTimeoutInSeconds + configProperties["minimumFetchInterval"] = + remoteConfig.info.configSettings.minimumFetchIntervalInSeconds + configProperties["lastFetchTime"] = remoteConfig.info.fetchTimeMillis + configProperties["lastFetchStatus"] = mapLastFetchStatus(remoteConfig.info.lastFetchStatus) + Log.d(TAG, "Sending fetchTimeout: " + configProperties["fetchTimeout"]) + return configProperties + } + + override fun didReinitializeFirebaseCore(): Task { + val taskCompletionSource = TaskCompletionSource() + + FlutterFirebasePlugin.cachedThreadPool.execute { + try { + removeEventListeners() + taskCompletionSource.setResult(null) + } catch (e: Exception) { + taskCompletionSource.setException(e) + } + } + + return taskCompletionSource.task + } + + private fun setupChannel(messenger: BinaryMessenger) { + FirebaseRemoteConfigHostApi.setUp(messenger, this) + FlutterFirebasePluginRegistry.registerPlugin( + METHOD_CHANNEL, + this + ) + + eventChannel = EventChannel(messenger, EVENT_CHANNEL) + eventChannel!!.setStreamHandler(this) + this.messenger = messenger + } + + private fun tearDownChannel() { + checkNotNull(messenger) + FirebaseRemoteConfigHostApi.setUp(messenger!!, null) + + messenger = null + eventChannel!!.setStreamHandler(null) + eventChannel = null + removeEventListeners() + } + + private fun getRemoteConfig(appName: String): FirebaseRemoteConfig { + val app = FirebaseApp.getInstance(appName) + return FirebaseRemoteConfig.getInstance(app) + } + + private fun setCustomSignals( + remoteConfig: FirebaseRemoteConfig, customSignalsArguments: Map + ): Task { + val taskCompletionSource = TaskCompletionSource() + FlutterFirebasePlugin.cachedThreadPool.execute { + try { + val customSignals = CustomSignals.Builder() + for ((key, value) in customSignalsArguments) { + if (value is String) { + customSignals.put(key, value) + } else if (value is Long) { + customSignals.put(key, value) + } else if (value is Int) { + customSignals.put(key, value.toLong()) + } else if (value is Double) { + customSignals.put(key, value) + } else if (value == null) { + customSignals.put(key, null) + } + } + Tasks.await(remoteConfig.setCustomSignals(customSignals.build())) + taskCompletionSource.setResult(null) + } catch (e: Exception) { + taskCompletionSource.setException(e) + } + } + return taskCompletionSource.task + } + + private fun parseParameters(parameters: Map): Map { + val parsedParameters: MutableMap = HashMap() + for (key in parameters.keys) { + parsedParameters[key] = createRemoteConfigValueMap( + parameters[key]!! + ) + } + return parsedParameters + } + + private fun createRemoteConfigValueMap( + remoteConfigValue: FirebaseRemoteConfigValue + ): Map { + val valueMap: MutableMap = HashMap() + valueMap["value"] = remoteConfigValue.asByteArray() + valueMap["source"] = mapValueSource(remoteConfigValue.source) + return valueMap + } + + private fun mapLastFetchStatus(status: Int): String { + return when (status) { + FirebaseRemoteConfig.LAST_FETCH_STATUS_SUCCESS -> "success" + FirebaseRemoteConfig.LAST_FETCH_STATUS_THROTTLED -> "throttled" + FirebaseRemoteConfig.LAST_FETCH_STATUS_NO_FETCH_YET -> "noFetchYet" + FirebaseRemoteConfig.LAST_FETCH_STATUS_FAILURE -> "failure" + else -> "failure" + } + } + + private fun mapValueSource(source: Int): String { + return when (source) { + FirebaseRemoteConfig.VALUE_SOURCE_DEFAULT -> "default" + FirebaseRemoteConfig.VALUE_SOURCE_REMOTE -> "remote" + FirebaseRemoteConfig.VALUE_SOURCE_STATIC -> "static" + else -> "static" + } + } + + override fun onListen(arguments: Any, events: EventSink) { + val argumentsMap = arguments as Map + val appName = Objects.requireNonNull(argumentsMap["appName"]) as String + val remoteConfig = getRemoteConfig(appName) + + listenersMap[appName] = remoteConfig.addOnConfigUpdateListener( + object : ConfigUpdateListener { + override fun onUpdate(configUpdate: ConfigUpdate) { + val updatedKeys = ArrayList(configUpdate.updatedKeys) + mainThreadHandler.post { events.success(updatedKeys) } + } + + override fun onError(error: FirebaseRemoteConfigException) { + events.error("firebase_remote_config", error.message, null) + } + }) + } + + override fun onCancel(arguments: Any) { + // arguments will be null on hot restart, so we will clean up listeners in didReinitializeFirebaseCore() + val argumentsMap = arguments as Map + ?: return + val appName = Objects.requireNonNull(argumentsMap["appName"]) as String + + val listener = listenersMap[appName] + if (listener != null) { + listener.remove() + listenersMap.remove(appName) + } + } + + /** Remove all registered listeners. */ + private fun removeEventListeners() { + for (listener in listenersMap.values) { + listener.remove() + } + listenersMap.clear() + } + + private fun handleFailure (callback: (Result) -> Unit, exception: Exception?) { + val details: MutableMap = + HashMap() + if (exception is FirebaseRemoteConfigFetchThrottledException) { + details["code"] = "throttled" + details["message"] = "frequency of requests exceeds throttled limits" + } else if (exception is FirebaseRemoteConfigClientException) { + details["code"] = "internal" + details["message"] = "internal remote config fetch error" + } else if (exception is FirebaseRemoteConfigServerException) { + details["code"] = "remote-config-server-error" + details["message"] = exception.message + + val cause = exception.cause + if (cause != null) { + val causeMessage = cause.message + if (causeMessage != null && causeMessage.contains("Forbidden")) { + // Specific error code for 403 status code to indicate the request was forbidden. + details["code"] = "forbidden" + } + } + } else { + details["code"] = "unknown" + details["message"] = "unknown remote config error" + } + callback(Result.failure(FlutterError( "firebase_remote_config", + exception?.message, + details))) + } + + companion object { + const val TAG: String = "FRCPlugin" + const val METHOD_CHANNEL: String = "plugins.flutter.io/firebase_remote_config" + const val EVENT_CHANNEL: String = "plugins.flutter.io/firebase_remote_config_updated" + } + + override fun fetch(appName: String, callback: (Result) -> Unit) { + getRemoteConfig(appName).fetch().addOnCompleteListener { task -> + if(task.isSuccessful){ + callback(Result.success(Unit)) + } + else { + handleFailure(callback, task.exception) + } + } + } + + override fun fetchAndActivate(appName: String, callback: (Result) -> Unit) { + getRemoteConfig(appName).fetchAndActivate().addOnCompleteListener { task -> + if(task.isSuccessful){ + callback(Result.success(task.result)) + } + else { + handleFailure(callback, task.exception) + } + } + } + + override fun activate(appName: String, callback: (Result) -> Unit) { + getRemoteConfig(appName).activate().addOnCompleteListener { task -> + if(task.isSuccessful){ + callback(Result.success(task.result)) + } + else { + handleFailure(callback, task.exception) + } + } + } + + override fun setConfigSettings( + appName: String, + settings: RemoteConfigPigeonSettings, + callback: (Result) -> Unit + ) { + val configSettings = + FirebaseRemoteConfigSettings.Builder() + .setFetchTimeoutInSeconds(settings.fetchTimeoutSeconds) + .setMinimumFetchIntervalInSeconds(settings.minimumFetchIntervalSeconds) + .build() + getRemoteConfig(appName).setConfigSettingsAsync(configSettings).addOnCompleteListener { task -> + if(task.isSuccessful){ + callback(Result.success(Unit)) + } + else { + handleFailure(callback, task.exception) + } + } + } + + override fun setDefaults(appName: String, defaultParameters: Map, callback: (Result) -> Unit) { + getRemoteConfig( + appName + ).setDefaultsAsync(defaultParameters).addOnCompleteListener { task -> + if(task.isSuccessful){ + callback(Result.success(Unit)) + } + else { + handleFailure(callback, task.exception) + } + } + } + + override fun ensureInitialized(appName: String, callback: (Result) -> Unit) { + getRemoteConfig(appName).ensureInitialized().addOnCompleteListener { task -> + if(task.isSuccessful){ + callback(Result.success(Unit)) + } + else { + handleFailure(callback, task.exception) + } + } + } + + override fun setCustomSignals(appName: String, customSignals: Map, callback: (Result) -> Unit) { + val remoteConfig = getRemoteConfig(appName) + setCustomSignals(remoteConfig, customSignals).addOnCompleteListener {task-> + if(task.isSuccessful){ + callback(Result.success(Unit)) + } + else { + handleFailure(callback, task.exception) + } + } + } + + override fun getAll(appName: String, callback: (Result>) -> Unit) { + val remoteConfig = getRemoteConfig(appName) + callback(Result.success(parseParameters(remoteConfig.all))) + } + + override fun getProperties( + appName: String, + callback: (Result>) -> Unit + ) { + val remoteConfig = getRemoteConfig(appName) + val configProperties = getConfigProperties(remoteConfig) + callback(Result.success(configProperties)) + } +} diff --git a/packages/firebase_remote_config/firebase_remote_config/android/src/main/kotlin/io/flutter/plugins/firebase/firebaseremoteconfig/FlutterFirebaseAppRegistrar.kt b/packages/firebase_remote_config/firebase_remote_config/android/src/main/kotlin/io/flutter/plugins/firebase/firebaseremoteconfig/FlutterFirebaseAppRegistrar.kt new file mode 100644 index 000000000000..d758db99adda --- /dev/null +++ b/packages/firebase_remote_config/firebase_remote_config/android/src/main/kotlin/io/flutter/plugins/firebase/firebaseremoteconfig/FlutterFirebaseAppRegistrar.kt @@ -0,0 +1,18 @@ +// Copyright 2025 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. +package io.flutter.plugins.firebase.firebaseremoteconfig + +import androidx.annotation.Keep +import com.google.firebase.components.Component +import com.google.firebase.components.ComponentRegistrar +import com.google.firebase.platforminfo.LibraryVersionComponent + +@Keep +class FlutterFirebaseAppRegistrar : ComponentRegistrar { + override fun getComponents(): List> { + return listOf( + LibraryVersionComponent.create(BuildConfig.LIBRARY_NAME, BuildConfig.LIBRARY_VERSION) + ) + } +} diff --git a/packages/firebase_remote_config/firebase_remote_config/android/src/main/kotlin/io/flutter/plugins/firebase/firebaseremoteconfig/GeneratedAndroidFirebaseRemoteConfig.g.kt b/packages/firebase_remote_config/firebase_remote_config/android/src/main/kotlin/io/flutter/plugins/firebase/firebaseremoteconfig/GeneratedAndroidFirebaseRemoteConfig.g.kt new file mode 100644 index 000000000000..40c1056cf62c --- /dev/null +++ b/packages/firebase_remote_config/firebase_remote_config/android/src/main/kotlin/io/flutter/plugins/firebase/firebaseremoteconfig/GeneratedAndroidFirebaseRemoteConfig.g.kt @@ -0,0 +1,338 @@ +// Copyright 2025, 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 (v25.3.2), do not edit directly. +// See also: https://pub.dev/packages/pigeon +@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") + +package io.flutter.plugins.firebase.firebaseremoteconfig + +import android.util.Log +import io.flutter.plugin.common.BasicMessageChannel +import io.flutter.plugin.common.BinaryMessenger +import io.flutter.plugin.common.EventChannel +import io.flutter.plugin.common.MessageCodec +import io.flutter.plugin.common.StandardMethodCodec +import io.flutter.plugin.common.StandardMessageCodec +import java.io.ByteArrayOutputStream +import java.nio.ByteBuffer +private object GeneratedAndroidFirebaseRemoteConfigPigeonUtils { + + 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) + ) + } + } + fun deepEquals(a: Any?, b: Any?): Boolean { + if (a is ByteArray && b is ByteArray) { + return a.contentEquals(b) + } + if (a is IntArray && b is IntArray) { + return a.contentEquals(b) + } + if (a is LongArray && b is LongArray) { + return a.contentEquals(b) + } + if (a is DoubleArray && b is DoubleArray) { + return a.contentEquals(b) + } + if (a is Array<*> && b is Array<*>) { + return a.size == b.size && + a.indices.all{ deepEquals(a[it], b[it]) } + } + if (a is List<*> && b is List<*>) { + return a.size == b.size && + a.indices.all{ deepEquals(a[it], b[it]) } + } + if (a is Map<*, *> && b is Map<*, *>) { + return a.size == b.size && a.all { + (b as Map).containsKey(it.key) && + deepEquals(it.value, b[it.key]) + } + } + return a == b + } + +} + +/** + * 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 +) : Throwable() + +/** Generated class from Pigeon that represents data sent in messages. */ +data class RemoteConfigPigeonSettings ( + val fetchTimeoutSeconds: Long, + val minimumFetchIntervalSeconds: Long +) + { + companion object { + fun fromList(pigeonVar_list: List): RemoteConfigPigeonSettings { + val fetchTimeoutSeconds = pigeonVar_list[0] as Long + val minimumFetchIntervalSeconds = pigeonVar_list[1] as Long + return RemoteConfigPigeonSettings(fetchTimeoutSeconds, minimumFetchIntervalSeconds) + } + } + fun toList(): List { + return listOf( + fetchTimeoutSeconds, + minimumFetchIntervalSeconds, + ) + } + override fun equals(other: Any?): Boolean { + if (other !is RemoteConfigPigeonSettings) { + return false + } + if (this === other) { + return true + } + return GeneratedAndroidFirebaseRemoteConfigPigeonUtils.deepEquals(toList(), other.toList()) } + + override fun hashCode(): Int = toList().hashCode() +} +private open class GeneratedAndroidFirebaseRemoteConfigPigeonCodec : StandardMessageCodec() { + override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { + return when (type) { + 129.toByte() -> { + return (readValue(buffer) as? List)?.let { + RemoteConfigPigeonSettings.fromList(it) + } + } + else -> super.readValueOfType(type, buffer) + } + } + override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { + when (value) { + is RemoteConfigPigeonSettings -> { + stream.write(129) + writeValue(stream, value.toList()) + } + else -> super.writeValue(stream, value) + } + } +} + + +/** Generated interface from Pigeon that represents a handler of messages from Flutter. */ +interface FirebaseRemoteConfigHostApi { + fun fetch(appName: String, callback: (Result) -> Unit) + fun fetchAndActivate(appName: String, callback: (Result) -> Unit) + fun activate(appName: String, callback: (Result) -> Unit) + fun setConfigSettings(appName: String, settings: RemoteConfigPigeonSettings, callback: (Result) -> Unit) + fun setDefaults(appName: String, defaultParameters: Map, callback: (Result) -> Unit) + fun ensureInitialized(appName: String, callback: (Result) -> Unit) + fun setCustomSignals(appName: String, customSignals: Map, callback: (Result) -> Unit) + fun getAll(appName: String, callback: (Result>) -> Unit) + fun getProperties(appName: String, callback: (Result>) -> Unit) + + companion object { + /** The codec used by FirebaseRemoteConfigHostApi. */ + val codec: MessageCodec by lazy { + GeneratedAndroidFirebaseRemoteConfigPigeonCodec() + } + /** Sets up an instance of `FirebaseRemoteConfigHostApi` to handle messages through the `binaryMessenger`. */ + @JvmOverloads + fun setUp(binaryMessenger: BinaryMessenger, api: FirebaseRemoteConfigHostApi?, messageChannelSuffix: String = "") { + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.fetch$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val appNameArg = args[0] as String + api.fetch(appNameArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(GeneratedAndroidFirebaseRemoteConfigPigeonUtils.wrapError(error)) + } else { + reply.reply(GeneratedAndroidFirebaseRemoteConfigPigeonUtils.wrapResult(null)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.fetchAndActivate$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val appNameArg = args[0] as String + api.fetchAndActivate(appNameArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(GeneratedAndroidFirebaseRemoteConfigPigeonUtils.wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(GeneratedAndroidFirebaseRemoteConfigPigeonUtils.wrapResult(data)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.activate$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val appNameArg = args[0] as String + api.activate(appNameArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(GeneratedAndroidFirebaseRemoteConfigPigeonUtils.wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(GeneratedAndroidFirebaseRemoteConfigPigeonUtils.wrapResult(data)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setConfigSettings$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val appNameArg = args[0] as String + val settingsArg = args[1] as RemoteConfigPigeonSettings + api.setConfigSettings(appNameArg, settingsArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(GeneratedAndroidFirebaseRemoteConfigPigeonUtils.wrapError(error)) + } else { + reply.reply(GeneratedAndroidFirebaseRemoteConfigPigeonUtils.wrapResult(null)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setDefaults$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val appNameArg = args[0] as String + val defaultParametersArg = args[1] as Map + api.setDefaults(appNameArg, defaultParametersArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(GeneratedAndroidFirebaseRemoteConfigPigeonUtils.wrapError(error)) + } else { + reply.reply(GeneratedAndroidFirebaseRemoteConfigPigeonUtils.wrapResult(null)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.ensureInitialized$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val appNameArg = args[0] as String + api.ensureInitialized(appNameArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(GeneratedAndroidFirebaseRemoteConfigPigeonUtils.wrapError(error)) + } else { + reply.reply(GeneratedAndroidFirebaseRemoteConfigPigeonUtils.wrapResult(null)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setCustomSignals$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val appNameArg = args[0] as String + val customSignalsArg = args[1] as Map + api.setCustomSignals(appNameArg, customSignalsArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(GeneratedAndroidFirebaseRemoteConfigPigeonUtils.wrapError(error)) + } else { + reply.reply(GeneratedAndroidFirebaseRemoteConfigPigeonUtils.wrapResult(null)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.getAll$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val appNameArg = args[0] as String + api.getAll(appNameArg) { result: Result> -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(GeneratedAndroidFirebaseRemoteConfigPigeonUtils.wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(GeneratedAndroidFirebaseRemoteConfigPigeonUtils.wrapResult(data)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.getProperties$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val appNameArg = args[0] as String + api.getProperties(appNameArg) { result: Result> -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(GeneratedAndroidFirebaseRemoteConfigPigeonUtils.wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(GeneratedAndroidFirebaseRemoteConfigPigeonUtils.wrapResult(data)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + } + } +} diff --git a/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigMessages.g.swift b/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigMessages.g.swift new file mode 100644 index 000000000000..4c6dae252f41 --- /dev/null +++ b/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigMessages.g.swift @@ -0,0 +1,422 @@ +// Copyright 2025, 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 (v25.3.2), 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)", + "\(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? +} + +func deepEqualsFirebaseRemoteConfigMessages(_ lhs: Any?, _ rhs: Any?) -> Bool { + let cleanLhs = nilOrValue(lhs) as Any? + let cleanRhs = nilOrValue(rhs) as Any? + switch (cleanLhs, cleanRhs) { + case (nil, nil): + return true + + case (nil, _), (_, nil): + return false + + case is (Void, Void): + return true + + case let (cleanLhsHashable, cleanRhsHashable) as (AnyHashable, AnyHashable): + return cleanLhsHashable == cleanRhsHashable + + case let (cleanLhsArray, cleanRhsArray) as ([Any?], [Any?]): + guard cleanLhsArray.count == cleanRhsArray.count else { return false } + for (index, element) in cleanLhsArray.enumerated() { + if !deepEqualsFirebaseRemoteConfigMessages(element, cleanRhsArray[index]) { + return false + } + } + return true + + case let (cleanLhsDictionary, cleanRhsDictionary) as ([AnyHashable: Any?], [AnyHashable: Any?]): + guard cleanLhsDictionary.count == cleanRhsDictionary.count else { return false } + for (key, cleanLhsValue) in cleanLhsDictionary { + guard cleanRhsDictionary.index(forKey: key) != nil else { return false } + if !deepEqualsFirebaseRemoteConfigMessages(cleanLhsValue, cleanRhsDictionary[key]!) { + return false + } + } + return true + + default: + // Any other type shouldn't be able to be used with pigeon. File an issue if you find this to be + // untrue. + return false + } +} + +func deepHashFirebaseRemoteConfigMessages(value: Any?, hasher: inout Hasher) { + if let valueList = value as? [AnyHashable] { + for item in valueList { + deepHashFirebaseRemoteConfigMessages(value: item, hasher: &hasher) + } + return + } + + if let valueDict = value as? [AnyHashable: AnyHashable] { + for key in valueDict.keys { + hasher.combine(key) + deepHashFirebaseRemoteConfigMessages(value: valueDict[key]!, hasher: &hasher) + } + return + } + + if let hashableValue = value as? AnyHashable { + hasher.combine(hashableValue.hashValue) + } + + return hasher.combine(String(describing: value)) +} + +/// Generated class from Pigeon that represents data sent in messages. +struct RemoteConfigPigeonSettings: Hashable { + var fetchTimeoutSeconds: Int64 + var minimumFetchIntervalSeconds: Int64 + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> RemoteConfigPigeonSettings? { + let fetchTimeoutSeconds = pigeonVar_list[0] as! Int64 + let minimumFetchIntervalSeconds = pigeonVar_list[1] as! Int64 + + return RemoteConfigPigeonSettings( + fetchTimeoutSeconds: fetchTimeoutSeconds, + minimumFetchIntervalSeconds: minimumFetchIntervalSeconds + ) + } + + func toList() -> [Any?] { + [ + fetchTimeoutSeconds, + minimumFetchIntervalSeconds, + ] + } + + static func == (lhs: RemoteConfigPigeonSettings, rhs: RemoteConfigPigeonSettings) -> Bool { + deepEqualsFirebaseRemoteConfigMessages(lhs.toList(), rhs.toList()) + } + + func hash(into hasher: inout Hasher) { + deepHashFirebaseRemoteConfigMessages(value: toList(), hasher: &hasher) + } +} + +private class FirebaseRemoteConfigMessagesPigeonCodecReader: FlutterStandardReader { + override func readValue(ofType type: UInt8) -> Any? { + switch type { + case 129: + return RemoteConfigPigeonSettings.fromList(readValue() as! [Any?]) + default: + return super.readValue(ofType: type) + } + } +} + +private class FirebaseRemoteConfigMessagesPigeonCodecWriter: FlutterStandardWriter { + override func writeValue(_ value: Any) { + if let value = value as? RemoteConfigPigeonSettings { + super.writeByte(129) + super.writeValue(value.toList()) + } else { + super.writeValue(value) + } + } +} + +private class FirebaseRemoteConfigMessagesPigeonCodecReaderWriter: FlutterStandardReaderWriter { + override func reader(with data: Data) -> FlutterStandardReader { + FirebaseRemoteConfigMessagesPigeonCodecReader(data: data) + } + + override func writer(with data: NSMutableData) -> FlutterStandardWriter { + FirebaseRemoteConfigMessagesPigeonCodecWriter(data: data) + } +} + +class FirebaseRemoteConfigMessagesPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable { + static let shared = + FirebaseRemoteConfigMessagesPigeonCodec( + readerWriter: FirebaseRemoteConfigMessagesPigeonCodecReaderWriter() + ) +} + +/// Generated protocol from Pigeon that represents a handler of messages from Flutter. +protocol FirebaseRemoteConfigHostApi { + func fetch(appName: String, completion: @escaping (Result) -> Void) + func fetchAndActivate(appName: String, completion: @escaping (Result) -> Void) + func activate(appName: String, completion: @escaping (Result) -> Void) + func setConfigSettings(appName: String, settings: RemoteConfigPigeonSettings, + completion: @escaping (Result) -> Void) + func setDefaults(appName: String, defaultParameters: [String: Any?], + completion: @escaping (Result) -> Void) + func ensureInitialized(appName: String, completion: @escaping (Result) -> Void) + func setCustomSignals(appName: String, customSignals: [String: Any?], + completion: @escaping (Result) -> Void) + func getAll(appName: String, completion: @escaping (Result<[String: Any?], Error>) -> Void) + func getProperties(appName: String, completion: @escaping (Result<[String: Any], Error>) -> Void) +} + +/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. +class FirebaseRemoteConfigHostApiSetup { + static var codec: FlutterStandardMessageCodec { FirebaseRemoteConfigMessagesPigeonCodec.shared } + /// Sets up an instance of `FirebaseRemoteConfigHostApi` to handle messages through the + /// `binaryMessenger`. + static func setUp(binaryMessenger: FlutterBinaryMessenger, api: FirebaseRemoteConfigHostApi?, + messageChannelSuffix: String = "") { + let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" + let fetchChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.fetch\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + fetchChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appNameArg = args[0] as! String + api.fetch(appName: appNameArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case let .failure(error): + reply(wrapError(error)) + } + } + } + } else { + fetchChannel.setMessageHandler(nil) + } + let fetchAndActivateChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.fetchAndActivate\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + fetchAndActivateChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appNameArg = args[0] as! String + api.fetchAndActivate(appName: appNameArg) { result in + switch result { + case let .success(res): + reply(wrapResult(res)) + case let .failure(error): + reply(wrapError(error)) + } + } + } + } else { + fetchAndActivateChannel.setMessageHandler(nil) + } + let activateChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.activate\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + activateChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appNameArg = args[0] as! String + api.activate(appName: appNameArg) { result in + switch result { + case let .success(res): + reply(wrapResult(res)) + case let .failure(error): + reply(wrapError(error)) + } + } + } + } else { + activateChannel.setMessageHandler(nil) + } + let setConfigSettingsChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setConfigSettings\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + setConfigSettingsChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appNameArg = args[0] as! String + let settingsArg = args[1] as! RemoteConfigPigeonSettings + api.setConfigSettings(appName: appNameArg, settings: settingsArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case let .failure(error): + reply(wrapError(error)) + } + } + } + } else { + setConfigSettingsChannel.setMessageHandler(nil) + } + let setDefaultsChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setDefaults\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + setDefaultsChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appNameArg = args[0] as! String + let defaultParametersArg = args[1] as! [String: Any?] + api.setDefaults(appName: appNameArg, defaultParameters: defaultParametersArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case let .failure(error): + reply(wrapError(error)) + } + } + } + } else { + setDefaultsChannel.setMessageHandler(nil) + } + let ensureInitializedChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.ensureInitialized\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + ensureInitializedChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appNameArg = args[0] as! String + api.ensureInitialized(appName: appNameArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case let .failure(error): + reply(wrapError(error)) + } + } + } + } else { + ensureInitializedChannel.setMessageHandler(nil) + } + let setCustomSignalsChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setCustomSignals\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + setCustomSignalsChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appNameArg = args[0] as! String + let customSignalsArg = args[1] as! [String: Any?] + api.setCustomSignals(appName: appNameArg, customSignals: customSignalsArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case let .failure(error): + reply(wrapError(error)) + } + } + } + } else { + setCustomSignalsChannel.setMessageHandler(nil) + } + let getAllChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.getAll\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + getAllChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appNameArg = args[0] as! String + api.getAll(appName: appNameArg) { result in + switch result { + case let .success(res): + reply(wrapResult(res)) + case let .failure(error): + reply(wrapError(error)) + } + } + } + } else { + getAllChannel.setMessageHandler(nil) + } + let getPropertiesChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.getProperties\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { + getPropertiesChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let appNameArg = args[0] as! String + api.getProperties(appName: appNameArg) { result in + switch result { + case let .success(res): + reply(wrapResult(res)) + case let .failure(error): + reply(wrapError(error)) + } + } + } + } else { + getPropertiesChannel.setMessageHandler(nil) + } + } +} diff --git a/packages/firebase_remote_config/firebase_remote_config/windows/messages.g.cpp b/packages/firebase_remote_config/firebase_remote_config/windows/messages.g.cpp new file mode 100644 index 000000000000..f8f200289fff --- /dev/null +++ b/packages/firebase_remote_config/firebase_remote_config/windows/messages.g.cpp @@ -0,0 +1,510 @@ +// Copyright 2025, 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 (v25.3.2), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +#undef _HAS_EXCEPTIONS + +#include "messages.g.h" + +#include +#include +#include +#include + +#include +#include +#include + +namespace firebase_remote_config_windows { +using flutter::BasicMessageChannel; +using flutter::CustomEncodableValue; +using flutter::EncodableList; +using flutter::EncodableMap; +using flutter::EncodableValue; + +FlutterError CreateConnectionError(const std::string channel_name) { + return FlutterError( + "channel-error", + "Unable to establish connection on channel: '" + channel_name + "'.", + EncodableValue("")); +} + +// RemoteConfigPigeonSettings + +RemoteConfigPigeonSettings::RemoteConfigPigeonSettings( + int64_t fetch_timeout_seconds, int64_t minimum_fetch_interval_seconds) + : fetch_timeout_seconds_(fetch_timeout_seconds), + minimum_fetch_interval_seconds_(minimum_fetch_interval_seconds) {} + +int64_t RemoteConfigPigeonSettings::fetch_timeout_seconds() const { + return fetch_timeout_seconds_; +} + +void RemoteConfigPigeonSettings::set_fetch_timeout_seconds(int64_t value_arg) { + fetch_timeout_seconds_ = value_arg; +} + +int64_t RemoteConfigPigeonSettings::minimum_fetch_interval_seconds() const { + return minimum_fetch_interval_seconds_; +} + +void RemoteConfigPigeonSettings::set_minimum_fetch_interval_seconds( + int64_t value_arg) { + minimum_fetch_interval_seconds_ = value_arg; +} + +EncodableList RemoteConfigPigeonSettings::ToEncodableList() const { + EncodableList list; + list.reserve(2); + list.push_back(EncodableValue(fetch_timeout_seconds_)); + list.push_back(EncodableValue(minimum_fetch_interval_seconds_)); + return list; +} + +RemoteConfigPigeonSettings RemoteConfigPigeonSettings::FromEncodableList( + const EncodableList& list) { + RemoteConfigPigeonSettings decoded(std::get(list[0]), + std::get(list[1])); + return decoded; +} + +PigeonInternalCodecSerializer::PigeonInternalCodecSerializer() {} + +EncodableValue PigeonInternalCodecSerializer::ReadValueOfType( + uint8_t type, flutter::ByteStreamReader* stream) const { + switch (type) { + case 129: { + return CustomEncodableValue(RemoteConfigPigeonSettings::FromEncodableList( + std::get(ReadValue(stream)))); + } + default: + return flutter::StandardCodecSerializer::ReadValueOfType(type, stream); + } +} + +void PigeonInternalCodecSerializer::WriteValue( + const EncodableValue& value, flutter::ByteStreamWriter* stream) const { + if (const CustomEncodableValue* custom_value = + std::get_if(&value)) { + if (custom_value->type() == typeid(RemoteConfigPigeonSettings)) { + stream->WriteByte(129); + WriteValue(EncodableValue( + std::any_cast(*custom_value) + .ToEncodableList()), + stream); + return; + } + } + flutter::StandardCodecSerializer::WriteValue(value, stream); +} + +/// The codec used by FirebaseRemoteConfigHostApi. +const flutter::StandardMessageCodec& FirebaseRemoteConfigHostApi::GetCodec() { + return flutter::StandardMessageCodec::GetInstance( + &PigeonInternalCodecSerializer::GetInstance()); +} + +// Sets up an instance of `FirebaseRemoteConfigHostApi` to handle messages +// through the `binary_messenger`. +void FirebaseRemoteConfigHostApi::SetUp( + flutter::BinaryMessenger* binary_messenger, + FirebaseRemoteConfigHostApi* api) { + FirebaseRemoteConfigHostApi::SetUp(binary_messenger, api, ""); +} + +void FirebaseRemoteConfigHostApi::SetUp( + flutter::BinaryMessenger* binary_messenger, + FirebaseRemoteConfigHostApi* api, + const std::string& message_channel_suffix) { + const std::string prepended_suffix = + message_channel_suffix.length() > 0 + ? std::string(".") + message_channel_suffix + : ""; + { + BasicMessageChannel<> channel( + binary_messenger, + "dev.flutter.pigeon.firebase_remote_config_platform_interface." + "FirebaseRemoteConfigHostApi.fetch" + + prepended_suffix, + &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler( + [api](const EncodableValue& message, + const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_app_name_arg = args.at(0); + if (encodable_app_name_arg.IsNull()) { + reply(WrapError("app_name_arg unexpectedly null.")); + return; + } + const auto& app_name_arg = + std::get(encodable_app_name_arg); + api->Fetch(app_name_arg, + [reply](std::optional&& output) { + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel( + binary_messenger, + "dev.flutter.pigeon.firebase_remote_config_platform_interface." + "FirebaseRemoteConfigHostApi.fetchAndActivate" + + prepended_suffix, + &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler( + [api](const EncodableValue& message, + const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_app_name_arg = args.at(0); + if (encodable_app_name_arg.IsNull()) { + reply(WrapError("app_name_arg unexpectedly null.")); + return; + } + const auto& app_name_arg = + std::get(encodable_app_name_arg); + api->FetchAndActivate( + app_name_arg, [reply](ErrorOr&& output) { + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + EncodableList wrapped; + wrapped.push_back( + EncodableValue(std::move(output).TakeValue())); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel( + binary_messenger, + "dev.flutter.pigeon.firebase_remote_config_platform_interface." + "FirebaseRemoteConfigHostApi.activate" + + prepended_suffix, + &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler( + [api](const EncodableValue& message, + const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_app_name_arg = args.at(0); + if (encodable_app_name_arg.IsNull()) { + reply(WrapError("app_name_arg unexpectedly null.")); + return; + } + const auto& app_name_arg = + std::get(encodable_app_name_arg); + api->Activate(app_name_arg, [reply](ErrorOr&& output) { + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + EncodableList wrapped; + wrapped.push_back( + EncodableValue(std::move(output).TakeValue())); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel( + binary_messenger, + "dev.flutter.pigeon.firebase_remote_config_platform_interface." + "FirebaseRemoteConfigHostApi.setConfigSettings" + + prepended_suffix, + &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler( + [api](const EncodableValue& message, + const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_app_name_arg = args.at(0); + if (encodable_app_name_arg.IsNull()) { + reply(WrapError("app_name_arg unexpectedly null.")); + return; + } + const auto& app_name_arg = + std::get(encodable_app_name_arg); + const auto& encodable_settings_arg = args.at(1); + if (encodable_settings_arg.IsNull()) { + reply(WrapError("settings_arg unexpectedly null.")); + return; + } + const auto& settings_arg = + std::any_cast( + std::get(encodable_settings_arg)); + api->SetConfigSettings( + app_name_arg, settings_arg, + [reply](std::optional&& output) { + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel( + binary_messenger, + "dev.flutter.pigeon.firebase_remote_config_platform_interface." + "FirebaseRemoteConfigHostApi.setDefaults" + + prepended_suffix, + &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler( + [api](const EncodableValue& message, + const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_app_name_arg = args.at(0); + if (encodable_app_name_arg.IsNull()) { + reply(WrapError("app_name_arg unexpectedly null.")); + return; + } + const auto& app_name_arg = + std::get(encodable_app_name_arg); + const auto& encodable_default_parameters_arg = args.at(1); + if (encodable_default_parameters_arg.IsNull()) { + reply(WrapError("default_parameters_arg unexpectedly null.")); + return; + } + const auto& default_parameters_arg = + std::get(encodable_default_parameters_arg); + api->SetDefaults(app_name_arg, default_parameters_arg, + [reply](std::optional&& output) { + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel( + binary_messenger, + "dev.flutter.pigeon.firebase_remote_config_platform_interface." + "FirebaseRemoteConfigHostApi.ensureInitialized" + + prepended_suffix, + &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler( + [api](const EncodableValue& message, + const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_app_name_arg = args.at(0); + if (encodable_app_name_arg.IsNull()) { + reply(WrapError("app_name_arg unexpectedly null.")); + return; + } + const auto& app_name_arg = + std::get(encodable_app_name_arg); + api->EnsureInitialized( + app_name_arg, [reply](std::optional&& output) { + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel( + binary_messenger, + "dev.flutter.pigeon.firebase_remote_config_platform_interface." + "FirebaseRemoteConfigHostApi.setCustomSignals" + + prepended_suffix, + &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler( + [api](const EncodableValue& message, + const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_app_name_arg = args.at(0); + if (encodable_app_name_arg.IsNull()) { + reply(WrapError("app_name_arg unexpectedly null.")); + return; + } + const auto& app_name_arg = + std::get(encodable_app_name_arg); + const auto& encodable_custom_signals_arg = args.at(1); + if (encodable_custom_signals_arg.IsNull()) { + reply(WrapError("custom_signals_arg unexpectedly null.")); + return; + } + const auto& custom_signals_arg = + std::get(encodable_custom_signals_arg); + api->SetCustomSignals( + app_name_arg, custom_signals_arg, + [reply](std::optional&& output) { + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel( + binary_messenger, + "dev.flutter.pigeon.firebase_remote_config_platform_interface." + "FirebaseRemoteConfigHostApi.getAll" + + prepended_suffix, + &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler( + [api](const EncodableValue& message, + const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_app_name_arg = args.at(0); + if (encodable_app_name_arg.IsNull()) { + reply(WrapError("app_name_arg unexpectedly null.")); + return; + } + const auto& app_name_arg = + std::get(encodable_app_name_arg); + api->GetAll(app_name_arg, + [reply](ErrorOr&& output) { + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + EncodableList wrapped; + wrapped.push_back( + EncodableValue(std::move(output).TakeValue())); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel( + binary_messenger, + "dev.flutter.pigeon.firebase_remote_config_platform_interface." + "FirebaseRemoteConfigHostApi.getProperties" + + prepended_suffix, + &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler( + [api](const EncodableValue& message, + const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_app_name_arg = args.at(0); + if (encodable_app_name_arg.IsNull()) { + reply(WrapError("app_name_arg unexpectedly null.")); + return; + } + const auto& app_name_arg = + std::get(encodable_app_name_arg); + api->GetProperties( + app_name_arg, [reply](ErrorOr&& output) { + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + EncodableList wrapped; + wrapped.push_back( + EncodableValue(std::move(output).TakeValue())); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } +} + +EncodableValue FirebaseRemoteConfigHostApi::WrapError( + std::string_view error_message) { + return EncodableValue( + EncodableList{EncodableValue(std::string(error_message)), + EncodableValue("Error"), EncodableValue()}); +} + +EncodableValue FirebaseRemoteConfigHostApi::WrapError( + const FlutterError& error) { + return EncodableValue(EncodableList{EncodableValue(error.code()), + EncodableValue(error.message()), + error.details()}); +} + +} // namespace firebase_remote_config_windows diff --git a/packages/firebase_remote_config/firebase_remote_config/windows/messages.g.h b/packages/firebase_remote_config/firebase_remote_config/windows/messages.g.h new file mode 100644 index 000000000000..c325943b6d22 --- /dev/null +++ b/packages/firebase_remote_config/firebase_remote_config/windows/messages.g.h @@ -0,0 +1,152 @@ +// Copyright 2025, 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 (v25.3.2), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +#ifndef PIGEON_MESSAGES_G_H_ +#define PIGEON_MESSAGES_G_H_ +#include +#include +#include +#include + +#include +#include +#include + +namespace firebase_remote_config_windows { + +// Generated class from Pigeon. + +class FlutterError { + public: + explicit FlutterError(const std::string& code) : code_(code) {} + explicit FlutterError(const std::string& code, const std::string& message) + : code_(code), message_(message) {} + explicit FlutterError(const std::string& code, const std::string& message, + const flutter::EncodableValue& details) + : code_(code), message_(message), details_(details) {} + + const std::string& code() const { return code_; } + const std::string& message() const { return message_; } + const flutter::EncodableValue& details() const { return details_; } + + private: + std::string code_; + std::string message_; + flutter::EncodableValue details_; +}; + +template +class ErrorOr { + public: + ErrorOr(const T& rhs) : v_(rhs) {} + ErrorOr(const T&& rhs) : v_(std::move(rhs)) {} + ErrorOr(const FlutterError& rhs) : v_(rhs) {} + ErrorOr(const FlutterError&& rhs) : v_(std::move(rhs)) {} + + bool has_error() const { return std::holds_alternative(v_); } + const T& value() const { return std::get(v_); }; + const FlutterError& error() const { return std::get(v_); }; + + private: + friend class FirebaseRemoteConfigHostApi; + ErrorOr() = default; + T TakeValue() && { return std::get(std::move(v_)); } + + std::variant v_; +}; + +// Generated class from Pigeon that represents data sent in messages. +class RemoteConfigPigeonSettings { + public: + // Constructs an object setting all fields. + explicit RemoteConfigPigeonSettings(int64_t fetch_timeout_seconds, + int64_t minimum_fetch_interval_seconds); + + int64_t fetch_timeout_seconds() const; + void set_fetch_timeout_seconds(int64_t value_arg); + + int64_t minimum_fetch_interval_seconds() const; + void set_minimum_fetch_interval_seconds(int64_t value_arg); + + private: + static RemoteConfigPigeonSettings FromEncodableList( + const flutter::EncodableList& list); + flutter::EncodableList ToEncodableList() const; + friend class FirebaseRemoteConfigHostApi; + friend class PigeonInternalCodecSerializer; + int64_t fetch_timeout_seconds_; + int64_t minimum_fetch_interval_seconds_; +}; + +class PigeonInternalCodecSerializer : public flutter::StandardCodecSerializer { + public: + PigeonInternalCodecSerializer(); + inline static PigeonInternalCodecSerializer& GetInstance() { + static PigeonInternalCodecSerializer sInstance; + return sInstance; + } + + void WriteValue(const flutter::EncodableValue& value, + flutter::ByteStreamWriter* stream) const override; + + protected: + flutter::EncodableValue ReadValueOfType( + uint8_t type, flutter::ByteStreamReader* stream) const override; +}; + +// Generated interface from Pigeon that represents a handler of messages from +// Flutter. +class FirebaseRemoteConfigHostApi { + public: + FirebaseRemoteConfigHostApi(const FirebaseRemoteConfigHostApi&) = delete; + FirebaseRemoteConfigHostApi& operator=(const FirebaseRemoteConfigHostApi&) = + delete; + virtual ~FirebaseRemoteConfigHostApi() {} + virtual void Fetch( + const std::string& app_name, + std::function reply)> result) = 0; + virtual void FetchAndActivate( + const std::string& app_name, + std::function reply)> result) = 0; + virtual void Activate(const std::string& app_name, + std::function reply)> result) = 0; + virtual void SetConfigSettings( + const std::string& app_name, const RemoteConfigPigeonSettings& settings, + std::function reply)> result) = 0; + virtual void SetDefaults( + const std::string& app_name, + const flutter::EncodableMap& default_parameters, + std::function reply)> result) = 0; + virtual void EnsureInitialized( + const std::string& app_name, + std::function reply)> result) = 0; + virtual void SetCustomSignals( + const std::string& app_name, const flutter::EncodableMap& custom_signals, + std::function reply)> result) = 0; + virtual void GetAll( + const std::string& app_name, + std::function reply)> result) = 0; + virtual void GetProperties( + const std::string& app_name, + std::function reply)> result) = 0; + + // The codec used by FirebaseRemoteConfigHostApi. + static const flutter::StandardMessageCodec& GetCodec(); + // Sets up an instance of `FirebaseRemoteConfigHostApi` to handle messages + // through the `binary_messenger`. + static void SetUp(flutter::BinaryMessenger* binary_messenger, + FirebaseRemoteConfigHostApi* api); + static void SetUp(flutter::BinaryMessenger* binary_messenger, + FirebaseRemoteConfigHostApi* api, + const std::string& message_channel_suffix); + static flutter::EncodableValue WrapError(std::string_view error_message); + static flutter::EncodableValue WrapError(const FlutterError& error); + + protected: + FirebaseRemoteConfigHostApi() = default; +}; +} // namespace firebase_remote_config_windows +#endif // PIGEON_MESSAGES_G_H_ diff --git a/packages/firebase_remote_config/firebase_remote_config_platform_interface/lib/src/method_channel/method_channel_firebase_remote_config.dart b/packages/firebase_remote_config/firebase_remote_config_platform_interface/lib/src/method_channel/method_channel_firebase_remote_config.dart index 96facbb25e81..23f155751e98 100644 --- a/packages/firebase_remote_config/firebase_remote_config_platform_interface/lib/src/method_channel/method_channel_firebase_remote_config.dart +++ b/packages/firebase_remote_config/firebase_remote_config_platform_interface/lib/src/method_channel/method_channel_firebase_remote_config.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_remote_config_platform_interface/src/pigeon/messages.pigeon.dart'; import 'package:flutter/services.dart'; import '../../firebase_remote_config_platform_interface.dart'; @@ -48,6 +49,8 @@ class MethodChannelFirebaseRemoteConfig extends FirebaseRemoteConfigPlatform { late DateTime _lastFetchTime; late RemoteConfigFetchStatus _lastFetchStatus; + final _api = FirebaseRemoteConfigHostApi(); + /// Gets a [FirebaseRemoteConfigPlatform] instance for a specific /// [FirebaseApp]. /// @@ -107,10 +110,7 @@ class MethodChannelFirebaseRemoteConfig extends FirebaseRemoteConfigPlatform { @override Future ensureInitialized() async { try { - await channel.invokeMethod( - 'RemoteConfig#ensureInitialized', { - 'appName': app.name, - }); + await _api.ensureInitialized(app.name); } catch (exception, stackTrace) { convertPlatformException(exception, stackTrace); } @@ -119,12 +119,9 @@ class MethodChannelFirebaseRemoteConfig extends FirebaseRemoteConfigPlatform { @override Future activate() async { try { - bool? configChanged = await channel - .invokeMethod('RemoteConfig#activate', { - 'appName': app.name, - }); + bool configChanged = await _api.activate(app.name); await _updateConfigParameters(); - return configChanged!; + return configChanged; } catch (exception, stackTrace) { convertPlatformException(exception, stackTrace); } @@ -133,9 +130,7 @@ class MethodChannelFirebaseRemoteConfig extends FirebaseRemoteConfigPlatform { @override Future fetch() async { try { - await channel.invokeMethod('RemoteConfig#fetch', { - 'appName': app.name, - }); + await _api.fetch(app.name); await _updateConfigProperties(); } catch (exception, stackTrace) { // Ensure that fetch status is updated. @@ -147,13 +142,10 @@ class MethodChannelFirebaseRemoteConfig extends FirebaseRemoteConfigPlatform { @override Future fetchAndActivate() async { try { - bool? configChanged = await channel.invokeMethod( - 'RemoteConfig#fetchAndActivate', { - 'appName': app.name, - }); + bool configChanged = await _api.fetchAndActivate(app.name); await _updateConfigParameters(); await _updateConfigProperties(); - return configChanged!; + return configChanged; } catch (exception, stackTrace) { // Ensure that fetch status is updated. await _updateConfigProperties(); @@ -211,13 +203,14 @@ class MethodChannelFirebaseRemoteConfig extends FirebaseRemoteConfigPlatform { RemoteConfigSettings remoteConfigSettings, ) async { try { - await channel - .invokeMethod('RemoteConfig#setConfigSettings', { - 'appName': app.name, - 'fetchTimeout': remoteConfigSettings.fetchTimeout.inSeconds, - 'minimumFetchInterval': - remoteConfigSettings.minimumFetchInterval.inSeconds, - }); + await _api.setConfigSettings( + app.name, + RemoteConfigPigeonSettings( + fetchTimeoutSeconds: remoteConfigSettings.fetchTimeout.inSeconds, + minimumFetchIntervalSeconds: + remoteConfigSettings.minimumFetchInterval.inSeconds, + ), + ); await _updateConfigProperties(); } catch (exception, stackTrace) { convertPlatformException(exception, stackTrace); @@ -227,10 +220,7 @@ class MethodChannelFirebaseRemoteConfig extends FirebaseRemoteConfigPlatform { @override Future setDefaults(Map defaultParameters) async { try { - await channel.invokeMethod('RemoteConfig#setDefaults', { - 'appName': app.name, - 'defaults': defaultParameters - }); + await _api.setDefaults(app.name, defaultParameters); await _updateConfigParameters(); } catch (exception, stackTrace) { convertPlatformException(exception, stackTrace); @@ -238,21 +228,13 @@ class MethodChannelFirebaseRemoteConfig extends FirebaseRemoteConfigPlatform { } Future _updateConfigParameters() async { - Map? parameters = await channel - .invokeMapMethod( - 'RemoteConfig#getAll', { - 'appName': app.name, - }); - _activeParameters = _parseParameters(parameters!); + Map parameters = await _api.getAll(app.name); + _activeParameters = _parseParameters(parameters); } Future _updateConfigProperties() async { - Map? properties = await channel - .invokeMapMethod( - 'RemoteConfig#getProperties', { - 'appName': app.name, - }); - final fetchTimeout = Duration(seconds: properties!['fetchTimeout']); + Map properties = await _api.getProperties(app.name); + final fetchTimeout = Duration(seconds: properties['fetchTimeout']); final minimumFetchInterval = Duration(seconds: properties['minimumFetchInterval']); final lastFetchMillis = properties['lastFetchTime']; @@ -310,13 +292,7 @@ class MethodChannelFirebaseRemoteConfig extends FirebaseRemoteConfigPlatform { @override Future setCustomSignals(Map customSignals) { try { - return channel.invokeMethod( - 'RemoteConfig#setCustomSignals', - { - 'appName': app.name, - 'customSignals': customSignals, - }, - ); + return _api.setCustomSignals(app.name, customSignals); } catch (exception, stackTrace) { convertPlatformException(exception, stackTrace); } diff --git a/packages/firebase_remote_config/firebase_remote_config_platform_interface/lib/src/pigeon/messages.pigeon.dart b/packages/firebase_remote_config/firebase_remote_config_platform_interface/lib/src/pigeon/messages.pigeon.dart new file mode 100644 index 000000000000..c9ed1ec45a6a --- /dev/null +++ b/packages/firebase_remote_config/firebase_remote_config_platform_interface/lib/src/pigeon/messages.pigeon.dart @@ -0,0 +1,393 @@ +// Copyright 2025, 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 (v25.3.2), 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, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers + +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'; + +PlatformException _createConnectionError(String channelName) { + return PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); +} + +List wrapResponse( + {Object? result, PlatformException? error, bool empty = false}) { + if (empty) { + return []; + } + if (error == null) { + return [result]; + } + return [error.code, error.message, error.details]; +} + +bool _deepEquals(Object? a, Object? b) { + if (a is List && b is List) { + return a.length == b.length && + a.indexed + .every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); + } + if (a is Map && b is Map) { + return a.length == b.length && + a.entries.every((MapEntry entry) => + (b as Map).containsKey(entry.key) && + _deepEquals(entry.value, b[entry.key])); + } + return a == b; +} + +class RemoteConfigPigeonSettings { + RemoteConfigPigeonSettings({ + required this.fetchTimeoutSeconds, + required this.minimumFetchIntervalSeconds, + }); + + int fetchTimeoutSeconds; + + int minimumFetchIntervalSeconds; + + List _toList() { + return [ + fetchTimeoutSeconds, + minimumFetchIntervalSeconds, + ]; + } + + Object encode() { + return _toList(); + } + + static RemoteConfigPigeonSettings decode(Object result) { + result as List; + return RemoteConfigPigeonSettings( + fetchTimeoutSeconds: result[0]! as int, + minimumFetchIntervalSeconds: result[1]! as int, + ); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! RemoteConfigPigeonSettings || + other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(encode(), other.encode()); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()); +} + +class _PigeonCodec extends StandardMessageCodec { + const _PigeonCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is int) { + buffer.putUint8(4); + buffer.putInt64(value); + } else if (value is RemoteConfigPigeonSettings) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 129: + return RemoteConfigPigeonSettings.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +class FirebaseRemoteConfigHostApi { + /// Constructor for [FirebaseRemoteConfigHostApi]. 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. + FirebaseRemoteConfigHostApi( + {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 fetch(String appName) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.fetch$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([appName]); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + + Future fetchAndActivate(String appName) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.fetchAndActivate$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([appName]); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as bool?)!; + } + } + + Future activate(String appName) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.activate$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([appName]); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as bool?)!; + } + } + + Future setConfigSettings( + String appName, RemoteConfigPigeonSettings settings) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setConfigSettings$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([appName, settings]); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + + Future setDefaults( + String appName, Map defaultParameters) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setDefaults$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([appName, defaultParameters]); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + + Future ensureInitialized(String appName) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.ensureInitialized$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([appName]); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + + Future setCustomSignals( + String appName, Map customSignals) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setCustomSignals$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([appName, customSignals]); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + + Future> getAll(String appName) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.getAll$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([appName]); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as Map?)! + .cast(); + } + } + + Future> getProperties(String appName) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.getProperties$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([appName]); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as Map?)! + .cast(); + } + } +} diff --git a/packages/firebase_remote_config/firebase_remote_config_platform_interface/pigeons/copyright.txt b/packages/firebase_remote_config/firebase_remote_config_platform_interface/pigeons/copyright.txt new file mode 100644 index 000000000000..4e197781c6db --- /dev/null +++ b/packages/firebase_remote_config/firebase_remote_config_platform_interface/pigeons/copyright.txt @@ -0,0 +1,3 @@ +Copyright 2025, 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. \ No newline at end of file diff --git a/packages/firebase_remote_config/firebase_remote_config_platform_interface/pigeons/messages.dart b/packages/firebase_remote_config/firebase_remote_config_platform_interface/pigeons/messages.dart new file mode 100644 index 000000000000..4c17ddd4f378 --- /dev/null +++ b/packages/firebase_remote_config/firebase_remote_config_platform_interface/pigeons/messages.dart @@ -0,0 +1,63 @@ +// Copyright 2025 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_remote_config_platform_interface', + kotlinOut: + '../firebase_remote_config/android/src/main/kotlin/io/flutter/plugins/firebase/firebaseremoteconfig/GeneratedAndroidFirebaseRemoteConfig.g.kt', + kotlinOptions: KotlinOptions( + package: 'io.flutter.plugins.firebase.firebaseremoteconfig', + ), + swiftOut: + '../firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigMessages.g.swift', + cppHeaderOut: '../firebase_remote_config/windows/messages.g.h', + cppSourceOut: '../firebase_remote_config/windows/messages.g.cpp', + cppOptions: CppOptions(namespace: 'firebase_remote_config_windows'), + copyrightHeader: 'pigeons/copyright.txt', + ), +) +class RemoteConfigPigeonSettings { + RemoteConfigPigeonSettings({ + required this.fetchTimeoutSeconds, + required this.minimumFetchIntervalSeconds, + }); + + int fetchTimeoutSeconds; + int minimumFetchIntervalSeconds; +} + +@HostApi(dartHostTestHandler: 'TestFirebaseRemoteConfigHostApi') +abstract class FirebaseRemoteConfigHostApi { + @async + void fetch(String appName); + + @async + bool fetchAndActivate(String appName); + + @async + bool activate(String appName); + + @async + void setConfigSettings(String appName, RemoteConfigPigeonSettings settings); + + @async + void setDefaults(String appName, Map defaultParameters); + + @async + void ensureInitialized(String appName); + + @async + void setCustomSignals(String appName, Map customSignals); + + @async + Map getAll(String appName); + + @async + Map getProperties(String appName); +} diff --git a/packages/firebase_remote_config/firebase_remote_config_platform_interface/pubspec.yaml b/packages/firebase_remote_config/firebase_remote_config_platform_interface/pubspec.yaml index 50df58d9a6e0..a1ec8ecce51c 100644 --- a/packages/firebase_remote_config/firebase_remote_config_platform_interface/pubspec.yaml +++ b/packages/firebase_remote_config/firebase_remote_config_platform_interface/pubspec.yaml @@ -23,3 +23,4 @@ dev_dependencies: flutter_test: sdk: flutter mockito: ^5.0.0 + pigeon: 25.3.2 diff --git a/packages/firebase_remote_config/firebase_remote_config_platform_interface/test/pigeon/test_api.dart b/packages/firebase_remote_config/firebase_remote_config_platform_interface/test/pigeon/test_api.dart new file mode 100644 index 000000000000..dd98cdad1226 --- /dev/null +++ b/packages/firebase_remote_config/firebase_remote_config_platform_interface/test/pigeon/test_api.dart @@ -0,0 +1,377 @@ +// Copyright 2025, 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 (v25.3.2), 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 +// 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_remote_config_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 if (value is RemoteConfigPigeonSettings) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 129: + return RemoteConfigPigeonSettings.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +abstract class TestFirebaseRemoteConfigHostApi { + static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => + TestDefaultBinaryMessengerBinding.instance; + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); + + Future fetch(String appName); + + Future fetchAndActivate(String appName); + + Future activate(String appName); + + Future setConfigSettings( + String appName, RemoteConfigPigeonSettings settings); + + Future setDefaults( + String appName, Map defaultParameters); + + Future ensureInitialized(String appName); + + Future setCustomSignals( + String appName, Map customSignals); + + Future> getAll(String appName); + + Future> getProperties(String appName); + + static void setUp( + TestFirebaseRemoteConfigHostApi? api, { + BinaryMessenger? binaryMessenger, + String messageChannelSuffix = '', + }) { + messageChannelSuffix = + messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + { + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.fetch$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, + (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.fetch was null.'); + final List args = (message as List?)!; + final String? arg_appName = (args[0] as String?); + assert(arg_appName != null, + 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.fetch was null, expected non-null String.'); + try { + await api.fetch(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 BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.fetchAndActivate$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, + (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.fetchAndActivate was null.'); + final List args = (message as List?)!; + final String? arg_appName = (args[0] as String?); + assert(arg_appName != null, + 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.fetchAndActivate was null, expected non-null String.'); + try { + final bool output = await api.fetchAndActivate(arg_appName!); + return [output]; + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.activate$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, + (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.activate was null.'); + final List args = (message as List?)!; + final String? arg_appName = (args[0] as String?); + assert(arg_appName != null, + 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.activate was null, expected non-null String.'); + try { + final bool output = await api.activate(arg_appName!); + return [output]; + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setConfigSettings$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, + (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setConfigSettings was null.'); + final List args = (message as List?)!; + final String? arg_appName = (args[0] as String?); + assert(arg_appName != null, + 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setConfigSettings was null, expected non-null String.'); + final RemoteConfigPigeonSettings? arg_settings = + (args[1] as RemoteConfigPigeonSettings?); + assert(arg_settings != null, + 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setConfigSettings was null, expected non-null RemoteConfigPigeonSettings.'); + try { + await api.setConfigSettings(arg_appName!, arg_settings!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setDefaults$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, + (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setDefaults was null.'); + final List args = (message as List?)!; + final String? arg_appName = (args[0] as String?); + assert(arg_appName != null, + 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setDefaults was null, expected non-null String.'); + final Map? arg_defaultParameters = + (args[1] as Map?)?.cast(); + assert(arg_defaultParameters != null, + 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setDefaults was null, expected non-null Map.'); + try { + await api.setDefaults(arg_appName!, arg_defaultParameters!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.ensureInitialized$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, + (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.ensureInitialized was null.'); + final List args = (message as List?)!; + final String? arg_appName = (args[0] as String?); + assert(arg_appName != null, + 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.ensureInitialized was null, expected non-null String.'); + try { + await api.ensureInitialized(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 BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setCustomSignals$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, + (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setCustomSignals was null.'); + final List args = (message as List?)!; + final String? arg_appName = (args[0] as String?); + assert(arg_appName != null, + 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setCustomSignals was null, expected non-null String.'); + final Map? arg_customSignals = + (args[1] as Map?)?.cast(); + assert(arg_customSignals != null, + 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setCustomSignals was null, expected non-null Map.'); + try { + await api.setCustomSignals(arg_appName!, arg_customSignals!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.getAll$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, + (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.getAll was null.'); + final List args = (message as List?)!; + final String? arg_appName = (args[0] as String?); + assert(arg_appName != null, + 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.getAll was null, expected non-null String.'); + try { + final Map output = await api.getAll(arg_appName!); + return [output]; + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.getProperties$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, + (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.getProperties was null.'); + final List args = (message as List?)!; + final String? arg_appName = (args[0] as String?); + assert(arg_appName != null, + 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.getProperties was null, expected non-null String.'); + try { + final Map output = + await api.getProperties(arg_appName!); + return [output]; + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + } +} From f07dce33d5b359a6bdf92ecf4d8bf10acc0ba36e Mon Sep 17 00:00:00 2001 From: Jude Kwashie Date: Mon, 7 Jul 2025 08:36:21 +0000 Subject: [PATCH 2/6] chore: migrate ios implementation to Swift --- .../ios/firebase_remote_config.podspec | 5 +- .../firebase_remote_config/Constants.swift | 6 + .../FLTFirebaseRemoteConfigPlugin.m | 353 ------------------ .../FLTFirebaseRemoteConfigUtils.m | 38 -- .../FirebaseRemoteConfigPlugin.swift | 297 +++++++++++++++ .../FirebaseRemoteConfigUtils.swift | 33 ++ .../include/FLTFirebaseRemoteConfigPlugin.h | 22 -- .../include/FLTFirebaseRemoteConfigUtils.h | 9 - .../macos/firebase_remote_config.podspec | 5 +- .../firebase_remote_config/Constants.swift | 1 + .../FLTFirebaseRemoteConfigPlugin.m | 1 - .../FLTFirebaseRemoteConfigUtils.m | 1 - .../FirebaseRemoteConfigMessages.g.swift | 1 + .../FirebaseRemoteConfigPlugin.swift | 1 + .../FirebaseRemoteConfigUtils.swift | 1 + .../include/FLTFirebaseRemoteConfigPlugin.h | 1 - .../include/FLTFirebaseRemoteConfigUtils.h | 1 - .../firebase_remote_config/pubspec.yaml | 4 +- 18 files changed, 348 insertions(+), 432 deletions(-) create mode 100644 packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/Constants.swift delete mode 100644 packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FLTFirebaseRemoteConfigPlugin.m delete mode 100644 packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FLTFirebaseRemoteConfigUtils.m create mode 100644 packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigPlugin.swift create mode 100644 packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigUtils.swift delete mode 100644 packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/include/FLTFirebaseRemoteConfigPlugin.h delete mode 100644 packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/include/FLTFirebaseRemoteConfigUtils.h create mode 120000 packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config/Sources/firebase_remote_config/Constants.swift delete mode 120000 packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config/Sources/firebase_remote_config/FLTFirebaseRemoteConfigPlugin.m delete mode 120000 packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config/Sources/firebase_remote_config/FLTFirebaseRemoteConfigUtils.m create mode 120000 packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigMessages.g.swift create mode 120000 packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigPlugin.swift create mode 120000 packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigUtils.swift delete mode 120000 packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config/Sources/firebase_remote_config/include/FLTFirebaseRemoteConfigPlugin.h delete mode 120000 packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config/Sources/firebase_remote_config/include/FLTFirebaseRemoteConfigUtils.h diff --git a/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config.podspec b/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config.podspec index 364c66c7885c..caff2c89e2f6 100644 --- a/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config.podspec +++ b/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config.podspec @@ -25,12 +25,13 @@ Pod::Spec.new do |s| s.authors = 'The Chromium Authors' s.source = { :path => '.' } - s.source_files = 'firebase_remote_config/Sources/firebase_remote_config/**/*.{h,m}' - s.public_header_files = 'firebase_remote_config/Sources/firebase_remote_config/include/*.h' + s.source_files = 'firebase_remote_config/Sources/firebase_remote_config/**/*.swift' s.ios.deployment_target = '13.0' s.dependency 'Flutter' + s.swift_version = '5.0' + s.dependency 'firebase_core' s.dependency 'Firebase/RemoteConfig', firebase_sdk_version diff --git a/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/Constants.swift b/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/Constants.swift new file mode 100644 index 000000000000..93504b8c0e19 --- /dev/null +++ b/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/Constants.swift @@ -0,0 +1,6 @@ +// 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. + +// Auto-generated file. Do not edit. +public let versionNumber = "5.4.7" diff --git a/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FLTFirebaseRemoteConfigPlugin.m b/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FLTFirebaseRemoteConfigPlugin.m deleted file mode 100644 index 619a1f3518bf..000000000000 --- a/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FLTFirebaseRemoteConfigPlugin.m +++ /dev/null @@ -1,353 +0,0 @@ -// Copyright 2019 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 FirebaseRemoteConfig; -#if __has_include() -#import -#else -#import -#endif - -#import "FLTFirebaseRemoteConfigPlugin.h" -#import "FLTFirebaseRemoteConfigUtils.h" - -NSString *const kFirebaseRemoteConfigChannelName = @"plugins.flutter.io/firebase_remote_config"; -NSString *const kFirebaseRemoteConfigUpdateChannelName = - @"plugins.flutter.io/firebase_remote_config_updated"; - -@interface FLTFirebaseRemoteConfigPlugin () -@property(nonatomic, retain) FlutterMethodChannel *channel; -@property(nonatomic, strong) - NSMutableDictionary *listenersMap; -@end - -@implementation FLTFirebaseRemoteConfigPlugin - -BOOL _fetchAndActivateRetry; - -+ (instancetype)sharedInstance { - static dispatch_once_t onceToken; - static FLTFirebaseRemoteConfigPlugin *instance; - _fetchAndActivateRetry = false; - - dispatch_once(&onceToken, ^{ - instance = [[FLTFirebaseRemoteConfigPlugin alloc] init]; - [[FLTFirebasePluginRegistry sharedInstance] registerFirebasePlugin:instance]; - }); - - return instance; -} - -- (instancetype)init { - self = [super init]; - if (!self) return self; - _listenersMap = [NSMutableDictionary dictionary]; - return self; -} - -+ (void)registerWithRegistrar:(NSObject *)registrar { - FlutterMethodChannel *channel = - [FlutterMethodChannel methodChannelWithName:kFirebaseRemoteConfigChannelName - binaryMessenger:[registrar messenger]]; - FlutterEventChannel *eventChannel = - [FlutterEventChannel eventChannelWithName:kFirebaseRemoteConfigUpdateChannelName - binaryMessenger:[registrar messenger]]; - - FLTFirebaseRemoteConfigPlugin *instance = [FLTFirebaseRemoteConfigPlugin sharedInstance]; - - [registrar addMethodCallDelegate:instance channel:channel]; - [eventChannel setStreamHandler:instance]; - - SEL sel = NSSelectorFromString(@"registerLibrary:withVersion:"); - if ([FIRApp respondsToSelector:sel]) { - [FIRApp performSelector:sel withObject:@LIBRARY_NAME withObject:@LIBRARY_VERSION]; - } -} - -- (void)detachFromEngineForRegistrar:(NSObject *)registrar { - self.channel = nil; -} - -- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)flutterResult { - FLTFirebaseMethodCallErrorBlock errorBlock = - ^(NSString *_Nullable code, NSString *_Nullable message, NSDictionary *_Nullable details, - NSError *_Nullable error) { - if (code == nil) { - details = [FLTFirebaseRemoteConfigUtils ErrorCodeAndMessageFromNSError:error]; - code = [details valueForKey:@"code"]; - message = [details valueForKey:@"message"]; - } - if ([@"unknown" isEqualToString:code]) { - NSLog(@"FLTFirebaseRemoteConfig: An error occurred while calling method %@", call.method); - } - flutterResult([FLTFirebasePlugin createFlutterErrorFromCode:code - message:message - optionalDetails:details - andOptionalNSError:error]); - }; - - FLTFirebaseMethodCallResult *methodCallResult = - [FLTFirebaseMethodCallResult createWithSuccess:flutterResult andErrorBlock:errorBlock]; - - if ([@"RemoteConfig#ensureInitialized" isEqualToString:call.method]) { - [self ensureInitialized:call.arguments withMethodCallResult:methodCallResult]; - } else if ([@"RemoteConfig#activate" isEqualToString:call.method]) { - [self activate:call.arguments withMethodCallResult:methodCallResult]; - } else if ([@"RemoteConfig#getAll" isEqualToString:call.method]) { - [self getAll:call.arguments withMethodCallResult:methodCallResult]; - } else if ([@"RemoteConfig#fetch" isEqualToString:call.method]) { - [self fetch:call.arguments withMethodCallResult:methodCallResult]; - } else if ([@"RemoteConfig#fetchAndActivate" isEqualToString:call.method]) { - [self fetchAndActivate:call.arguments withMethodCallResult:methodCallResult]; - } else if ([@"RemoteConfig#setConfigSettings" isEqualToString:call.method]) { - [self setConfigSettings:call.arguments withMethodCallResult:methodCallResult]; - } else if ([@"RemoteConfig#setDefaults" isEqualToString:call.method]) { - [self setDefaults:call.arguments withMethodCallResult:methodCallResult]; - } else if ([@"RemoteConfig#getProperties" isEqualToString:call.method]) { - [self getProperties:call.arguments withMethodCallResult:methodCallResult]; - } else if ([@"RemoteConfig#setCustomSignals" isEqualToString:call.method]) { - [self setCustomSignals:call.arguments withMethodCallResult:methodCallResult]; - } else { - methodCallResult.success(FlutterMethodNotImplemented); - } -} - -#pragma mark - Remote Config API -- (void)setCustomSignals:(id)arguments withMethodCallResult:(FLTFirebaseMethodCallResult *)result { - FIRRemoteConfig *remoteConfig = [self getFIRRemoteConfigFromArguments:arguments]; - NSDictionary *customSignals = arguments[@"customSignals"]; - - [remoteConfig setCustomSignals:customSignals - withCompletion:^(NSError *_Nullable error) { - if (error != nil) { - result.error(nil, nil, nil, error); - } else { - result.success(nil); - } - }]; -} - -- (void)ensureInitialized:(id)arguments withMethodCallResult:(FLTFirebaseMethodCallResult *)result { - FIRRemoteConfig *remoteConfig = [self getFIRRemoteConfigFromArguments:arguments]; - [remoteConfig ensureInitializedWithCompletionHandler:^(NSError *initializationError) { - if (initializationError != nil) { - result.error(nil, nil, nil, initializationError); - } else { - result.success(nil); - } - }]; -} - -- (void)activate:(id)arguments withMethodCallResult:(FLTFirebaseMethodCallResult *)result { - FIRRemoteConfig *remoteConfig = [self getFIRRemoteConfigFromArguments:arguments]; - [remoteConfig activateWithCompletion:^(BOOL changed, NSError *error) { - if (error != nil) { - result.error(nil, nil, nil, error); - } else { - result.success(@(changed)); - } - }]; -} - -- (void)getAll:(id)arguments withMethodCallResult:(FLTFirebaseMethodCallResult *)result { - FIRRemoteConfig *remoteConfig = [self getFIRRemoteConfigFromArguments:arguments]; - NSDictionary *parameters = [self getAllParametersForInstance:remoteConfig]; - result.success(parameters); -} - -- (void)fetch:(id)arguments withMethodCallResult:(FLTFirebaseMethodCallResult *)result { - FIRRemoteConfig *remoteConfig = [self getFIRRemoteConfigFromArguments:arguments]; - [remoteConfig fetchWithCompletionHandler:^(FIRRemoteConfigFetchStatus status, NSError *error) { - if (error != nil) { - result.error(nil, nil, nil, error); - } else { - result.success(nil); - } - }]; -} - -- (void)getProperties:(id)arguments withMethodCallResult:(FLTFirebaseMethodCallResult *)result { - FIRRemoteConfig *remoteConfig = [self getFIRRemoteConfigFromArguments:arguments]; - NSDictionary *configProperties = [self configPropertiesForInstance:remoteConfig]; - result.success(configProperties); -} - -- (void)setDefaults:(id)arguments withMethodCallResult:(FLTFirebaseMethodCallResult *)result { - FIRRemoteConfig *remoteConfig = [self getFIRRemoteConfigFromArguments:arguments]; - [remoteConfig setDefaults:arguments[@"defaults"]]; - result.success(nil); -} - -- (void)setConfigSettings:(id)arguments withMethodCallResult:(FLTFirebaseMethodCallResult *)result { - NSNumber *fetchTimeout = arguments[@"fetchTimeout"]; - NSNumber *minimumFetchInterval = arguments[@"minimumFetchInterval"]; - FIRRemoteConfigSettings *remoteConfigSettings = [[FIRRemoteConfigSettings alloc] init]; - remoteConfigSettings.fetchTimeout = [fetchTimeout doubleValue]; - remoteConfigSettings.minimumFetchInterval = [minimumFetchInterval doubleValue]; - FIRRemoteConfig *remoteConfig = [self getFIRRemoteConfigFromArguments:arguments]; - [remoteConfig setConfigSettings:remoteConfigSettings]; - result.success(nil); -} - -- (void)fetchAndActivate:(id)arguments withMethodCallResult:(FLTFirebaseMethodCallResult *)result { - FIRRemoteConfig *remoteConfig = [self getFIRRemoteConfigFromArguments:arguments]; - [remoteConfig fetchAndActivateWithCompletionHandler:^( - FIRRemoteConfigFetchAndActivateStatus status, NSError *error) { - if (error != nil) { - if (error.code == 999 && _fetchAndActivateRetry == false) { - // Note: see issue for details: https://github.com/firebase/flutterfire/issues/6196 - // Only calling once as the issue noted describes how it works on second retry - // Issue appears to indicate the error code is: 999 - _fetchAndActivateRetry = true; - NSLog(@"FLTFirebaseRemoteConfigPlugin: Retrying `fetchAndActivate()` due to a cancelled " - @"request with the error code: 999."); - [self fetchAndActivate:arguments withMethodCallResult:result]; - } else { - result.error(nil, nil, nil, error); - } - } else { - if (status == FIRRemoteConfigFetchAndActivateStatusSuccessFetchedFromRemote) { - result.success(@(YES)); - } else { - result.success(@(NO)); - } - } - }]; -} - -- (FIRRemoteConfig *_Nullable)getFIRRemoteConfigFromArguments:(NSDictionary *)arguments { - NSString *appName = arguments[@"appName"]; - FIRApp *app = [FLTFirebasePlugin firebaseAppNamed:appName]; - return [FIRRemoteConfig remoteConfigWithApp:app]; -} - -- (NSDictionary *)getAllParametersForInstance:(FIRRemoteConfig *)remoteConfig { - NSMutableSet *keySet = [[NSMutableSet alloc] init]; - [keySet addObjectsFromArray:[remoteConfig allKeysFromSource:FIRRemoteConfigSourceStatic]]; - [keySet addObjectsFromArray:[remoteConfig allKeysFromSource:FIRRemoteConfigSourceDefault]]; - [keySet addObjectsFromArray:[remoteConfig allKeysFromSource:FIRRemoteConfigSourceRemote]]; - - NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init]; - for (NSString *key in keySet) { - parameters[key] = [self createRemoteConfigValueDict:[remoteConfig configValueForKey:key]]; - } - return parameters; -} - -- (NSMutableDictionary *)createRemoteConfigValueDict:(FIRRemoteConfigValue *)remoteConfigValue { - NSMutableDictionary *valueDict = [[NSMutableDictionary alloc] init]; - valueDict[@"value"] = [FlutterStandardTypedData typedDataWithBytes:[remoteConfigValue dataValue]]; - valueDict[@"source"] = [self mapValueSource:[remoteConfigValue source]]; - return valueDict; -} - -- (NSString *)mapLastFetchStatus:(FIRRemoteConfigFetchStatus)status { - if (status == FIRRemoteConfigFetchStatusSuccess) { - return @"success"; - } else if (status == FIRRemoteConfigFetchStatusFailure) { - return @"failure"; - } else if (status == FIRRemoteConfigFetchStatusThrottled) { - return @"throttled"; - } else if (status == FIRRemoteConfigFetchStatusNoFetchYet) { - return @"noFetchYet"; - } else { - return @"failure"; - } -} - -- (NSString *)mapValueSource:(FIRRemoteConfigSource)source { - if (source == FIRRemoteConfigSourceStatic) { - return @"static"; - } else if (source == FIRRemoteConfigSourceDefault) { - return @"default"; - } else if (source == FIRRemoteConfigSourceRemote) { - return @"remote"; - } else { - return @"static"; - } -} - -#pragma mark - FLTFirebasePlugin - -- (void)cleanupWithCompletion { - for (FIRConfigUpdateListenerRegistration *listener in self.listenersMap.allValues) { - [listener remove]; - } - [self.listenersMap removeAllObjects]; -} - -- (void)didReinitializeFirebaseCore:(void (^)(void))completion { - _fetchAndActivateRetry = false; - [self cleanupWithCompletion]; - completion(); -} - -- (NSDictionary *_Nonnull)pluginConstantsForFIRApp:(FIRApp *)firebase_app { - FIRRemoteConfig *firebaseRemoteConfig = [FIRRemoteConfig remoteConfigWithApp:firebase_app]; - NSDictionary *configProperties = [self configPropertiesForInstance:firebaseRemoteConfig]; - - NSMutableDictionary *configValues = [[NSMutableDictionary alloc] init]; - [configValues addEntriesFromDictionary:configProperties]; - [configValues setValue:[self getAllParametersForInstance:firebaseRemoteConfig] - forKey:@"parameters"]; - return configValues; -} - -- (NSDictionary *_Nonnull)configPropertiesForInstance:(FIRRemoteConfig *)remoteConfig { - NSNumber *fetchTimeout = @([[remoteConfig configSettings] fetchTimeout]); - NSNumber *minimumFetchInterval = @([[remoteConfig configSettings] minimumFetchInterval]); - NSNumber *lastFetchMillis = @([[remoteConfig lastFetchTime] timeIntervalSince1970] * 1000); - - NSMutableDictionary *configProperties = [[NSMutableDictionary alloc] init]; - [configProperties setValue:@([fetchTimeout longValue]) forKey:@"fetchTimeout"]; - [configProperties setValue:@([minimumFetchInterval longValue]) forKey:@"minimumFetchInterval"]; - [configProperties setValue:@([lastFetchMillis longValue]) forKey:@"lastFetchTime"]; - [configProperties setValue:[self mapLastFetchStatus:[remoteConfig lastFetchStatus]] - forKey:@"lastFetchStatus"]; - return configProperties; -} - -- (NSString *_Nonnull)firebaseLibraryName { - return @LIBRARY_NAME; -} - -- (NSString *_Nonnull)firebaseLibraryVersion { - return @LIBRARY_VERSION; -} - -- (NSString *_Nonnull)flutterChannelName { - return kFirebaseRemoteConfigChannelName; -} - -- (FlutterError *_Nullable)onCancelWithArguments:(id _Nullable)arguments { - NSString *appName = (NSString *)arguments[@"appName"]; - // arguments will be null on hot restart, so we will clean up listeners in - // didReinitializeFirebaseCore() - if (!appName) return nil; - [self.listenersMap[appName] remove]; - [self.listenersMap removeObjectForKey:appName]; - return nil; -} - -- (FlutterError *_Nullable)onListenWithArguments:(id _Nullable)arguments - eventSink:(nonnull FlutterEventSink)events { - NSString *appName = (NSString *)arguments[@"appName"]; - if (!appName) return nil; - FIRRemoteConfig *remoteConfig = [self getFIRRemoteConfigFromArguments:arguments]; - self.listenersMap[appName] = - [remoteConfig addOnConfigUpdateListener:^(FIRRemoteConfigUpdate *_Nullable configUpdate, - NSError *_Nullable error) { - if (error) { - // Handle the error - NSLog(@"Error while receiving remote config update: %@", error.localizedDescription); - return; - } - if (configUpdate) { - events([configUpdate.updatedKeys allObjects]); - } - }]; - return nil; -} - -@end diff --git a/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FLTFirebaseRemoteConfigUtils.m b/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FLTFirebaseRemoteConfigUtils.m deleted file mode 100644 index 8abe959ffc8c..000000000000 --- a/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FLTFirebaseRemoteConfigUtils.m +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2020 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 FirebaseRemoteConfig; - -#import "FLTFirebaseRemoteConfigUtils.h" - -@implementation FLTFirebaseRemoteConfigUtils -+ (NSDictionary *)ErrorCodeAndMessageFromNSError:(NSError *)error { - NSMutableDictionary *codeAndMessage = [[NSMutableDictionary alloc] init]; - switch (error.code) { - case FIRRemoteConfigErrorInternalError: - if ([error.userInfo[NSLocalizedDescriptionKey] containsString:@"403"]) { - // See PR for details: https://github.com/firebase/flutterfire/pull/9629 - [codeAndMessage setValue:@"forbidden" forKey:@"code"]; - NSString *updateMessage = - [NSString stringWithFormat:@"%@%@", error.userInfo[NSLocalizedDescriptionKey], - @". You may have to enable the Remote Config API on Google " - @"Cloud Platform for your Firebase project."]; - [codeAndMessage setValue:updateMessage forKey:@"message"]; - } else { - [codeAndMessage setValue:@"internal" forKey:@"code"]; - [codeAndMessage setValue:error.userInfo[NSLocalizedDescriptionKey] forKey:@"message"]; - } - break; - case FIRRemoteConfigErrorThrottled: - [codeAndMessage setValue:@"throttled" forKey:@"code"]; - [codeAndMessage setValue:@"frequency of requests exceeds throttled limits" forKey:@"message"]; - break; - default: - [codeAndMessage setValue:@"unknown" forKey:@"code"]; - [codeAndMessage setValue:@"unknown remote config error" forKey:@"message"]; - break; - } - return codeAndMessage; -} -@end diff --git a/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigPlugin.swift b/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigPlugin.swift new file mode 100644 index 000000000000..c05835c2e0f9 --- /dev/null +++ b/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigPlugin.swift @@ -0,0 +1,297 @@ +// Copyright 2025 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. + +#if canImport(FlutterMacOS) + import FlutterMacOS +#else + import Flutter +#endif + +#if canImport(firebase_core) + import firebase_core +#else + import firebase_core_shared +#endif +import FirebaseRemoteConfig + +let kFirebaseRemoteConfigChannelName = "plugins.flutter.io/firebase_remote_config" +let kFirebaseRemoteConfigUpdatedChannelName = "plugins.flutter.io/firebase_remote_config_updated" + +extension FlutterError: Error {} + +public class FirebaseRemoteConfigPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, + FLTFirebasePluginProtocol, FirebaseRemoteConfigHostApi { + private var listenersMap: [String: ConfigUpdateListenerRegistration] = [:] + private var fetchAndActivateRetry = false + + static let shared: FirebaseRemoteConfigPlugin = { + let instance = FirebaseRemoteConfigPlugin() + FLTFirebasePluginRegistry.sharedInstance().register(instance) + instance.fetchAndActivateRetry = false + return instance + }() + + public static func register(with registrar: FlutterPluginRegistrar) { + let binaryMessenger: FlutterBinaryMessenger + + #if os(macOS) + binaryMessenger = registrar.messenger + #elseif os(iOS) + binaryMessenger = registrar.messenger() + #endif + + let instance = shared + FirebaseRemoteConfigHostApiSetup.setUp(binaryMessenger: binaryMessenger, api: instance) + + let eventChannel = FlutterEventChannel( + name: kFirebaseRemoteConfigUpdatedChannelName, + binaryMessenger: registrar.messenger() + ) + eventChannel.setStreamHandler(instance) + + if FirebaseApp.responds(to: NSSelectorFromString("registerLibrary:withVersion:")) { + FirebaseApp.perform( + NSSelectorFromString("registerLibrary:withVersion:"), + with: instance.firebaseLibraryName(), + with: instance.firebaseLibraryVersion() + ) + } + } + + public func didReinitializeFirebaseCore(_ completion: @escaping () -> Void) { + completion() + } + + public func pluginConstants(for firebaseApp: FirebaseApp) -> [AnyHashable: Any] { + let firebaseRemoteConfig = RemoteConfig.remoteConfig(app: firebaseApp) + let configProperties = configProperties(for: firebaseRemoteConfig) + var configValues: [String: Any] = configProperties + configValues["parameters"] = getAllParameters(for: firebaseRemoteConfig) + + return configValues + } + + func fetch(appName: String, completion: @escaping (Result) -> Void) { + getRemoteConfig(from: appName).fetch { status, error in + if let error { + completion(.failure(self.createFlutterError(error))) + } else { + completion(Result.success(())) + } + } + } + + func fetchAndActivate(appName: String, completion: @escaping (Result) -> Void) { + getRemoteConfig(from: appName).fetchAndActivate { status, error in + if let error { + completion(.failure(self.createFlutterError(error))) + } else { + completion(Result.success(status == .successFetchedFromRemote)) + } + } + } + + func activate(appName: String, completion: @escaping (Result) -> Void) { + getRemoteConfig(from: appName).activate { status, error in + if let error { + completion(.failure(self.createFlutterError(error))) + } else { + completion(Result.success(status)) + } + } + } + + func setConfigSettings(appName: String, settings: RemoteConfigPigeonSettings, + completion: @escaping (Result) -> Void) { + let fetchTimeout = settings.fetchTimeoutSeconds + let minFetchInterval = settings.minimumFetchIntervalSeconds + let configSettings = RemoteConfigSettings() + configSettings.fetchTimeout = Double(fetchTimeout) + configSettings.minimumFetchInterval = Double(minFetchInterval) + getRemoteConfig(from: appName).configSettings = configSettings + completion(.success(())) + } + + func setDefaults(appName: String, defaultParameters: [String: Any?], + completion: @escaping (Result) -> Void) { + var filtered: [String: NSObject] = [:] + + for (key, value) in defaultParameters { + if let nonNil = value, let obj = nonNil as? NSObject { + filtered[key] = obj + } + } + + getRemoteConfig(from: appName).setDefaults(filtered) + completion(.success(())) + } + + func ensureInitialized(appName: String, completion: @escaping (Result) -> Void) { + getRemoteConfig(from: appName).ensureInitialized { error in + if let error { + completion(.failure(self.createFlutterError(error))) + } else { + completion(.success(())) + } + } + } + + func setCustomSignals(appName: String, customSignals: [String: Any?], + completion: @escaping (Result) -> Void) { + let signalValues = convertToCustomSignalValues(customSignals) + Task { + do { + try await getRemoteConfig(from: appName).setCustomSignals(signalValues) + completion(.success(())) + } catch { + completion(.failure(createFlutterError(error))) + } + } + } + + func getAll(appName: String, completion: @escaping (Result<[String: Any?], any Error>) -> Void) { + let remoteConfig = getRemoteConfig(from: appName) + let allKeys = Set(remoteConfig.allKeys(from: .static)) + .union(remoteConfig.allKeys(from: .default)) + .union(remoteConfig.allKeys(from: .remote)) + + var parameters: [String: Any] = [:] + for key in allKeys { + let value = remoteConfig.configValue(forKey: key) + parameters[key] = [ + "value": FlutterStandardTypedData(bytes: value.dataValue), + "source": mapSource(value.source), + ] + } + completion(.success(parameters)) + } + + func getProperties(appName: String, + completion: @escaping (Result<[String: Any], any Error>) -> Void) { + let config = getRemoteConfig(from: appName) + completion(.success(configProperties(for: config))) + } + + public func firebaseLibraryName() -> String { + "flutter-fire-rc" + } + + public func firebaseLibraryVersion() -> String { + versionNumber + } + + public func flutterChannelName() -> String { + kFirebaseRemoteConfigChannelName + } + + public func onListen(withArguments arguments: Any?, + eventSink events: @escaping FlutterEventSink) -> FlutterError? { + guard let args = arguments as? [String: Any], let appName = args["appName"] as? String else { + return nil + } + let remoteConfig = getRemoteConfig(from: appName) + listenersMap[appName] = remoteConfig.addOnConfigUpdateListener { update, error in + if let error { + print("Remote Config update error: \(error.localizedDescription)") + return + } + if let update { + events(Array(update.updatedKeys)) + } + } + return nil + } + + public func onCancel(withArguments arguments: Any?) -> FlutterError? { + guard let args = arguments as? [String: Any], let appName = args["appName"] as? String else { + return nil + } + listenersMap[appName]?.remove() + listenersMap.removeValue(forKey: appName) + return nil + } + + private func getRemoteConfig(from appName: String) -> RemoteConfig { + let app = FLTFirebasePlugin.firebaseAppNamed(appName) + return RemoteConfig.remoteConfig(app: app!) + } + + private func getAllParameters(for remoteConfig: RemoteConfig) -> [String: Any] { + var keySet = Set() + keySet.formUnion(remoteConfig.allKeys(from: .static)) + keySet.formUnion(remoteConfig.allKeys(from: .default)) + keySet.formUnion(remoteConfig.allKeys(from: .remote)) + + var parameters: [String: Any] = [:] + for key in keySet { + parameters[key] = createRemoteConfigValueDict(remoteConfig.configValue(forKey: key)) + } + + return parameters + } + + private func createRemoteConfigValueDict(_ remoteConfigValue: RemoteConfigValue) + -> [String: Any] { + [ + "value": FlutterStandardTypedData(bytes: remoteConfigValue.dataValue), + "source": mapSource(remoteConfigValue.source), + ] + } + + private func mapSource(_ source: RemoteConfigSource) -> String { + switch source { + case .static: return "static" + case .default: return "default" + case .remote: return "remote" + @unknown default: return "static" + } + } + + private func mapFetchStatus(_ status: RemoteConfigFetchStatus) -> String { + switch status { + case .success: return "success" + case .failure: return "failure" + case .throttled: return "throttled" + case .noFetchYet: return "noFetchYet" + @unknown default: return "failure" + } + } + + private func configProperties(for config: RemoteConfig) -> [String: Any] { + [ + "fetchTimeout": Int(config.configSettings.fetchTimeout), + "minimumFetchInterval": Int(config.configSettings.minimumFetchInterval), + "lastFetchTime": Int(config.lastFetchTime?.timeIntervalSince1970 ?? 0 * 1000), + "lastFetchStatus": mapFetchStatus(config.lastFetchStatus), + ] + } + + private func createFlutterError(_ error: Error) -> FlutterError { + let nsError = error as NSError + return FlutterError( + code: "firebase_remote_config", + message: nsError.localizedDescription, + details: nsError.userInfo["details"] + ) + } + + private func convertToCustomSignalValues(_ raw: [String: Any?]) -> [String: CustomSignalValue?] { + raw.mapValues { value in + guard let unwrapped = value else { + return nil + } + + switch unwrapped { + case let string as String: + return .string(string) + case let int as Int: + return .integer(int) + case let double as Double: + return .double(double) + default: + return nil + } + } + } +} diff --git a/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigUtils.swift b/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigUtils.swift new file mode 100644 index 000000000000..6d0c43c11442 --- /dev/null +++ b/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigUtils.swift @@ -0,0 +1,33 @@ +import FirebaseRemoteConfig + +class FLTFirebaseRemoteConfigUtils { + static func errorCodeAndMessage(from error: NSError) -> [String: String] { + var codeAndMessage: [String: String] = [:] + + switch error.code { + case RemoteConfigError.internalError.rawValue: + if let description = error.userInfo[NSLocalizedDescriptionKey] as? String, + description.contains("403") { + // See PR for details: https://github.com/firebase/flutterfire/pull/9629 + codeAndMessage["code"] = "forbidden" + let updateMessage = + "\(description). You may have to enable the Remote Config API on Google Cloud Platform for your Firebase project." + codeAndMessage["message"] = updateMessage + } else { + codeAndMessage["code"] = "internal" + codeAndMessage["message"] = error + .userInfo[NSLocalizedDescriptionKey] as? String ?? "Internal error" + } + + case RemoteConfigError.throttled.rawValue: + codeAndMessage["code"] = "throttled" + codeAndMessage["message"] = "frequency of requests exceeds throttled limits" + + default: + codeAndMessage["code"] = "unknown" + codeAndMessage["message"] = "unknown remote config error" + } + + return codeAndMessage + } +} diff --git a/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/include/FLTFirebaseRemoteConfigPlugin.h b/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/include/FLTFirebaseRemoteConfigPlugin.h deleted file mode 100644 index 373d9968ba84..000000000000 --- a/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/include/FLTFirebaseRemoteConfigPlugin.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2020 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 - -#if TARGET_OS_OSX -#import -#else -#import -#endif - -#import - -#if __has_include() -#import -#else -#import -#endif - -@interface FLTFirebaseRemoteConfigPlugin - : FLTFirebasePlugin -@end diff --git a/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/include/FLTFirebaseRemoteConfigUtils.h b/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/include/FLTFirebaseRemoteConfigUtils.h deleted file mode 100644 index b66b368582b8..000000000000 --- a/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/include/FLTFirebaseRemoteConfigUtils.h +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2020 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 -#import - -@interface FLTFirebaseRemoteConfigUtils : NSObject -+ (NSDictionary *)ErrorCodeAndMessageFromNSError:(NSError *)error; -@end diff --git a/packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config.podspec b/packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config.podspec index bd6848969f94..cccf62e8c869 100644 --- a/packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config.podspec +++ b/packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config.podspec @@ -43,11 +43,12 @@ Pod::Spec.new do |s| s.authors = 'The Chromium Authors' s.source = { :path => '.' } - s.source_files = 'firebase_remote_config/Sources/firebase_remote_config/**/*.{h,m}' - s.public_header_files = 'firebase_remote_config/Sources/firebase_remote_config/include/*.h' + s.source_files = 'firebase_remote_config/Sources/firebase_remote_config/**/*.swift' s.platform = :osx, '10.13' + s.swift_version = '5.0' + # Flutter dependencies s.dependency 'FlutterMacOS' diff --git a/packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config/Sources/firebase_remote_config/Constants.swift b/packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config/Sources/firebase_remote_config/Constants.swift new file mode 120000 index 000000000000..083bde6d82e3 --- /dev/null +++ b/packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config/Sources/firebase_remote_config/Constants.swift @@ -0,0 +1 @@ +../../../../ios/firebase_remote_config/Sources/firebase_remote_config/Constants.swift \ No newline at end of file diff --git a/packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config/Sources/firebase_remote_config/FLTFirebaseRemoteConfigPlugin.m b/packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config/Sources/firebase_remote_config/FLTFirebaseRemoteConfigPlugin.m deleted file mode 120000 index 34822edf39e9..000000000000 --- a/packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config/Sources/firebase_remote_config/FLTFirebaseRemoteConfigPlugin.m +++ /dev/null @@ -1 +0,0 @@ -../../../../ios/firebase_remote_config/Sources/firebase_remote_config/FLTFirebaseRemoteConfigPlugin.m \ No newline at end of file diff --git a/packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config/Sources/firebase_remote_config/FLTFirebaseRemoteConfigUtils.m b/packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config/Sources/firebase_remote_config/FLTFirebaseRemoteConfigUtils.m deleted file mode 120000 index 0153ec17ac71..000000000000 --- a/packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config/Sources/firebase_remote_config/FLTFirebaseRemoteConfigUtils.m +++ /dev/null @@ -1 +0,0 @@ -../../../../ios/firebase_remote_config/Sources/firebase_remote_config/FLTFirebaseRemoteConfigUtils.m \ No newline at end of file diff --git a/packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigMessages.g.swift b/packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigMessages.g.swift new file mode 120000 index 000000000000..9cfdf1fb9fe9 --- /dev/null +++ b/packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigMessages.g.swift @@ -0,0 +1 @@ +../../../../ios/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigMessages.g.swift \ No newline at end of file diff --git a/packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigPlugin.swift b/packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigPlugin.swift new file mode 120000 index 000000000000..0437bee6524a --- /dev/null +++ b/packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigPlugin.swift @@ -0,0 +1 @@ +../../../../ios/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigPlugin.swift \ No newline at end of file diff --git a/packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigUtils.swift b/packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigUtils.swift new file mode 120000 index 000000000000..6d0c78cfe177 --- /dev/null +++ b/packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigUtils.swift @@ -0,0 +1 @@ +../../../../ios/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigUtils.swift \ No newline at end of file diff --git a/packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config/Sources/firebase_remote_config/include/FLTFirebaseRemoteConfigPlugin.h b/packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config/Sources/firebase_remote_config/include/FLTFirebaseRemoteConfigPlugin.h deleted file mode 120000 index 48fd0d6dd6a0..000000000000 --- a/packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config/Sources/firebase_remote_config/include/FLTFirebaseRemoteConfigPlugin.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../ios/firebase_remote_config/Sources/firebase_remote_config/include/FLTFirebaseRemoteConfigPlugin.h \ No newline at end of file diff --git a/packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config/Sources/firebase_remote_config/include/FLTFirebaseRemoteConfigUtils.h b/packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config/Sources/firebase_remote_config/include/FLTFirebaseRemoteConfigUtils.h deleted file mode 120000 index 953c4f4f73ba..000000000000 --- a/packages/firebase_remote_config/firebase_remote_config/macos/firebase_remote_config/Sources/firebase_remote_config/include/FLTFirebaseRemoteConfigUtils.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../ios/firebase_remote_config/Sources/firebase_remote_config/include/FLTFirebaseRemoteConfigUtils.h \ No newline at end of file diff --git a/packages/firebase_remote_config/firebase_remote_config/pubspec.yaml b/packages/firebase_remote_config/firebase_remote_config/pubspec.yaml index 47be47a9cb87..73f79075559b 100644 --- a/packages/firebase_remote_config/firebase_remote_config/pubspec.yaml +++ b/packages/firebase_remote_config/firebase_remote_config/pubspec.yaml @@ -39,8 +39,8 @@ flutter: package: io.flutter.plugins.firebase.firebaseremoteconfig pluginClass: FirebaseRemoteConfigPlugin ios: - pluginClass: FLTFirebaseRemoteConfigPlugin + pluginClass: FirebaseRemoteConfigPlugin macos: - pluginClass: FLTFirebaseRemoteConfigPlugin + pluginClass: FirebaseRemoteConfigPlugin web: default_package: firebase_remote_config_web From 339c7708c654f23d0d977cd1e0defffeb0a270d2 Mon Sep 17 00:00:00 2001 From: Jude Kwashie Date: Mon, 7 Jul 2025 08:42:00 +0000 Subject: [PATCH 3/6] chore: add license header to FirebaseRemoteConfigUtils.swift --- .../firebase_remote_config/FirebaseRemoteConfigUtils.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigUtils.swift b/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigUtils.swift index 6d0c43c11442..983987589c41 100644 --- a/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigUtils.swift +++ b/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigUtils.swift @@ -1,3 +1,7 @@ +// Copyright 2025 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 FirebaseRemoteConfig class FLTFirebaseRemoteConfigUtils { From 2b61fe85fa4755fb67fd910b51dcffa1bbd302a1 Mon Sep 17 00:00:00 2001 From: Jude Kwashie Date: Mon, 7 Jul 2025 09:15:07 +0000 Subject: [PATCH 4/6] chore: reference `binaryMessenger` --- .../firebase_remote_config/FirebaseRemoteConfigPlugin.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigPlugin.swift b/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigPlugin.swift index c05835c2e0f9..efc37811db97 100644 --- a/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigPlugin.swift +++ b/packages/firebase_remote_config/firebase_remote_config/ios/firebase_remote_config/Sources/firebase_remote_config/FirebaseRemoteConfigPlugin.swift @@ -46,7 +46,7 @@ public class FirebaseRemoteConfigPlugin: NSObject, FlutterPlugin, FlutterStreamH let eventChannel = FlutterEventChannel( name: kFirebaseRemoteConfigUpdatedChannelName, - binaryMessenger: registrar.messenger() + binaryMessenger: binaryMessenger ) eventChannel.setStreamHandler(instance) From 692a5134d5157b4e69654b5fe05691c61f7f5664 Mon Sep 17 00:00:00 2001 From: Jude Kwashie Date: Tue, 8 Jul 2025 12:09:06 +0000 Subject: [PATCH 5/6] chore: fix ci --- .../pigeons/messages.dart | 1 - .../test/pigeon/test_api.dart | 377 ------------------ 2 files changed, 378 deletions(-) delete mode 100644 packages/firebase_remote_config/firebase_remote_config_platform_interface/test/pigeon/test_api.dart diff --git a/packages/firebase_remote_config/firebase_remote_config_platform_interface/pigeons/messages.dart b/packages/firebase_remote_config/firebase_remote_config_platform_interface/pigeons/messages.dart index 4c17ddd4f378..c89ebcbdd2ed 100644 --- a/packages/firebase_remote_config/firebase_remote_config_platform_interface/pigeons/messages.dart +++ b/packages/firebase_remote_config/firebase_remote_config_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_remote_config_platform_interface', kotlinOut: '../firebase_remote_config/android/src/main/kotlin/io/flutter/plugins/firebase/firebaseremoteconfig/GeneratedAndroidFirebaseRemoteConfig.g.kt', diff --git a/packages/firebase_remote_config/firebase_remote_config_platform_interface/test/pigeon/test_api.dart b/packages/firebase_remote_config/firebase_remote_config_platform_interface/test/pigeon/test_api.dart deleted file mode 100644 index dd98cdad1226..000000000000 --- a/packages/firebase_remote_config/firebase_remote_config_platform_interface/test/pigeon/test_api.dart +++ /dev/null @@ -1,377 +0,0 @@ -// Copyright 2025, 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 (v25.3.2), 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 -// 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_remote_config_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 if (value is RemoteConfigPigeonSettings) { - buffer.putUint8(129); - writeValue(buffer, value.encode()); - } else { - super.writeValue(buffer, value); - } - } - - @override - Object? readValueOfType(int type, ReadBuffer buffer) { - switch (type) { - case 129: - return RemoteConfigPigeonSettings.decode(readValue(buffer)!); - default: - return super.readValueOfType(type, buffer); - } - } -} - -abstract class TestFirebaseRemoteConfigHostApi { - static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => - TestDefaultBinaryMessengerBinding.instance; - static const MessageCodec pigeonChannelCodec = _PigeonCodec(); - - Future fetch(String appName); - - Future fetchAndActivate(String appName); - - Future activate(String appName); - - Future setConfigSettings( - String appName, RemoteConfigPigeonSettings settings); - - Future setDefaults( - String appName, Map defaultParameters); - - Future ensureInitialized(String appName); - - Future setCustomSignals( - String appName, Map customSignals); - - Future> getAll(String appName); - - Future> getProperties(String appName); - - static void setUp( - TestFirebaseRemoteConfigHostApi? api, { - BinaryMessenger? binaryMessenger, - String messageChannelSuffix = '', - }) { - messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; - { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.fetch$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger); - if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); - } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.fetch was null.'); - final List args = (message as List?)!; - final String? arg_appName = (args[0] as String?); - assert(arg_appName != null, - 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.fetch was null, expected non-null String.'); - try { - await api.fetch(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 BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.fetchAndActivate$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger); - if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); - } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.fetchAndActivate was null.'); - final List args = (message as List?)!; - final String? arg_appName = (args[0] as String?); - assert(arg_appName != null, - 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.fetchAndActivate was null, expected non-null String.'); - try { - final bool output = await api.fetchAndActivate(arg_appName!); - return [output]; - } on PlatformException catch (e) { - return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); - } - }); - } - } - { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.activate$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger); - if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); - } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.activate was null.'); - final List args = (message as List?)!; - final String? arg_appName = (args[0] as String?); - assert(arg_appName != null, - 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.activate was null, expected non-null String.'); - try { - final bool output = await api.activate(arg_appName!); - return [output]; - } on PlatformException catch (e) { - return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); - } - }); - } - } - { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setConfigSettings$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger); - if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); - } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setConfigSettings was null.'); - final List args = (message as List?)!; - final String? arg_appName = (args[0] as String?); - assert(arg_appName != null, - 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setConfigSettings was null, expected non-null String.'); - final RemoteConfigPigeonSettings? arg_settings = - (args[1] as RemoteConfigPigeonSettings?); - assert(arg_settings != null, - 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setConfigSettings was null, expected non-null RemoteConfigPigeonSettings.'); - try { - await api.setConfigSettings(arg_appName!, arg_settings!); - return wrapResponse(empty: true); - } on PlatformException catch (e) { - return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); - } - }); - } - } - { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setDefaults$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger); - if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); - } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setDefaults was null.'); - final List args = (message as List?)!; - final String? arg_appName = (args[0] as String?); - assert(arg_appName != null, - 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setDefaults was null, expected non-null String.'); - final Map? arg_defaultParameters = - (args[1] as Map?)?.cast(); - assert(arg_defaultParameters != null, - 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setDefaults was null, expected non-null Map.'); - try { - await api.setDefaults(arg_appName!, arg_defaultParameters!); - return wrapResponse(empty: true); - } on PlatformException catch (e) { - return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); - } - }); - } - } - { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.ensureInitialized$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger); - if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); - } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.ensureInitialized was null.'); - final List args = (message as List?)!; - final String? arg_appName = (args[0] as String?); - assert(arg_appName != null, - 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.ensureInitialized was null, expected non-null String.'); - try { - await api.ensureInitialized(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 BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setCustomSignals$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger); - if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); - } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setCustomSignals was null.'); - final List args = (message as List?)!; - final String? arg_appName = (args[0] as String?); - assert(arg_appName != null, - 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setCustomSignals was null, expected non-null String.'); - final Map? arg_customSignals = - (args[1] as Map?)?.cast(); - assert(arg_customSignals != null, - 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.setCustomSignals was null, expected non-null Map.'); - try { - await api.setCustomSignals(arg_appName!, arg_customSignals!); - return wrapResponse(empty: true); - } on PlatformException catch (e) { - return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); - } - }); - } - } - { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.getAll$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger); - if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); - } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.getAll was null.'); - final List args = (message as List?)!; - final String? arg_appName = (args[0] as String?); - assert(arg_appName != null, - 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.getAll was null, expected non-null String.'); - try { - final Map output = await api.getAll(arg_appName!); - return [output]; - } on PlatformException catch (e) { - return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); - } - }); - } - } - { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.getProperties$messageChannelSuffix', - pigeonChannelCodec, - binaryMessenger: binaryMessenger); - if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); - } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.getProperties was null.'); - final List args = (message as List?)!; - final String? arg_appName = (args[0] as String?); - assert(arg_appName != null, - 'Argument for dev.flutter.pigeon.firebase_remote_config_platform_interface.FirebaseRemoteConfigHostApi.getProperties was null, expected non-null String.'); - try { - final Map output = - await api.getProperties(arg_appName!); - return [output]; - } on PlatformException catch (e) { - return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); - } - }); - } - } - } -} From 21964cdcf0ac6a0dc938f4bd594c126eaa097262 Mon Sep 17 00:00:00 2001 From: Jude Kwashie Date: Tue, 8 Jul 2025 13:58:10 +0000 Subject: [PATCH 6/6] chore: remove uncecessary log --- .../firebase/firebaseremoteconfig/FirebaseRemoteConfigPlugin.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/firebase_remote_config/firebase_remote_config/android/src/main/kotlin/io/flutter/plugins/firebase/firebaseremoteconfig/FirebaseRemoteConfigPlugin.kt b/packages/firebase_remote_config/firebase_remote_config/android/src/main/kotlin/io/flutter/plugins/firebase/firebaseremoteconfig/FirebaseRemoteConfigPlugin.kt index 4a3f4ebea8ae..e0d832f040a4 100644 --- a/packages/firebase_remote_config/firebase_remote_config/android/src/main/kotlin/io/flutter/plugins/firebase/firebaseremoteconfig/FirebaseRemoteConfigPlugin.kt +++ b/packages/firebase_remote_config/firebase_remote_config/android/src/main/kotlin/io/flutter/plugins/firebase/firebaseremoteconfig/FirebaseRemoteConfigPlugin.kt @@ -76,7 +76,6 @@ class FirebaseRemoteConfigPlugin remoteConfig.info.configSettings.minimumFetchIntervalInSeconds configProperties["lastFetchTime"] = remoteConfig.info.fetchTimeMillis configProperties["lastFetchStatus"] = mapLastFetchStatus(remoteConfig.info.lastFetchStatus) - Log.d(TAG, "Sending fetchTimeout: " + configProperties["fetchTimeout"]) return configProperties }