Skip to content

How do I test push notifications on a real iPhone automatically?

For a development build, send your push to Apple Push Notification service (APNs) with a script that posts to its sandbox using curl. Mobster cannot send a push to a real iPhone. Its MCP tool read_notifications reads Notification Center on the phone: each notification’s app, title, body and time. You can separately open the push’s deep link with mobster test --device "Test iPhone" and check the destination screen. That check does not test delivery or a notification tap. On a simulator, a check’s conditions.push sends the payload itself with xcrun simctl push, with no APNs involved.

Send the push to the iPhone from a script

Apple’s guide to command-line tools sends a push with curl and a JSON web token (JWT). You need your Team ID, an APNs key ID with its private key, your app’s bundle ID as the topic, and the device token your app got when it registered with APNs. Apple’s guide builds the token in shell, then sends:

curl -v \  --header "apns-topic: com.example.yourapp" \  --header "apns-push-type: alert" \  --header "authorization: bearer $AUTHENTICATION_TOKEN" \  --data '{"aps":{"alert":{"title":"Order shipped","body":"Order 1042 is on its way"}},"orderID":"1042"}' \  --http2 https://api.sandbox.push.apple.com/3/device/$DEVICE_TOKEN
  • Custom keys, such as orderID, go beside the aps dictionary, never inside it. APNs refuses a payload over 4,096 bytes.
  • To skip building the JWT by hand, fill in Apple’s Push Notification Console and press Get cURL Command. The console also keeps a delivery log for the development environment.

Read what arrived with Mobster

Add the MCP server to your coding agent with --allow-device "Test iPhone", so it refuses every other real iPhone. Before the send, check how your app presents foreground notifications. A foreground push may not appear in Notification Center. After the send, the agent calls:

{"name": "read_notifications", "arguments": {"device": "Test iPhone", "since": "10m"}}

It reads up to 30 notifications from Notification Center, each with its app, title, body and time. It then returns to your app. It never taps a notification. A code in a notification comes back masked. Keep the phone unlocked. Follow the setup in the Mobster docs: Settings › Notifications › Show Previews › Always. Run the curl step, then call read_notifications.

Use a separate check to open the deep link your app associates with the payload. This checks the destination screen. It does not verify how your app handles the notification or maps its payload to that link:

# .mobster/checks/push-order-shipped.yamlversion: 1name: The order-shipped push opens the order with its tracking numberapp:  bundle: com.example.yourapplaunch:  url: yourapp://orders/1042?from=pushexpect:  - text: Shipped  - visible: { id: tracking_number }

Run it on the phone:

mobster test .mobster/checks/push-order-shipped.yaml --device "Test iPhone"

On a phone, the link opens in your app or the check ends as couldn’t run. The exit code is the verdict: 0 passed, 1 failed, 2 needs review, 3 couldn’t run. A launch-only check like this one calls no model.

In the next release, dialogs: { permissions: allow } in a check with steps will answer the iOS notification permission alert. On a phone, Mobster will press the alert’s narrowest grant, and only while iOS’s own alert is in front.

Deliver the push itself on a simulator

A simulator takes a push without APNs. xcrun simctl push delivers a JSON payload that has an aps key and is 4,096 bytes or less:

xcrun simctl push booted com.example.yourapp payload.json

A "Simulator Target Bundle" key in the payload lets you leave out the bundle ID. simctl supports application remote pushes only, not VoIP, Complication or File Provider pushes.

A Mobster check does the same with conditions.push. Mobster sends the payload with simctl push once your app is in front, so the check shows what your app does with a push that arrives while it’s open:

conditions:  push: payloads/order-shipped.json

On a real iPhone, Mobster skips conditions.push and says why: “push changes the state of a phone, so Mobster leaves it alone on a real iPhone. Run the check on a simulator to use it.” The report lists it under the skipped conditions.

What Mobster does and doesn’t do here

  • It doesn’t send pushes to a real iPhone. Your script, your server or Apple’s console sends them.
  • You can read Notification Center on the phone. You can separately check the deep link’s destination screen.
  • It sends a push on a simulator from a check, with simctl push.

Mobster for Mac is free on simulators and iPhones.

Questions

  • Can I send a push to a real iPhone without a server?

    Yes. Apple's Push Notification Console sends a test push to a device token from your browser. Use its Get cURL Command button to get the same request for your script.

  • Does Mobster send pushes to a real iPhone?

    No. A real iPhone gets pushes only through Apple Push Notification service, so you send them. Use Mobster to read Notification Center. You can separately check the deep link's destination screen. That check does not test delivery or a notification tap. On a simulator, a check's conditions.push sends the payload with xcrun simctl push.

  • Does read_notifications tap the notification?

    No. It reads up to 30 notifications with their app, title, body and time. Codes come back masked. It then returns to your app. You can separately check the screen reached by the push's deep link. That does not test tapping the notification.

Sources