Geofence alerts/local notifications not waking up locked phone when triggered - ios

I'm making an app that used circular regions for geofences. When the phone is active or the app is open, the geofence notifications are working fine in both simulator and device (iPhone 6 running 10.3.1).
In the simulator it works fine; When the user enters a region, it wakes up, makes a sound and shows an alert on the lock screen.
On the phone, the "didEnterRegion" delegate calls are made when entering the region (I log some messages) but the phone is not making an alert and waking up. When I push the home button once, I can see the alert on the lock screen, but I want it to wake up and show the alert instantly - like when I get a message. It works in the simulator, so I wonder what could be wrong? It has worked for me a few times, where the alert was shown on both the phone and my watch, but 95% of the time it's not working - the notifications are generated but only visible if I manually wake up the phone.
How to fix this?
Here's the code I use for creating the local notification:
// https://blog.codecentric.de/en/2016/11/setup-ios-10-local-notification/
let location = CLLocation(latitude: item.coordinate.latitude, longitude: item.coordinate.longitude)
GeoTools.decodePosition(location: location) {
(address, city) in
let content = UNMutableNotificationContent()
content.title = "Camera nearby!"
content.subtitle = item.id
content.body = "\(address), \(city)"
content.categoryIdentifier = Constants.notificationCategoryId
content.sound = UNNotificationSound.default()
content.threadIdentifier = item.id
// FIXME make action for clicking notification
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 0.001, repeats: false) // FIXME HACK
let request = UNNotificationRequest(identifier: "camNotification", content: content, trigger: trigger)
let unc = UNUserNotificationCenter.current()
unc.removeAllPendingNotificationRequests()
unc.add(request, withCompletionHandler: { (error) in
if let error = error {
print(error)
}
else {
print("completed")
}
})
}

Here is some code that I just verified wakes the device when notification is presented:
let message = "CLRegion event"
// Show an alert if application is active:
if UIApplication.shared.applicationState == .active {
if let viewController = UIApplication.shared.keyWindow?.rootViewController {
showSimpleAlertWithTitle(nil, message: message, viewController: viewController)
}
}
else {
// Otherwise app is in background, present a local notification:
let content = UNMutableNotificationContent()
content.body = message
content.sound = UNNotificationSound.default()
content.categoryIdentifier = "message"
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1.0, repeats: false)
let request = UNNotificationRequest(identifier: "com.foobar", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
}
Really the only diff is that I don't call removeAllPendingNotifications() so if you must remove notifications I wonder if removePendingNotificationRequests(withIdentifiers identifiers: [String]) might be more precise?

Related

Calling removeAllPendingNotificationRequests() in applicationWillTerminate not working on iPad

I set up a dummy project to reproduce the issue I'm seeing. In my ContentView, I schedule some repeating notifications.
struct ContentView: View {
var body: some View {
VStack {
Button("Schedule notifications") {
let content = UNMutableNotificationContent()
content.title = "Title"
content.body = "body"
content.sound = UNNotificationSound.default
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 60, repeats: true)
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
}
Button("Request Permission") {
let current = UNUserNotificationCenter.current()
current.requestAuthorization(
options: [.sound, .alert],
completionHandler: { completed, wrappedError in
guard let error = wrappedError else {
return
}
})
}
}
}
}
Then in my AppDelegate, I attempt to cancel those repeating notifications before the app terminates.
func applicationWillTerminate(_ application: UIApplication) {
UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
print("============== removing all notifications")
}
What I'm finding is that my scheduled notifications are still delivered, even though I can see my print statement in the Xcode console. But if I run the same test on an iPhone, my notification is not delivered, as expected.
Am I doing something wrong, or is this a bug? I'm using 13.4.1 on iPad, and 13.3.1 on iOS
I am also facing the same problem. Found out that applicationWillTerminate method has approximately five seconds to perform any tasks and return. And removeAllPendingNotificationRequests returns immediatly but removes scheduled notification asynchronously in secondary thread.
I think 5 seconds are more than necessary to clear notification.

UILocalNotification are showing only on lock screen/Notification screen

