How to handle UserNotification through widget iOS14 to main app in swift? - swift

I have an iOS app with widget iOS 14 where I send UserNotifications from both sides. On main app side all UNUserNotificationCenterDelegate is working fine. On widget side when I fetched events from calendar then UserNotifications are triggered. When I'm received notification in main-app side and click on it, then didReceive response: UNNotificationResponse delegate or other delegates did not called and also did not called in widget side.
here is my code in Widgets
class NotificationCenter: NSObject, ObservableObject {
#Published var dumbData: UNNotificationResponse?
override init() {
super.init()
UNUserNotificationCenter.current().delegate = self
}
}
extension NotificationCenter: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.alert, .sound, .badge])
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
completionHandler()
}
func userNotificationCenter(_ center: UNUserNotificationCenter, openSettingsFor notification: UNNotification?) {
}
func scheduleNotification(at date: Date, identifierUnic : String, body: String, titles:String) {
UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
let triggerWeekly = Calendar.current.dateComponents([.day, .month, .hour,.minute, .year], from: date)
let trigger = UNCalendarNotificationTrigger(dateMatching: triggerWeekly, repeats: true)
let content = UNMutableNotificationContent()
content.title = titles
content.body = body
content.sound = UNNotificationSound.default
content.categoryIdentifier = "todoList2"
let request = UNNotificationRequest(identifier: identifierUnic, content: content, trigger: trigger)
UNUserNotificationCenter.current().delegate = self
UNUserNotificationCenter.current().add(request) {(error) in
if let error = error {
print(" We had an error: \(error)")
}}
}
}
//and fire in
#ObservedObject var notificationCenter: NotificationCenter
func getTimeline(in context: Context, completion: #escaping (Timeline<Entry>) -> ()) {
eventManager.checkPermission { (eventArray) in
print(eventArray)
if eventArray.count > 0{
for item in eventArray{
notificationCenter.scheduleNotification(at: item.startDate ?? Date(), identifierUnic: item.eventIdentifier ?? "", body: item.title, titles: item.title)
}
}
}
var entries: [SimpleEntry] = []
// Generate a timeline consisting of five entries an hour apart, starting from the current date.
let currentDate = Date()
for hourOffset in 0 ..< 5 {
let entryDate = Calendar.current.date(byAdding: .second, value: hourOffset, to: currentDate)!
let entry = SimpleEntry(date: entryDate)
entries.append(entry)
}
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
On Main App Side
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
print("userInfo \(userInfo)")
}
What is missing in it?
Is it right way to handle UserNotification on two Targets?
Any other way to do this?
Please let me know?
Thank you

I have a similar situation that I still can't figure out how to do... I have a widget that will display calendar events. How can I refresh the widget when calendar changed? I tried to use notificationCenter but don't know where to put it. What I want to do is that when there is a calendar change, my widget should be reloaded (refresh timeline.)
Thank you for your help.

Related

Why my didRecive and willPresent aren’t working(action buttons do nothing)?

