React Native SDK
Preview · 0.1.0-beta.3 · React Native 0.74+
Download the npm-compatible package, keep it in your project (for example vendor/) so teammates and CI resolve it, and install it:
npm install ./vendor/kemegames-pushhub-react-native-0.1.0-beta.3.tgz
cd ios && pod install
On iOS, complete the iOS setup below before pod install.
import * as PushHub from '@kemegames/pushhub-react-native';
await PushHub.configure({ appId, publishableKey: 'ph_pub_YOUR_KEY' });
await PushHub.start();
await PushHub.identify('player-1842', { tier: 'gold' });
await PushHub.track('level_completed', { level: '20' });
The typed TurboModule surface ships CommonJS, ESM, and declarations. Android and iOS implementations delegate to native cores; autolinking metadata is included. From package 0.1.0-beta.3 the Android module runs React Native codegen for its spec (PushHubSpec), which new-architecture apps (React Native 0.76 and later) need: with 0.1.0-beta.2 and earlier, their Android build fails with "'PushHubSpec.h' file not found". The module itself is served through the new architecture's interop layer, and the JavaScript package passes strict TypeScript and clean package builds. Native example-app builds remain Preview gates.
Android. Nothing else to install. The module depends on com.kemegames.pushhub:pushhub-android:0.1.0-beta.7 and adds PushHub's Maven repository (https://docs.pushhub.kemegames.com/maven, limited to the com.kemegames.pushhub group) to your Gradle projects, so the Android core and its dependencies resolve on a clean machine. Earlier packages expected the core in Maven local; that step is no longer needed. If your android/settings.gradle sets RepositoriesMode.FAIL_ON_PROJECT_REPOS (React Native's template doesn't), add that repository there instead. React Native's Android build wants JDK 17. Add your Firebase configuration (google-services.json).
Status. A new React Native app with the module installed from its tgz is checked by scripts/sdk-consumer-validation/react-native.mjs (npm install, TypeScript, gradlew assembleDebug). It hasn't been run on a device or emulator, so the module stays Preview. Instant refresh and signed config come from the Android core, but they haven't been shown through the React Native API, so this guide doesn't claim them.
iOS setup
Version requirements: the iOS module ships in package 0.1.0-beta.2. It needs the next iOS SDK release, which is pending a macOS build, and doesn't build against iOS SDK 0.1.0-beta.1. Package 0.1.0-beta.1 has no working iOS module.
1. Add the iOS SDK pod
The iOS SDK isn't on the CocoaPods trunk, so add it to ios/Podfile inside your app target, then run pod install:
target 'YourApp' do
pod 'PushHub', :git => 'https://github.com/kemegames-studio/pushhub.git', :commit => 'COMMIT_SHA'
# ...the rest of the target, including use_native_modules!
end
Pin a commit that contains PushHub.podspec, or the iOS SDK release tag once it's published (:tag => '...'). Without this line, pod install fails with Unable to find a specification for 'PushHub'. The pods need iOS 15 or later: if your Podfile sets platform :ios lower, raise it to '15.0'.
2. Forward the APNs token
Enable the Push Notifications capability in Xcode. Then forward the APNs callbacks from your AppDelegate to the module, so PushHub.start() receives the device token.
Swift AppDelegate (the template since React Native 0.77):
import PushHubReactNative
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
PushHubAppDelegate.didRegisterForRemoteNotifications(withDeviceToken: deviceToken)
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
PushHubAppDelegate.didFailToRegisterForRemoteNotifications(withError: error)
}
Objective-C AppDelegate.mm:
#import "PushHubReactNative-Swift.h" // with use_frameworks!: <PushHubReactNative/PushHubReactNative-Swift.h>
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
[PushHubAppDelegate didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
[PushHubAppDelegate didFailToRegisterForRemoteNotificationsWithError:error];
}
Behaviour on iOS
start()registers with APNs without showing a prompt, waits up to 60 seconds for the token from yourAppDelegate, then resolves the installation ID. It rejects ifconfigure()hasn't resolved, if APNs registration fails, or if no token arrives in time.requestPermission()shows the iOS notification prompt the first time and resolves whether the user allowed notifications. On Android it resolves the current permission state while the prompt is still open.reportOpened(payload, actionId)takes the notification'suserInfo, as your notification library delivers it.- Every other method behaves as on Android. Failures reject with the code
pushhub_error(pushhub_config_errorfor remote config), anddiagnostics()returns the same keys on both platforms.
Remote config
Remote config lets you change values in a shipped app, such as starter coins, a maintenance flag or a store layout, from the PushHub dashboard. You reach it through PushHub.config, which has the same API as the JavaScript SDK's client.config.
Version requirements: remote config needs package 0.1.0-beta.2 or later; 0.1.0-beta.1 doesn't have it. On Android, 0.1.0-beta.2 uses Android SDK 0.1.0-beta.6. On iOS it needs the next iOS SDK release, which is pending a macOS build: the iOS native module calls the iOS SDK's PushHub.config API, so it doesn't build against an earlier iOS SDK.
Setup and defaults
Set in-app defaults before you read any value. The getters use them before the first fetch and for any key the server leaves out.
import * as PushHub from '@kemegames/pushhub-react-native';
PushHub.config.setDefaults({
starter_coins: 100,
maintenance_mode: false,
welcome_text: 'Welcome!',
store_layout: { tabs: ['offers', 'coins'] },
});
setDefaults replaces the previous defaults.
Fetch on load
await PushHub.configure({ appId, publishableKey: 'ph_pub_YOUR_KEY' });
await PushHub.start();
const changed = await PushHub.config.fetchAndActivate(); // true when the active values changed
Fetching needs a registered installation, so call it after start(). To apply config only at a safe moment, such as a loading screen, fetch and activate separately:
await PushHub.config.fetch(); // downloads, but the getters don't change yet
// ...later, on the loading screen
if (await PushHub.config.activate()) applyConfig();
Read values
const coins = PushHub.config.getNumber('starter_coins');
const maintenance = PushHub.config.getBoolean('maintenance_mode');
const text = PushHub.config.getString('welcome_text');
const layout = PushHub.config.getJson<{ tabs: string[] }>('store_layout'); // parsed copy, or undefined
const { value, source } = PushHub.config.getValue('starter_coins'); // source: 'remote', 'default' or 'static'
const all = PushHub.config.getAll();
Each getter looks for the active remote value first, then your in-app default, and otherwise returns the type's zero value ('', false, 0, or undefined for JSON). The getters are synchronous and never throw.
Listen for updates
const unsubscribe = PushHub.config.onUpdated(keys => {
if (keys.includes('maintenance_mode')) refreshMaintenanceBanner();
});
// later: unsubscribe();
The listener runs after an activation that changed values, with only the keys that were added, removed or changed.
Offline behaviour
The native SDK stores the active values, ETag, version and last fetch time on the device. When configure() resolves, the last activated config is loaded, so it is available offline and on the next cold start before any network call. PushHub.config.ready() resolves once that load has finished.
A failed fetch rejects the promise from fetchAndActivate() or fetch() and keeps the active values. If you don't await the promise, the rejection isn't reported as unhandled. Each fetch sends the stored ETag, so an unchanged config costs a 304 Not Modified.
Fetch interval and timeout
A fetch reaches the network at most once per interval. The interval is the larger of the server's minimumFetchIntervalSeconds (900 seconds by default) and PushHub.config.minimumFetchIntervalSeconds. Inside the interval, a fetch succeeds and uses the stored config. Pass { force: true } to skip the interval in development builds. PushHub.config.fetchTimeoutMs (10000 by default) is passed to the native SDK in whole seconds, from 1 to 300.
PushHub.config.minimumFetchIntervalSeconds = 3600;
await PushHub.config.fetchAndActivate({ force: __DEV__ });
Instant refresh
When you publish, roll back or schedule a publish with Notify devices set to Test devices only or All players, PushHub sends the environment's devices a silent message, and the native SDK fetches and activates the newer config.
- Android: from package 0.1.0-beta.2, which uses Android SDK 0.1.0-beta.6, refreshes are applied while the app is running; no code is needed. Only that case is supported: the Android core 0.1.0-beta.7 in package 0.1.0-beta.3 can configure itself when Android starts the app's process for a refresh, but that hasn't been validated with React Native, so don't rely on it. See SDK support levels.
- iOS: needs the next iOS SDK release (pending a macOS build). In Xcode, add Background Modes and turn on Remote notifications, then forward background notifications from your
AppDelegateto PushHub first.
Signed config is not available in this SDK. It has no API to set trusted signing keys, so it accepts unsigned config even when Config signing is turned on for the environment. Native verification exists in the Android core only when your own Android code sets the keys. See SDK support levels.
Swift AppDelegate (add override if your app delegate's superclass already implements this method):
import PushHub
func application(_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
if PushHub.handleBackgroundNotification(userInfo, completionHandler: completionHandler) { return }
completionHandler(.noData)
}
Objective-C AppDelegate.mm:
extern "C" BOOL PushHubHandleBackgroundNotification(NSDictionary *userInfo, void (^completionHandler)(UIBackgroundFetchResult));
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
if (PushHubHandleBackgroundNotification(userInfo, completionHandler)) return;
completionHandler(UIBackgroundFetchResultNoData);
}
If another library also needs this callback, call it where these examples call completionHandler with "no data". PushHub returns false for every notification that isn't its own and doesn't call the handler then.
The native SDK activates the new values right away. PushHub.config shows them, and onUpdated listeners run, the next time JavaScript calls fetchAndActivate() or activate() (a call inside the minimum interval makes no request but still picks them up), or at the next launch. Instant refresh is best effort, so keep fetching on load.
Platforms
- Android: uses the Android SDK's
PushHub.config(0.1.0-beta.7 in package 0.1.0-beta.3). - iOS: uses the iOS SDK's
PushHub.config, available in the next iOS SDK release.