Unity SDK

Notifications

Schedule on-device local notifications and send remote push (FCM on Android, APNs on iOS) from the KalmForge dashboard. Reflection-based bridges to Unity's mobile-notifications and Firebase Messaging packages - the SDK compiles and no-ops on platforms without them.

Overview

KalmForge.LocalNotifications wraps the official Unity com.unity.mobile.notifications package via reflection, so the SDK compiles without it and silently no-ops on platforms that don't support it. Templates authored on the dashboard are fetched on boot, cached on disk, and scheduled automatically when the app goes to the background.

Install the package

  1. Window → Package Manager → Add by name: com.unity.mobile.notifications.
  2. iOS: capabilities are enabled by the build post-processor that ships with the SDK.
  3. Android: the SDK uses the channel id "default" unless you override it on a notification.
Note
If the package is missing, every method on LocalNotifications logs a one-shot warning and returns. Your code paths stay safe.

Requesting permission

exampleC#
1bool granted = await LocalNotifications.RequestPermissionAsync();
2if (!granted) Debug.Log("Player declined notifications");

On iOS this prompts for badge + sound + alert. On Android 13+ this requests POST_NOTIFICATIONS. Older platforms return true immediately.

Scheduling a notification

exampleC#
1LocalNotifications.Schedule(new LocalNotification {
2 Id = "energy_full",
3 Title = "Your energy is full!",
4 Body = "Come back and play.",
5 FireAt = System.DateTime.Now.AddHours(2),
6 Channel = "default",
7 DeepLink = "myapp://energy",
8});
9
10// Replace by id (same Id cancels the previous schedule before scheduling again):
11LocalNotifications.Schedule(new LocalNotification { Id = "energy_full", /* ... */ });
12
13// Cancel:
14LocalNotifications.Cancel("energy_full");
15LocalNotifications.CancelAll();
Heads up
Scheduling a fire time in the past is ignored with a warning. Convert timezones carefully - FireAt is local device time.

Dashboard templates

Author re-engagement templates on the dashboard (title, body, trigger, deep-link, Android channel). Sync once on boot - they're cached for offline use and re-scheduled on every app close:

exampleC#
1await LocalNotifications.SyncTemplatesAsync(); // device language
2await LocalNotifications.SyncTemplatesAsync("fr"); // explicit override
3
4// Manually schedule one template before close, e.g. with a custom fire time:
5LocalNotifications.ScheduleTemplate("daily_streak", System.DateTime.Now.AddHours(20));

Triggers supported on the dashboard:

  • delay - now + N seconds.
  • time_of_day - HH:mm, optionally restricted to ISO weekdays (Mon=1).
  • on_demand - never auto-scheduled; trigger via ScheduleTemplate(key).

