I got local notifications working for my iOS project some time last year before putting it aside, but when I came back a few weeks ago I noticed that they no longer worked. I've dug around for a few days and I'm completely stumped. The badge still updates properly with a background fetch, but the notification alert is no longer sent. Here's a minimal setup of what I have for testing.
AppDelegate:
func application(application: UIApplication, willFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {
application.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalMinimum)
application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes: [.Badge, .Alert], categories: nil))
return true
}
func application(application: UIApplication, performFetchWithCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
let localNotification = UILocalNotification()
localNotification.alertAction = "Message"
application.presentLocalNotificationNow(localNotification)
completionHandler(.NoData)
}
Things I've tried:
Uninstalling/reinstalling the app (device and simulators)
Changing the Bundle identifier
Disabling/re-enabling background fetch capability
Present delayed local notification by adding the fireDate attribute
Tried willFinishLaunchingWithOptions and didFinishLaunchingWithOptions
I've debugged it and am 100% certain that performFetchWithCompletionHandler executes when I simulate background fetch and that presentLocalNotificationNow is called
Setup:
Xcode 7.3.1 (started on previous release, probably 6.x.x)
Swift 2.2 (started the project on 2.1 and was functional)
iOS 9 (started the project on 8 and was functional)
Did something change in the API that I missed, or is there some error in what I'm doing? Thanks for any help!
UILocalNotification requires that alertBody be set in order to display. So having the following works:
func application(application: UIApplication, performFetchWithCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
let localNotification = UILocalNotification()
localNotification.alertBody = "You have a notification"
localNotification.alertAction = "Message"
application.presentLocalNotificationNow(localNotification)
completionHandler(.NoData)
}
Related
IOS 13.7 IPhone Xr, Xcode Version 11.7 (11E801a)
I edited app scheme and set launch mode to "Wait for the executable to be launched". I run app, and send a simple notification on my device.
this notification:
{
"aps":{
"content-available":1
}
}
in xcode status changed from "Waiting to attach to test on iPhone" to "Running test on iPhone", but the notification wasn't received.
i try catch notification in this method:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void)
Next notification I receive successfully. This case can be repeated after application reload.
Can you help me find out why I don't receive the first notification
Please try this payload :
{"aps":{"alert":"","content-available":1}}
If your app wasn’t running, the push notification is passed to your AppDelegate in the launchOptions
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
...
let notificationOption = launchOptions?[.remoteNotification]
if let notification = notificationOption as? [String:AnyObject]{
// received notification
}
...
}
In Android it is possible to overwrite an existing push notification if you keep using the same notification id.
Is the same possible for iOS in any way?
It seems hard to find any information about replacing an push notification, because a lot of answers are using silent push notifications and remove them manually.
I use Cordova so I have limited options for background processes when receiving push notifications.
On iOS I cannot run code to manually remove any push notifications when the app is in the background.
No, in iOS you can not overwrite already scheduled remote / local notification.
You have to schedule another push notification.
By maintaining some flag or checking on key / value. You have to remove previous notification while reviving new notification.
Hope this helps.
Updated
As an alternate, once you receive push notification, on based of information you received in push notification payload.
You can schedule local notification.
import UIKit
import PushKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate,PKPushRegistryDelegate {
var window: UIWindow?
let notificationObject = UILocalNotification()
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
return true
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
notificationObject.fireDate = NSDate(timeIntervalSinceNow: 1)
notificationObject.alertBody = "Title"
notificationObject.alertAction = "Open"
notificationObject.soundName = "SoundFile.mp3"
notificationObject.category = ""
notificationObject.userInfo = "As per payload you receive"
UIApplication.sharedApplication().scheduleLocalNotification(notificationObjectCall)
}
Swift 4.2 Code
import UIKit
import PushKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, PKPushRegistryDelegate{
func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
<#code#>
}
var window: UIWindow?
let notificationObject = UILocalNotification()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
notificationObject.fireDate = NSDate(timeIntervalSinceNow: 1) as Date
notificationObject.alertBody = "Title"
notificationObject.alertAction = "Open"
notificationObject.soundName = "SoundFile.mp3"
notificationObject.category = ""
notificationObject.userInfo = "As per payload you receive"
UIApplication.shared.scheduleLocalNotification(notificationObject)
}
As you can see in above code, local notification object is declared globally. So that object will get overwrite again and again whenever you receive payload.
For remote notification, you can not make object and overwrite it.
I am not much aware of cordova, but in native iOS, this way, you can do overwrite using local notification.
Hope you understand what technique is been used and help you figure out solution.
In iOS10 (and higher) and also via the new v2 apns protocol (from Apple) you can now use apns-collapse-id which is more or less the same as tag for Android to overwrite older notifs. More info here https://medium.com/the-guardian-mobile-innovation-lab/how-to-replace-the-content-of-an-ios-notification-2d8d93766446
I am working Push notification and i have done all steps to setup push notification.
i can able to receive notification when application in background but when application in foreground its landing on didReceiveRemoteNotification but its not firing.
here my code in AppDelegate.swift
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let notificationSettings = UIUserNotificationSettings(forTypes: [.Badge, .Alert, .Sound], categories: nil)
application.registerUserNotificationSettings(notificationSettings)
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
if application.applicationState == UIApplicationState.Active {
let localNotification = UILocalNotification()
let date = NSDate(timeIntervalSinceNow: 10)
localNotification.fireDate = date
let timeZone = NSTimeZone.localTimeZone()
localNotification.timeZone = timeZone
localNotification.alertBody = "Sample Notification Body"
localNotification.userInfo = userInfo
print(localNotification)
localNotification.applicationIconBadgeNumber = UIApplication.sharedApplication().applicationIconBadgeNumber + 1
UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
}
}
What mistake i am doing ??
Thanks in advance
What I suspect is, you're trying to show the remote notification when the app is active using local notification.
Well, either its local notification or remote, you can't see any UI alert when the app is active, instead the delegate methods for local or remote notification are called accordingly.
However iOS 10 adds the ability to view it inside the app. Here's the discussion: https://stackoverflow.com/a/37844312/593709
For showing any alert on pre-iOS-10, while your app is active, you need to use UIAlertController or some other implementation like MPNotificationView.
When the application is in Foreground and running, it does not show notification banner. Banner alert is only shown when Application is either in background(inactive) or is not running at all. The delegate method
application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void)
is called for Remote(Push) Notification and
application(application: UIApplication, didReceiveLocalNotification notification: UILocalNotification)
is called for local notification.
You can use any 3rd party library bryx-inc/BRYXBanner to show a banner for your notification alert message . The delegate method didReceiveRemoteNotification can be used to update badge icon and show message in banner view.
for testing I use OneSignal service for send push notification on my device and I handle it in AppDelegate in this way:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
OneSignal.initWithLaunchOptions(launchOptions, appId: “[app ID]”)//this method I register device on apple server
return true
}
func application(application: UIApplication,
didReceiveRemoteNotification userInfo: [NSObject : AnyObject],
fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void){
print(“ARRIVED")
handleNotificationContent()// it’s not important for my question
}
My problem is that when I receive a notification and the app is in foreground , alert shows automatically and I don’t want to show it.
How do I solve this problem?
This code worked for me.
This is applicable for Swift 2.2 and Xcode 7.3.1
//Initialize One Signal using this code
OneSignal.initWithLaunchOptions(launchOptions, appId: oneSignalId, handleNotificationReceived: { (notification) in
//Put your business logic here like adding an alert controller or posting an NSNotification.
}, handleNotificationAction: { (nil) in
// This block gets called when the user reacts to a notification received
}, settings: [kOSSettingsKeyAutoPrompt : false, kOSSettingsKeyInAppAlerts: false])
//set kOSSettingsKeyAutoPrompt to false
You need to set the kOSSettingsKeyInFocusDisplayOption to None in initWithLaunchOptions, to disable the automatic display of inapp alerts.
OneSignal Api Reference
Problem: I have Iphone 6s 64GB, running iOS 8.3, and my app doesn't show in the notification center. I tried all previous suggestions of deleting the apps, turn phone off, then on, wait 5 minutes, reinstall app thru Xcode, launch the app again, and still my app does not show in notification center.
BTW, I verified that if my app is running in foreground, I could process remote notification correctly.
Does anyone have this issue. Thanks.
This is my code in AppDelegate.swift.
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
{
var type = UIUserNotificationType.Badge | UIUserNotificationType.Alert | UIUserNotificationType.Sound
var setting = UIUserNotificationSettings(forTypes: type, categories: nil)
UIApplication.sharedApplication().registerForRemoteNotifications()
println("..... application didFinishLaunchingWithOptions()")
if let remoteNotification = launchOptions?[UIApplicationLaunchOptionsRemoteNotificationKey] as? NSDictionary {
// there is a notification...do stuff...
println("dinFInishLaunchingWithOption().. calling didREceiveRemoteNotification")
self.application(application, didReceiveRemoteNotification: remoteNotification as [NSObject : AnyObject])
}
return true
}
I believe you have a small issue with your code
var type = UIUserNotificationType.Badge | UIUserNotificationType.Alert | UIUserNotificationType.Sound
var setting = UIUserNotificationSettings(forTypes: type, categories: nil)
UIApplication.sharedApplication().registerForRemoteNotifications()
Don't you need to provide registerForRemoteNotifications with the notifications you want to register with?
If you look at the documentation for registerForRemoteNotifications, it states you need to use registerUserNotificationSettings: first.
// Calling this will result in either
// application:didRegisterForRemoteNotificationsWithDeviceToken: or
// application:didFailToRegisterForRemoteNotificationsWithError: to be
// called on the application delegate. Note: these callbacks will be
// made only if the application has successfully registered for user
// notifications with registerUserNotificationSettings:, or if it is
// enabled for Background App Refresh.
#availability(iOS, introduced=8.0)
func registerForRemoteNotifications()
I think this is what you should do:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let notificationSettings = UIUserNotificationSettings(forTypes: .Badge | .Alert | .Sound, categories: nil)
application.registerUserNotificationSettings(notificationSettings)
application.registerForRemoteNotifications()
// Rest of your setup code...
return true
}
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
println("Did Register For Remote Notification")
}
func application(application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: NSError) {
println("Failed to Register For Remote Notifications: \(error.localizedDescription)")
}
This information might help if you change the types of registered notifications:
So iOS stores notification settings for 24 hours after the app is deleted. You could attempt to delete the app wait at least 24 hours then reinstall the app. Then open the App and say Yes to allowing Notifications you can then navigate to your setting and see the notifications settings.
Then only other way I know of to reset notifications settings without waiting for the 24 hour period is wiping the device and not restoring it from a backup.
Once your done with your test then you can restore it from the backup.
Alternatively you could change your app's bundle identifier when testing. It should cause iOS to think of it as a different app.