I have a public function for local notification and i want to call that function in applicationWillTerminate. When i try this, nothing happens. I'm sure local notification function is ok because when i call that in viewDidLoad, it works properly.
This is the function for local notification;
func scheduleLocalNotification(second: Double) {
if #available(iOS 10.0, *) {
let center = UNUserNotificationCenter.current()
let content = UNMutableNotificationContent()
content.badge = 1
content.title = "Title!"
content.body = "Message!"
content.categoryIdentifier = "id"
content.userInfo = ["key": "value"]
content.sound = UNNotificationSound.default()
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: second, repeats: false)
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
center.add(request)
} else {
// Fallback on earlier versions
}
}
AppDelegate;
func applicationWillTerminate(_ application: UIApplication) {
scheduleLocalNotification(second: 30.0)
print("Terminating!")
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
I searched SO for similar problems and found 2 questions, but none of them solve my issue.
Send local notification after termination app swift 2
Local notification on application termination
Edit: Request Authorization;
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let launchedBefore = UserDefaults.standard.bool(forKey: "launchedBefore")
if (!launchedBefore) {
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in
if granted {
print("Authorization granted!")
} else {
print("Authorization not granted!")
}
}
} else {
let settings = UIUserNotificationSettings(types: [.alert, .badge , .sound], categories: nil)
UIApplication.shared.registerUserNotificationSettings(settings)
}
UserDefaults.standard.set(true, forKey: "launchedBefore")
}
// Override point for customization after application launch.
return true
}
An answer to the second question you linked to seems relevant - applicationWillTerminate is not guaranteed to get called if the system decides to terminate your app while it's in the background.
just a note about applicationWillTerminate: as Apple states, and "Uncommon" says, there is no guarantee that is called.
But I think is a design problem:
a) if You want to add a local notification, do it as soon as user have set it.
b) if You are passing via "applicationWillTerminate" to save cpu time calling "add notification" only when exiting, be sure there is no big penalty saving notifications every time. As a std behaviour, in iOS we should save everything as soon as user are set it.
in my apps I simply add notification on "back" or "close".
c) Be sure to control/reset notifications already set.
If you are only testing iOS behaviour, you are in corner edge, see "eskimo" answer here:
https://forums.developer.apple.com/thread/13557
Related
I want to set the iOS app icon badge to zero (which removes the badge on the icon) when the app terminates. (...with Swift 4, iOS 10.x, 11.x)
In my ViewController I successfully requested local notification like this:
#objc func requestLocalNotification() -> Bool {
let center = UNUserNotificationCenter.current()
var result = true
center.requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in
if granted {
result = true
} else {
result = false
}
}
return result
}
And I successfully set the app icon badge with this:
#objc func notifyBadge(_ badge: Int) {
let content = UNMutableNotificationContent()
content.badge = badge as NSNumber
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request)
}
I can remove the badge when the app starts with this in AppDelegate.swift:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
application.applicationIconBadgeNumber = 0
return true
}
It seemed to make sense to try to do it here when the user double taps the home button:
func applicationWillTerminate(_ application: UIApplication) {
application.applicationIconBadgeNumber = 0
}
But it does not work.
I tried to use the shared application object instead of the passed application parameter:
UIApplication.shared.applicationIconBadgeNumber = 0
There are similar questions (and answers) on SO to reset the app icon badge but I only found 1 that pertains to doing it when the app is terminated by the user:
iphone how to catch if the app is terminated to update the badge icon
But that question is over 5 years old and was not actually answered with a solution. Perhaps someone has figured out a way to do it since then?
I'm tasked to implement a feature that sends qualified users a link to an online survey when they first suspend the app. Ideally I'd do this with a Notification of some type (e.g. local, push). Is there a way to have the app trigger a notification to the user when they suspend such that tapping it would open the survey link (perhaps via relaunching the app first)?
In AppDelegate, you need to save whether or not the user has ever opened the app before.
AppDelegate
//make sure to import the framework
//additionally, if you want to customize the notification's UI,
//import the UserNotificationsUI
import UserNotifications
//default value is true, because it will be set false if this is not the first launch
var firstLaunch: Bool = true
let defaults = UserDefaults.standard
//also make sure to include *UNUserNotificationCenterDelegate*
//in your class declaration of the AppDelegate
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
//get whether this is the very first launch
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
if let bool = defaults.object(forKey: "firstLaunch") as? Bool {
firstLaunch = bool
}
defaults.set(false, forKey: "firstLaunch")
defaults.synchronize()
//ask the user to allow notifications
//maybe do this some other place, where it is more appropriate
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .sound]) { (granted, error) in}
return true
}
//schedule your notification when exiting the app, if necessary
func applicationDidEnterBackground(_ application: UIApplication) {
//update the variable
if let bool = defaults.object(forKey: "firstLaunch") as? Bool {
firstLaunch = bool
}
if !firstLaunch {
//abort mission if it's not the first launch
return
}
//customize your notification's content
let content = UNMutableNotificationContent()
content.title = "Survey?"
content.body = "Would you like to take a quick survey?"
content.sound = UNNotificationSound.default()
//schedule the notification
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
let request = UNNotificationRequest(identifier: "takeSurvey", content: content, trigger: trigger)
let center = UNUserNotificationCenter.current()
center.add(request, withCompletionHandler: nil)
}
At last, handle the response you got and open your link. That's it!
I want to send a notification, on every 60 seconds interval, after application entered to the background. But when you enter the background, notification is sending immediately, after that every 60 seconds is sending notification. I don't want to send notification immediately, how can i do that?
Here is my code,
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .sound]) { (granted, error) in
// Enable or disable features based on authorization.
}
return true
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
let content = UNMutableNotificationContent()
content.title = NSString.localizedUserNotificationString(forKey: "Hello,", arguments: nil)
content.body = NSString.localizedUserNotificationString(forKey: "Some", arguments: nil)
content.sound = UNNotificationSound.default()
content.badge = NSNumber(value: UIApplication.shared.applicationIconBadgeNumber + 1)
content.categoryIdentifier = "com.mahmut.localNotification"
let trigger = UNTimeIntervalNotificationTrigger.init(timeInterval: 60.0, repeats: true)
let request = UNNotificationRequest.init(identifier: "60Sec", content: content, trigger: trigger)
let center = UNUserNotificationCenter.current()
center.add(request)
}
try using it this way... instead of 4 you could use your own time... and later in your function you can use the timer concept to send a notification every 60 seconds.
let triggerTime = (Int64(NSEC_PER_SEC) * 4)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, triggerTime), dispatch_get_main_queue(), { () -> Void in
self.callYourFunction()
})
This is a followup to my previous question asked here How can I register user's ios device for receiving push messages from place other than AppDelegate?
Currently in my Swift application I have a UISwitch that is set to off by default and when user turns it on - I want him to register for receiving push notifications. However, when he turns that switch off - he should unregister from push notifications (and do not receive any push msgs later on until registering again).
So I created a class for managing push registration:
class NotificationManager {
static var shared = NotificationManager()
private var application : UIApplication?
func setup(application: UIApplication) {
self.application = application
}
func register () {
guard let application = application else {
print("Attempt to register without calling setup")
return
}
print("registering for push")
let notificationTypes: UIUserNotificationType = [UIUserNotificationType.alert, UIUserNotificationType.badge, UIUserNotificationType.sound]
let pushNotificationSettings = UIUserNotificationSettings(types: notificationTypes, categories: nil)
application.registerUserNotificationSettings(pushNotificationSettings)
application.registerForRemoteNotifications()
}
func unregister(){
guard let application = application else {
print("Attempt to register without calling setup")
return
}
print("unregistering for push")
let notificationTypes: UIUserNotificationType = [UIUserNotificationType.alert, UIUserNotificationType.badge, UIUserNotificationType.sound]
let pushNotificationSettings = UIUserNotificationSettings(types: notificationTypes, categories: nil)
application.registerUserNotificationSettings(pushNotificationSettings)
if(application.isRegisteredForRemoteNotifications){
print("unregistering went ok")
application.unregisterForRemoteNotifications()
}
}
}
and then my listener of UISwitch is:
func messagesStateChanged(_ sender: UISwitch){
if(sender.isOn){
defaults.set("true", forKey: "popupMessages")
defaults.synchronize()
NotificationManager.shared.register()
} else {
defaults.set("false", forKey: "popupMessages")
defaults.synchronize()
NotificationManager.shared.unregister()
}
}
Now in my AppDelegate I have the following method:
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let deviceTokenString = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
print(deviceTokenString)
}
and when user turns on the switch for the first time - I see the string registering for push and device token printed in the console.
When user then turns off the switch, I see in console:
unregistering for push
unregistering went ok
But then, when I turn on the switch again, I only see:
registering for push
and I don't see a token string. Seems like the method from AppDelegate: didRegisterForRemoteNotificationsWithDeviceToken is never called. When I turn off the switch later on, I also only see:
unregistering for push
and I'm not getting a confirmation that unregistering went ok.
Why I cannot unregister and then register again for push messages?
I've designed it all like this because I want to add user a choice for receiving push notifications or not directly in my application.
I could not find some help for my problem here on SO or somewhere else, so I hope somebody of you guys can help me.
I'm trying to implement the functionality of a reminder in my iOS app, that the user enables by himself. The reminder is a local notification.
I'm not asking the user for permission for local notifications until he enables the reminder. After that I have to check, if the user granted the permissions and schedule a notification if so.
The core problem here is that the code doesn't stop executing on the ´registerUserNotificationSettings´ method and wait for the result of the upcoming UIAlertController. Therefore I cannot check for permissions directly after calling the method.
I also don't want to ask for permission at the first start of the app because the user doesn't know why my app should send notifications and I think that this would be too much for the first start and for an optional feature.
I know of the appdelegate message ´didRegisterUserNotificationSettings´, which is sent after the question was answered. An idea is to firstly store the notification in a global variable and schedule it if the permission was granted. But that would be too much code that will be executed if the notification isn't even scheduled at the end.
So I'm looking for some solution to ask the user for permission and then decide whether to create and schedule the notification or not.
This is my problematic piece of code in the ´value changed´-event of a UISwitch, which does not work as intended:
//check if the app is allowed to send notifications or ask the user
var settingTypes = UIApplication.sharedApplication().currentUserNotificationSettings()?.types
if (settingTypes?.contains(.Alert) == false) {
let notificationSettings = UIUserNotificationSettings(forTypes: [.Alert, .Sound], categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(notificationSettings)
//get NotificationSettings again after the user was asked
settingTypes = UIApplication.sharedApplication().currentUserNotificationSettings()?.types
}
//now check if if notifications are finally permitted
if (settingTypes?.contains(.Alert) == true) {
let notification = UILocalNotification()
* create notification *
UIApplication.sharedApplication().scheduleLocalNotification(notification)
}
else {
switchNotification.on = false
}
Does anyone has an idea?
Thank you in advance
Declare a Closure in AppDelegate
var userNotificationChanged: ((settings: UIUserNotificationSettings) -> Void)?
and call the closure when user registers for notification
Your AppDelegate didRegisterUserNotificationSettings would look like below
func application(application: UIApplication, didRegisterUserNotificationSettings notificationSettings: UIUserNotificationSettings) {
if (userNotificationChanged != nil) {
userNotificationChanged!(settings: notificationSettings)
}
}
Now in your valueChanged event of UISwitch set the closure and perform your required action inside:
(UIApplication.sharedApplication().delegate as! AppDelegate).userNotificationChanged = { (settings: UIUserNotificationSettings) -> Void in
//Schedule your notification here
}
Put this simple code in didFinishLaunchingWithOptions
if #available(iOS 8.0, *)
{
if application.respondsToSelector("isRegisteredForRemoteNotifications")
{
let types:UIUserNotificationType = ([.Alert, .Sound, .Badge])
let settings:UIUserNotificationSettings = UIUserNotificationSettings(forTypes: types, categories: nil)
application.registerUserNotificationSettings(settings)
application.registerForRemoteNotifications()
}
}
else{
let types: UIRemoteNotificationType = [.Alert, .Badge, .Sound]
application.registerForRemoteNotificationTypes(types)
}