Skip to content

Commit

Permalink
Additional notification content, cancel already scheduled notificatio…
Browse files Browse the repository at this point in the history
…ns of legacy scheduler, use background tasks for updating scheduled notifications
  • Loading branch information
Supereg committed Sep 16, 2024
1 parent 036e52a commit 0d8fb4d
Show file tree
Hide file tree
Showing 11 changed files with 507 additions and 153 deletions.
12 changes: 0 additions & 12 deletions Sources/SpeziScheduler/Constants.swift

This file was deleted.

22 changes: 22 additions & 0 deletions Sources/SpeziScheduler/Notifications/BackgroundMode.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// This source file is part of the Stanford Spezi open-source project
//
// SPDX-FileCopyrightText: 2024 Stanford University and the project authors (see CONTRIBUTORS.md)
//
// SPDX-License-Identifier: MIT
//


struct BackgroundMode {
static let processing = BackgroundMode(rawValue: "processing")
static let fetch = BackgroundMode(rawValue: "fetch")

let rawValue: String

init(rawValue: String) {
self.rawValue = rawValue
}
}


extension BackgroundMode: RawRepresentable, Codable, Hashable, Sendable {}
69 changes: 69 additions & 0 deletions Sources/SpeziScheduler/Notifications/Event+Notifications.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//
// This source file is part of the Stanford Spezi open-source project
//
// SPDX-FileCopyrightText: 2024 Stanford University and the project authors (see CONTRIBUTORS.md)
//
// SPDX-License-Identifier: MIT
//

import Foundation
import Spezi
@preconcurrency import UserNotifications

Check warning on line 11 in Sources/SpeziScheduler/Notifications/Event+Notifications.swift

View workflow job for this annotation

GitHub Actions / Build and Test Swift Package visionOS / Test using xcodebuild or run fastlane

'@preconcurrency' attribute on module 'UserNotifications' has no effect

Check warning on line 11 in Sources/SpeziScheduler/Notifications/Event+Notifications.swift

View workflow job for this annotation

GitHub Actions / Build and Test Swift Package visionOS / Test using xcodebuild or run fastlane

'@preconcurrency' attribute on module 'UserNotifications' has no effect

Check warning on line 11 in Sources/SpeziScheduler/Notifications/Event+Notifications.swift

View workflow job for this annotation

GitHub Actions / Build and Test Swift Package visionOS / Test using xcodebuild or run fastlane

'@preconcurrency' attribute on module 'UserNotifications' has no effect

Check warning on line 11 in Sources/SpeziScheduler/Notifications/Event+Notifications.swift

View workflow job for this annotation

GitHub Actions / Build and Test UI Tests / Test using xcodebuild or run fastlane

'@preconcurrency' attribute on module 'UserNotifications' has no effect

Check warning on line 11 in Sources/SpeziScheduler/Notifications/Event+Notifications.swift

View workflow job for this annotation

GitHub Actions / Build and Test UI Tests / Test using xcodebuild or run fastlane

'@preconcurrency' attribute on module 'UserNotifications' has no effect

Check warning on line 11 in Sources/SpeziScheduler/Notifications/Event+Notifications.swift

View workflow job for this annotation

GitHub Actions / Build and Test UI Tests / Test using xcodebuild or run fastlane

'@preconcurrency' attribute on module 'UserNotifications' has no effect


// TODO: eventually move somewhere else!

Check failure on line 14 in Sources/SpeziScheduler/Notifications/Event+Notifications.swift

View workflow job for this annotation

GitHub Actions / SwiftLint / SwiftLint / SwiftLint

