Firebase AppSpike

The Free Firebase Remote Config Alternative

Migrating from Firebase is a one-click config import, the API your code already calls, and a diff that is mostly a class name. The only change on your bill is that there is no bill.

Your code today: Firebase
Kotlin
val config = Firebase.remoteConfig

config.setDefaultsAsync(defaults)

config.fetchAndActivate()
    .addOnCompleteListener { /* initialization complete */ }

val paywall = config.getBoolean("paywall_enabled")
val message = config.getString("welcome_message")
Your code after: AppSpike
Kotlin
val config = AppSpikeRemoteConfig

config.setDefaults(defaults)

scope.launch { config.fetchAndActivate() } // initialization complete

val paywall = config.getBoolean("paywall_enabled")
val message = config.getString("welcome_message")
Your code today: Firebase
Swift
let config = RemoteConfig.remoteConfig()

config.setDefaults(["paywall_enabled": false as NSObject])

config.fetchAndActivate { status, error in /* initialization complete */ }

let paywall = config["paywall_enabled"].boolValue
let message = config["welcome_message"].stringValue
Your code after: AppSpike
Swift
let config = AppSpike.remoteConfig()

config.setDefaults(["paywall_enabled": false])

try await config.fetchAndActivate() // initialization complete

let paywall = config["paywall_enabled"].boolValue
let message = config["welcome_message"].stringValue
On iOS the API is native Swift. Subscript reads stay subscript reads (no NSObject casts), completion handlers become async/await, and a @RemoteConfigProperty wrapper is available for SwiftUI.
Your code today: Firebase
Dart
final config = FirebaseRemoteConfig.instance;

await config.setDefaults({'paywall_enabled': false});

await config.fetchAndActivate();

final paywall = config.getBool('paywall_enabled');
final message = config.getString('welcome_message');
Your code after: AppSpike
Dart
final config = AppSpikeRemoteConfig.instance;

await config.setDefaults({'paywall_enabled': false});

await config.fetchAndActivate();

final paywall = config.getBool('paywall_enabled');
final message = config.getString('welcome_message');
On Flutter the API mirrors firebase_remote_config method-for-method. The class name changes, and firebase_core is removed from your dependencies.
Your code today: Firebase
TypeScript
import remoteConfig from '@react-native-firebase/remote-config';

await remoteConfig().setDefaults({ paywall_enabled: false });

await remoteConfig().fetchAndActivate();

const paywall = remoteConfig().getBoolean('paywall_enabled');
const message = remoteConfig().getString('welcome_message');
Your code after: AppSpike
TypeScript
import { remoteConfig } from '@appspike/react-native-remote-config';

await remoteConfig().setDefaults({ paywall_enabled: false });

await remoteConfig().fetchAndActivate();

const paywall = remoteConfig().getBoolean('paywall_enabled');
const message = remoteConfig().getString('welcome_message');
On React Native only the import changes. Same call sites, no native modules, and it runs in Expo Go.
Your code today: Firebase
C#
var config = FirebaseRemoteConfig.DefaultInstance;

await config.SetDefaultsAsync(defaults);

await config.FetchAndActivateAsync();

var paywall = config.GetValue("paywall_enabled").BooleanValue;
var message = config.GetValue("welcome_message").StringValue;
Your code after: AppSpike
C#
var config = AppSpikeRemoteConfig.DefaultInstance;

await config.SetDefaultsAsync(defaults);

await config.FetchAndActivateAsync();

var paywall = config.GetValue("paywall_enabled").BooleanValue;
var message = config.GetValue("welcome_message").StringValue;
On Unity the API matches Firebase Remote Config for Unity. Change the instance class, delete the google-services file, and keep everything else.
Your code today: Firebase
TypeScript
import { initializeApp } from 'firebase/app';
import { fetchAndActivate, getBoolean, getRemoteConfig }
  from 'firebase/remote-config';

const rc = getRemoteConfig(initializeApp(firebaseConfig));

await fetchAndActivate(rc);

const paywall = getBoolean(rc, 'paywall_enabled');
Your code after: AppSpike
TypeScript
import { initializeApp } from '@appspike/web/app';
import { fetchAndActivate, getBoolean, getRemoteConfig }
  from '@appspike/web/remote-config';

