> This page is part of the [Customer.io documentation](https://docs.customer.io). For the complete index, see [llms.txt](https://docs.customer.io/llms.txt).
> Last updated: November 13, 2026
> To take actions in Customer.io from an agent, use the [Customer.io CLI](https://docs.customer.io/ai/cli/get-started.md) or the [MCP server](https://docs.customer.io/ai/mcp/get-started.md).

# Silent push

A silent push wakes your app in the background and hands it the data set on the message, without showing anything to the person. Use this page to set your app up to receive silent pushes.

## How it works

When you send a [silent push](https://docs.customer.io/messaging/channels/push/silent-push/), iOS wakes your app in the background and calls your app delegate. The SDK reads the push, reports *Delivered* to Customer.io, and gives your code the key/value data set on the message. A silent push never shows anything, so the SDK never reports *Opened*.

Silent push needs version 5.2.0 or later of the iOS SDK. If you install the SDK with CocoaPods, use version 5.2.1 or later, because CocoaPods doesn’t have version 5.2.0 of the `CustomerIO` pod.

Customer.io sends silent pushes to every iOS device a message targets, and doesn’t check which SDK version the device uses. On a device with an older SDK version, nothing reads the data and nothing reports *Delivered*.

Silent pushes don’t need the person’s permission to show notifications. Your app still needs to register for remote notifications, which the SDK does for you when `autoFetchDeviceToken` is on.

## Set up silent push

1.  In Xcode, select your app target, go to *Signing & Capabilities*, add the **Background Modes** capability, and check **Remote notifications**. This adds `remote-notification` to `UIBackgroundModes` in your `Info.plist`. Without it, iOS doesn’t wake your app for a silent push.

2.  Initialize `MessagingPushAPN` or `MessagingPushFCM` in `application(_:didFinishLaunchingWithOptions:)` on every launch, as described in [set up push notifications](https://docs.customer.io/integrations/sdk/ios/push/push-setup/). iOS can launch your app in the background just to deliver a silent push, and that launch needs the SDK too.

3.  To read the data, call `MessagingPush.shared.handleBackgroundPush(userInfo:)` from `application(_:didReceiveRemoteNotification:fetchCompletionHandler:)`. If you use `CioAppDelegateWrapper`, add this method to the `AppDelegate` class that the wrapper wraps. The code is the same for APNs and FCM.

    ```
    import CioMessagingPush
    import UIKit

    class AppDelegate: UIResponder, UIApplicationDelegate {
        func application(
            _ application: UIApplication,
            didReceiveRemoteNotification userInfo: [AnyHashable: Any],
            fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
        ) {
            switch MessagingPush.shared.handleBackgroundPush(userInfo: userInfo) {
            case .received(let push):
                // push.data holds the keys and values set on the message.
                // Key your work on push.deliveryID: the same push can arrive more than once.
                refreshAccount(push.data, deliveryID: push.deliveryID) {
                    completionHandler(.newData)
                }
            case .unsupported:
                // A Customer.io silent push the SDK can't read, like one with a value that isn't text.
                completionHandler(.noData)
            case .notBackground:
                // Not a Customer.io silent push. Pass it to your other handlers, then call completionHandler.
                completionHandler(.noData)
            }
        }
    }
    ```

Your app owns the completion handler. Call it once, as soon as your work ends.

### How the SDK reports Delivered

When automatic push tracking (`autoTrackPushEvents`) is on, which is the default, the SDK reports *Delivered* for each silent push:

*   With `CioAppDelegateWrapper`, the SDK reports *Delivered* before your handler runs. It does this even if your app doesn’t implement `application(_:didReceiveRemoteNotification:fetchCompletionHandler:)`.
*   Without the wrapper, `handleBackgroundPush(userInfo:)` reports it.

The SDK reports each delivery once, even when both the wrapper and your call to `handleBackgroundPush(userInfo:)` see the same push. If you turn automatic push tracking off, `push.deliveryToken` is the device token you need to [report the delivery yourself](https://docs.customer.io/integrations/sdk/ios/push/push-metrics/#metrics-direct).

### Firebase’s app delegate proxy

If you use FCM with `CioAppDelegateWrapper` and leave Firebase’s app delegate proxy on, which is Firebase’s default, your app needs GoogleUtilities 8.1.5 or later. With earlier versions, silent pushes never reach `CioAppDelegateWrapper`, so your app gets no data and the SDK doesn’t report *Delivered*. New installs get a version that works. If you use CocoaPods, run `pod update GoogleUtilities` to update an existing app. You can also set `FirebaseAppDelegateProxyEnabled` to `NO` in your `Info.plist`.

## What to plan for

Apple decides when to deliver a silent push, and doesn’t always deliver it:

*   **Time**: iOS gives your app about 30 seconds to handle a silent push. Apps that run long or use a lot of power get woken less often.
*   **Repeats**: The same push can arrive more than once. Use `push.deliveryID` to skip work you already did.
*   **Force-quit apps**: iOS doesn’t wake an app the person swiped away in the app switcher. Silent pushes reach the app again after the person opens it.
*   **Offline devices**: While a device is offline, Apple keeps only the newest push for your app, so an earlier silent push can be dropped. Treat each silent push as a signal to sync, and fetch anything you missed from your own backend.
*   **Throttling**: Apple treats silent pushes as low priority and can delay or drop them, like in Low Power Mode. Apple’s guidance is to send no more than two or three an hour.
