After digging deeper with everyone’s suggestions, I found the root cause. The issue was a combination of problems related to all three focus areas:
Push Notification Registration Issue:
Our device token registration was happening correctly on initial app launch, but we weren’t properly handling the scenario where iOS refreshes tokens in the background. I added proper handling for the token refresh callback:
function onTokenRefresh(newToken) {
RegisterDevice(newToken, GetUserId(), "iOS");
UpdateLocalTokenCache(newToken);
}
Workflow Logic Triggers:
The workflow was firing correctly, but the SendPushNotification action was using a cached user list that didn’t account for users who had reinstalled the app. I modified the workflow to query active device registrations in real-time:
SELECT DeviceToken, Platform
FROM DeviceRegistration
WHERE UserId = @UserId AND IsActive = 1
Device Token Management:
The critical issue was that we weren’t invalidating old tokens when new ones were registered. This caused the notification service to attempt delivery to stale tokens first, and our retry logic wasn’t configured properly. I implemented:
- Token invalidation on new registration - set IsActive=0 for old tokens before inserting new ones
- Added a cleanup job to remove tokens older than 90 days
- Implemented proper error handling to catch APNs feedback about invalid tokens
- Updated the notification payload to include the correct content-available:1 flag for background delivery
The payload structure now looks like:
{
"aps": {
"alert": {
"title": "New Approval Request",
"body": "You have a pending expense approval"
},
"badge": 1,
"sound": "default",
"content-available": 1
},
"taskId": "12345",
"priority": "high"
}
After deploying these changes, iOS push notifications are working reliably again. The key was ensuring the entire token lifecycle was managed properly - from registration through refresh to invalidation. Also critical was querying live device registrations in the workflow rather than relying on cached data.
Thanks everyone for the guidance - the suggestions about token refresh and payload structure pointed me in the right direction!