How to handle push notification allow from settings in iOS - ios

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) {
}

Related

How to get unique device id in iOS

I am working on iOS app for push notification feature i need to send unique device id of iOS device to server ,in android secure androd id getting for every device,is there any way to get unique device id of iOS.
I found some answers vendor id and ad id are they unique
code:
Secure.getString(getContext().getContentResolver(),Secure.ANDROID_ID);
For get UUID you can use this code
UIDevice *currentDevice = [UIDevice currentDevice];
NSString *deviceId = [[currentDevice identifierForVendor] UUIDString];
But for push notifications you need device token and it will create after user will accept permission and UIApplication delegate method will call
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
There is no legal way to uniquely identify an iOS device. Period.
You can get only compromise solutions: IDFA, Vendor ID or APNS Device Token.
Every above-mentioned ID can change during the device lifecycle, thus they cannot be used as unique device identifiers.
for Step by step by Step Integration of APNS in your application , you can get the steps in here
iOS9 Apple says that Device Token might change each time your app is installed. So the best way is to reregister the Device token on each launch.
Step-1
There are two steps to register for push notifications. First, you must obtain the user’s permission to show any kind of notification, after which you can register for remote notifications. If all goes well, the system will then provide you with a device token, which you can think of as an “address” to this device.
This method creates an instance of UIUserNotificationSettings and passes it to registerUserNotificationSettings(_:).
UIUserNotificationSettings stores settings for the type of notification your app will use. For the UIUserNotificationTypes, you can use any combination of the following:
.Badge allows the app to display a number on the corner of the app’s icon.
.Sound allows the app to play a sound.
.Alert allows the app to display text.
The set of UIUserNotificationCategorys that you currently pass nil to allows you to specify different categories of notifications your app can handle. This becomes necessary when you want to implement actionable notifications, which you will use later
- (void)applicationDidFinishLaunching:(UIApplication *)app {
// other setup tasks here....
// Register the supported interaction types.
UIUserNotificationType types = UIUserNotificationTypeBadge |
UIUserNotificationTypeSound | UIUserNotificationTypeAlert;
UIUserNotificationSettings *mySettings =
[UIUserNotificationSettings settingsForTypes:types categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:mySettings];
// Register for remote notifications.
[[UIApplication sharedApplication] registerForRemoteNotifications];
}
Build and run. When the app launches, you should receive a prompt that asks for permission to send you notifications:
Tap OK and poof! The app can now display notifications.
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{
if (notificationSettings.types != UIUserNotificationTypeNone) {
//register to receive notifications
[application registerForRemoteNotifications];
}
}
Here, you first check whether the user has granted you any notification permissions; if they have, you directly call registerForRemoteNotifications().
Again, methods in UIApplicationDelegate are called to inform you about the status of registerForRemoteNotifications().
// Handle remote notification registration.
- (void)application:(UIApplication *)app
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)devToken {
const void *devTokenBytes = [devToken bytes];
self.registered = YES;
// send your Device Token to server
}
As the names suggest, the system calls application(:didRegisterForRemoteNotificationsWithDeviceToken:) when the registration is successful, and otherwise calls application(:didFailToRegisterForRemoteNotificationsWithError:).
- (void)application:(UIApplication *)app
didFailToRegisterForRemoteNotificationsWithError:(NSError *)err {
NSLog(#"Error in registration. Error: %#", err);
}
Swift
let defaults = NSUserDefaults.standardUserDefaults()
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
// PUSH NOTIFICATION
let deviceToken = defaults.objectForKey(UserDefaultsContracts.KEY_DEVICE_TOKEN) as String?
if (deviceToken == nil) {
print("There is no deviceToken saved yet.")
var types: UIUserNotificationType = UIUserNotificationType.Badge |
UIUserNotificationType.Alert |
UIUserNotificationType.Sound
var settings: UIUserNotificationSettings = UIUserNotificationSettings( forTypes: types, categories: nil )
application.registerUserNotificationSettings( settings )
application.registerForRemoteNotifications()
}
return true
}
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData!) {
print("Got token data! (deviceToken)")
var characterSet: NSCharacterSet = NSCharacterSet( charactersInString: "<>" )
var deviceTokenString: String = ( deviceToken.description as NSString )
.stringByTrimmingCharactersInSet( characterSet )
.stringByReplacingOccurrencesOfString( " ", withString: "" ) as String
print( deviceTokenString )
defaults.setObject(deviceTokenString, forKey: UserDefaultsContracts.KEY_DEVICE_TOKEN)
}
func application(application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: NSError!) {
print("Couldn’t register: (error)")
}
}
for more information you get in Apple Documents
For Objectice-C:
UIDevice *device = [UIDevice currentDevice];
NSString *currentDeviceId = [[device identifierForVendor]UUIDString];
For Swift:
let device_id = UIDevice.currentDevice().identifierForVendor?.UUIDString
As per the Apple Documentation,
Device tokens can change, so your app needs to reregister every time
it is launched and pass the received token back to your server. If you
fail to update the device token, remote notifications might not make
their way to the user’s device. Device tokens always change when the
user restores backup data to a new device or computer or reinstalls
the operating system. When migrating data to a new device or computer,
the user must launch your app once before remote notifications can be
delivered to that device.
Never cache a device token; always get the token from the system
whenever you need it. If your app previously registered for remote
notifications, calling the registerForRemoteNotifications method again
does not incur any additional overhead, and iOS returns the existing
device token to your app delegate immediately. In addition, iOS calls
your delegate method any time the device token changes, not just in
response to your app registering or re-registering.
So the best way is to re-register for the token on each launch. For that you can call registerForPushNotifications(application) in applicationDidFinishLaunching() method.
The delegate method for the above method is didRegisterForRemoteNotificationsWithDeviceToken in which you can the deviceToken and send it to server.
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
let tokenChars = UnsafePointer<CChar>(deviceToken.bytes)
var tokenString = ""
for i in 0..<deviceToken.length {
tokenString += String(format: "%02.2hhx", arguments: [tokenChars[i]])
}
print("Device Token:", tokenString)
}
You should receiver in
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
var deviceTokenStr = String(format: "%#", deviceToken)
deviceTokenStr = deviceTokenStr.stringByReplacingOccurrencesOfString("<", withString: "")
deviceTokenStr = deviceTokenStr.stringByReplacingOccurrencesOfString(">", withString: "")
deviceTokenStr = deviceTokenStr.stringByReplacingOccurrencesOfString(" ", withString: "")
}
Or if you want to get unique device id , you can use
let UUID = NSUUID().UUIDString
as I did in my app, you can use first generated uuid and save it in Keychain file to use it as unique device id (because uuid is changed in every running for ur app and also device token) so u can save a uuid or any custom id u generate in keychain it will remain forever even user uninstall and in install the app many times

