Managing APN notification for multiple users on the same phone - swift

I am wondering how to manage Apple Push Notifications when many users are on the same device for the same app, because the device token is unique per device.
If I have 2 registered users on my app on the same device, how to prevent the push notification to appear if the notification is for userA but userB is currently logged into the app?
If the app is in the foreground, that could be easily managed by an userid that prevents the code to be executed if the APN is not for this user.
But how to handle this if the app is in the background?
The device will received the APN, will sound an alert, and will display the notification whatever user is currently logged into the app...

Based on your comment about being able to handle the situation if the app is in the foreground, I assume the notification has some info identifying the user the notification is for. It seems like if you store some info identifying the current user logged in, in NSUserDefaults, you should be able to retrieve that info in didReceiveRemoteNotification and based on that decide if the notification should be handled or not.
Update adding more info:
You will also need to implement application:didReceiveRemoteNotification:fetchCompletionHandler
and turn your notification into a "silent" notification, then after you have filtered out the ones you want to display from the ones you want to suppress, you would create local notifications for those that you want to show. You also need to add a UIBackgroundModes in your info.plist and include a "content-available", key in your payload. See here for more details.
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>remote-notification</string>
</array>
It's critical to implement the version with fetchCompletionHandler since the one without that is not called when the app is in background.
Here is an example from one of my apps, that receives "silent" notifications for location update requests, and also receives some "non-silent" notifications. If the app is active I show a "toast", if the app is in the background I create a local notification from the remote notification and then trigger it.
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler handler: (UIBackgroundFetchResult) -> Void ) {
log?.info("******* got a remote message = \(userInfo) *******")
let message: String = (userInfo["message"] ?? "stop") as! String
log?.info("message = \(message)")
switch message {
case "update_locations":
log?.info("starting location update cycle")
locationUploader?.startUploading(handler)
case "micropost_created":
log?.info("got a new micropost notification")
let notification = UILocalNotification()
let userName = userInfo["user_name"] as? String ?? ""
let content = userInfo["content"] as? String ?? ""
notification.alertTitle = "Micropost from \(userName)"
let alertBody = "\(userName) said: \(content)"
notification.alertBody = alertBody
notification.soundName = UILocalNotificationDefaultSoundName
application.presentLocalNotificationNow(notification)
if let topMostView = application.keyWindow {
topMostView.makeToast(message: alertBody)
}
handler(.NewData)
default:
handler(.NoData)
}
}

For resolving the above issue i have two options
1. On logout remove the token from server DB for logged in user and every time when new user gets login into app request for new token and send onto server.
2. On logout app can unregister itself from notification center and on login app has to register it self for notification and update token on server.
Here is the code for register and unregister notification.
On logout: [[UIApplication sharedApplication] unregisterForRemoteNotifications];
On login:
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:
(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];

Related

iOS. How to know if faceid prompt is presented?

Is there any way (in swift) to know if the system faceId prompt is being presented? I can't see any event, notification or delegate method. I should avoid the presentation of a view triggered by an asynchronous event in case the app is trying to authenticate the user.
There is no specific event, notification, or delegate method in Swift that allows you to determine if the system Face ID prompt is being presented. However, you can use the LAContext class to check the availability and state of Face ID on the device, and then use that information to determine if the prompt is likely to be displayed.
You can use the canEvaluatePolicy method of LAContext to check if the device supports Face ID and if the user has configured it.
let context = LAContext()
var error: NSError?
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
//FaceID is available
}
Then you can use the evaluatePolicy method to check if the user already authenticated recently or not.
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "Log in with Face ID") { success, error in
if success {
// Face ID authentication was successful
} else {
// Face ID authentication failed
}
}
It's important to notice that you should avoid presenting a view triggered by an asynchronous event in case the app is trying to authenticate the user.

Accept notifications later than the first launch

In my application, user can use notifications sent from the app. At first launch, there is an alert with refuse or allow notifications made in the appdelegate.
If I accept notifications, all is OK.
I made a screen / viewcontroller in which user can accept or no notification with a switch. If user refused notifications at first launch, how can I access to the controls in appdelegate like if I was at first launch ?
Thanks
According to Apple's Developer Documentation and this answer (for clarification), push notifications permissions can only be requested once. iOS stores the user's decision after it has been made and there is no way to request again. Do prepare for the fact that some users might not want notifications, you can always check the status of the permission your app received:
let center = UNUserNotificationCenter.current()
center.getNotificationSettings { settings in
guard settings.authorizationStatus == .authorized else { return }
if settings.alertSetting == .enabled {
// Schedule an alert-only notification.
} else {
// Schedule a notification with a badge and sound.
}
}