I want to make an actionable notification. It appears only if my app is closed. When I press action button nothing happening
My class of notification
class Notfication : NSObject, UNUserNotificationCenterDelegate {
func request(answer : rope, RopesBas : RopesBase){
let taskCategory = UNNotificationCategory(identifier: "task", actions: [UNNotificationAction(identifier: "done", title: "Done")], intentIdentifiers: [])
UNUserNotificationCenter.current().setNotificationCategories([taskCategory])
print("0 stage")
let content = UNMutableNotificationContent()
content.title = answer.name
content.sound =
UNNotificationSound.default
content.categoryIdentifier = "task"
content.userInfo=["name" : answer.name]
print("1 stage")
// show this notification five seconds from now
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
print("2 stage")
//UNUserNotificationCenter.current().delegate = self
// choose a random identifier
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
print("3 stage")
// add our notification request
UNUserNotificationCenter.current().add(request){ (error : Error?) in
if let theError = error {
print(theError.localizedDescription)
}
}
print("4 stage")
print(UNUserNotificationCenter.current().delegate)
print("ok")
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
print("5 stage")
let userInfo = response.notification.request.content.userInfo
let id = "dad"
print("6 stage")
print(response.actionIdentifier)
switch response.actionIdentifier {
case "done":
base.ropes.removeAll(where: {$0.name == id})
break
case UNNotificationDefaultActionIdentifier,
UNNotificationDismissActionIdentifier:
break
default :
break
}
print(base.ropes.count)
completionHandler()
}
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
print("q")
completionHandler([.badge,.banner,.sound,.list])
}
}
I’m declaring this class at sheet view
struct Adding: View {
private let publisher = Notfication()
And using it when I press the button in a sheet view
Button(action: {
RopesDB.ropes.append(rope(name: answer.answer))
publisher.request(answer: RopesDB.ropes.last!, RopesBas: RopesDB)
dismiss()
}, label: {
HStack{
Spacer()
Text(answer.answer)
Spacer()
}
I’ve tried to put delegate into
class AppDelegate: NSObject, UIApplicationDelegate{
internal func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
let center = UNUserNotificationCenter.current()
let options : UNAuthorizationOptions = [.alert, .badge, .sound]
center.requestAuthorization(options: options) { granted, error in
if granted{
UNUserNotificationCenter.current().delegate = Notfication()
}
if let error = error {
print(error.localizedDescription)
}
}
return true
}
//
}
And into the class where it comment
What my mistake?
IDE Swiftplaygrounds(iPad 9, iOS 15.5)
Apple states in its documentation that notifications are only shown when the app is in the background. When the app already is in the foreground, the notification will be directly passed into your app:
https://developer.apple.com/documentation/usernotifications/handling_notifications_and_notification-related_actions
You will need to receive the notification in your delegate's userNotificationCenter(_:willPresent:withCompletionHandler:) method and there your app can decide what to actually do with it. E.g. throw the user to a certain view, show a modal etc.
I found out that this thing can’t work on Playgrounds, I suppose

How can I open a specific view on notification tap in SwiftUI?

I am trying to send a user to a DetailView once the notification is tapped. I've followed multiple instructions such as this one. However, when I test it out, tapping on the notification still brings me to the wrong view (the settingsView).
In App Delegate, I currently have this setup (for updated code scroll below):
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
registerBackgroundTasks()
FirebaseApp.configure()
UNUserNotificationCenter.current().delegate = self
return true
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
if response.actionIdentifier == "open" {
NotificationCenter.default.post(name: NSNotification.Name("DetailViewNew"), object: nil)
}
completionHandler()
}
In SettingsDetailView (where the notification is triggered and setup)
func configureNewAlert() {
let center = UNUserNotificationCenter.current()
center.removeAllDeliveredNotifications()
center.removeAllPendingNotificationRequests()
let content = UNMutableNotificationContent()
content.title = "Header"
content.body = "Test"
content.sound = UNNotificationSound.default
if let selectedHour = Calendar.current.dateComponents([.hour], from: userData.wakeUpTime).hour, let selectedMinute = Calendar.current.dateComponents([.minute], from: userData.wakeUpTime).minute{
var dateComponents = DateComponents()
dateComponents.hour = selectedHour
dateComponents.minute = selectedMinute
let open = UNNotificationAction(identifier: "open", title: "Open ", options: .foreground)
let cancel = UNNotificationAction(identifier: "cancel", title: "Close", options: .destructive)
let categories = UNNotificationCategory(identifier: "action", actions: [open,cancel], intentIdentifiers: [])
UNUserNotificationCenter.current().setNotificationCategories([categories])
content.categoryIdentifier = "action"
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)
let randomIdentifier = UUID().uuidString
let request = UNNotificationRequest(identifier: randomIdentifier, content: content, trigger: trigger)
center.add(request) { error in
if let error = error {
print(error.localizedDescription)
}
}
}
}
And in my DetailView (where I want the user to land when tapping the notification)
struct DetailViewNew: View {
let poemPublisher = NotificationCenter.default.publisher(for: NSNotification.Name("DetailViewNew"))
#State private var showScreen = false
var body: some View {
ZStack{
Text("Text")
}
.onReceive(poemPublisher) { notification in
self.showScreen = true
}
}
}
EDIT
I removed the categories and action, as I just want to have the user either tap the notification or dismiss it (not custom actions required).
When I do so, the completion handler if response.actionIdentifier == UNNotificationDefaultActionIdentifier does indeed get called. Now the question is how do I route it so it opens the DetailView when tapped?
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
if response.actionIdentifier == UNNotificationDismissActionIdentifier {
print ("Message Closed")
}
else if response.actionIdentifier == UNNotificationDefaultActionIdentifier {
print ("App is Open")
NotificationCenter.default.post(name: NSNotification.Name("DetailViewNew"), object: nil)
}
completionHandler()
}
Any suggestions? Thanks.

