Please find below a complete app example.
It has a button, when you press it, a local notification is created. However, the UnNotificationCenter.delegate is called twice, and I can't understand why.
I am trying to move my project from Objective-C to Swift, and my similar code there doesn't get called twice, so I'm confused.
Can anybody shine a light on this? Pointers appreciated.
App:
@main
struct NotifTestApp: App {
init() {
UNUserNotificationCenter.current().delegate = NotificationReceiveHandler.shared
configureUserNotifications()
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
private func configureUserNotifications() {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in
if granted {
print("Notification permission granted.")
} else if let error = error {
print("Error requesting notification permissions: \(error)")
}
}
}
}
class NotificationReceiveHandler: NSObject, UNUserNotificationCenterDelegate {
static let shared = NotificationReceiveHandler()
//>> THIS IS CALLED TWICE WHEN I PRESS THE BUTTON
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
NSLog(">>> Will present notification!")
completionHandler([.sound])
}
}
///THE UI
struct ContentView: View {
var body: some View {
VStack {
Text("πΎ")
.imageScale(.large)
.foregroundStyle(.tint)
Text("Notification test!")
Text("When i press the button, will present is called twice!!").font(.footnote)
.padding(10)
Button("Create Notification") {
createNotification(
message: "This is a test notification",
header: "Test Notification",
category: "TEST_CATEGORY",
playSound: true,
dictionary: nil,
imageName: nil)
}
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(8)
}
.padding()
}
}
#Preview {
ContentView()
}
private func createNotification(message: String, header: String, category: String, playSound: Bool = true, dictionary: NSDictionary? = nil, imageName: String? = nil) {
let content = UNMutableNotificationContent()
content.title = header
content.body = message
content.categoryIdentifier = category
content.badge = NSNumber(value: 0)
if let imageName = imageName, let imageURL = Bundle.main.url(forResource: imageName, withExtension: "png") {
do {
let attachment = try UNNotificationAttachment(identifier: "image", url: imageURL, options: nil)
content.attachments = [attachment]
} catch {
print("Error creating notification attachment: \(error)")
}
}
content.sound = playSound ? UNNotificationSound(named: UNNotificationSoundName("event.aiff")) : nil
if let infoDict = dictionary {
content.userInfo = infoDict as! [AnyHashable: Any]
}
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
}
User Notifications
RSS for tagPush user-facing notifications to the user's device from a server or generate them locally from your app using User Notifications.
Posts under User Notifications tag
154 Posts
Sort by:
Post
Replies
Boosts
Views
Activity
Currently if the app is hidden and locked behind face id even though the notifications are not displayed but the Notification Service Extension still wakes up for the notification.
Is it the expected behaviour ? Or should we expect the NSE would not be used as well when the app is hidden.
Is there a way to export all push notification console information via API or webhook?
in Push Notifications Platform :
when i test the device remove our app , the status result is β- discarded as device was offlineβ
when i test close the notification auth in our app, the status result is β- stored for device power considerations"
but i saw the flow in doc , I didn't directly saw that these two states are like this https://developer.apple.com/documentation/usernotifications/viewing-the-status-of-push-notifications-using-metrics-and-apns
how can i know the directly status in this two cases ?
About 2 or 3 months ago, when my app receive a Silent Push notification([aps][content-available] = 1), only func "application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler" had triggered.
But now, when my app receive Silent push, my app triggered 3 func :"didFinishLaunchingWithOptions"; "application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo" and "application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler".
Does Apple update this logic? Are there any solution to push silent that just triggered only one func like before?
I am developing a web application with PWA and Vue.js (javascript).
I would like to add a Web Push notification function, so I referred to the following site
to execute notification permission and received the response result "granted".
https://developer.mozilla.org/ja/docs/Web/API/Notification/requestPermission_static
I have tried both requestPermission(); and requestPermission(callback).
However, in this state, an error occurred when subscribing to pushManager, and push notification registration could not be executed.
As a workaround, I found that by changing the notification permission setting from the OS Settings > Notification screen to Off and On,
subscribing to pushManager was successful.
Is there anything else I need to implement other than calling requestPermission(); or requestPermission(callback) to make it executable without following the workaround steps?
In addition, this phenomenon was not occurring around the beginning of June 2024, and it was confirmed that the problem occurred as of August 5th.
The confirmed OS and models are as follows.
iOS 17.4 Mobile Safari iPad
iOS 17.3 Mobile Safari
iOS 15.8 Mobile Safari
iOS 17.3 Mobile Safari iPad
iOS 15.8 Mobile Safari iPad
iOS 17.4 Mobile Safari
iOS 16.7 Mobile Safari
Not sure if this is new (undocumented) behavior or a bug in iOS 18, but I'm seeing the UserNotifications willPresent method being triggered twice in a row for a single push notification.
I've filed a feedback assistant ticket on this, but I'm curious if anyone else has encountered this with the new betas?
I am trying to start a live activity via push token with the below headers and payload. I am using the 'fetch-http2' npm module to execute the APNS request in a deno/typescript environment, and am authenticating via token/p8. The device receives the alert portion of the payload, but the live activity does not appear on the device. I get a 200 OK response from APNS with the unique ID, and dashboard shows notification was successfully sent to the device.
The odd thing, when backgrounding the app dismisses to the Dynamic Island with the animation as if there were a live activity happening, but there is not. When I check Activity<MyAttributes>.activities on app launch, it's empty. I don't see any errors in Xcode when I have the app running/foregrounded when sending the request. Quitting the app restores normal behavior.
TL;DR:
I have regular APNS alert push notification requests working without issue from the same environment. However when attempting to start a live activity via APNS, the alert is received but the live activity does not appear, despite the app/system seemingly thinking there is one. What could I be missing, or what else could I try?
I have checked that:
My generated JWT and device token are valid according to CloudKit dashboard (again, standard push alerts are working as expected)
I can successfully start a live activity locally/from foreground via Activity.request
I have added Supports Live Activities and Supports Live Activities Frequent Updates to my app's info.plist, and also have the required capabilities enabled (remote push, background processing, background fetch)
I am using the current device push-to-start token (obtained from device via Activity.pushToStartTokenUpdates) for the device token in the APNS request (NOT the update token)
My ActivityAttributes and ActivityAttributes.ContentState values and types are correct
"headers": {
"authorization": "bearer {jwt}",
"apns-push-type": "liveactivity",
"apns-topic": "{bundleId}.push-type.liveactivity"
}
"aps": {
"attributes-type": "LiveActivityAttributes",
"attributes": {
"title": "Test Event"
},
"content-state": {
"status": 1
},
"event": "start",
"alert": {
"title": "Alert Title",
"body": "Live Activity has started."
},
"sound": "default",
"timestamp": Math.round(Date.now() / 1000)
}
Hello,
We are developing a parental control app consisting of two parts: a parent app to manage settings and a child app to enforce these settings using iOS's Screen Time API, CoreData, and other components. We've attempted to use silent notifications with Firebase Cloud Messaging (FCM) to communicate updates from the parent app to the child app. Our current implementation involves background modes for remote messages and background tasks.
However, we're facing a challenge: while normal FCM push notifications with a 'message' key work as expected, silent notifications (with only a 'data' key) do not trigger the desired behavior in the child app, even though FCM returns a success response.
We're looking for assistance with two main issues:
Alternative Approaches: Is there a better way to notify the child app of changes? We're considering a system where the child app periodically checks for updates via API and then updates CoreData and managed settings. Any recommendations for this architecture or a more reliable notification system would be greatly appreciated.
Debugging Silent Notifications: If our current approach using silent notifications is feasible, could someone help us debug why these notifications are not working as expected? We've been stuck on this for a week, and any help would be a lifesaver.
Here's the relevant part of our AppDelegate code:
import UIKit
import FirebaseCore
import FirebaseMessaging
import BackgroundTasks
@objc class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
let gcmMessageIDKey = "gcm.message_id"
let backgroundTaskIdentifier = "com.your-company.your-app.silentnotification"
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
Messaging.messaging().delegate = self
// Register for remote notifications
UNUserNotificationCenter.current().delegate = self
application.registerForRemoteNotifications()
// Register background task
BGTaskScheduler.shared.register(forTaskWithIdentifier: backgroundTaskIdentifier, using: nil) { task in
self.handleBackgroundTask(task: task as! BGProcessingTask)
}
return true
}
// Handle incoming remote notifications
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
if let aps = userInfo["aps"] as? [String: Any],
let contentAvailable = aps["content-available"] as? Int, contentAvailable == 1 {
// This is a silent notification
handleSilentNotification(userInfo: userInfo, completionHandler: completionHandler)
} else {
// This is a regular notification
Messaging.messaging().appDidReceiveMessage(userInfo)
completionHandler(.newData)
}
}
// Handle silent notification
func handleSilentNotification(userInfo: [AnyHashable: Any], completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
let request = BGProcessingTaskRequest(identifier: backgroundTaskIdentifier)
request.requiresNetworkConnectivity = true
do {
try BGTaskScheduler.shared.submit(request)
performAPICall { result in
switch result {
case .success(_):
completionHandler(.newData)
case .failure(_):
completionHandler(.failed)
}
}
} catch {
completionHandler(.failed)
}
}
// Handle background task
func handleBackgroundTask(task: BGProcessingTask) {
task.expirationHandler = {
task.setTaskCompleted(success: false)
}
performAPICall { result in
task.setTaskCompleted(success: result != nil)
}
}
// Perform API call (placeholder implementation)
func performAPICall(completion: @escaping (Data?) -> Void) {
// Your API call implementation here
// For testing, you can use a simple delay:
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
completion(Data())
}
}
}
extension AppDelegate: MessagingDelegate {
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
print("FCM token: \(fcmToken ?? "nil")")
// TODO: Send this token to your server
}
}
Additionally, here is how we're sending notifications from the server side using Node.js:
// Import the required Firebase Admin SDK (assumed to be initialized elsewhere)
// const { getMessaging } = require('firebase-admin/messaging');
/**
* Sends a background push notification to an iOS device
* @returns {Promise<string>} The message ID if successful
* @throws Will throw an error if the sending process fails
*/
async function sendBackgroundPushNotification() {
// Construct the message object for a background push notification
const message = {
apns: {
headers: {
// Set the priority of the push notification
"apns-priority": "5",
priority: "5",
// Indicate that this is a background refresh notification
"content-available": "1",
content_available: "1",
// Specify the push type as background
"apns-push-type": "background",
// Set the topic to your app's bundle identifier
"apns-topic": "com.your-company.your-app", // Replace with your actual bundle identifier
},
payload: {
aps: {
// This tells iOS to wake up your app in the background
"content-available": 1,
},
},
},
// Custom data payload to be sent with the notification
// Modify this object to include the data you want to send
data: {
// Add your custom key-value pairs here
},
// Uncomment the following block if you want to include a visible notification
// notification: {
// title: "Notification Title",
// body: "Notification Body",
// },
token
token: "DEVICE_FCM_TOKEN_PLACEHOLDER",
};
try {
// Attempt to send the message using Firebase Cloud Messaging
const response = await getMessaging().send(message);
console.log("Successfully sent background data to iOS:", response);
return response;
} catch (error) {
console.error("Error sending background data to iOS:", error);
throw error;
}
}
// Example usage:
// sendBackgroundPushNotification()
// .then((response) => console.log("Message sent successfully:", response))
// .catch((error) => console.error("Failed to send message:", error));
We would really appreciate any insights or guidance on these issues. Thank you!
I am attempting to send a push notification via APNS from a supabase edge function (deno/typescript). I receive a 200 OK response from APNS, and using the "apns-unique-id" I am able to look up the notification in the dashboard which shows the notification was successfully sent to the device. However the app/device does not receive the push.
Using the dashboard, I can successfully send a test push to the device,
and I also have FCM already implemented and working, so I assume the iOS/client-side setup is not the problem. I also verified that my generated JWT and device token are valid via the dashboard, plus I'm getting 200 back with the request, so I also assume that auth is not an issue either. Yet even with a simple alert payload such as below, nothing comes through.
What else could be causing this issue? Perhaps I'm missing a step? The details of the request I'm making are below:
url = "https://api.sandbox.push.apple.com:443/3/device/{deviceToken}"
headers = {
"authorization": "Bearer {jwt}",
"apns-push-type": "alert",
"apns-topic": bundleId,
"apns-priority": 10
}
body = {
"aps": {
"alert": {
"title": "Test Notification",
"body": "This is a test notification.",
"sound": "default"
}
}
}
According to the documentation (https://developer.apple.com/documentation/usernotificationsui/unnotificationcontentextension), a Notification Content Extension should consist of a UIViewController that adopts the UNNotificationContentExtension protocol.
The only problem is that UNNotificationContentExtension's methods are not @MainActor isolated, but UIViewController is, which produces this error when you try to build your code with Complete concurrency checking turned on:
Main actor-isolated instance method 'didReceive' cannot be used to satisfy nonisolated protocol requirement
If you add nonisolated, you are then left with another problem in that UNNotification is not Sendable.
What is the recommended solution to this problem?
Experiencing 403s (InvalidProviderToken) [https://api.push.apple.com/3/device/] due to invalid or unverifiable provider tokens. We have noticed that after retrying the request inline, the notification is delivered successfully without any changes to the request even with same authorization token.
Someone please guide or suggest why it is happening as the JWT token which is generated is correct because we are caching the jwt token and using the same token to send out the subsequent request which is kind of successfull.
Sending the notification for multiple team Ids under the same application.
Tried all these steps
Force restart
DIsabled focus mode
Notifications are enabled in both device and in app settings
Disabled summary notifications.
Tried sending notification from pusher tool, that also is not showing up in the center, for the device effected but on another device it is working fine for the same application(same version too)
What could be the reason and a possible solution?
Tried all these steps
Force restart
DIsabled focus mode
Notifications are enabled in both device and in app settings
Disabled summary notifications.
Tried sending notification from pusher tool, that also is not showing up in the center.
What could be the reason and a possible solution?
When a user receives a provisional notification and they click the "Keep" button and choose deliver immediately, there is no OS setting changes. The notifications are stuck in "delivered quietly" unless the user happens to change that in their Settings.
I've implemented pass generation successfully and it's updated through Apple's silent notification, which updates the passes to their latest versions.
I want to send some marketing push notifications to the Apple Wallet App as shown in my post at stackoverflow.
Here is the Silent notification implementation, which is working perfectly fine.
const options: apn.ProviderOptions = {
token: {
key: fs.readFileSync("./certs/APNs_AuthKey_7YYF346FU5.p8"),
keyId: "******",
teamId: "******",
},
pfx: fs.readFileSync("./certs/private_key.pem"),
cert: fs.readFileSync("./certs/certificate.pem"),
production: true,
rejectUnauthorized: true,
};
const apnProvider = new apn.Provider(options);
async function sendSilentPushNotification(
deviceTokens: string[],
serialNumber: string
) {
try {
const notification = new apn.Notification();
notification.topic = "pass.com.digital.passmaker";
notification.payload = {
aps: {
"content-available": 1,
},
serialNumber,
};
notification.priority = 5;
return await apnProvider.send(notification, deviceTokens);
} catch (error) {
logger.error("Apple Notification error: " + error);
return error;
}
}
Here is the marketing notification I am trying to send, it is working with success, but I don't see any notification on mobile phone.
please help me to fix it.
async function sendCustomPushNotification(
deviceTokens: string[],
serialNumber: string,
title: string,
body: string,
category?: string,
badge?: number
) {
try {
const notification = new apn.Notification();
notification.topic = "pass.com.digital.passmaker";
// Set the title and body of the notification
notification.alert = {
title: title,
subtitle: "Pass Update",
body: body,
};
// Set the sound to play when the notification is received
notification.sound = "default";
// Set the badge number on the app icon (optional)
if (badge !== undefined) {
notification.badge = badge;
}
notification.contentAvailable = true;
notification.mutableContent = true;
notification.aps.category = category;
notification.aps.alert = {
title: title,
body: body,
};
notification.aps.badge = badge;
notification.aps["content-available"] = 1;
notification.aps["launch-image"] =
"https://banner2.cleanpng.com/20180423/gkw/.......jpg";
// You can still include the serialNumber in the custom payload
notification.payload = {
serialNumber: serialNumber,
aps: {
"content-available": 1,
"mutable-content": 1,
"interruption-level": "time-sensitive",
},
};
// Set to high priority
notification.priority = 10;
return await apnProvider.send(notification, deviceTokens);
} catch (error) {
logger.error("Apple Notification error: " + error);
return error;
}
}
I literally receive a success response from api returning the device pushToken with no errors. However, no notification show on my iPhone
I'm a macOS app developer, and I'm facing an issue with custom notification sounds in my app. After upgrading the app to include new custom notification sounds, the changes do not reflect until the system is restarted. The sounds do not update immediately after the app upgrade.
Is there a way to refresh or reload the custom notification sounds without needing a full system restart? Any guidance or best practices to handle this would be greatly appreciated.
Thank you!
Hello,
I'm a macOS app developer, and I'm facing an issue with custom notification sounds in my app. After upgrading the app to include new custom notification sounds, the changes do not reflect until the system is restarted. The sounds do not update immediately after the app upgrade.
Is there a way to refresh or reload the custom notification sounds without needing a full system restart? Any guidance or best practices to handle this would be greatly appreciated.
Thank you!
Hello,
We a company that deals with alarm systems in hospitals and we would need to manage, for one of our apps, that the application, even if in the background, can be reactivated and brought to the foreground showing the type of alarm received.
We learned of a Dutch company who, they say, thanks to a special agreement with Apple, managed to achieve this using Local Push.
We have already done something similar with Android through Foreground services and we would like to do something similar for iOS too but at the moment we have not succeeded because it seems the OS of mobile devices does not allow it.
Apart from the normal documentation on local push I can't find much other information about it.
Is there anyone at apple who could help me?
Thanks
hi, I am trying to schedule a UNNotificationRequest at a certain date using UNCalendarNotificationTrigger, and I also want to update the badge count accordingly. However the badge property in UNNotificationContent can only be set when adding UNNotificationRequest, not when the trigger is fired or notification is actually delivered. How can I set up-to-date badge count if notification is scheduled in the future?
For example, if I scheduled notification A to 3 hours later with badge count 1, and in between I got notification B, badge count should be 2 when A is delivered but it will be 1.
I am aware of didPresent delegate which can be used when app is in foreground when notification is delivered, but is there any delegate that is called when UNNotificationRequest is delivered and app is backgrounded or we are using NSE? Thanks.