iOS: How to expire a local notification with a region trigger? - ios

I have a local geofence notification, set up with the following code:
func startMonitoring(annotation:MKAnnotation) {
let region = CLCircularRegion(center: annotation.coordinate, radius: 10.0, identifier: "randomID")
region.notifyOnExit = false
let notification = UILocalNotification()
notification.region = region
notification.alertBody = "You got it!"
notification.category = self.notificationCategory
notification.soundName = "my_sound.wav"
notification.userInfo = ["ID": "randomID"]
UIApplication.sharedApplication().scheduleLocalNotification(notification)
}
This works great, but the user has to get to the area within a certain time limit for the action of the notification to be available. How can I make the notification NOT fire if the time limit has passed?
I currently only have "WhenInUse" authorization for the user's location and would prefer to keep it that way.

You'd be better off monitoring the CLCircularRegion manually using CLLocationManager, and when your app is notified that the user has entered the region, check whether it's in the time limit and post your UILocalNotification manually.
See the section Using Regions to Monitor Boundary Crossings here: https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLLocationManager_Class/index.html

You can setup background fetch with setminimumbackgroundfetchinterval , and cancel the scheduled notification if it's going to expire.
It's not 100% reliable because you cannot control exact time, but it's better than nothing.

Related

Local Notification on offline (Swift)

I want to receive a local notification in my app (swift) when I'm not connected to Internet and I have some information registered in my Local Data base.
Is it possible to do that please?
Local Notification doesnot requires internet.
About Local Notification from apple developer site
Local notifications give you a way to alert the user at times when your app might not be running. You schedule local notifications at a time when your app is running either in the foreground or background. After scheduling a notification, the system takes on the responsibility of delivering the notification to the user at the appropriate time. Your app does not need to be running for the system to deliver the notification.
For more info check this link. You can also check this link for tutorial.
do like this :
public func presentNotification(_ notifAction: String, notifBody: String) {
let application = UIApplication.shared
let applicationState = application.applicationState
if applicationState == UIApplicationState.background {
let localNotification = UILocalNotification()
localNotification.alertBody = notifBody
localNotification.alertAction = notifAction
localNotification.soundName = UILocalNotificationDefaultSoundName
localNotification.applicationIconBadgeNumber += 1
application.presentLocalNotificationNow(localNotification)
}
}
UIApplicationState has these states :
case active
case inactive
case background

Local Notifications After Device Restart

I start my app and schedule my local notifications. This is a simplified version of the code I'm using:
let content = UNMutableNotificationContent()
content.body = "Wild IBEACON appeared!"
let region = CLBeaconRegion(proximityUUID: uuid, identifier: "iBeacon region")
let trigger = UNLocationNotificationTrigger(region: region, repeats: true)
let request = UNNotificationRequest(identifier: "iBeacon notification", content: content, trigger: trigger)
notificationCenter.add(request)
They trigger while my app is in the background. So far, so good.
Then I restart the device. I don't force-quit the app.
And now the notifications don't trigger anymore. I need to open the app again.
Is there a way to let my schedules survive the restart?
The UNLocationNotificationTrigger is a new helper classes added in iOS10 to make it easier to trigger notifications based on beacon or geofence detections. According to the documentation, it is designed to be used only when the app is in use:
Apps must request access to location services and must have when-in-use permissions to use this class. To request permission to use location services, call the requestWhenInUseAuthorization() method of CLLocationManager before scheduling any location-based triggers.
https://developer.apple.com/reference/usernotifications/unlocationnotificationtrigger
Based on the above permissions, the app will only trigger when in use. The documentation does not explicitly say that it won't work in the background, so you might try requesting always location permission with the requestAlwaysAuthorization() instead of requestWhenInUseAuthorization() (be sure you put the correct key in your plist if you do this), to see if this helps.
An alternative would be to not use this helper class and instead manually start up CoreLocation and beacon monitoring, then create your own UILocalNotification manually when you get the region entry callback:
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
if let region = region as? CLBeaconRegion {
let notificationMessage = "Wild IBEACON appeared!"
let notification = UILocalNotification()
notification.alertBody = notificationMessage
notification.alertAction = "OK"
UIApplication.shared.presentLocalNotificationNow(notification)
}
}
The above approach is known to work across app restarts.

How to Run a Task in swift on a particular date-time in background either application is on or off