const rc = getRemoteConfig(initializeApp({ apiKey }));

await fetchAndActivate(rc);

const paywall = getBoolean(rc, 'paywall_enabled');
On the web the modular function names are identical. Change the import path and pass an API key instead of a Firebase config object.
Built by app developers for our own apps first No fetch limits, free at any scale Drop-in Firebase API on every platform Plain-JSON config you can export anytime

Free Is Not a Trick. It Is Our Business Model.

AppSpike is built by Pyde Technologies, app developers first. Our own apps, PokeRaid and PokeTrade, two of the most popular apps in their category, run on the same SDK we ship to you. Remote Config exists because when Firebase's usage-based pricing was announced, our own projected config bill came to about $25,000 a year. We built this to make that bill $0, then opened it to everyone.

AppSpike's business is built on its ad revenue optimization platform. Remote Config is free because it makes the platform more useful. There is no per-fetch billing waiting for you at scale, and no premium tier that hides features you will need later.

You don't need to use AppSpike ads to use Remote Config. Initialize the SDK in config-only mode and your existing ad stack stays untouched.

What free includes

Unlimited fetches. Unlimited parameters (up to the 3,000-parameter engineering limit). All targeting conditions. Version history and rollback. Firebase config import. Every platform we ship.

What we gain

Some Remote Config users try our ad optimization later. That is the whole business model. If you never do, the config service still costs us close to nothing to run. Your config is served as static, CDN-cached JSON, and evaluation happens on your users' devices, not our servers.

What Firebase Remote Config Costs as You Grow

If you are under 100K fetches a day, Firebase costs you nothing today. The bill starts when your app grows. As of September 2026, Firebase bills $0.06 per 10K fetches beyond the daily free tier.

Config Fetches / Day Firebase / Year AppSpike / Year
100K $0 (inside the free tier) $0
500K ~$876 $0
2M ~$4,161 $0
10M ~$21,681 $0

A full 365-day year at Firebase's published overage rate. Your fetch volume depends on how often each device fetches. Foreground refreshes, shorter intervals, and realtime reconnects push the Firebase figure up, never down.

Three Reasons Teams Switch

Costs That Grow With Your Success

A config bill that grows with your user count means you pay more for being successful. AppSpike serves your config as immutable, CDN-cached files that are evaluated on the device. That costs almost nothing, so we don't meter it.

No Lock-In, In Either Direction

You can export your Firebase config, but no other tool can import it. AppSpike imports your Firebase config in one click, and your AppSpike config is plain JSON you can export and take with you at any time. Leaving should stay easy forever, not only joining.

User Data That Stays on the Device

Firebase uploads your custom signals (subscription tier, player level, whatever you target on) to Google with every fetch. AppSpike evaluates conditions on the device, so signals never leave the phone. Same targeting, no transmission.

What Happens When Things Go Wrong

You can only trust config infrastructure if you know how it behaves during failures. Here are the exact answers.

If our servers are unreachable

Your app keeps running on the last activated config, which is persisted on the device. Keys that were never fetched resolve to the in-app defaults you set with setDefaults. A failed fetch never degrades a running app. It's the same offline model Firebase uses.

If you publish a bad config

Every publish is an immutable version. The dashboard diffs any two versions and rolls back in one click. The rollback itself is just a new publish, fully audited with author and timestamp.

If you want to switch gradually

The Firebase and AppSpike SDKs coexist in one app with different artifacts and different storage. Run both during the rollout, control which one is read with a flag, and move traffic 5% → 50% → 100% before removing Firebase. The migration guide documents this pattern end to end.

If you want to leave

Your config is plain JSON. Parameters, conditions, and version history are yours to export at any time. We want the product to keep you, not the cost of leaving.

Everything You Use Today, Without the Usage Bill

Same API as Firebase, on Every Platform

Each SDK mirrors the Firebase SDK of its own platform: Firebase Android naming on Android and KMP, a native Swift API on iOS, the firebase_remote_config Dart surface on Flutter, the @react-native-firebase shape on React Native, the Firebase Unity API in C#, and the modular firebase/remote-config functions on the web. Call sites translate one-to-one.

