DEV Community

unity source code
unity source code

Posted on

Push Notifications in Unity: The Retention System Most Devs Ship Too Late (Or Not At All)

If you've shipped a Unity mobile game before, you've probably lived through this exact sequence: install numbers look decent, first-session engagement looks fine, and then you open your Day 7 retention cohort and quietly die inside.

It happens to almost everyone. And the frustrating part is that it's rarely because the game is bad — it's because nothing in the app is actively reminding players it exists. Phones are crowded, competitive environments. Unlike a messaging app or email client, there's no built-in reason for someone to reopen your game unprompted. That gap is exactly what push notifications are for, and it's one of the highest-leverage, lowest-cost systems you can add to a mobile title — yet a surprising number of solo devs and small studios either skip it entirely or bolt it on so late (and so poorly) that it barely moves the needle.

This post walks through why notifications matter, the difference between local and remote notifications, the actual Unity implementation using Firebase, and the practical mistakes that quietly wreck open rates and drive uninstalls.

Why This Actually Matters for Retention

Every dev obsesses over acquisition — ad creatives, ASO, influencer deals, UA spend. Comparatively little energy goes into what happens after the first session ends, even though that's usually where the real losses happen.

Push notifications are effective specifically because they don't require additional acquisition spend. You already have the player. You already (assuming they opted in) have permission to reach them again. The only question is whether you're using that channel well.

Games with a deliberate notification strategy consistently show better Day 7 and Day 30 retention than games without one. And retention compounds directly into revenue — a player who comes back regularly has more chances to spend currency, watch a rewarded ad, or engage with a live event than someone who opened your app once and forgot it existed.

If you're deep into building a specific genre and want to see how retention mechanics get baked directly into game design rather than bolted on afterward, it's worth checking out this breakdown of building progression and matching systems into a fashion-style Unity game — a genre where daily-return hooks are basically part of the core loop: Building a Fashion Game in Unity: Outfit Matching & Progression.

Local vs. Remote Notifications: Know Which One You Need

A lot of confusion here comes from developers treating "push notifications" as one monolithic feature. There are actually two distinct systems, and picking the right one for a given use case matters both architecturally and strategically.

Local notifications are scheduled entirely on-device — no server call, no network dependency at delivery time. The OS fires the notification based on instructions the app gave it earlier. Perfect for anything timer-based: energy regeneration, harvest-ready alerts, streak reminders, "your build finished" messages.

Remote notifications (push notifications in the strict sense) come from a server, typically Firebase Cloud Messaging, and travel over the network. These are for anything dynamic or centrally controlled: sale announcements, live-ops events, new content drops, or personalized win-back campaigns for players who've gone quiet.

These aren't competing systems — a solid retention stack runs both. Local for predictable, mechanical triggers. Remote for anything that needs server-side targeting or dynamic copy.

Before You Touch Any Code

Make sure you've got:

  • Unity 2021 LTS or newer (2022 LTS is the safer bet for package compatibility)
  • A Firebase project (free tier is enough)
  • Android Studio installed for SDK/build tooling
  • An Apple Developer account if you're targeting iOS (required for APNs credentials)
  • A physical Android and/or iOS device — notifications frequently misbehave on emulators/simulators

That last one wastes more dev hours than it should. If your notifications "aren't firing," check whether you're testing on a simulator before you start tearing apart your code.

Picking Your Stack

Three realistic options:

Firebase Cloud Messaging (FCM)

Free, cross-platform, full control over server-side logic. This is the default choice for most teams since there's no subscription cost and it integrates cleanly with Unity's official SDK.

OneSignal

A solid third-party option if you'd rather not run your own backend messaging logic. Generous free tier, built-in analytics, A/B testing, and a dashboard usable by non-technical teammates.

Unity Mobile Notifications Package (local only)

Unity's first-party package for local, device-scheduled notifications. No server, lightweight, fine if your retention strategy is purely timer-driven.

For most commercial titles, the practical answer is a hybrid: Unity Mobile Notifications for local reminders, FCM for remote campaigns.

The Implementation, Step by Step

1. Set Up Firebase

Create a Firebase project, register your app with a package name matching your Unity Player Settings exactly, and download the platform config files (google-services.json for Android, GoogleService-Info.plist for iOS).

2. Install the SDK

Import the Firebase Messaging Unity package via Assets > Import Package > Custom Package, let the External Dependency Manager resolve native Android/iOS libraries, and drop the config files into your Assets folder.

3. Android Config

Confirm your package name matches Firebase's registration, verify your keystore under Publishing Settings, and enable Custom Main Manifest / Custom Gradle Template if you need custom notification icons or channels. Rebuild once so dependencies settle.

4. iOS Config (APNs)

