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)
}
}
Related
How can i send Local Notifications while app is running in the background? in Swift 4. My app continuously use an Json file and I want the app to continue running so that it sends the user a Local Notification. I want a if label = label2 in background, App push notification for user.
label.text = myString
if label.text! == label2.text! {
let content = UNMutableNotificationContent()
content.title = "\(label2.text!)Değeri Kaydedildi!"
content.body = "Değer döndüğünde haber verilecek."
content.sound = UNNotificationSound.default()
let trigger = UNTimeIntervalNotificationTrigger.init(timeInterval: 5, repeats: false)
let request = UNNotificationRequest.init(identifier: "deneme", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
}
in AppDelegate, try to find function didReceiveRemoteNotification
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
#if DEBUG
print("Get User Info fetchCompletionHandler \(userInfo)")
#endif
// Your notification is in userInfo, you should cast it first before you show it
// Do what you want
completionHandler(.newData)
}
I'm trying to implement Background Fetch API in my app for that I've configured as below.
I've enabled Background Fetch from Capabilities.
In AppDelegate.swift
Added this in didFinishLaunchingWithOptions method
UIApplication.shared.setMinimumBackgroundFetchInterval(30)
Implemented this method too to perform task.
func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
debugPrint("New notification fired from AppDelegate...!!")
let notif = UNMutableNotificationContent()
notif.title = "New notification from App delegate"
notif.subtitle = "Cool App!"
notif.body = "I liked it!"
UNUserNotificationCenter.current().requestAuthorization(options: [.sound, .badge, .alert], completionHandler: { (isGranted, error) in
DispatchQueue.main.async {
let notifTrigger = UNTimeIntervalNotificationTrigger(timeInterval: 0.1, repeats: false)
let request = UNNotificationRequest(identifier: "myNotification", content: notif, trigger: notifTrigger)
UNUserNotificationCenter.current().add(request) { (error) in
if error != nil{
print(error!)
} else {
// do something
}
}
}
})
}
After configuring all the things local notification not firing. Why so?
Am I missing something?
I've also tried this tutorial
Any help will be appreciated!
You are not calling completionHandler in performFetchWithCompletionHandler. I am able to test BackgroundFetch with below code:
DispatchQueue.main.async {
let notifTrigger = UNTimeIntervalNotificationTrigger(timeInterval: 6.0, repeats: false)
let request = UNNotificationRequest(identifier: "myNotification", content: notif, trigger: notifTrigger)
UNUserNotificationCenter.current().add(request) { (error) in
if error != nil{
print(error!)
completionHandler(.failed) // Add this line
} else {
completionHandler(.noData) // Add this line
}
}
}
You can test Background Fetch with below steps:
Run your application.
Goto Xcode toolbar.
Select Debug > Simulate Background Fetch.
Now you will able to Test Background Fetch.
There is similar question about this:
Background Fetch Does Not Appear to Fire
Try to force it to run on the simulator, If fetch event works in
simulator, that proves that everything is correctly set up. There's
nothing you can do but wait.
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
Running on Xcode 8.3 with swift 3.1
Below is my code in AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .sound, .badge], completionHandler: { granted, error in
if granted {
print("User accept notification。")
}
else {
print("User don't like receive any notification!")
}
})
let content = UNMutableNotificationContent()
content.title = "Will it rain?"
content.body = "It never rains in (Southern) California!"
content.sound = UNNotificationSound.default()
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
let request = UNNotificationRequest(identifier: "local_notification", content: content, trigger: trigger)
center.add(request)
return true
}
I build and run it on my iPhone and will show a message to ask me whether I like to receive any notification and I click accept.
Then, nothing happen.
I expect it will show a notification from local with some text "Will it rain" like in my code.
You have to make your AppDelegate to adopt UNUserNotificationCenterDelegate
In applicationDidFinishLaunching method, add this center.delegate = self
Then implement this code:
extension AppDelegate: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.alert])
}
}
From Apple Docs:
To respond to the delivery of notifications, you must implement a
delegate for the shared UNUserNotificationCenter object. Your delegate
object must conform to the UNUserNotificationCenterDelegate protocol,
which the notification center uses to deliver notification information
to your app. A delegate is required if your notifications contain
custom actions.
Reference:
https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/SchedulingandHandlingLocalNotifications.html
https://developer.apple.com/reference/usernotifications/unusernotificationcenterdelegate
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