CLCircularRegion and wake up app

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)
}

How to get notification additionaldata(payloadData) that was at AppDelegate' didFinishLunchingWithOptions if the user didn't open the notification

I am currently using OneSignal for notification service to my app.I really need a help with accessing notification additionaldata(payload data) from AppDelegate inside didFinishLunchingWithOption where OneSignal API can give me like this.
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var data : [NSObject : AnyObject]!
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let oneSignal = OneSignal(launchOptions: launchOptions, appId: "48755d3d-abc0-4bac-8f71-095729bb3a65") { (message, additionalData, isActive) in
NSLog("OneSignal Notification opened:\nMessage: %#", message)
if additionalData != nil {
NSLog("additionalData: %#", additionalData)
self.data = additionalData
print(self.data)
}
}
oneSignal.enableInAppAlertNotification(true)
return true
}
}
but,I can only get the data if user click notification when appear or open it from notification center.So,if user neglect that notification without tapping when appear or without swiping or tapping from notification center,how do i get the additional data?
Actually,I want to store all payload data every time it comes into my device at realm database and fetch the data from my server according to that payload data.
You should use application(_:didReceiveRemoteNotification:fetchCompletionHandler:).
If you have enabled remote notifications background mode most of your notifications will be delivered even if the app is not running in the foreground. The only caveat being that the app must have been launched (since notifications are being pushed the user has done this) and it mustn't have been force-quit by the user.
More info in Apple's docs about that specific method.
Or in the "Local and Remote Notification Programming Guide's" notification handling chapter
You can extract all the payload in did finishLaunching by following method..
Let data = launchOptions.objectForKey(UIApplicationLaunchOptionsUIApplicationLaunchOptionsRemoteNotificationUIApplicationLaunchOptionsUIApplicationLaunchOptionsRemoteNotificationKey)

Check if iOS push notifications status is not determined

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
}

Identify "Not Determined" case for Local Notifications settings

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".

Resources