I take advantage of native push notifications to inform my customers when a session is able to start or finish. My app performs a number of duties when a session is began or ended. At present, the person has to launch the app to manually begin/finish a session. I wish to supply notification actions to begin/finish a session with out launching the app within the foreground.
When the app is operating within the background and I choose a notification motion, the UNUserNotificationCenterDelegate’s didReceive technique is named, I obtain a UNNotificationResponse, and my app performs the duties related to beginning/ending a session as anticipated.
Nonetheless, if I power shut my app from the App Switcher after which choose a notification motion when the notification is available in, it seems the didReceive delegate technique will not be being referred to as. My app does not run the duties wanted to begin/finish a session correctly.
Apple’s docs on Actionable Notifications states:
“Actionable notifications let the person reply to a delivered notification with out launching the corresponding app. Different notifications show info in a notification interface, however the person’s solely plan of action is to launch the app. For an actionable notification, the system shows a number of buttons along with the notification interface. Tapping a button sends the chosen motion to the app, which then processes the motion within the background.“
Am I doing one thing improper right here or maybe misunderstanding how notification actions work?
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
let injectionContainer = AppContainer()
let dbMaintenanceManager = DBMaintenanceManager()
var window: UIWindow?
func software(_ software: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]?) -> Bool {
requestNotificationAuthorization()
dbMaintenanceManager.performMaintenanceTasks()
let look = UINavigationBarAppearance()
look.backgroundColor = Shade.primaryBlue
look.titleTextAttributes = [.foregroundColor: UIColor.white]
UINavigationBar.look().standardAppearance = look
UINavigationBar.look().scrollEdgeAppearance = look
UINavigationBar.look().tintColor = .white
let mainVC = injectionContainer.makeMainViewController()
window = UIWindow(body: UIScreen.primary.bounds)
window?.overrideUserInterfaceStyle = .gentle
window?.makeKeyAndVisible()
window?.rootViewController = mainVC
return true
}
func applicationWillEnterForeground(_ software: UIApplication) {
dbMaintenanceManager.performMaintenanceTasks()
}
non-public func requestNotificationAuthorization() {
let notificationCenter = UNUserNotificationCenter.present()
notificationCenter.requestAuthorization(choices: [.alert, .sound, .badge]) { granted, error in
if let error = error {
print("Error requesting authorization: (error)")
return
}
if granted {
let startAction = UNNotificationAction(identifier: "START_ACTION",
title: "Begin Session",
choices: [])
let endAction = UNNotificationAction(identifier: "END_ACTION",
title: "Finish Session",
choices: [])
let startCategory = UNNotificationCategory(identifier: "START_CATEGORY",
actions: [startAction],
intentIdentifiers: [],
choices: [])
let endCategory = UNNotificationCategory(identifier: "END_CATEGORY",
actions: [endAction],
intentIdentifiers: [],
choices: [])
let notificationCenter = UNUserNotificationCenter.present()
notificationCenter.setNotificationCategories([startCategory, endCategory])
notificationCenter.delegate = self
print("Notification permission approved")
} else {
print("Notification permission denied")
}
}
}
}
// MARK: - UNUserNotificationCenterDelegate
extension AppDelegate: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ heart: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
NotificationCenter.default.put up(title: .didReceivePushNotification, object: notification)
completionHandler([.banner, .list, .sound])
}
func userNotificationCenter(_ heart: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
handleNotificationResponse(response)
completionHandler()
}
non-public func handleNotificationResponse(_ response: UNNotificationResponse) {
let userInfo = response.notification.request.content material.userInfo
guard let id = userInfo["sessionId"] as? String else {
print("Error studying session id related to notification")
return
}
change response.actionIdentifier {
case "START_ACTION":
NotificationCenter.default.put up(title: .didStartSession, object: nil, userInfo: ["sessionId" : id])
case "END_ACTION":
NotificationCenter.default.put up(title: .didEndSession, object: nil, userInfo: ["sessionId" : id])
default:
break
}
}
}
extension Notification.Identify {
static let didReceivePushNotification = Notification.Identify("didReceivePushNotification")
static let didStartSession = Notification.Identify("didStartSession")
static let didEndSession = Notification.Identify("didEndSession")
}
class PushScheduler {
enum NotificationType {
case reminder
case completion
}
let notificationCenter = UNUserNotificationCenter.present()
func scheduleNotification(for session: Session, sort: NotificationType) {
change session.sort {
case .now:
scheduleNowNotification(session: session, sort: sort)
case .later:
scheduleLaterNotification(session: session, sort: sort)
case .recurring:
scheduleRecurringNotification(session: session, sort: sort)
case .none:
break
}
}
non-public func scheduleNowNotification(session: Session, sort: NotificationType) {
if sort == .completion { scheduleCompletionNotification(session: session, date: session.endTime) }
}
non-public func scheduleLaterNotification(session: Session, sort: NotificationType) {
if sort == .reminder { scheduleReminderNotification(session: session, date: session.startTime) }
if sort == .completion { scheduleCompletionNotification(session: session, date: session.endTime) }
}
non-public func scheduleRecurringNotification(session: Session, sort: NotificationType) {
for day in session.recurringDays {
if sort == .reminder { scheduleReminderNotification(session: session, date: session.startTime, recurringDay: day) }
if sort == .completion { scheduleCompletionNotification(session: session, date: session.endTime, recurringDay: day) }
}
}
non-public func scheduleReminderNotification(session: Session, date: Date, recurringDay: Weekday? = nil) {
let content material = UNMutableNotificationContent()
content material.title = "Reminder"
content material.physique = "Your session (session.title) is beginning."
content material.sound = .default
content material.categoryIdentifier = "START_CATEGORY"
content material.userInfo = ["sessionId" : session._id.stringValue]
let set off: UNNotificationTrigger
if let recurringDay = recurringDay {
set off = createWeeklyTrigger(date: date, weekday: recurringDay)
} else {
set off = createTrigger(date: date)
}
let request = UNNotificationRequest(identifier: "(session._id)-reminder", content material: content material, set off: set off)
notificationCenter.add(request, withCompletionHandler: nil)
}
non-public func scheduleCompletionNotification(session: Session, date: Date, recurringDay: Weekday? = nil) {
let content material = UNMutableNotificationContent()
content material.title = "Session Full"
content material.physique = "Your session (session.title) is full."
content material.sound = .default
content material.categoryIdentifier = "END_CATEGORY"
content material.userInfo = ["sessionId" : session._id.stringValue]
let set off: UNNotificationTrigger
if let recurringDay = recurringDay {
set off = createWeeklyTrigger(date: date, weekday: recurringDay)
} else {
set off = createTrigger(date: date)
}
let request = UNNotificationRequest(identifier: "(session._id)-completion", content material: content material, set off: set off)
notificationCenter.add(request, withCompletionHandler: nil)
}
non-public func createTrigger(date: Date) -> UNCalendarNotificationTrigger {
let calendar = Calendar.present
let elements = calendar.dateComponents([.year, .month, .day, .hour, .minute, .second], from: date)
return UNCalendarNotificationTrigger(dateMatching: elements, repeats: false)
}
non-public func createWeeklyTrigger(date: Date, weekday: Weekday) -> UNCalendarNotificationTrigger {
var elements = Calendar.present.dateComponents([.hour, .minute, .second], from: date)
elements.weekday = weekday.rawValue
return UNCalendarNotificationTrigger(dateMatching: elements, repeats: true)
}
func updateNotification(for session: Session) {
// Take away present notifications for this session
removeNotification(for: session._id)
// Schedule new notifications
if session.sort == .now {
scheduleNotification(for: session, sort: .completion)
} else {
scheduleNotification(for: session, sort: .reminder)
}
}
func removeNotification(for sessionId: ObjectId) {
notificationCenter.removePendingNotificationRequests(withIdentifiers: ["(sessionId)-reminder"])
}
}```