I’m prompting my user to enable push notifications after the user logs in to my app. I know how to test if push notifications are enabled or disabled using:
isRegisteredForRemoteNotifications
and it works just fine. It returns YES for enabled and NO for not available but I want to be able to figure out how to check for Not Determined (the user wasn’t prompted to enable push notifications to begin with). Is there a way to test that?
Thanks in advance!
You create Not Determined state yourself.
func registerNotification() {
// 1.Call register API
// ...
// 2.Save a bool value to indicate you've called this method or not.
let appleSuggestedUserDefaultsKeyPrefix = "com.yourcompany.product-"
let key = appleSuggestedUserDefaultsKeyPrefix + "didCallRegisterNotificationAPI"
NSUserDefaults.standardUserDefaults().setBool(true, forKey: key)
}
And in didFinishLaunchingWithOptions method you need check if you have called registerNotification().
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {
let didCallRegisterNotificationAPI = NSUserDefaults.standardUserDefaults().boolForKey(...)
if didCallRegisterNotificationAPI {
// If registered or user denied, call this method will NOT show the alert.
// Just the same as you did before.
registerNotification()
} else {
print("registerNotification() has not been called")
}
}
And finally you can call registerNotification() directly anywhere anytime as you need and the alert is under your control now.
isRegisteredForRemoteNotifications is a Bool. There is no undetermined status. You can verify this is the reference.
When the user first installs your app they must either allow or disallow push notifications. There is no other possible option.
However, maybe you're asking because you can delete the app, reinstall, and it won't ask you for permission. That's because the permission is remembered.
Resetting the Push Notifications Permissions Alert on iOS
The first time a push-enabled app registers for push notifications, iOS asks the user if they wish to receive notifications for that app. Once the user has responded to this alert it is not presented again unless the device is restored or the app has been uninstalled for at least a day.
If you want to simulate a first-time run of your app, you can leave the app uninstalled for a day. You can achieve the latter without actually waiting a day by following these steps:
Delete your app from the device.
Turn the device off completely and turn it back on.
Go to Settings > General > Date & Time and set the date ahead a day or more.
Turn the device off completely again and turn it back on.
Reference
Associated Question: When I delete my iOS application push notification state remains
EDIT:
You can only use isRegisteredForRemoteNotifications to check that they are simply not registered, whether that's due to declining or due to you never trying to register.
However, as long as you try to register in a valid way (valid certs and profiles etc) and the user declines, your app will call did register, but with nil UIUserNotificationSettings:
func application(application: UIApplication, didRegisterUserNotificationSettings notificationSettings: UIUserNotificationSettings) {
if notificationSettings.types == nil {
println("You didn't allow notifcations")
}
}
You can make use of UNNotificationSettings's authorizationStatus property.
private func checkNotificationsAuthorizationStatus() {
let notificationCenter = UNUserNotificationCenter.current()
notificationCenter.getNotificationSettings { notificationSettings in
switch notificationSettings.authorizationStatus {
case .authorized:
print("Authorized to schedule or receive notifications.")
case .denied:
print("Not authorized to schedule or receive notifications.")
case .notDetermined:
print("Not determined whether the app is allowed to schedule notifications.")
case .provisional: // Available from iOS 12.0
print("Provisionally authorized to post noninterruptive user notifications.")
#unknown default:
print("Error")
}
}
}
Use it in didFinishLaunchingWithOptions like:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
UIApplication.shared.registerForRemoteNotifications()
self.checkNotificationsAuthorizationStatus()
return true
}
Related
I am working on an app that has one section from where user can tap on a mobile number to make cellular calls. To make call, I am using following code.
if let phoneCallURL = URL(string: "tel://\(9999999999)") {
let application:UIApplication = UIApplication.shared
if (application.canOpenURL(phoneCallURL)) {
if #available(iOS 10.0, *) {
application.open(phoneCallURL, options: [:], completionHandler: nil)
} else {
// Fallback on earlier versions
application.openURL(phoneCallURL)
}
}
}
This shows an alert with option to Call or Cancel. Now when I select Call option, I need to check the state of the ongoing call.
To do this I am making use of CallKit framework.
var callObserver = CXCallObserver()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
callObserver.setDelegate(self, queue: nil)
}
func callObserver(_ callObserver: CXCallObserver, callChanged call: CXCall) {
if call.hasConnected {
}
if call.isOutgoing {
}
if call.hasEnded {
}
if call.isOnHold {
}
}
This delegate methods receives callback when an outgoing call starts or ends.
Now since my app does not have any functionality related to VOIP, I just want to know if it is OK to use CallKit framework like this to know the call status.
Will my app be rejected for Appstore upload if I use CallKit like this and not have a VOIP functionality in the app?
Any help on this will be much appreciated.
In general, CallKit.framework's CXCallObserver API may be used by any app, regardless of whether that app provides VoIP calling functionality itself or not.
Note however that it's not guaranteed that any particular call that your app observes are the result of the user deciding to call based on your app's opening of a tel: URL. It is possible that the user declined to open the tel: URL that your app offered to open, and instead dialed a call separately or received an incoming call around the same time period, and your app would receive similar-looking CXCallObserver delegate callbacks regardless. It's not possible to reliably distinguish between calls initiated outside your app from those started from your app.
I have Integrated the push notification in my app. I am getting the device token in my AppDelegate and sending to the server in my LoginViewController. When alert comes if user click on "Don't allow" I am not calling my device registration service. But I have no idea how to handle this scenario.
If user click on don't allow in the push notification alert and go inside the app, then he open device settings and enable the push notification.
In this case how can I call my device registration service?
Please help me
Thanks
Actually there is no delegate for it to observe that user changed the status for push notifications.
But you can check by using this method in applicationDidBecomeActive: method like this
Objective C :
iOS 8 and above
if (![[UIApplication sharedApplication] isRegisteredForRemoteNotifications]){
NSLog(#"push notifications are disabled");
}
For iOS 10
UIRemoteNotificationType types = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
if (types == UIRemoteNotificationTypeNone) {
NSLog(#"push notifications are disabled");
}
Swift :
iOS 8 and above
let isRegisteredForRemoteNotifications = UIApplication.shared.isRegisteredForRemoteNotifications
if isRegisteredForRemoteNotifications {
NSLog(#"push notifications are enabled");
} else {
NSLog(#"push notifications are disabled");
}
For iOS 10
let current = UNUserNotificationCenter.current()
current.getNotificationSettings(completionHandler: { (settings) in
if settings.authorizationStatus == .denied {
NSLog(#"push notifications are disabled");
}
})
Hope it helps.
Note
Any changes in the settings restart the app.
so If user turn on the notification from settings then your app will be restarted and you will get the device token in app-delegate and you can send it to your server. if your login is save locally and you were sending device_token in login api, then you need to use a separate service to send device_token to server.
In AppDelegate
If user Allow Notification, the token will be returned in func bellow and you will call your service to sending to your server
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
}
If user Don't Allow Notification, you will receive error in func bellow
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
}
I added push notification support to my iOS app using OneSignal some time before. The app is made in Xcode with Swift.
I want to send a test push notification only to my test device(s). I the documentation I found the following manual: How do I send a notification to a single user?
I managed to create the segment but I don't know where to put this peace of code: OneSignal.sendTag("is_test", "true")
Does anybody know where I have to put this piece of code to make it working as I described above?
I uploaded my code here: https://codeshare.io/DxcNn
Thanks,
David.
Update:
OneSignal now also supports to set a device as test device without doing something in the code. You can also download your own app from App Store and use it as test device. Just select you device from devices list one OneSignal and mark it as test device. You can find your device in the list by model, version and/or time added.
The sendTag method is from the device sdk. In your case iOS.
https://documentation.onesignal.com/docs/ios-native-sdk#section--sendtag-
You should do this anytime after initWithLaunchOptions in the app delegate. Updated code based on comments
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {
let oneSignal = OneSignal(launchOptions: launchOptions, appId: "here_is_my_onesignal_app_id") { (message, additionalData, isActive) in
NSLog("OneSignal Notification opened:\nMessage: %#", message)
if additionalData != nil {
NSLog("additionalData: %#", additionalData)
// Check for and read any custom values you added to the notification
// This done with the "Additonal Data" section the dashbaord.
// OR setting the 'data' field on our REST API.
if let customKey = additionalData["customKey"] as! String? {
NSLog("customKey: %#", customKey)
}
}
}
OneSignal.defaultClient().sendTag("is_test", value: "true")
// Override point for customization after application launch.
return true
}
In application we have mechanism like native Reminder app in iOS with firing notifications when user enter or exit in some region.
But two devices behave differently (5 and 5s) in same time. All devices have enable notifications, and allow use locations.
Two devices have a some "travel" and in the route created 10 points. First device (5) when came to finish received only 6 notifications, (5s) don't receive any notification.
But my question is how I can know when my app is restart in background or continue working. Because, all log in app I redirect into a file, and after download container and analyze what happened in app in travel time.
I noticed app restart in same times when device is enter to region and my log marks fired in the file but notifications don't receive. This is happended when app try to get some information from web service in didFinishLaunchingWithOptions
And maybe this is problem. How to know distinguish restart app or continue working. Thx.
Are you checking UIApplicationLaunchOptionsLocationKey in didFinishLaunchingWithOptions similar to (sorry, Swift is what I have now):
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
if launchOptions?[UIApplicationLaunchOptionsLocationKey] != nil {
// app was launched in response to incoming location event
}
}
Additionally, if you're not already doing this you may need to create notifications differently if app is in background:
// Show an alert if application is active
if UIApplication.sharedApplication().applicationState == .Active {
if let message = notefromRegionIdentifier(region.identifier) {
if let viewController = window?.rootViewController {
showSimpleAlertWithTitle(nil, message: message, viewController: viewController)
}
}
}
else {
// Otherwise present a local notification:
let notification = UILocalNotification()
notification.alertBody = notefromRegionIdentifier(region.identifier)
notification.soundName = "Default";
UIApplication.sharedApplication().presentLocalNotificationNow(notification)
}
As of iOS8 you to need to register and prompt the user for the use of Local Notifications. Therefore I'd like to implement a way to double check these permissions.
How can I check for the case of Local Notifications settings being not determined/not set yet? So far I only know how to check for Local Notifications being granted or denied, like this …
var currentStatus = UIApplication.sharedApplication().currentUserNotificationSettings()
var requiredStatus:UIUserNotificationType = UIUserNotificationType.Alert
if currentStatus.types == requiredStatus {
… //GRANTED
} else {
… //DENIED
}
The problem using this is I also get a Denied if there is nothing set so far. How can I differentiate all 3 cases?
Granted (Notification, Alert Type)
Denied (Notification, Alert Type)
Undefined/Not set yet (therefore local notification app settings not created yet)
As an alternative solution it would be helpful to have a comparable delegate method to CoreLocation's authorization didChangeAuthorizationStatus in order to react on the user's selection on the permissions alert. Is there something like this in order to get the state of user interaction with the privacy alert for local notifications?
The solution I implemented :
In app delegate, I detect when didRegisterUserNotificationSettings is shot. And I save in userdefaults a bool to true :
func application(application: UIApplication, didRegisterUserNotificationSettings notificationSettings: UIUserNotificationSettings) {
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "notificationsDeterminedKey")
NSUserDefaults.standardUserDefaults().synchronize()
}
And when I need to know the status :
if NSUserDefaults.standardUserDefaults().boolForKey("notificationsDeterminedKey") {
let grantedSettings = UIApplication.sharedApplication().currentUserNotificationSettings()
if grantedSettings.types == UIUserNotificationType.None {
// Denied
} else {
// Granted
} else {
// Not Determined
}
I just found an appropriate UIApplication delegate method that helps to solve this:
- (void)application:(UIApplication *)application
didRegisterUserNotificationSettings:
(UIUserNotificationSettings *)notificationSettings {
You can find more details on this in WWDC14 Session 713 "What’s New in iOS Notifications".