Lifecycle

  • App launch → all previously scheduled local notifications are cancelled (so they don't fire while the player is active).
  • App close / pause to background → every cached template is re-scheduled per its trigger.
  • Manual Schedule() calls survive across these transitions because the cancellation step only clears the SDK's known ids when launching, not when backgrounding.

Remote push setup

KalmForge.RemotePush acquires the platform push token (APNs on iOS, FCM on Android), registers it with the dashboard, and lets you send campaigns from Project → Notifications → Campaigns. Four steps: configure dashboard credentials, run the in-Editor Setup Wizard for Android + iOS client config, enable APNs on the App ID, then call RegisterAsync() at runtime.

Tip
Open KalmForge → Project Setup Wizard in Unity - it installs Newtonsoft.Json + Firebase Cloud Messaging, copies yourgoogle-services.json / GoogleService-Info.plistinto Assets/, patches AndroidManifest.xml, generates iOS entitlements + an Xcode post-processor, and adds theHAS_FIREBASE_PUSH_NOTIFICATIONS scripting define. The steps below explain what it does and how to do it manually if you prefer. See the Getting Started → Project Setup Wizard section for the full walkthrough.

1. Configure credentials on the dashboard

Open Project → Notifications → Setup and run the wizard. You need:

  • Android (FCM) - a Firebase service account JSON (Firebase Console → Project Settings → Service accounts → Generate new private key). This is not the same file as google-services.json; the service account key is server-side credentials, the google-services.jsonships inside the app.
  • iOS (APNs) - a .p8 auth key from developer.apple.com (Certificates, Identifiers & Profiles → Keys → +), plus your Team ID, Key ID, app Bundle ID, and whether you're targeting sandbox (development builds) or production.
Note
The wizard validates both credentials against Google and Apple before saving - if validation fails you'll see exactly what to fix.

2. Android: Firebase + google-services.json

Easiest path: run the Setup Wizard's Firebase and Config files steps - it imports Firebase Cloud Messaging and copies google-services.json to Assets/google-services.json for you. Manual setup:

  1. In the Firebase Console, add an Android app to the same Firebase project whose service account JSON you uploaded to the dashboard. Use your app's package name (e.g.com.studio.game).
  2. Download the generated google-services.json and drop it into your Unity project at:
    exampleC#
    1Assets/google-services.json
    The file must sit directly under Assets/ at the root - Firebase's Unity build processor looks for it there. Do not rename it, nest it under Resources/, or commit it toStreamingAssets/. The wizard's Config filesstep provides a dropzone that handles this automatically.
  3. Install Firebase Cloud Messaging for Unity(com.google.firebase.messaging) from the Firebase Unity SDK, or let the wizard's Firebase step pull it from the KalmForge SDK store. Importing the package also pulls in Firebase Core, which reads google-services.json at build time.
  4. The wizard's Android step patchesAssets/Plugins/Android/AndroidManifest.xml withPOST_NOTIFICATIONS, RECEIVE_BOOT_COMPLETED, the FCM intent filter, and the default notification channel meta-data. If you maintain the manifest yourself, mirror those entries by hand.
Heads up
Same Firebase project on both sides. If thegoogle-services.json in Assets/ belongs to a different Firebase project than the service account JSON uploaded to the dashboard, FCM will accept the device token but every send will be rejected with SenderId mismatch.

3. iOS: APNs entitlements

Run the Setup Wizard's iOS step - it creates Assets/KalmForge/iOS/Entitlements.entitlements with aps-environment set, plus Assets/KalmForge/Editor/KalmForgePushPostProcess.cs, which is invoked by Unity on every iOS build to add the Push Notifications capability, the entitlements file, and UIBackgroundModes = remote-notification to the generated Xcode project. Then:

  1. In the Apple Developer portal, enable the Push Notifications capability on the App ID matching your Unity Bundle Identifier.
  2. The wizard-generated post-processor addsUIBackgroundModes = remote-notification andNSUserNotificationsUsageDescription to the generatedInfo.plist. No game-side changes required.
  3. In Xcode, confirm the Push Notifications capability is listed under Signing & Capabilities. If it isn't, add it - Unity occasionally drops it when the bundle ID changes.
  4. Match the sandbox toggle in the dashboard to your build: development builds (aps-environment = development) must use sandbox, App Store / TestFlight builds must use production. The wrong environment yieldsBadDeviceToken on send.

4. Register the device token

After requesting notification permission, callRemotePush.RegisterAsync(). Safe to call on every launch - the dashboard upserts by token.

exampleC#
1using KalmForge;
2
3bool granted = await LocalNotifications.RequestPermissionAsync();
4if (!granted) return;
5
6// Optional segmentation properties (string / number / bool).
7var props = new System.Collections.Generic.Dictionary<string, object> {
8 { "level", 12 },
9 { "vip", true },
10};
11
12bool ok = await RemotePush.RegisterAsync(props);
13Debug.Log($"Push registered: {ok}");

Already have a token from your own provider? Skip acquisition and register it directly:

exampleC#
1await RemotePush.RegisterTokenAsync(token, platform: "android", properties: props);

API reference

LocalNotifications - static API
NameTypeDescription
RequestPermissionAsync()Task<bool>Prompt for permission on iOS / Android 13+.
Schedule(LocalNotification)voidSchedule (or replace by Id) a single notification.
Cancel(id)voidCancel one previously scheduled notification.
CancelAll()voidCancel everything the SDK has scheduled.
SyncTemplatesAsync(language?)TaskFetch + cache enabled templates. Auto-rescheduled on app close.
ScheduleTemplate(key, fireAt?)voidSchedule one cached template, optionally overriding its fire time.
ScheduleAllTemplates()voidManually re-schedule every cached template now.
CacheFileNamestring (const)"kalmforge_push_templates.json".
LocalNotification - payload
NameTypeDescription
IdstringRequired. Used for de-dup and cancellation.
TitlestringNotification title.
BodystringNotification body.
FireAtDateTimeLocal device time. Past values are ignored.
ChannelstringAndroid channel id. Defaults to "default".
DeepLinkstringOptional payload delivered when the user taps the notification.

REST endpoint

examplebash
1GET /api/public/sdk/push/templates?lang=en
2Headers: X-API-Key: kf_xxx_yyy
3
4Response:
5{
6 "templates": [
7 {
8 "key": "daily_streak",
9 "title": "Don't lose your streak!",
10 "body": "Tap to claim today's bonus.",
11 "trigger": { "type": "time_of_day", "time_of_day": "19:00", "weekdays": [1,2,3,4,5] },
12 "android_channel": "default",
13 "deep_link": "myapp://daily"
14 }
15 ]
16}
Back to DocsKalmForge SDK · v1.0.1