local notification with background fetch swift 4 - ios

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

Related

Display notification with the app in the foreground

I have an iOS flutter app and I want to display a local notification when the app is in the foreground.
The code I'm going to paste works if the app is in the background, but not when it is open.
Create the notification:
// Create notification content
let content = UNMutableNotificationContent()
content.title = title
content.sound = UNNotificationSound.default
// Set trigger
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 2, repeats: false)
// Submit request
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
center.add(request) { (error : Error?) in
if let theError = error {
print(theError.localizedDescription)
}
}
In the method:
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
}
I am adding the following, driven by other responses to this same problem:
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
}
None of the above works. The notification is not displayed if the app is in the foreground.
What should I do to display the local notification even if the app is in the foreground?
On iOS the system does not automatically display notifications that are delivered while your app is running in the foreground.
From the documentation
If your app is in the foreground, the system delivers the notification to your app for handling.
You need to implement the userNotificationCenter(_:willPresent:withCompletionHandler:) delegate function and call the provided completion handler with the desired presentation option, probably .banner
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler(.banner)
}
You will need to establish your Notification Center delegate in an appropriate place, such as didFinishLaunching

Can I make network calls within my iOS background fetch?

I am currently trying to create notifications when a user has new messages. I'm trying to do this with local notifications because I'm very much a beginner and it seems(?) easier than push notifications. My question is, can I check my Firebase database during my background fetch?
What I've experienced is that the background fetch function works- but only before my app memory has been suspended, thus negating the point of the background fetch. I run it, I simulate a background fetch, but unless the app was just open, it does nothing and tells me "Warning: Application delegate received call to -application:performFetchWithCompletionHandler: but the completion handler was never called."
Here's my code if it's useful. I know it probably seems like a funky way to go about this.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
//Firebase
FirebaseApp.configure()
//there was other firebase stuff here that I don't think is relevant to this question
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { (didAllow, error) in
}
UIApplication.shared.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalMinimum)
return true
}
func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
myDatabase.child("users").child(userID!).child("hasNewMessages").observeSingleEvent(of: .value) { (snapshot) in
if snapshot.value as! Bool == true {
let content = UNMutableNotificationContent()
content.title = "You have unread messages"
content.badge = 1
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
let request = UNNotificationRequest(identifier: "testing", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
}
}
}
It is probably better to look at using push notifications as then your user's don't have to wait until iOS decides to invoke your background fetch; they can be notified of new messages immediately.
However, your problem is as described by the message you see in the console; you need to invoke the completionHandler that was passed to the background fetch method when you have finished your background operation to let iOS know what happened. It uses this information to tune how often and when your background fetch method is called.
func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
myDatabase.child("users").child(userID!).child("hasNewMessages").observeSingleEvent(of: .value) { (snapshot) in
if snapshot.value as! Bool == true {
let content = UNMutableNotificationContent()
content.title = "You have unread messages"
content.badge = 1
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
let request = UNNotificationRequest(identifier: "testing", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
}
completionHandler(.newData)
}
}

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

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?

Repeating local notification is triggered immediately -- how to defer?

My goal is to set a notification that will occur N seconds in the future for the first time, and then repeat every N seconds.
However, creating a repeating notification seems to trigger the UNUserNotificationCenterDelegate immediately.
App delegate:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let center = UNUserNotificationCenter.current()
center.delegate = self
return true
}
func startRequest() {
let content = UNMutableNotificationContent()
content.body = bodyText
content.categoryIdentifier = categoryIdentifier
content.sound = UNNotificationSound.default()
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 60, repeats: true)
trigger.nextTriggerDate()
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
let center = UNUserNotificationCenter.current()
center.add(request)
}
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
// This callback received right after the request is made
completionHandler([.alert, .sound])
}
I can work around this by creating a non-repeating notification and then starting repeating notifications when it expires. However, I was hoping there was some way to specify a first trigger date -- if memory serves right, the old notification API had this ability, and perhaps I am misunderstanding this new API
Thanks!
The issue may be that you aren't setting the title on the UNMutableNotificationContent().
content.title = "Title goes Here"
Also, to see if there is an error adding the request, you can add the following code.
center.add(request) { (error : Error?) in
if let theError = error {
// Handle any errors
}
}
I got this from Apple's docs - https://developer.apple.com/reference/usernotifications/unmutablenotificationcontent

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