Rich Targeting

Country, language, platform, app version and build, percentage rollouts with stable bucketing, custom signals, time windows, install cohorts, and per-device targeting. First matching condition wins, the same mental model as Firebase.

One-Click Firebase Import

Upload your exported Firebase config. Parameters, conditions, groups, tag colors, and ordering carry over. Anything unconvertible is flagged in a warning report instead of silently dropped. Nothing publishes until you review the diff.

Built Native for Each Platform

iOS gets subscripts, a @RemoteConfigProperty wrapper, and Codable decoding via Swift Package Manager. React Native is pure TypeScript with no native modules, so it runs in Expo Go. Flutter is pure Dart with no firebase_core. The web package has zero runtime dependencies. No google-services files anywhere.

Kotlin Multiplatform

The SDK is KMP at its core. Add one dependency to commonMain and read config from shared code across Android, iOS, and wasmJs. No other config provider ships this natively.

Versioned Publishing, Full History

Draft → diff → publish. Every version is immutable with author and timestamp. Select any two to diff, and roll back in one click. Nothing reaches devices until you publish.

AppSpike vs Firebase

Including the things Firebase does that we don't do yet. You would find out anyway, so it is better to learn it here than in production.

Capability Firebase Remote Config AppSpike Remote Config
Price at scale 100K fetches/day free, then $0.06 per 10K Free, no fetch metering
Client API Platform-specific SDKs Same as Firebase
Config import No import path from other providers One-click import from Firebase
Condition evaluation On Google's servers On the device, where you can inspect it
Custom signals Uploaded with every fetch Never leave the device
Percentage rollouts Stable SHA-256 bucketing
Version history & rollback
Real-time config updates Real-time Remote Config Via data push notifications
A/B testing integration Firebase A/B Testing With percentage conditions
Analytics audience targeting Google Analytics audiences Use custom signals instead
Platforms Android, iOS, Web, Flutter, Unity Android, iOS, Web, Kotlin Multiplatform, Flutter, React Native, Unity
Kotlin Multiplatform Native

If your rollout depends on Google Analytics audiences today, Firebase is still the right tool for that piece. The two SDKs work together in one app, so you can migrate everything else first. Comparing more than the two of us? See our roundup of every free Firebase Remote Config alternative, our own included.

Get Started with the SDK

Everything you need to integrate AppSpike Remote Config, from installation to production.

1. Install the SDK

Remote Config reuses your AppSpike app API key and device identity, so there are no new credentials. Call AppSpike.initialize with your API key at app startup before fetching, as with the rest of the SDK.

Kotlin — build.gradle.kts
repositories {
    maven("https://maven.pkg.github.com/pydetech/appspike-sdk-kmp-dist")
}

dependencies {
    implementation("dev.appspike:remote-config:1.2.1")
}
Swift — Package.swift
// Add the AppSpike package dependency
.package(url: "https://github.com/pydetech/appspike-sdk-ios-dist", from: "1.0.0")

// Add both products to your target
.product(name: "AppSpikeSDKCore", package: "appspike-sdk-ios-dist"),
.product(name: "AppSpikeRemoteConfig", package: "appspike-sdk-ios-dist")

Distributed via Swift Package Manager. Add AppSpikeSDKCore and AppSpikeRemoteConfig as products.

Kotlin — commonMain
// commonMain dependencies
implementation("dev.appspike:remote-config:1.2.1")

KMP apps add the artifact to the shared source set. Targets Android, iOS, and wasmJs.

YAML — pubspec.yaml
dependencies:
  appspike_remote_config:
    git:
      url: https://github.com/pydetech/appspike-sdk-flutter-dist.git
      ref: 0.1.0

Pure Dart, no Firebase core and no google-services files. The API mirrors firebase_remote_config method-for-method, with AppSpikeRemoteConfig.instance as the entry point.

Shell
npm install github:pydetech/appspike-sdk-react-native-dist#0.1.0
npm install @react-native-async-storage/async-storage

Installs as @appspike/react-native-remote-config. Pure TypeScript with no native modules, so it works with any React Native version, Expo (including Expo Go), and Hermes out of the box. The API mirrors @react-native-firebase/remote-config.