Whenever app receive silent push , I am displaying the local notification.
If at the time of receiving the silent push , iPhone is locked , local notification are displaying but if application is in background and iPhone is not locked then local notification are not displaying. What could be wrong. ? I am using below code ?
let content = UNMutableNotificationContent()
content.title = "Connect"
content.body = indentificationText
content.sound = UNNotificationSound.default()
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1,
repeats: false)
let appDelegate = UIApplication.shared.delegate as! AppDelegate
UNUserNotificationCenter.current().delegate = appDelegate
content.userInfo = payload.dictionaryPayload
let request = UNNotificationRequest(identifier: content.title, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: { (error) in
NSLog("UNUserNotificationCenter Add completion Handler : \(String(describing: error?.localizedDescription))")
})
The app may be suspended in the background , so the code that creates the local notification isn't executed . . .

iOS local notifications showing in Simulator but not device

I have a Firebase listener that launches a notification when it fires like so:
chat.ref.child("lastMessage").observe(.value, with: { snapshot in
let lastMessage = snapshot.value as! String
chat.lastMessage = lastMessage
self.tableView.reloadData()
chat.lastMessageText = "New Message!"
let content = UNMutableNotificationContent()
content.title = "You have a new Message!"
content.subtitle = chat.title
content.body = chat.lastMessage
content.badge = 1
let trigger = UNTimeIntervalNotificationTrigger (timeInterval: 1, repeats: false)
let request = UNNotificationRequest(identifier: "timerDone", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
}
Also have:
override func viewDidLoad() {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge], completionHandler: {didAllow, error in })
}
The problem I am having is, although the HomeScreen notifications are working in the simulator, they are not showing on the device. All permissions are granted on the device. Reading some questions here I found some answers that suggested I add the following in the app delegate:
application.beginBackgroundTask(withName: "showNotification", expirationHandler: nil)
but this actually disabled notifications on the simulator too. The notifications did work on the device before but stopped very suddenly. Any thoughts?

UNLocationNotificationTrigger not triggering

I'm trying to create a notification which triggers when entering a region using UNNotificationLocationTrigger. I implemented it like this:
let destination = //destination is added by user through interface//
let notification = UNMutableNotificationContent()
notification.title = "You've reached your destination"
notification.body = "Some"
notification.sound = UNNotificationSound.default()
let destRegion = CLCircularRegion(center: destination, radius: 1000.0, identifier: "DistanceToDestination")
destRegion.notifyOnEntry = true
let trigger = UNLocationNotificationTrigger(region: destRegion, repeats: false)
let request = UNNotificationRequest(identifier: "destAlarm", content: notification, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: { error in
if error == nil {
print("Successful notification")
} else {
print(error ?? "Error")
}
})
I tried it on a real device and walked around. It worked 2 times perfectly when I entered the region (I checked it with a map and region as circle overlay in map). But after that all other attempts failed.
EDIT:
For those reading the comments. It's not working again. So it seems to be pretty inconsistent. If somebody know if it's a bug or know how to fix it I would be very grateful.
First of all, you should call locationmanager.startupdatinglocation
Second of all, you should change the trigger so that repeats = true

How to display a message on top?

I'm new to WatchKit development. I would like to display a message regardless of what app is currently being used or whether the watch is active or not, like how the built-in Timer app shows the label "Timer Done". The user should then be able to click an "OK" button and dismiss the message.
I have tried using both alerts and modal views, but showing them programmatically still requires my app to be active. Using the notifications system is not a viable solution because that would rely on an iPhone.
I've been stuck on this for many hours, any insight would be helpful, thanks.
You need to ask permission in your iOS App first:
UNUserNotificationCenter.current().requestAuthorization(options: [.alert,.badge,.sound])
{ (granted, error) in
if !granted
{
print("User did not give permissions to send notitications...")
}
}
Then in your watchOS App you can create a notification for some time in the future, make sure you give it a UUID (this may be a bug in watchOS3):
let content: UNMutableNotificationContent = UNMutableNotificationContent()
content.title = "Title"
content.subtitle = "Subtitle"
content.body = "message"
content.sound = UNNotificationSound.default()
content.categoryIdentifier = "aCategory"
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: duration, repeats: false)
let id: String = WatchNotify.getUUID()
let request = UNNotificationRequest.init(identifier: id,
content: content,
trigger: trigger)
UNUserNotificationCenter.current().add(request)
{
(error) in // ...
}
....
class func getUUID() -> String
{
let uuidObj = CFUUIDCreate(nil)
let uuidString = CFUUIDCreateString(nil, uuidObj)!
return uuidString as String
}

Resources