How to remove iOS app icon badge when the app terminates? - ios

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?

Related

local notification with background fetch swift 4

I am struggling to get a local notification to run using background fetch.
Ideally what I need is to update my JSON data and if it contains a value then send a local notification to the user.
as yet I am unable to even send a local notification using background fetch.
I have checked out the following: Send Local Notifications while App is running in the background Swift 2.0
ios call function in background mode
and a tutorial https://www.raywenderlich.com/143128/background-modes-tutorial-getting-started
I am still unable to send even a notification let alone do my json query in the background to check for a value.
here is my code for appdelegate:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
UIApplication.shared.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalMinimum)
return true
}
func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
if let vc = window?.rootViewController as? FirstViewController {
vc.mynotifications()
}
}
and my notification code from the view controller:
func mynotifications() {
let content = UNMutableNotificationContent()
content.title = "title"
content.subtitle = "subtitle"
content.body = "test"
content.badge = 1
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 0.5, repeats: false)
let request = UNNotificationRequest(identifier: "timerdone", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
}
I have background fetch enabled in my settings.
Any assistance in getting this to work would be appreciated. If you could also point me in the right direction to tie in the json request in the background that would also be a bonus.
Thanks

Badge Count is not shown in iOS App

The Badge count is not shown in my app for background and termination cases.
I added this code in appdelegate didfinishlaunch
UIApplication.shared.applicationIconBadgeNumber = 0
if #available(iOS 10, *)
{
UNUserNotificationCenter.current().delegate = self
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in
guard error == nil else
{
return
}
if granted
{
UIApplication.shared.registerForRemoteNotifications()
}
}
}
else
{
let settings : UIUserNotificationSettings = UIUserNotificationSettings(types: [.alert, .sound , .badge], categories: nil)
UIApplication.shared.registerUserNotificationSettings(settings)
UIApplication.shared.registerForRemoteNotifications()
}
UIApplication.shared.applicationIconBadgeNumber += 1
print("Badge Count ===> \( UIApplication.shared.applicationIconBadgeNumber)")
The print shows correct count. But not shown in the app.
Actually I dont need to update badge count for each notification. For some share or like we need to update badge count. So I created a method and update the count.
func setBadgeCount()
{
if(self.currentAppState() == 1 || self.currentAppState() == 3)
{
sleep(2)
UIApplication.shared.applicationIconBadgeNumber += 1
print("Badge Count ===> \( UIApplication.shared.applicationIconBadgeNumber)")
}
}
callthis method from
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void)
{
setBadgeCount()
}
The app is not going to update the badge number with this method unless the app is actually open. If you want to update the badge number upon receiving a notification then you have to set the badge number in the payload of notification. Below is the sample of payload looks like. Put your desired number there and send it will work.
{
"aps": {
"alert": "Test Push Notification",
"sound": "yourSound.aiff",
"Badge": "desiredNumber"
}
}
You have written this code in didFinishLaunchingWithOptions which is called only once. So how would it update badge number.
Only add UIApplication.shared.applicationIconBadgeNumber = 0 in didFinishLaunchingWithOptions
Instead use,
func application(application: UIApplication, didReceiveLocalNotification notification: UILocalNotification) {
application.applicationIconBadgeNumber = application.applicationIconBadgeNumber + 1;
}
For push notification use,
func application(application: UIApplication,didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
application.applicationIconBadgeNumber = application.applicationIconBadgeNumber + 1;
}
As per your updated question, you are not getting proper badge count when app is entering background or terminating , while you are getting proper log. This is because to handle these cases we need to store badge counts somewhere. You can store it in UserDefaults and then fetch it accordingly.
Just go to onesignal message settings and turn on badge count

iOS: How can I notify/alert the user when he/she suspends the app?

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!

Swift - Calling Local Notification Method in applicationWillTerminate

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

UNUserNotification is sending notification instantly in ios

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()
})

Resources