BS
BleepingSwift
Published on
5 min read

> Testing Push Notifications on iOS Simulators with xcrun simctl push

Testing push notifications has historically been one of the more tedious parts of iOS development. You needed a physical device, a valid APNs certificate or key, a server to send the payload, and a lot of patience. Starting with Xcode 11.4, Apple introduced a much simpler approach: xcrun simctl push, which lets you send push notifications directly to the iOS Simulator from the command line.

The Basics

The simctl push command sends a simulated push notification to an app running on the simulator. At its simplest, you need a JSON payload file and either the app's bundle identifier or a booted simulator.

Create a file called notification.apns with your push payload:

{
  "aps": {
    "alert": {
      "title": "New Message",
      "body": "You have a new message from Sarah"
    },
    "badge": 3,
    "sound": "default"
  }
}

Then send it to your app:

xcrun simctl push booted com.yourcompany.yourapp notification.apns

The booted keyword targets whichever simulator is currently running. If you have multiple simulators running, you can specify a device UUID instead:

xcrun simctl list devices | grep Booted
# Find the UUID, then:
xcrun simctl push 8A5C2F1D-3E4B-4A6C-9D8E-1F2A3B4C5D6E com.yourcompany.yourapp notification.apns

Embedding the Bundle Identifier

Typing the bundle identifier every time gets old quickly. You can embed it directly in the payload file using the Simulator Target Bundle key:

{
  "Simulator Target Bundle": "com.yourcompany.yourapp",
  "aps": {
    "alert": {
      "title": "Order Shipped",
      "body": "Your order #12345 has shipped!"
    },
    "sound": "default"
  }
}

Now the command simplifies to:

xcrun simctl push booted notification.apns

Rich Notification Payloads

You can test more complex notification types including rich media, categories, and custom data. Here's a payload that includes an action category and custom data:

{
  "Simulator Target Bundle": "com.yourcompany.yourapp",
  "aps": {
    "alert": {
      "title": "Flash Sale!",
      "subtitle": "Limited time offer",
      "body": "50% off all items for the next 2 hours"
    },
    "badge": 1,
    "sound": "default",
    "category": "SALE_CATEGORY",
    "mutable-content": 1
  },
  "saleId": "summer-flash-2026",
  "discount": 50
}

The mutable-content flag tells iOS to invoke your Notification Service Extension if you have one, allowing you to modify the notification before display.

Testing Background Notifications

Silent push notifications (used for background updates) work too. Just include the content-available key and omit the alert:

{
  "Simulator Target Bundle": "com.yourcompany.yourapp",
  "aps": {
    "content-available": 1
  },
  "updateType": "sync",
  "timestamp": 1709424000
}

Your app needs the Background Modes capability with "Remote notifications" enabled for this to trigger your app delegate's application(_:didReceiveRemoteNotification:fetchCompletionHandler:) method.

Setting Up Your App

For notifications to appear, your app needs to request permission. Here's a minimal setup:

import SwiftUI
import UserNotifications

@main
struct NotificationTestApp: App {
    init() {
        requestNotificationPermission()
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }

    private func requestNotificationPermission() {
        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in
            if granted {
                print("Notification permission granted")
            }
        }
    }
}

When testing, make sure to:

  1. Launch your app at least once to trigger the permission request
  2. Accept the notification permission dialog
  3. Background the app (notifications won't show as banners while your app is in the foreground unless you implement UNUserNotificationCenterDelegate)

Handling Foreground Notifications

By default, iOS doesn't display notification banners when your app is in the foreground. If you want to see banners while testing in the app, implement UNUserNotificationCenterDelegate:

import SwiftUI
import UserNotifications

@main
struct NotificationTestApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        UNUserNotificationCenter.current().delegate = self
        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { _, _ in }
        return true
    }

    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification) async -> UNNotificationPresentationOptions {
        return [.banner, .sound, .badge]
    }
}

Creating a Test Script

For frequent testing, create a shell script with multiple notification types:

#!/bin/bash

# Push a simple alert
cat << 'EOF' | xcrun simctl push booted -
{
    "Simulator Target Bundle": "com.yourcompany.yourapp",
    "aps": {
        "alert": "Quick test notification"
    }
}
EOF

The - argument tells simctl to read the payload from stdin, which is convenient for quick one-liners without needing a separate file.

Drag and Drop Alternative

If you prefer a visual approach, you can drag an .apns file directly onto the Simulator window. Make sure the file includes the Simulator Target Bundle key so iOS knows which app should receive it.

Limitations

While simctl push is great for development, keep in mind that it doesn't test the actual APNs infrastructure. Your production server, certificates, and network connectivity aren't involved. Before shipping, you should still test end-to-end with real push notifications on a physical device.

The simulator also doesn't support all notification features identically. Interactive notifications and notification extensions work, but behaviors around delivery timing and grouping may differ slightly from real devices.

For comprehensive testing strategies and device configuration, check out Apple's documentation on Generating a remote notification and the simctl man page.

Sample Project

Want to see this code in action? Check out the complete sample project on GitHub:

View on GitHub

The repository includes a working Xcode project with all the examples from this article, plus unit tests you can run to verify the behavior.

subscribe.sh

// Stay Updated

Get notified when I publish new tutorials on Swift, SwiftUI, and iOS development. No spam, unsubscribe anytime.

>

By subscribing, you agree to our Privacy Policy.