JSON — Packages/manifest.json
{
  "dependencies": {
    "dev.appspike.sdk-core": "https://github.com/pydetech/appspike-sdk-unity-dist.git?path=dev.appspike.sdk-core#0.1.0",
    "dev.appspike.remote-config": "https://github.com/pydetech/appspike-sdk-unity-dist.git?path=dev.appspike.remote-config#0.1.0",
    "com.unity.nuget.newtonsoft-json": "3.2.1"
  }
}

Requires Unity 2021.3+. List dev.appspike.sdk-core first, since Unity's package manager can't resolve git-to-git dependencies. The API matches Firebase Remote Config for Unity: DefaultInstance, FetchAndActivateAsync(), GetValue().

Shell
npm install github:pydetech/appspike-sdk-web-dist#0.1.0

Installs as @appspike/web with zero runtime dependencies, ESM and CommonJS builds, and TypeScript declarations. The modular API mirrors firebase/remote-config, including fetchAndActivate, getValue, and setCustomSignals.

2. Set Defaults and Fetch

The lifecycle mirrors Firebase exactly: set in-app defaults, optionally configure fetch settings, then fetch and activate.

Kotlin
// 1. Set in-app defaults (persisted across cold starts)
AppSpikeRemoteConfig.setDefaults(
    mapOf(
        "welcome_message" to "Hello",
        "paywall_enabled" to false,
        "max_retries" to 3L,
    )
)

// 2. Fetch and activate
scope.launch {
    try {
        AppSpikeRemoteConfig.fetchAndActivate()
        // initialization complete
    } catch (e: RemoteConfigThrottledException) {
        // Within minimum fetch interval, cached config stands
    } catch (e: RemoteConfigFetchException) {
        // Network failure, previously activated config stays
    }
}

// 3. Read values, same names as Firebase
val msg     = AppSpikeRemoteConfig.getString("welcome_message")
val enabled = AppSpikeRemoteConfig.getBoolean("paywall_enabled")
val retries = AppSpikeRemoteConfig.getLong("max_retries")
Swift
let config = AppSpike.remoteConfig()

// 1. Set in-app defaults
config.setDefaults([
    "welcome_message": "Hello",
    "paywall_enabled": false,
    "max_retries": 3,
])

// 2. Fetch and activate
do {
    try await config.fetchAndActivate()
    // initialization complete
} catch is RemoteConfigThrottledError {
    // Within minimum fetch interval, cached config stands
} catch {
    // Network failure, previously activated config stays
}

// 3. Read values with the native Swift subscript API
let msg     = config["welcome_message"].stringValue
let enabled = config["paywall_enabled"].boolValue
let retries = config["max_retries"].numberValue
Kotlin — commonMain
// Shared code, same API on Android, iOS, and wasmJs

// 1. Set in-app defaults (persisted across cold starts)
AppSpikeRemoteConfig.setDefaults(
    mapOf(
        "welcome_message" to "Hello",
        "paywall_enabled" to false,
        "max_retries" to 3L,
    )
)

// 2. Fetch and activate from any coroutine scope
scope.launch {
    try {
        AppSpikeRemoteConfig.fetchAndActivate()
        // initialization complete
    } catch (e: RemoteConfigThrottledException) {
        // Within minimum fetch interval, cached config stands
    } catch (e: RemoteConfigFetchException) {
        // Network failure, previously activated config stays
    }
}

// 3. Read values from common code, same names as Firebase
val msg     = AppSpikeRemoteConfig.getString("welcome_message")
val enabled = AppSpikeRemoteConfig.getBoolean("paywall_enabled")
val retries = AppSpikeRemoteConfig.getLong("max_retries")
Dart
final remoteConfig = AppSpikeRemoteConfig.instance;

// 1. Set in-app defaults (persisted, used before the first fetch)
await remoteConfig.setDefaults(const {
  'welcome_message': 'Hello',
  'paywall_enabled': false,
  'max_retries': 3,
});

// 2. Fetch and activate
await remoteConfig.fetchAndActivate();
// initialization complete