I am working on alarm application, i need to schedule alarm on specific time, I use scheduleLocalNotification for scheduling alarms and it's working fine as i want. BUT I need to run to a request to my API server before triggering alarm. In that request I want to check some parameters returning from API server, If that satisfies some condition.
If any one have a method that run on a particular date - time in swift
Please help me for that
func addAlarm (newAlarm: Alarm) {
// Create persistent dictionary of data
var alarmDictionary = NSUserDefaults.standardUserDefaults().dictionaryForKey(ALARMS_KEY) ?? Dictionary()
// Copy alarm object into persistent data
alarmDictionary[newAlarm.UUID] = newAlarm.toDictionary()
// Save or overwrite data
NSUserDefaults.standardUserDefaults().setObject(alarmDictionary, forKey: ALARMS_KEY)
scheduleNotification(newAlarm, category: "ALARM_CATEGORY")
scheduleNotification(newAlarm, category: "FOLLOWUP_CATEGORY")
}
/* NOTIFICATION FUNCTIONS */
func scheduleNotification (alarm: Alarm, category: String) {
let notification = UILocalNotification()
notification.category = category
notification.repeatInterval = NSCalendarUnit.Day
switch category {
case "ALARM_CATEGORY":
notification.userInfo = ["UUID": alarm.UUID]
notification.alertBody = "Time to wake up!"
notification.fireDate = alarm.wakeup
notification.timeZone = NSTimeZone.localTimeZone()
notification.soundName = "loud_alarm.caf"
break
case "FOLLOWUP_CATEGORY":
notification.userInfo = ["UUID": alarm.followupID]
notification.alertBody = "Did you arrive yet?"
notification.fireDate = alarm.arrival
notification.timeZone = NSTimeZone.localTimeZone()
notification.soundName = UILocalNotificationDefaultSoundName
break
default:
print("ERROR SCHEDULING NOTIFICATION")
return
}
print("Notification=\(notification)")
// For debugging purposes
if alarm.isActive {
UIApplication.sharedApplication().scheduleLocalNotification(notification)
}
}
Waking up an app through a local notification is not possible, this is available only for remote notifications. According to the Notification Programming Guide:
When a remote notification arrives, the system handles user
interactions normally when the app is in the background. It also
delivers the notification payload to the
application:didReceiveRemoteNotification:fetchCompletionHandler:
method of the app delegate in iOS and tvOS
But there is still a catch; even then it is not guaranteed that the app will be launched since, according to didReceiveRemoteNotification:fetchCompletionHandler: documentation:
However, the system does not automatically launch your app if the user
has force-quit it. In that situation, the user must relaunch your app
or restart the device before the system attempts to launch your app
automatically again.
I don't think there is a guaranteed way to schedule a block for execution in some later moment, independently from the state of the app at that time. Depending on your specific requirements and frequency, you could perhaps register for the fetch background mode and implement application:performFetchWithCompletionHandler: to opportunistically fetch and validate server data. Last note: make sure that you are a responsible background app (from my experience Apple takes this requirement seriously)

ANCS : what is the concept of PositiveAction?

I'm trying to work with the Apple Notification Center Service to make interactions between a Bluetooth peripheral and an iOS device.
In the documentation Apple mention the 2 notification actions: EventFlagPositiveAction and EventFlagNegativeAction…
So far, the Negative part works: once the notification is transmitted to the peripheral, this latter one can trigger the negative action, resulting dismissal of the notification.
But I cannot trigger the Positive side of the force... My Notification has a single action button and I want this button to be considered as the positive action... But I don't know how it works: is it implicit ? do all actions have the positive flag ? or should I do something to make it recognized as the positive one ?
This is more a conceptual question about ACNS, but for information, below is the code I'm using:
1st to register for the local notification in the AppDelegate:
let notificationTypes = UIUserNotificationType.Alert.union(UIUserNotificationType.Sound).union(UIUserNotificationType.Badge)
let launchAction = UIMutableUserNotificationAction()
launchAction.identifier = "LAUNCH_ACTION"
launchAction.title = "OK"
launchAction.activationMode = UIUserNotificationActivationMode.Foreground
launchAction.destructive = false
/* this is this UIMutableUserNotificationAction that I want to trigger from my external device, and should be considered as the famous positive action I am looking for */
let notificationCategory = UIMutableUserNotificationCategory()
notificationCategory.identifier = "LAUNCH_NOTIFICATION"
notificationCategory.setActions([launchAction], forContext: .Minimal)
application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes: notificationTypes, categories: NSSet(array:[notificationCategory]) as? Set<UIUserNotificationCategory>))
And 2nd, later to create a notification
let localNotification:UILocalNotification = UILocalNotification()
localNotification.alertAction = "Hello"
localNotification.alertBody = "World"
localNotification.fireDate = NSDate(timeIntervalSinceNow: 5)
localNotification.soundName = UILocalNotificationDefaultSoundName
localNotification.hasAction = true
localNotification.category = "LAUNCH_NOTIFICATION"
UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
Ok, so then I have had the answer for my question from Apple developer technical Support.
Posting this here, hopefully this will help someone else:
the first thing to understand is that “Positive actions are only wired up for telephony related notifications (incoming call, missed call, and voicemail). There is currently no way to pass through the positive action for an app alert.
Things are easier to understand now…

trigger localNotification only when user enters a specified region

This is my code for the didEnterRegion function. Problem is that the notification triggers while entering as well as exiting. What can I do to trigger the notification only when the user enters the location ?
func locationManager(manager: CLLocationManager!, didEnterRegion region: CLRegion!) {
var localNotification:UILocalNotification = UILocalNotification()
localNotification.alertBody = "you've entered rohini sector -8"
localNotification.region = region
UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
NSLog("Entering region")
}
You don't need to specify the region in the local notification. You already know that the device is inside the region, because you had a call to didEnterRegion - specifying the region on the notification is redundant - You can simply post the notification without the region specified.
You are not specifying wether you want to monitor entering or exiting the region. When you create your region you set notifyOnExit or notifyOnEntry . If you only want exit then make that value true and the other false... or if only entry.. do the opposite
///// snipped taken from ray wenderlichs code...
// 1
let region = CLCircularRegion(center: geotification.coordinate,
radius: geotification.radius, identifier: geotification.identifier)
// 2
region.notifyOnEntry = false
region.notifyOnExit = true

Resources