I'm struggling to get push notifications to work with Swift with iOS 10. Registering seems to be going through successfully, but creating a notificaiton does nothing on the device and returns a nil error. Any ideas what I'm missing?
import Foundation
import UIKit
import UserNotifications
class SPKPushNotifications
{
class func register(application:UIApplication){
if #available(iOS 10.0, *) {
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options:[.badge, .alert, .sound]) { (granted, error) in
// Enable or disable features based on authorization.
application.registerForRemoteNotifications()
}
} else {
let notificationTypes: UIUserNotificationType = [UIUserNotificationType.alert, UIUserNotificationType.badge, UIUserNotificationType.sound]
let pushNotificationSettings = UIUserNotificationSettings(types: notificationTypes, categories: nil)
application.registerUserNotificationSettings(pushNotificationSettings)
application.registerForRemoteNotifications()
}
}
class func unregister(application:UIApplication){
application.unregisterForRemoteNotifications()
}
class func create(title:String, body:String, delay:Double, repeats:Bool){
if #available(iOS 10.0, *) {
let content = UNMutableNotificationContent()
content.title = title
content.body = body
content.sound = UNNotificationSound.default() //idk if we're gonna want something else
content.badge = NSNumber(value:UIApplication.shared.applicationIconBadgeNumber+1)
let trigger = UNTimeIntervalNotificationTrigger(timeInterval:delay, repeats:repeats)
let request = UNNotificationRequest(identifier:title, content:content, trigger:trigger)
let center = UNUserNotificationCenter.current()
center.add(request){ (error) in
print(error)
}
} else {
// Fallback on earlier versions
}
}
class func delete(){
if #available(iOS 10.0, *) {
let center = UNUserNotificationCenter.current()
center.removeAllDeliveredNotifications()
} else {
// Fallback on earlier versions
}
}
}
You won't see the notification if the application is in the foreground. Try adding the request to the notification center when the application is in the background.
You can do (for this test only) that by adding a few second sleep and moving your application to the background. Or scheduling the notification to a later time when the application is not running in the foreground.
Related
Apple has approve my request for an entitlement to use critical alerts. I attached my entitlement to my bundle identifier and created a provisioning profile and manually signed my app according to Apple's instructions: https://help.apple.com/developer-account/#/dev38c81d4cd
I also added a properties.entitlements file with the correctly modified bundle identifier and boolean "YES". My "Build Settings" does not have a "Code Signing Entitlements" within "Signing".
When I run the code below, I get the prompt to accept alerts, but then no critical alert. Any suggestions? Thanks!
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var authOptions: UNAuthorizationOptions?
if #available(iOS 12.0, *) {
authOptions = [.alert, .badge, .sound, .criticalAlert]
} else {
authOptions = [.alert, .badge, .sound]
}
UNUserNotificationCenter.current().requestAuthorization(options:
authOptions!) { (granted, error) in
if !granted {
print("The application requires Notifications permission to display push notifications. Please enable it in settings.")
} else {
let userNotificationCenter = UNUserNotificationCenter.current()
var contentTitle:String?
var contentMessage:String?
var contentSound:UNNotificationSound?
contentTitle = "Message:"
contentMessage = "Critical Alert!"
contentSound = .defaultCritical
let content = UNMutableNotificationContent()
content.title = contentTitle!
content.subtitle = contentMessage!
content.sound = contentSound
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
userNotificationCenter.add(request)
}
}
}
}
I used UNNotificationContentExtension to survey from the user.
Condition is I do not open the parent app.
Here is the emoji action
if #available(iOSApplicationExtension 12.0, *) {
// API call here
self.extensionContext?.dismissNotificationContentExtension()
} else {
// Fallback on earlier versions
}
Each emoji have actions. When user tap the emoji I will send the response into server and remove this notification. Everythings will happens on the extension part
What's the issue?
Using dismissNotificationContentExtension notification dismiss and hide instant. Its again found in the notification screen. How could I remove this notification when user tap emoji button.
This is how my solution working.
Cons: All delivered notification of the same category removed instead of doing remove current message.
#IBAction func btnActionHappy(_ sender: Any) {
UNUserNotificationCenter.current().getDeliveredNotifications { notifications in
if #available(iOSApplicationExtension 12.0, *) {
self.extensionContext?.dismissNotificationContentExtension()
} else {
// Fallback on earlier versions
}
let matchingNotifications = notifications.filter({ $0.request.content.categoryIdentifier == "debitOverdraftNotification" })
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: matchingNotifications.map({ $0.request.identifier }))
print("Somethings")
}
}
You can do it using UNUserNotificationCenter & UNNotificationContentExtension protocol
Add action using UNUserNotificationCenter
let center = UNUserNotificationCenter.current()
center.delegate = self
center.requestAuthorization (options: [.alert, .sound]) {(_, _) in
}
let clearAction = UNNotificationAction(identifier: "sadEmoji", title: "Emoji", options: [])
let category = UNNotificationCategory(identifier: "NotifCategory", actions: [clearAction], intentIdentifiers: [], options: [])
center.setNotificationCategories([category])
Add a delegate method of the protocol UNNotificationContentExtension in your extension's view controller
func didReceive(_ response: UNNotificationResponse, completionHandler completion: #escaping (UNNotificationContentExtensionResponseOption) -> Void) {
if response.actionIdentifier == "sadEmoji" {
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: "NotifCategory")
}
completion(.dismiss)
}
Try it and let me know it works.
You can remove the current notification using removeDeliveredNotifications(withIdentifiers:).
var notification: UNNotification?
func didReceive(_ notification: UNNotification) {
self.notification = notification
...
}
#IBAction func btnActionHappy(_ sender: Any) {
if #available(iOSApplicationExtension 12.0, *) {
extensionContext?.dismissNotificationContentExtension()
}
if let identifier = notification?.request.identifier {
let center = UNUserNotificationCenter.current()
center.removeDeliveredNotifications(withIdentifiers: [identifier])
}
}
Using IOS 9 and Swift 2
I have written a function to call Notification when needed.
How do i add Msg Tone and Vibration when notification received :
func getNotification(message:String)
{
let curentSeting = UIApplication.sharedApplication().currentUserNotificationSettings()
if(curentSeting!.types == .None)
{
let newSeting = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(newSeting)
}
let nfnVar = UILocalNotification()
nfnVar.fireDate = NSDate(timeIntervalSinceNow: 0)
nfnVar.alertBody = message
nfnVar.soundName = UILocalNotificationDefaultSoundName
nfnVar.userInfo = ["NamKey":"NamVey"]
UIApplication.sharedApplication().scheduleLocalNotification(nfnVar)
}
I have a local notification scheduled in my app, and right now I get a generic cancel (cross) button as I swipe the alert to the left.
I'm curious if I can add custom buttons/actions to it like on the image below?
I prepared for you some snipped code which shows notification with one button 10 second after ViewDidLoad method did shown.
import UIKit
class TestViewController: UIViewController {
let category = UIMutableUserNotificationCategory()
override func viewDidLoad() {
super.viewDidLoad()
let restartAction = UIMutableUserNotificationAction()
restartAction.identifier = "xx"
restartAction.destructive = false
restartAction.title = "Restart"
restartAction.activationMode = .Background
restartAction.authenticationRequired = false
let categoryIdentifier = "category.identifier"
category.identifier = categoryIdentifier
category.setActions([restartAction], forContext: .Minimal)
category.setActions([restartAction], forContext: .Default)
let categories = Set(arrayLiteral: category)
let settings = UIUserNotificationSettings(forTypes: [.Alert, .Sound], categories: categories)
UIApplication.sharedApplication().registerUserNotificationSettings(settings)
let localNotif = UILocalNotification()
localNotif.alertBody = "testBody"
localNotif.category = categoryIdentifier
// Notification will be shown after 10 second (IMPORTANT: if you want to see notification you have to close or put app into background)
localNotif.fireDate = NSDate().dateByAddingTimeInterval(10)
UIApplication.sharedApplication().scheduleLocalNotification(localNotif)
}
}
Note: you have to handle action in AppDelegate method:
func application(application: UIApplication, handleActionWithIdentifier identifier: String?,
forLocalNotification notification: UILocalNotification, completionHandler: () -> Void) {
completionHandler()
}
Of course my code is not as clean as it should be, but you have to know that I wrote it only for presentation purposes.
This code is written in Swift but convertion to Objective C should be very simple.
I'm trying to implemented a Switch in my App's local SettingsVC to toggle Notification On/Off. I've decided for now to leave the user registered with the server (both APNS and AppBoy), and just toggle on/off the local presentation of Notifications.
(That's if the user has registered previously. If they haven't ever registered, this code should register the first time they flip to "on")
Here's my code when the user flips the switch:
func didFlipNotificationSwitch() {
let isEnabled = isEnabledRemoteNotificationTypes()
if (isEnabled) {
if #available(iOS 8.0, *) {
let settings = UIUserNotificationSettings(forTypes: [.None], categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(settings)
} else {
UIApplication.sharedApplication().registerForRemoteNotificationTypes([.None])
}
} else {
if #available(iOS 8.0, *) {
let settings = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(settings)
UIApplication.sharedApplication().registerForRemoteNotifications()
} else {
UIApplication.sharedApplication().registerForRemoteNotificationTypes([.Alert, .Badge, .Sound])
}
}
print("isEnabled \(isEnabled)")
}
isEnabledRemoteNotificationTypes() is a convenience method that checks for registered based on OS - it's works fine. Here it is:
func isEnabledRemoteNotificationTypes() -> Bool {
if #available(iOS 8.0, *) {
let types = UIApplication.sharedApplication().currentUserNotificationSettings()?.types
if (types == UIUserNotificationType.None) {
return false
} else {
return true
}
} else {
let types = UIApplication.sharedApplication().enabledRemoteNotificationTypes()
if (types == UIRemoteNotificationType.None) {
return false
} else {
return true
}
}
}
The problem is that once registered (isEnabled returns true) I'm trying to deregister locally by settings types to .None as you can see in the method above. This doesn't seem to be working. After the line setting them to .None is called, I print out notificationSettings in my appDel didRegisterUserNotificationSettings callback right after setting to .None:
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings {
}
And get this:
Printing description of notificationSettings:
<UIUserNotificationSettings: 0x12684c840; types: (UIUserNotificationTypeAlert UIUserNotificationTypeBadge UIUserNotificationTypeSound);>
Why isn't it registering .None -OR- Is there a better way to turn off Notification presentation while still maintaining server registration?