Is there way to use push notifications in our ionic 4 project?

We currently have an Ionic and Firebase project that we coded. In this project, we want to use push notifications. But our trouble is:
We are looking for a push notification plugin, like WhatsApp application. For example, when we send a message to a person, we want the notification to go to the person we're texting from, not everyone. But we couldn't find a free way to do that. Do you have any suggestions? Thank you.
Firebase Cloud Messaging By using cordova-plugin and ionic-native:Ref. Url
import { FCM } from '#ionic-native/fcm/ngx';
constructor(private fcm: FCM) {}
this.fcm.getToken().then(token => {
//you can store device token in firebase, later you can target push notification from firebase console this token id
backend.registerToken(token);
});
this.fcm.onNotification().subscribe(data => {
if(data.wasTapped){ / * true , means the user tapped the notification from the notification tray and that’s how he opened the app. */
console.log("Received in background");
} else {// false , means that the app was in the foreground (meaning the user was inside the app at that moment)
console.log("Received in foreground");
};
});
this.fcm.onTokenRefresh().subscribe(token => {
//update device token
backend.registerToken(token);
});
I don't recommend you to use FCM plugin. It has no methods to manage your notifications in your app (clear all or clear some special notification.
It is better to use phonegap-push-plugin or One Signal

APNS notification does not work when app is not running or background

I'm using pyapns to send notification to iPhone.
receiving notification when the app is running was success.
but, when app is not running or app is on background, it can't receive notification.
is it related with URL identifier or scheme? if not, what is the problem..?
Did you get called in
- (void)application:didReceiveRemoteNotification:
while you were in the app?
the problem from notification msg.
among the notify function args, there is notifications list arg.
note that, when you make this parameter, a dictionary of this list should contain below. so that the application can do alert reaction.
thePayLoad = {
'aps': {
'alert':'this option should be contained if you want to see the alert msg',
'sound':'k1DiveAlarm.caf',
'badge':42
},
'test_data': { 'foo': 'bar' }
}
In my case, I just sent msg not containing above. and in didReceiveRemoteNotification function, I made alert msg. but msg not containing alert information. so the device couldn't react.

How to prompt user to turn on Location Services...again

I want to have the same functionality as the Map app, where user is prompted every time they press the 'current location' button to turn on their Location Services if they are off:
Turn off location services
User presses 'getCurrentLocation' button
App tries to get location using CLLocationManager
User gets 'Turn On Location Services..." message that shows "Settings" and "Cancel" buttons.
User taps 'Cancel'
User presses ''getCurrentLocation' button again
App tries to get location using CLLocationManager again
User does not get 'Turn On Location Services..." message any more
In the Map app, the user gets "Turn On Location Services..." message every time. How can I get my app to do the same? I made user I am using a new instance of CLLocationManager, in case that was the problem, but it was not. I can't see any settings that would affect this.
If I make my own Alert I cannot get the same 'Settings' button functionality. Also, I don't want the user to see multiple Alerts that look the same.
Any ideas?
New in iOS 8 there is a constant called UIApplicationOpenSettingsURLString.
From the "What's new in iOS" document under UIKit is the line:
You can take the user directly to your app-related settings in the Settings app. Pass the UIApplicationOpenSettingsURLString constant to the openURL: method of the UIApplication class.
From Apple's documentation:
UIApplicationOpenSettingsURLString
Used to create a URL that you can pass to the openURL: method. When you open the URL built from this string, the system launches the Settings app and displays the app’s custom settings, if it has any.
You can pass this into the UIApplication openURL: method. It might look something like:
NSURL *settings = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
if ([[UIApplication sharedApplication] canOpenURL:settings])
[[UIApplication sharedApplication] openURL:settings];
If you want to point the user back to the Location Services screen in the Settings app, you can do so by sending them to a special URL, like so:
[[UIApplication sharedApplication] openURL:[NSURL URLWithString: #"prefs:root=LOCATION_SERVICES"]];
You can query the shared CLLocationManager instance if the location service is enabled. The correct way is to respect the users choice to disable location services.
But if you want to, just start the location service anyway and the user will be prompted to start it again. If the user opts in on the request locations will begin to be reported on your delegate as usual. If the user instead denies your request you will get a failure callback to the locationManager:didFailWithError: delegate method. The error will have an error code of kCLErrorDenied.
I would strongly discourage you from doing this, but you can try to start the service again if the user says no, and the user will be asked again. Most users will hate you for it though.