// 3. Read values, same names as firebase_remote_config
final message = remoteConfig.getString('welcome_message');
final paywall = remoteConfig.getBool('paywall_enabled');
final retries = remoteConfig.getInt('max_retries');
TypeScript
// 1. Set in-app defaults
await remoteConfig().setDefaults({
  welcome_message: 'Hello',
  paywall_enabled: false,
  max_retries: 3,
});

// 2. Fetch and activate
await remoteConfig().fetchAndActivate();
// initialization complete

// 3. Read values, same names as @react-native-firebase
const message = remoteConfig().getString('welcome_message');
const paywall = remoteConfig().getBoolean('paywall_enabled');
const retries = remoteConfig().getNumber('max_retries');
C#
var remoteConfig = AppSpikeRemoteConfig.DefaultInstance;

// 1. Set in-app defaults
await remoteConfig.SetDefaultsAsync(new Dictionary<string, object>
{
    { "welcome_message", "Hello" },
    { "paywall_enabled", false },
    { "max_retries", 3L },
});

// 2. Fetch and activate
await remoteConfig.FetchAndActivateAsync();

// 3. Read values, same names as Firebase for Unity
var message = remoteConfig.GetValue("welcome_message").StringValue;
var paywall = remoteConfig.GetBoolean("paywall_enabled");
var retries = remoteConfig.GetLong("max_retries");
TypeScript
const app = initializeApp({ apiKey: 'YOUR_API_KEY' });
const remoteConfig = getRemoteConfig(app);

// 1. Set in-app defaults
remoteConfig.defaultConfig = {
  welcome_message: 'Hello',
  paywall_enabled: false,
  max_retries: 3,
};

// 2. Fetch and activate
await fetchAndActivate(remoteConfig);

// 3. Read values, same modular API as firebase/remote-config
const message = getString(remoteConfig, 'welcome_message');
const paywall = getBoolean(remoteConfig, 'paywall_enabled');
const retries = getNumber(remoteConfig, 'max_retries');

3. Done

Your app now reads config from AppSpike. Values resolve in this order: remote → in-app default → type zero-value (empty string, false, 0). Accessors never block the calling thread. They read from an in-memory cache and are safe on the main thread.

Full reference documentation

Every accessor and lifecycle method, all targeting conditions, percentage rollout mechanics, and config limits.

API Reference & Targeting →

Try It Before You Commit to It

Steps 1 and 2 don't touch your app. Import your Firebase config, look around the dashboard, and decide with your actual config in front of you.

1

Export your Firebase config

Either path gives you the same JSON file with your parameters, conditions, and groups.

From the Firebase console: open Remote Config, click the menu at the top of the Parameters page, and choose Download current config file.

Or from the terminal:

Shell
firebase remoteconfig:get -o template.json
2

Import and preview in the console

Go to Remote Config → Import in the AppSpike console and upload your template.json. The importer runs a dry-run first, asks you to map Firebase app IDs to AppSpike apps, and shows a diff before applying. Anything it can't convert is flagged in a warning report, never silently dropped. Nothing reaches devices until you publish.

3

Update your app code

The mapping is nearly 1:1 on every platform. Pick yours:

Firebase (Kotlin)AppSpike (Kotlin)
Firebase.remoteConfigAppSpikeRemoteConfig (singleton object)
setDefaultsAsync(map)setDefaults(map)
setConfigSettingsAsync(settings)setConfigSettings(settings)
fetchAndActivate() → Task<Boolean>suspend fetchAndActivate(): Boolean
getString / getBoolean / getLong / getDouble / getValue / getAllIdentical names
setCustomSignals(signals)Identical, same builder pattern
addOnConfigUpdateListenerIdentical, updates arrive via data push notifications
FirebaseRemoteConfigFetchThrottledExceptionRemoteConfigThrottledException

The one mechanical change: Firebase's Task-returning …Async methods become suspend functions, so replace .addOnCompleteListener { } chains with a coroutine.

The SDK is Kotlin Multiplatform at its core, so a KMP app migrates once, in shared code. Add the dependency to commonMain and move your config reads behind a shared interface. The API is the same Firebase-style surface as on Android, callable from Android, iOS, and wasmJs targets.

Kotlin — commonMain
// commonMain dependencies
implementation("dev.appspike:remote-config:1.2.1")

