pass an array of data and display it in notifications - swift

I do not know how to implement the function so that notifications come one by one from the data array. Here is my function that displays only one,last notification :
func addNotificationWithTimeIntervalTrigger(title :String){
let content = UNMutableNotificationContent()
content.title = title
content.subtitle = "Subtitle"
content.body = "Body"
//content.badge = 1
content.sound = UNNotificationSound.default()
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 3, repeats: false)
let reguest = UNNotificationRequest(identifier: "timeInterval", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(reguest) { (error) in
}
}
Here I just pass the data from my tableView :
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
switch indexPath.row {
case 0:
UserNotificationManager.shared.addNotificationWithTimeIntervalTrigger(title:"aaa")
default: break
}
My notification :
How to make the notifications go one by one from the array?

Make sure that every scheduled notification has a different identifier , otherwise the new one will replace the old
let reguest = UNNotificationRequest(identifier: "timeInterval", content: content, trigger: trigger)

This could be achieved by using the indexPath.row to get an object from your data model. You have not shared your data model, but an array is a useful way to store your objects for situations like this.
With some changes, your custom function could look like this. You can now pass an integer index to get the correct object from your model.
func addNotificationWithTimeIntervalTrigger(title: String, index: Int) {
guard let thisObject = yourDataModelArray[index] as? YourObjectType else { return }
let content = UNMutableNotificationContent()
content.title = title // This could be taken from data model instead
content.subtitle = thisObject.subtitle
content.body = thisObject.body
content.sound = UNNotificationSound.default()
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 3, repeats: false)
let reguest = UNNotificationRequest(identifier: "timeInterval", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(reguest) { (error) in
if let error = error {
// Error handling
}
}
}
Then you could call it like this. No switch statement needed as it pulls data from your data model based on the indexPath.row. Note that you could also store the titles in your data model, meaning you would not have to pass this as an argument.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
UserNotificationManager.shared.addNotificationWithTimeIntervalTrigger(title:"Custom title", index: indexPath.row)
}

Try This Separate File created for local Notifications
import Foundation
import UIKit
import UserNotifications
struct NotificationHandlerStruct {
static var notifyTimer : Timer?
}
class LocalNotificationHandler: NSObject, UNUserNotificationCenterDelegate
{
static var shared = LocalNotificationHandler()
//MARK: Schedule Notification
func scheduleNotification(Title title: String, Subtitle subtitle: String, BodyMessage body: String, AlertContent contentRx:[AnyHashable:Any]) {
/// Remove Previous Displayed Notification in case if you need
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: ["gruveoCall"])
let content = UNMutableNotificationContent()
//adding title, subtitle, body and badge
content.title = title
content.subtitle = subtitle
content.sound = UNNotificationSound.default()
content.body = body
content.badge = 0
content.userInfo = contentRx
//getting the notification trigger
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 0.01, repeats: false)
//getting the notification request
let request = UNNotificationRequest(identifier: "gruveoCall", content: content, trigger: trigger)
//adding the notification to notification center
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
/// Comment Code below if you do not want to repeat same notification again after some interval of time
if NotificationHandlerStruct.notifyTimer == nil {
NotificationHandlerStruct.notifyTimer = Timer.scheduledTimer(withTimeInterval: 5, repeats: true, block: { (timer) in
self.sendNotification(NotificationContent: content)
})
}
else{
NotificationHandlerStruct.notifyTimer?.invalidate()
NotificationHandlerStruct.notifyTimer = nil
}
}
//MARK: Repeat Notification
func sendNotification(NotificationContent content: UNMutableNotificationContent) {
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: ["gruveoCall"])
//getting the notification trigger
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 0.01, repeats: false)
//getting the notification request
let request = UNNotificationRequest(identifier: "gruveoCall", content: content, trigger: trigger)
//adding the notification to notification center
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
}
//MARK: Stop Timer
func stopTimer() {
if NotificationHandlerStruct.notifyTimer != nil {
NotificationHandlerStruct.notifyTimer?.invalidate()
NotificationHandlerStruct.notifyTimer = nil
}
}
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
//displaying the ios local notification when app is in foreground
completionHandler([.alert, .badge, .sound])
}
}
Usage
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
LocalNotificationHandler.shared.scheduleNotification(Title: self.providerListArray![indexPath.row], Subtitle: "My Subtitle", BodyMessage: "Some Message", AlertContent: ["aps":["data":"your Content"]])
}

Related

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.

Why aren't local notifications showing up on Mac?

