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…
Related
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)
I am currently making an app which counts the steps walked and check with goal and release local notification if met.
I have set up the local notification but I want that to trigger just once at that moment. I got this working through dispatch_once_t:
if stepsData >= stepsGoalData {
let localNotification = UILocalNotification()
UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
localNotification.fireDate = NSDate()
localNotification.alertBody = "Acheived"
localNotification.soundName = UILocalNotificationDefaultSoundName
}
But in case if the user increases the stepsGoalData, currently the code doesn't trigger the notification. Can someone please provide me with an idea to handle this case. Thank you!
So, you should really just change the check for wether to notify or not so that it considers not just the count but also a flag to indicate if the notification has been made. This can be a var defined beside your stepsGoalData as a simple Bool.
Now, your check would be:
if stepsData >= stepsGoalData && !hasNotified {
hasNotified = true
...
And when you set the stepsGoalData to a new target value you also set hasNotified = false.
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.
Iam trying to make a regular UILocalNotification with default sound
this is my function
func makeLocalNotificationForNow(str : String , id : String)
{
let notification = UILocalNotification()
notification.alertBody = str // text that will be displayed in the notification
notification.alertAction = "open" // text that is displayed after "slide to..." on the lock screen - defaults to "slide to view"
notification.fireDate = NSDate(timeIntervalSinceNow: 5) // todo item due date (when notification will be fired)
notification.soundName = UILocalNotificationDefaultSoundName // play default sound
UIApplication.sharedApplication().scheduleLocalNotification(notification)
}
in my didFinishLaunchingWithOptions function I put
let settings = UIUserNotificationSettings(forTypes: [.Alert, .Badge , .Sound], categories: nil)
application.registerUserNotificationSettings(settings)
application.registerForRemoteNotifications()
Note : I also use parse push notifications
when I make the notification , it appears but without an alert or sound (I need to swipe the from top to see the notification
what's wrong ?!
as far as i know notifications only make a sound when the app is in the background, meaning quit you app, put your phone on locked mode and send a notification through the parse website, then you should hear the sound, if not, check the settings on the app you're making to see if sound alerts are enabled, and last resource, check that your phone is not on vibrate only
it was a stupid mistake , the problem was my phone , I changed the phone and it all worked
I believe I am using cancelLocalNotification improperly.
I have a recurring notification that runs conditionally, which was created using the following code:
let localNotification: UILocalNotification = UILocalNotification()
localNotification.alertAction = "Inactive Membership"
localNotification.alertBody = "Our system has detected that your membership is inactive..."
localNotification.fireDate = NSDate(timeIntervalSinceNow: 5)
localNotification.repeatInterval = .Minute
UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
This notification successfully runs every single minute (for testing purposes). Obviously, I'd like a way to conditionally remove these notifications, so I've tried to use cancelLocalNotification to do so.
How I think cancelLocalNotification works
My intuition is that cancelLocalNotification will remove all the notifications for that specific notification object. Here's how I'm using it.
UIApplication.sharedApplication().cancelLocalNotification(localNotification)
What actually happens
I've stepped through my function and verified that the cancelLocalNotification code does get called. However, I keep getting my notification every minute.
My Question
How do I properly cancel a UILocalNotification that has been scheduled?
Full code:
static func evaluateMemberStatusNotifications() {
let userDefaults = Global.app.userDefaults
let localNotification: UILocalNotification = UILocalNotification()
print("evaluating profile notifications")
// is the user active? if so no notification
let isActive : Bool = userDefaults.valueForKey("ActiveMember") as! Bool // false == inactive
print("is the user active?")
if !isActive {
print("user is not active, checking if notification code has run")
// if userDefaults is nil for this value, we'll set it to false
if (userDefaults.valueForKey("ProfileNotificationHasRun") == nil) {
print("nil! setting ProfileNotificationHasRun to 'false'")
userDefaults.setValue(false, forKey: "ProfileNotificationHasRun")
}
let statusNotification = userDefaults.valueForKey("ProfileNotificationHasRun") as! Bool
// has this code been run? If not run it
if !statusNotification {
print("running notification code")
// we schedule a notification
localNotification.alertAction = "Inactive Membership"
localNotification.alertBody = "Our system has detected that your membership is inactive."
localNotification.fireDate = NSDate(timeIntervalSinceNow: 5)
localNotification.category = "status"
localNotification.repeatInterval = .Day
UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
userDefaults.setValue(true, forKey: "ProfileNotificationHasRun")
} else {
print("notification code has already run, time interval has been set")
}
} else {
print("member is active, remove Inactive notification")
// if the member is active, we remove the notification so the user doesn't
// keep getting notified
UIApplication.sharedApplication().cancelLocalNotification(localNotification)
userDefaults.setValue(false, forKey: "ProfileNotificationHasRun")
}
}
You're creating a new UILocalNotification (line 3 of your gist) and then cancelling it. That new notification was never scheduled. You need to get the existing, scheduled notification object and cancel that.
You can access your existing, scheduled notifications through the UIApplication.sharedApplication().scheduledLocalNotifications array.
Or you could just cancel all scheduled local notifications by calling UIApplication.sharedApplication().cancelAllLocalNotifications().
UPDATE
Each call to evaluateMemberStatusNotifications creates a new instance of UILocalNotification, then (depending on the ActiveMember value) either configures and schedules that new notification, or tries to delete the new (unscheduled) notification.
Instead, you should just create a new notification in the !isActive branch where you want to schedule it.
In the other branch, you need to find the existing notification in the scheduledLocalNotifications array and (if you find it) cancel that existing notification.
Since you say you have other notifications that you don't want to mess with, you should use the userInfo property of the notification to identify it. For example, when configuring the notification before scheduling it:
localNotification.alertAction = "Inactive Membership"
localNotification.alertBody = "Our system has detected that your membership is inactive. You may continue using this application though some features may not be available to you until your membership is reinstated."
localNotification.fireDate = NSDate(timeIntervalSinceNow: 5)
localNotification.category = "status"
localNotification.repeatInterval = .Day
// *** NEW
localNotification.userInfo = [ "cause": "inactiveMembership"]
And when you want to cancel the existing notification:
let maybeNotification = UIApplication.sharedApplication().scheduledLocalNotifications?.filter({ (notification: UILocalNotification) -> Bool in
notification.userInfo?["cause"] as? String == "inactiveMembership"
}).first
if let notification = maybeNotification {
UIApplication.sharedApplication().cancelLocalNotification(notification)
}