How to handle UNNotificationAction when app is closed? - ios

How to handle new iOS10 Notification Action when app is closed (not in background) ?
when app is minimalized everything works fine with:
UNUserNotificationCenter.current().delegate = x
and handling it in
class x: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Swift.Void) {
}
}
but nothing is called when app is closed and user tap action in notification... maybe i can't handle background task and i always have to launch app?

The notification action buttons handling can be done in both Extension as well as in Containing App.
When the action button is tapped, the handle first goes to the Extension and then to the Containing App if required. If Extension does not handle the notification action, the handle is passed on to the containing app.
Tapping a button launches your app (either in the foreground or
background) and gives you a chance to perform the indicated action.
Handling in extension:
func didReceive(_ response: UNNotificationResponse, completionHandler completion: #escaping (UNNotificationContentExtensionResponseOption) -> Void)
{
//You need to handle all the actions that appear with notification..
completion(.dismissAndForwardAction)
}
The completion closure takes a value of type UNNotificationContentExtensionResponseOption:
enum UNNotificationContentExtensionResponseOption : UInt
{
case doNotDismiss //the custom UI is not dismissed after handling the action
case dismiss //the custom UI is dismissed after handling the action
case dismissAndForwardAction //the custom UI is dismissed after handling the action and the control is then passed to containing app for any additional handling
}
Handling in Containing App:
extension AppDelegate : UNUserNotificationCenterDelegate
{
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void)
{
// Action handling - handling for all actions is not required
completionHandler()
}
}
For more you can refer to this(https://github.com/pgpt10/RichNotificationSample) tutorial.

Yes, it always launch the app, when user tap action in notification, button launches your app.
Some lines from apple doc:
Tapping a button launches your app (either in the foreground or background) and gives you a chance to perform the indicated action. You use this class to specify the text that is displayed in the button and the information your app needs to perform the corresponding action.

"Tapping a button launches your app (either in the foreground or background) and gives you a chance ... "
These lines appear in the doc for UIUserNotificationAction, which has been deprecated in iOS10.
The original question refers to the UNUserNotificationCenterDelegate in iOS 11.
Relevant doc: Declaring Your Actionable Notification Types
Quote from the doc:
When the user selects an action, the system launches your app in the
background and notifies the shared UNUserNotificationCenter object,
which notifies its delegate. Use your delegate object's
userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:
method to identify the selected action and provide an appropriate
response.

Related

SwiftUi | Create an error message in a model or class that is available in all views

I want to create a generic api utility that I can implement in any model. Therefore, I am currently faced with the problem of outputting error messages from the model regardless of a view. The error messages are needed to trigger a "no internet" or "poor internet connection" notification.
In general, it would help me a lot, regardless of the current view, to generate error messages in different classes in order to then inform the user of a failed process.
Here is my Code so far:
func execute(requestBody: [String: Any], withCompletion completion: #escaping (Data?, Int) -> Void) {
if !CheckApiReachability().getIsApiReachable() {
//trigger error message here
}
I have now used the Notification Center to implement an event which triggers an alert in my content view.
Emmit/ Post the Notification:
let nc = NotificationCenter.default
self.nc.post(name: Notification.Name("InternetConnectionErrorAppeared"), object: nil)
Receive/ Subscribe/ Listen to the emited event and trigger something:
.onReceive(nc.publisher(for: Notification.Name("InternetConnectionErrorAppeared"))) { output in
print("-- Api call failed triggered status code event")
}

How do I make a UNUserNotification that's consistent through app states?

I make a UNUserNotification like this:
let content = UNMutableNotificationContent()
content.title = "a title"
content.body = "a body"
content.sound = .default
content.categoryIdentifier = "\(type(of: self))"
let request = UNNotificationRequest(
identifier: UUID().uuidString,
content: content,
trigger: UNTimeIntervalNotificationTrigger(timeInterval: 0.01, repeats: false)
)
let category = UNNotificationCategory(
identifier: content.categoryIdentifier,
actions: [
UNNotificationAction(identifier: Strings.accept.id, title: Strings.accept.title, options: [.authenticationRequired, .foreground]),
UNNotificationAction(identifier: Strings.reject.id, title: Strings.reject.title, options: [.authenticationRequired, .destructive])
],
intentIdentifiers: [],
options: .customDismissAction
)
It will show a notification with a title and body, and two custom action buttons (accept and reject, it's an incoming call). It plays a sound too, but for some reason not if the app is foregrounded.
I also try to fake a persistent notification with it, like the way Whatsapp does it by repeating a local notification every ~4 seconds, but the second time it shows the notification the sound is cut off a bit. It's not the sound of the first one, the second one shows with its own sound playing but this time it doesn't fully play out. The sound is only ~1-2 seconds.
I don't understand why there are these differences, how do I make it always play a sound (foreground, background, not running) and how do I make sure the sound is not cut off the second time the notification is shown?
When notifications are received whilst the app is running the notifications are normally silenced so that the app can handle them in a more native way.
If I remember rightly, receiving the notification will call:
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler:
#escaping (UNNotificationPresentationOptions) -> Void) {
if someCondition {
completionHandler(.alert)
} else {
completionHandler(.init(rawValue: 0))
}
}
This method is called when the notification is received and if the app is running in the foreground, it will normally be silenced. The completion handler determines what action should be taken and whether or not a notification should show or a sound made etc.
In this case if someCondition is true then it should show the notification natively as it normally would, otherwise dont do anything.
Try calling completionHandler(.alert) in this method in your app and your notifications should show
Checkout the documentation here

iOS push notification banner clear action button [duplicate]

This question already has answers here:
How to detect "clear" notifications
(3 answers)
Closed 4 years ago.
i want to do something when user clear notification from home screen is it possible to get trigger when user tap on clear button on push notification banner, i ended up with adding custom button on view but thats not feasible.
image is attached i want trigger when user tap on this clear action any idea ???
{
“aps” : {
“category” : “MEETING_INVITATION”
“alert” : {
“title” : “Weekly Staff Meeting”
“body” : “Every Tuesday at 2pm”
},
},
“MEETING_ID” : “123456789”,
“USER_ID” : “ABCD1234”
}
i have done this but its adding button on view like popup
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler:
#escaping () -> Void) {
// Get the meeting ID from the original notification.
let userInfo = response.notification.request.content.userInfo
let meetingID = userInfo["MEETING_ID"] as! String
let userID = userInfo["USER_ID"] as! String
// Perform the task associated with the action.
switch response.actionIdentifier {
case "ACCEPT_ACTION":
sharedMeetingManager.acceptMeeting(user: userID,
meetingID: meetingID)
break
case "DECLINE_ACTION":
sharedMeetingManager.declineMeeting(user: userID,
meetingID: meetingID)
break
// Handle other actions…
default:
break
}
// Always call the completion handler when done.
completionHandler()
}
From docs about actionIdentifier:
This parameter may contain one the identifier of one of your UNNotificationAction objects or it may contain a system-defined identifier. The system defined identifiers are UNNotificationDefaultActionIdentifier and UNNotificationDismissActionIdentifier, which indicate that the user opened the app or dismissed the notification without any further actions.
So you don’t have to use your own identifiers, use system’s.
Instead of
"ACCEPT_ACTION"
use
UNNotificationDefaultActionIdentifier
and instead of
"DECLINE_ACTION"
use
UNNotificationDismissActionIdentifier

How to change iOS notification message upon receiving?

When I push notification from OneSignal, I want to push something like
", you have received a message"
I want to replace $name in app with the username something like
notificationMessage = UserDefaults.standard.string(forKey: "username") + notificationMessage
Is it possible to override notification?
If you mean to change the alert that the System shows, then NO you can't change those. They are managed by the OS.
For foreground only:
If you have some internal alert that you'd like to pop—when the app is in foreground then you're free to do as you wish
For example you could do something like:
func userNotificationCenter(center: UNUserNotificationCenter, willPresentNotification notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
//1. extract notification data
let message = notification.request.content.body
let title = notification.request.content.title
// 2. use the message and title and change their values
// 3. use your new message and title and show your own custom alert.
// 4. I excluded the alert so you could show whatever you like yourself. But still I want to increase the badge and have sound when notification arrives...
completionHandler([.badge, .sound])
}
you can't change the request itself since it's a get only...
Having that said I don't suggest this. Your logic of this should be handled on the server you push these notifications. This should be unnecessary.

How to retrieve realm object from different function? Swift

I currently have a function that writes a realm object into the database and writes a notification. When the object is written, it is added into a uicollectionview and displayed. The function below adds the object and sets a notification based on a UIDatePicker. That code is not shown since it is a lot that doesn't pertain to the problem:
func createTaskWithDate() {
let task = Task()
task.name = textField.text!
//notification code under here
try! realm.write {
realm.add(task)
updateData()
}
I also have another function that responds to an action on a notification set. When the user taps on the notification, they are given the option to "mark as complete". When the user taps "mark as complete", I am trying to retrieve the object from the "createTaskWithDate()" method and delete it from the collection view however I'm unsure on how to retrieve that object from that method:
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
let identifier = response.actionIdentifier
let request = response.notification.request
if identifier == "complete" {
try! realm.write {
//realm.delete(task object from createTaskWithDate())
updateData()
}
}
completionHandler()
}
How could I go about doing this?
When setting up the notification, you should store the name/primary key of the object the notification is about in your notification request. You can store any information in UNMutableNotificationContent.userInfo and access it in userNotificationCenter(_:didReceive:withCompletionHandler:) by
let userInfo = response.notification.content.userInfo.
Then you can retrieve the object from Realm with the primary key stored in userInfo.
You want to create new element when your User Notification have fired or find element that already exist? If you want to find, you can query that element from data base and than delete it. You can query by date or identifier (you can implement it using User Defaults, by adding identifier field to element and increment it each time when you create new element).
For UI modification after object manipulation you can use object notifications from Realm. You can read about it here.

Resources