I am trying to send local notifications from my Mac app(written in swift using Cocoa)
So far I've written this function
func scheduleNotification() {
let content = UNMutableNotificationContent()
content.title = "Test"
content.body = "This is a test"
content.sound = .default
content.badge = 1
let now = Date()
let int = timePicker.dateValue.timeIntervalSince(now)
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: int, repeats: false)
let request = UNNotificationRequest(identifier: "Test notification", content: content, trigger: trigger)
center.add(request) { (err) in
if err == nil {
print("Success")
}
}
}
But for some reason it doesn't show a banner or anything in the Notification Center. Is something wrong with my code or something else causing this? (I checked and the app has the permission to send notifications)
And also, what is the equivalent on macOS for the
UIApplication.shared.applicationBadgeNumber = 0 on iOS?
If app is on foreground, you have to use UNUserNotificationCenterDelegate.
UNUserNotificationCenter.current().delegate = self
Firstly notify that delegate in viewDidLoad.
extension ViewController: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification,
withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
return completionHandler([.alert, .sound, .badge])
}
}
And after you can use delegate like that. Check this out more information this post -> Local Notifications
So, as it turns out asking for permission is mandatory in MacOS Catalina, that was all that missing.

UNUserNotificationCenter. Notifications are not allowed for this application

I noob in development application on OSX. I want to create app with Share extension. After content loading I want to show Notification, but I getting error "Notifications are not allowed for this application". I don't understand why requestAuthorization method not show dialog windows with permission, and how I can allow application to send notifications.
This is my code:
import Cocoa
import UserNotifications
class ShareViewController: NSViewController {
override func loadView() {
self.view = NSView()
// Insert code here to customize the view
let item = self.extensionContext!.inputItems[0] as! NSExtensionItem
NSLog("Attachment = %#", item.attachments! as NSArray)
showNotification()
let outputItem = NSExtensionItem()
let outputItems = [outputItem]
self.extensionContext!.completeRequest(returningItems: outputItems, completionHandler: nil)
}
func showNotification() -> Void {
let notificationCenter = UNUserNotificationCenter.current()
notificationCenter.requestAuthorization(options: [.alert, .badge]) {
(granted, error) in
if granted {
print("Yay!")
} else {
print("D'oh") // Print this, not authorized
}
}
let content = UNMutableNotificationContent()
content.title = "Hello"
content.body = "This is example"
content.sound = UNNotificationSound.default
content.badge = 1
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
notificationCenter.add(request) { (error : Error?) in
if let theError = error {
print(theError) // Print Domain=UNErrorDomain Code=1 "Notifications are not allowed for this application"
}
}
}
}
I don't see anywhere in the documentation that says you can't schedule new Local Notifications from an extension. I do however see an apple support ticket where someone had to deal with this issue, as do I.
Basically this failure is a race-condition. The key is to not call the contentHandler of this extension method:
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: #escaping (UNNotificationContent) -> Void) {
until after the completion handler of
notificationCenter.add(request: UNNotificationRequest>, withCompletionHandler: ((Error?) -> Void)
is called. It worked for me. Make sense?
Apple does't allow send Notifications from Share extensions.
Documentation

open sms app with text and url in swift by using MFMessageComposeViewController

Hi I want to open sms app with teat along url I write following code but I facing
error like:Static member 'canSendText' cannot be used on instance of type 'MFMessageComposeViewController'
var controller1 = MFMessageComposeViewController()
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if indexPath.section == 0
{
if (controller1.canSendText()) {
let urlToShare = "http://www.appzoy.com"
controller1.body = "Hey I just gave an Awesome Assessment on UAssess App you can also try it. I scored , Try to beat my score \(urlToShare)"
controller1.messageComposeDelegate = self as? MFMessageComposeViewControllerDelegate
self.present(controller1, animated: true, completion: nil)
}
}
}
As mentioned into doc you should use:
if MFMessageComposeViewController.canSendText() {
print("SMS services are available")
}
Try This
//MARK:- Send Mail
//MARK:-
func sendMail(emailTitle : String , messageBody : String , toRecipents : [String]){
if (MFMailComposeViewController.canSendMail()) {
let mc: MFMailComposeViewController = MFMailComposeViewController()
let emailTitle = emailTitle //Feedback
let messageBody = messageBody //"Feature request or bug report?"
let toRecipents = toRecipents //["friend#stackoverflow.com"]
mc.mailComposeDelegate = self
mc.setSubject(emailTitle)
mc.setMessageBody(messageBody, isHTML: false)
mc.setToRecipients(toRecipents)
self.present(mc, animated: true, completion: nil)
}else{
//Show alert
}
}
The call sendMail function
let urlToShare = "http://www.appzoy.com"
self.sendMail(emailTitle: "Title of Mail", messageBody: "Hey I just gave an Awesome Assessment on UAssess App you can also try it. I scored , Try to beat my score \(urlToShare)", toRecipents: ["abc#mail.com", "def#mail.com"])

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.