// shared code, same API as Android
val paywall = AppSpikeRemoteConfig.getBoolean("paywall_enabled")

If today each platform calls Firebase separately, this is the moment to consolidate: port the Android call sites with the mapping in the Android tab, then delete the duplicated iOS config code entirely.

Firebase (Swift)AppSpike (Swift)
RemoteConfig.remoteConfig()AppSpike.remoteConfig()
config["key"].stringValueIdentical subscripts
setDefaults([String: NSObject])setDefaults([String: Any]), no NSObject casts
fetchAndActivate(completionHandler:)try await fetchAndActivate()
addOnConfigUpdateListenerAsyncSequence of config updates, delivered via data push notifications
NSError with status codesTyped errors: RemoteConfigThrottledError, RemoteConfigFetchError

SwiftUI apps can also bind values directly with the @RemoteConfigProperty wrapper, and JSON parameters decode straight to Codable types through the subscript API.

firebase_remote_configappspike_remote_config
FirebaseRemoteConfig.instanceAppSpikeRemoteConfig.instance
Firebase.initializeApp(options: ...)AppSpike.instance.initialize(apiKey: ..., modules: [remoteConfig])
setDefaults / setConfigSettings / setCustomSignalsIdentical
fetch / activate / fetchAndActivateIdentical
getString / getBool / getInt / getDouble / getValue / getAllIdentical
onConfigUpdated streamIdentical, fires on activate
FirebaseException with codesRemoteConfigThrottledException / RemoteConfigFetchException

The value API is identical, so most migrations only touch imports and initialization. Drop firebase_core entirely, since there are no google-services files.

@react-native-firebase/remote-config@appspike/react-native-remote-config
import remoteConfig from '@react-native-firebase/remote-config'import { AppSpike, appSpikeRemoteConfig, remoteConfig } from '@appspike/react-native-remote-config'
Auto-init from google-services filesAppSpike.initialize({ apiKey, modules: [appSpikeRemoteConfig] })
getString / getBoolean / getNumber / getValue / getAllIdentical call sites
setDefaults / setConfigSettings / fetch / activate / fetchAndActivateIdentical
setDefaultsFromResource(name)Not supported, pass the object to setDefaults
onConfigUpdated((event, error) => ...)onConfigUpdated((event) => ...), no error argument

Pure TypeScript with no native modules, so there are no pods to reinstall and it runs in Expo Go. getKeysByPrefix is included as an extension beyond the Firebase RN surface.

Firebase Remote Config (Unity)AppSpike (Unity)
FirebaseRemoteConfig.DefaultInstanceAppSpikeRemoteConfig.DefaultInstance
Firebase.RemoteConfig namespaceAppSpike.RemoteConfig
google-services.json + CheckAndFixDependenciesAsync()AppSpikeSdk.InitializeAsync(apiKey, modules)
SetDefaultsAsync / FetchAsync / ActivateAsync / FetchAndActivateAsyncIdentical
GetValue(key).StringValue, ConfigSettings, InfoIdentical
OnConfigUpdateListener eventIdentical, fires on activate
FirebaseExceptionRemoteConfigFetchException / RemoteConfigThrottledException

One helpful difference: ConfigValue accessors never throw. An unconvertible value reads as the zero value instead of raising FormatException, and typed getters fall through to your in-app default.

firebase/remote-config@appspike/web/remote-config
import { initializeApp } from 'firebase/app'import { initializeApp } from '@appspike/web/app'
Firebase config object{ apiKey: 'YOUR_APPSPIKE_API_KEY' }
getRemoteConfig / fetchConfig / activate / fetchAndActivate / ensureInitializedIdentical function names
getValue / getAll / getString / getBoolean / getNumberIdentical
rc.settings / rc.defaultConfig / rc.lastFetchStatusIdentical
setCustomSignals(rc, signals)Identical function names

Same modular API shape, so your defaultConfig, settings, and getter calls stay unchanged. AppSpike's web SDK also adds getKeysByPrefix and config update listeners, which Firebase's web SDK doesn't have.

The SDK README also ships a copy-paste AI migration prompt. Point your coding agent at it and it converts your call sites for you.

4

Publish, verify, and cut over