Is there a way a widget can catch UNUserNotificationCenterDelegate? SWIFT

Please help
UserNotifications
Import UserNotifications
put this in viewDidLoad
func setupNotif() {
UNUserNotificationCenter.current().requestAuthorization(options [.alert,.sound]) {
(granted, error) in
}
let notificationContent = UNMutableNotificationContent()
notificationContent.title = "Push Invitation"
notificationContent.subtitle = "Push subtitle"
notificationContent.body = "Please check notif"
notificationContent.sound = .default
notificationContent.categoryIdentifier = categoryIdentifier
let notificationTrigger = UNTimeIntervalNotificationTrigger.init(timeInterval: timeInterval, repeats: false)
let notificationRequest = UNNotificationRequest.init(identifier: "SampleNotif", content: notificationContent, trigger: notificationTrigger)
UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
UNUserNotificationCenter.current().add(notificationRequest) {(error) in if (error != nil) {} else { }}
}
this is the delegate for UUNotification center
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
}
**My answer, there is no way to do that.. as per apple.. i think this question might pop up in the future..
#available(iOS 10.0, *)
open class UNUserNotificationCenter : NSObject {
// The delegate can only be set from an application
weak open var delegate: UNUserNotificationCenterDelegate?

UserNotifications Extension Service UNNotificationAction didReceive action delegate not getting called.

I am creating Notification Service extension for local notification with UNNotificationAction but on tap of it delegate not getting called.
userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void)
here are my 3 actions
// Define Actions
let actionReadLater = UNNotificationAction(identifier: Notification.Action.readLater, title: "Read Later", options: [])
let actionShowDetails = UNNotificationAction(identifier: Notification.Action.showDetails, title: "Show Details", options: [.foreground])
let actionUnsubscribe = UNNotificationAction(identifier: Notification.Action.unsubscribe, title: "Unsubscribe", options: [.destructive, .authenticationRequired])
// Define Category
let tutorialCategory = UNNotificationCategory(identifier: Notification.Category.tutorial, actions: [actionReadLater, actionShowDetails, actionUnsubscribe], intentIdentifiers: [], options: [])
// Register Category
UNUserNotificationCenter.current().setNotificationCategories([tutorialCategory])
to schedule notification I am using
private func scheduleLocalNotification() {
// Create Notification Content
let notificationContent = UNMutableNotificationContent()
// Configure Notification Content
notificationContent.title = "Cocoacasts"
notificationContent.subtitle = "Local Notifications"
notificationContent.body = "In this tutorial, you learn how to schedule local notifications with the User Notifications framework."
notificationContent.userInfo = ["customNumber": 100]
// Set Category Identifier
notificationContent.categoryIdentifier = Notification.Category.tutorial
// Add Trigger
let notificationTrigger = UNTimeIntervalNotificationTrigger(timeInterval: 10.0, repeats: false)
// Create Notification Request
let notificationRequest = UNNotificationRequest(identifier: "exampleNotification", content: notificationContent, trigger: notificationTrigger)
// Add Request to User Notification Center
UNUserNotificationCenter.current().add(notificationRequest) { (error) in
if let error = error {
print("Unable to Add Notification Request (\(error), \(error.localizedDescription))")
}
}
}
My extension is getting displayed but actions are not getting triggered & without extension actions are working fine.
Try something like that:
class NotificationViewController: UIViewController, UNNotificationContentExtension {
#IBOutlet var label: UILabel?
override func viewDidLoad() {
super.viewDidLoad()
// Do any required interface initialization here.
preferredContentSize = CGSize.init(width: self.view.bounds.width / 2, height: self.view.bounds.height / 5)
}
func didReceive(_ notification: UNNotification) {
self.label?.text = "Extension"
}
// Implement this method
func didReceive(_ response: UNNotificationResponse, completionHandler completion: #escaping (UNNotificationContentExtensionResponseOption) -> Void) {
if response.notification.request.content.categoryIdentifier == "yourCategory" {
switch response.actionIdentifier {
case "first":
// Do something
completion(.dismissAndForwardAction)
case "second":
// Do something
completion(.dismissAndForwardAction)
default:
break;
}
}
}
}
You should implement the UNNotificationContentExtension's method:
func didReceive(_ response: UNNotificationResponse, completionHandler completion: #escaping (UNNotificationContentExtensionResponseOption) -> Void) {}
And inside of this block you can check your actions. Hope it helps.

Swift Local push notification action view

I'm trying to create a notification with two possible action buttons like "Quick reply" and "Cancel", but I can't find any code examples for this. Please could someone explain how to do this in Swift 3.
It's difficult to give a precise example of what you are looking for. Here's a quick UNUserNotificationCenter implementation with an action.
import UserNotifications
Define a category ID constant
private let categoryID = "Category"
Setup and register UNUserNotificationCenter
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
// Configure User Notification Center
UNUserNotificationCenter.current().delegate = self
// Define Actions
let actionShowSomething = UNNotificationAction(identifier: "ShowSomething", title: "Show Something", options: [])
// Define Category
let category = UNNotificationCategory(identifier: categoryID, actions: [actionShowSomething], intentIdentifiers: [], options: [])
// Register Category
UNUserNotificationCenter.current().setNotificationCategories([category])
}
Example event for triggering the notification
// MARK: - Actions
#IBAction func sheduleNotification(sender: UIButton) {
// Request Notification Settings
UNUserNotificationCenter.current().getNotificationSettings { (notificationSettings) in
switch notificationSettings.authorizationStatus {
case .notDetermined:
self.requestAuthorization(completionHandler: { (success) in
guard success else { return }
// Schedule Local Notification
self.scheduleLocalNotification()
})
case .authorized:
// Schedule Local Notification
self.scheduleLocalNotification()
case .denied:
print("Application Not Allowed to Display Notifications")
}
}
}
sheduleNotification implementation
// MARK: - Methods
private func scheduleLocalNotification() {
// Create Notification Content
let notificationContent = UNMutableNotificationContent()
// Configure Notification Content
notificationContent.title = "Title"
notificationContent.subtitle = "Subtitle"
notificationContent.body = "Body"
// Set Category Identifier
notificationContent.categoryIdentifier = categoryID
// Add Trigger
let notificationTrigger = UNTimeIntervalNotificationTrigger(timeInterval: 10.0, repeats: false)
// Create Notification Request
let notificationRequest = UNNotificationRequest(identifier: "cocoacasts_local_notification", content: notificationContent, trigger: notificationTrigger)
// Add Request to User Notification Center
UNUserNotificationCenter.current().add(notificationRequest) { (error) in
if let error = error {
print("Unable to Add Notification Request (\(error), \(error.localizedDescription))")
}
}
}
Handle user notification authorization
private func requestAuthorization(completionHandler: #escaping (_ success: Bool) -> ()) {
// Request Authorization
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { (success, error) in
if let error = error {
print("Request Authorization Failed (\(error), \(error.localizedDescription))")
}
completionHandler(success)
}
}
UnUserNotificationDelegate implementation
extension ViewController: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.alert])
}
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse, withCompletionHandler
completionHandler: #escaping () -> Void) {
completionHandler()
}
}
Result
You can achieve this by UIMutableUserNotificationAction
for more information look here:-
1) https://nrj.io/simple-interactive-notifications-in-ios-8/
2) http://www.imore.com/interactive-notifications-ios-8-explained