Generate an APNs Authentication Key from your Apple Developer account (preferred over a certificate — it doesn't expire), upload it under Firebase's Cloud Messaging settings, and enable Push Notifications + Background Modes → Remote Notifications in Xcode after building out from Unity.

5. Unity Code

Minimal setup for initializing Firebase Messaging and capturing device tokens:

using Firebase;
using Firebase.Messaging;
using UnityEngine;

public class PushNotificationManager : MonoBehaviour
{
    void Start()
    {
        FirebaseApp.CheckAndFixDependenciesAsync().ContinueWith(task =>
        {
            if (task.Result == DependencyStatus.Available)
            {
                FirebaseMessaging.TokenReceived += OnTokenReceived;
                FirebaseMessaging.MessageReceived += OnMessageReceived;
            }
            else
            {
                Debug.LogError("Firebase dependency error: " + task.Result);
            }
        });
    }

    void OnTokenReceived(object sender, TokenReceivedEventArgs token)
    {
        // Send this token to your backend to target this device
        Debug.Log("Token: " + token.Token);
    }

    void OnMessageReceived(object sender, MessageReceivedEventArgs e)
    {
        // Handle in-app display when the app is in the foreground
        Debug.Log("Message: " + e.Message.Notification?.Title);
    }
}
Enter fullscreen mode Exit fullscreen mode

Attach this to a persistent GameObject with DontDestroyOnLoad so it initializes once per session and survives scene loads.

6. Foreground vs. Background

Behavior differs by app state. Foreground: MessageReceived fires and you decide how to display it — most devs swap in a soft in-app banner instead of a jarring system popup. Background/closed: the OS handles display automatically from your server payload. Make sure that payload includes both a notification block (for OS-level display) and a data block (for custom in-app handling).

7. Local Notifications

using Unity.Notifications.Android;

var channel = new AndroidNotificationChannel()
{
    Id = "game_channel",
    Name = "Game Notifications",
    Importance = Importance.Default,
    Description = "Reminders and rewards",
};
AndroidNotificationCenter.RegisterNotificationChannel(channel);

var notification = new AndroidNotification();
notification.Title = "Your reward is ready!";
notification.Text = "Come back and claim your daily bonus.";
notification.FireTime = System.DateTime.Now.AddHours(4);

AndroidNotificationCenter.SendNotification(notification, "game_channel");
Enter fullscreen mode Exit fullscreen mode

8. Test on Real Devices

Send a test campaign from the Firebase Console under Engage → Messaging, and verify all three app states — foreground, background, fully closed. Confirm tapping a notification deep-links correctly to the intended screen. A notification that doesn't route the player anywhere useful wastes the exact moment of intent you worked to create.

Best Practices That Actually Matter

Getting the plumbing right is half the job. The other half is not turning your notification channel into the reason someone uninstalls.

  • Cap frequency. One to two per day max for most casual games. Cross that consistently and opt-out rates climb fast.
  • Time around actual activity, not a fixed global schedule.
  • Write benefit-driven copy. "Your energy is full — play now!" beats vague copy like "come back and play."
  • Segment your audience. Whales, lapsed players, and new users need different messaging.
  • Respect opt-outs completely. If someone disables notifications at the OS level, that's final.
  • A/B test copy and timing. Small wording or scheduling tweaks often move open rates more than intuition suggests.

Mistakes That Keep Showing Up

  • Forgetting to request runtime permission on Android 13+ via POST_NOTIFICATIONS — skip this and delivery silently fails on newer devices.
  • Testing on only one platform. Android and iOS handle background delivery differently enough that something working on one can completely fail on the other.
  • Hardcoding notification copy directly into a build instead of pulling from remote config — every wording tweak then needs a full release.
  • Ignoring time zones on server-side campaigns for a global player base.

Genre Shapes Strategy

Notification cadence isn't one-size-fits-all. Puzzle and merge games benefit from daily challenge reminders and streak nudges. Skill/arcade titles often use notifications for new levels, leaderboard resets, or tournament results. Action and combat games lean on remote, server-driven campaigns for limited-time events or PvP tournaments where urgency matters more than routine reminders.

If you're exploring which base project or genre fits a notification-driven retention loop best, browsing a range of ready-built Unity projects is a fast way to see how timers, rewards, and notification hooks are typically structured in practice: Unity Source Code — All Products.

Measuring Whether It's Working

Shipping the pipeline isn't the finish line — it's the start of a feedback loop. Track open rate (is your copy/timing landing at all?), opt-out rate over time (are you sending too often?), and session return rate specifically attributable to a notification, not just organic reopens. Firebase's console gives basic delivery data out of the box, but piping notification events into your analytics stack lets you slice performance by cohort and campaign type — a "daily reward ready" local ping and a "flash sale ending" remote push are solving different problems and shouldn't be judged against the same baseline.

A Quick Note on Permission Prompts

When you ask for permission matters almost as much as how you ask. Requesting it the instant someone opens the app — before any positive experience — tends to produce lower opt-in than waiting until after a meaningful first-session moment, like finishing the tutorial. A lot of successful games use a soft, in-game prompt first ("get notified when your energy refills") with a clear opt-in button, and only trigger the native OS permission dialog after the player agrees. That two-step approach usually beats a cold native prompt because the player has context before the system interrupts them.

Build vs. Buy

Here's the honest math: a correct, cross-platform notification system — Firebase setup, platform-specific config, Android permission handling, APNs for iOS, and real-device testing across every app state — is not a one-afternoon task, especially the first time. Even for an experienced dev, it can eat several days of focused engineering time.

That's exactly why plenty of developers start from an existing, structured Unity codebase instead of building every system from a blank project. A solid foundation with monetization, UI, and notification infrastructure already wired in frees up your time for what actually makes your game different, instead of re-solving problems the industry has already solved many times over.

For the full step-by-step technical breakdown — complete C# implementation for both Firebase Cloud Messaging and Unity's local notification package, platform-specific config for Android and iOS, and a detailed FAQ covering edge cases — check out the original in-depth guide: How to Add Push Notifications to a Unity Mobile Game (Step-by-Step). It goes deeper than what's practical to cover in a single post, with code you can drop directly into your project.

Wrapping Up

Push notifications are a small technical investment with a disproportionately large payoff in retention and revenue. The mechanics — Firebase for remote, native scheduling for local — aren't especially exotic once you've built the system once. What separates a good strategy from a bad one is restraint: fewer, better-timed, more personalized messages instead of maximum volume.

If you're still deciding on your game's technical foundation, starting from an existing, production-ready Unity project can shortcut a meaningful chunk of this infrastructure work. Either way, the goal doesn't change — give players a genuine, well-timed reason to open your game again, and then get out of their way.

Top comments (0)