Publish your AppSpike config, run the app, and observe that the parameters arrive with the values you expect. Then roll out at your own pace: the two SDKs work together in one app, so you can move traffic gradually with a flag.

Things to Know Before You Switch

  • Evaluation is client-side. Your full config (all conditions and values) is delivered to the device. Never put secrets in Remote Config. It's the same rule as Firebase, but here non-matching conditional values are visible too.
  • Percentage cohorts re-randomize. AppSpike's bucketing hash differs from Firebase's, so a device in Firebase's "10%" may not be in AppSpike's "10%". Complete in-flight gradual rollouts before switching.
  • Analytics-backed targeting doesn't carry over. Conditions using Google Analytics audiences or user properties import as inert placeholders, flagged for review. Re-express them with custom signals.
  • Real-time updates arrive via data push notifications. A publish triggers a push that wakes the SDK to fetch and activate, and your update listener fires as the new values land. If you built an FCM-based refresh workaround for Firebase, you can drop it.

Frequently Asked Questions

Why is it free? What does AppSpike gain?

We built Remote Config for ourselves first: Firebase's usage-based pricing put our own apps' config bill at roughly $25,000 a year, and replacing it cost us less than that. AppSpike's business is built on ad revenue optimization, and Remote Config is free because it brings developers into the platform, and because it genuinely costs very little to run. Your config is published as an immutable, CDN-cached JSON file, and all condition evaluation happens on your users' devices, so we don't run compute per fetch.

There's no plan to meter it later. If that ever changed, your config is plain JSON you can export and take elsewhere, which is exactly the lock-in situation we built this to avoid.

What happens to my app if AppSpike is down, or gone?

Nothing visible to your users. The last activated config is persisted on each device and keeps serving, and keys never fetched resolve to your in-app defaults. A total outage means devices serve slightly stale config until we're back, the same failure model as Firebase.

If you ever want to leave permanently, export your config as JSON and re-create it elsewhere. Your config data is never locked in.

Is my users' data safe with you?

Structurally safer than server-side evaluation. Conditions are evaluated on the device, so custom signals like subscription tier or player level are never transmitted to us or anyone else. Firebase uploads signals with every fetch. We can't see yours at all.

The trade-off to know: because the device evaluates conditions, your full config (including conditional values for segments a user isn't in) is delivered to the device. Don't put secrets in Remote Config, the same rule Firebase documents for its client-delivered config.

Do you support A/B testing and real-time updates?

Real-time updates: yes. Publishing a new version triggers a data push notification that wakes the SDK to fetch and activate, and your update listener fires as the new values land. No polling loop, no FCM workaround.

A/B testing: yes, with percentage conditions. Clauses that share a seed split the same population, so ranges like 0 to 50 and 50 to 100 build clean experiment arms, and a device keeps its group across fetches. Measure the results in the analytics tool you already use. A managed experiment dashboard with built-in statistics is on our roadmap.

How hard is the migration, really?

The config side takes minutes and no code: one export command, one upload, then a review of the warning report. On the code side, Android and KMP keep Firebase naming, so reads are untouched and async calls change shape (Tasks become coroutines). On iOS you map Firebase calls onto a native Swift API where subscript reads stay subscript reads. The Flutter, React Native, Unity, and Web SDKs mirror their Firebase counterparts method-for-method, so those migrations are mostly imports and initialization. The SDK README includes an AI migration prompt that automates the call-site conversion.

The one real surprise: percentage rollout groups are re-randomized because our bucketing hash differs from Firebase's. Finish in-flight gradual rollouts before cutting over.

Which platforms do you support?

Android, iOS (native Swift API via Swift Package Manager), Kotlin Multiplatform (Android, iOS, wasmJs from shared code), Flutter, React Native, Unity, and Web. Whatever you ship on, the config API keeps the same shape.

Do I have to use AppSpike's ad products?

No. Initialize the SDK in config-only mode and the ad side stays inactive and your existing mediation and ad stack are untouched. Remote Config needs the SDK session for fetching, nothing more.

Import Your Firebase Config.
Look Around, Then Decide.

Export from Firebase, upload to the console, and look around. No SDK, no code changes, no card. If it is not for you, you only spent five minutes.