Todo Violation: TODOs should be resolved (eventually move somewhere else...) (todo)
extension Event {
func scheduleNotification( // swiftlint:disable:this function_default_parameter_at_end
isolation: isolated (any Actor)? = #isolation,
notifications: LocalNotifications
) async throws {
let isAllDay = occurrence.schedule.duration.isAllDay

let content = UNMutableNotificationContent()
content.title = String(localized: task.title) // TODO: check, localization might change!

Check failure on line 23 in Sources/SpeziScheduler/Notifications/Event+Notifications.swift

View workflow job for this annotation

GitHub Actions / SwiftLint / SwiftLint / SwiftLint

Todo Violation: TODOs should be resolved (check, localization might chan...) (todo)
// TODO: there is otherwise localizedUserNotificationString(forKey:arguments:)

Check failure on line 24 in Sources/SpeziScheduler/Notifications/Event+Notifications.swift

View workflow job for this annotation

GitHub Actions / SwiftLint / SwiftLint / SwiftLint

Todo Violation: TODOs should be resolved (there is otherwise localizedUs...) (todo)
content.body = String(localized: task.instructions)
// TODO: instructions might be longer! specify notification specific description?

Check failure on line 26 in Sources/SpeziScheduler/Notifications/Event+Notifications.swift

View workflow job for this annotation

GitHub Actions / SwiftLint / SwiftLint / SwiftLint

Todo Violation: TODOs should be resolved (instructions might be longer! ...) (todo)

// TODO: support: subtitle, sound, attachments??, userInfo?, relevanceScore, filterCriteria (focus)

Check failure on line 28 in Sources/SpeziScheduler/Notifications/Event+Notifications.swift

View workflow job for this annotation

GitHub Actions / SwiftLint / SwiftLint / SwiftLint

Todo Violation: TODOs should be resolved (support: subtitle, sound, atta...) (todo)
// TODO: targetContentIdentifier (which application window to bring forward)

Check failure on line 29 in Sources/SpeziScheduler/Notifications/Event+Notifications.swift

View workflow job for this annotation

GitHub Actions / SwiftLint / SwiftLint / SwiftLint

Todo Violation: TODOs should be resolved (targetContentIdentifier (which...) (todo)

if let category = task.category {
// TODO: allow to derive the category easily (adding custom actions is possible!!) => providing custom notifications UI!

Check failure on line 32 in Sources/SpeziScheduler/Notifications/Event+Notifications.swift

View workflow job for this annotation

GitHub Actions / SwiftLint / SwiftLint / SwiftLint

Todo Violation: TODOs should be resolved (allow to derive the category e...) (todo)
content.categoryIdentifier = SchedulerNotifications.notificationCategory(for: category)
}

if !isAllDay {
content.interruptionLevel = .timeSensitive // TODO: document required entitlement!
}

content.userInfo[SchedulerNotifications.notificationTaskIdKey] = task.id

// TODO: make the grouping "approach" an option? (`notificationThread` global, task, custom).
content.threadIdentifier = SchedulerNotifications.notificationThreadIdentifier(for: task.id)

let start: Date
if isAllDay {
// default to 9am // TODO: customize that?
guard let morning = Calendar.current.date(bySettingHour: 9, minute: 0, second: 0, of: occurrence.start) else {
preconditionFailure("Failed to set hour of start date \(occurrence.start)")
}
start = morning
} else {
start = occurrence.start
}


// TODO: or what is generally a good way to customize the notification


let interval = start.timeIntervalSince(.now)

// TODO: set a different trigger for all day notifications (9 am?)
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: interval, repeats: false)

let request = UNNotificationRequest(identifier: SchedulerNotifications.notificationId(for: self), content: content, trigger: trigger)

try await notifications.add(request: request)
}
}
43 changes: 43 additions & 0 deletions Sources/SpeziScheduler/Notifications/LegacyTaskModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//
// This source file is part of the Stanford Spezi open-source project
//
// SPDX-FileCopyrightText: 2024 Stanford University and the project authors (see CONTRIBUTORS.md)
//
// SPDX-License-Identifier: MIT
//

import Foundation
import UserNotifications


/// Minimal model of the legacy event model to retrieve data to provide some interoperability with the legacy version.
struct LegacyEventModel {
let notification: UUID?
}


/// Minimal model of the legacy task model to retrieve data to provide some interoperability with the legacy version.
struct LegacyTaskModel {
let id: UUID
let notifications: Bool
let events: [LegacyEventModel]
}


extension LegacyEventModel: Decodable, Hashable, Sendable {}


extension LegacyTaskModel: Decodable, Hashable, Sendable {}


extension LegacyEventModel {
func cancelNotification() {
guard let notification else {
return
}

let center = UNUserNotificationCenter.current()
center.removeDeliveredNotifications(withIdentifiers: [notification.uuidString])
center.removePendingNotificationRequests(withIdentifiers: [notification.uuidString])
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// This source file is part of the Stanford Spezi open-source project
//
// SPDX-FileCopyrightText: 2024 Stanford University and the project authors (see CONTRIBUTORS.md)
//
// SPDX-License-Identifier: MIT
//


struct PermittedBackgroundTaskIdentifier {
static let speziSchedulerNotificationsScheduling = PermittedBackgroundTaskIdentifier(
rawValue: "edu.stanford.spezi.scheduler.notifications-scheduling"
)

let rawValue: String

init(rawValue: String) {
self.rawValue = rawValue
}
}


extension PermittedBackgroundTaskIdentifier: RawRepresentable, Hashable, Sendable, Codable {}
Loading

0 comments on commit 0d8fb4d

Please sign in to comment.