generated from StanfordSpezi/SpeziTemplateApplication
-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Handling Background Notifications (#40)
# *Handling Background Notifications* ## ♻️ Current situation & Problem - #31 - #36 Currently, Prisma is only configured to handle notifications in the foreground. Handling notifications requires writing timestamps to Firestore when notifications are received and opened on the user's device, but this was not possible with the current configuration of background notification handling due to Apple throttling the amount of requests per hour [(see this link)](https://developer.apple.com/documentation/usernotifications/pushing-background-updates-to-your-app). ## ⚙️ Release Notes As a workaround, we utilize the UNNotificationServiceExtension class to provide an entry-point for pushing updates to Firestore on the arrival of background notifications. Normally we would use this extension to modify the notification’s content or download content related to the extension, but we are taking advantage of the fact that we are allowed a 30 second period to do so, in order to write to Firestore. Doing so required [creating a shared keychain for cross-app authentication](https://firebase.google.com/docs/auth/ios/single-sign-on), allowing access of the auth state for Firebase between the application and the extension, utilized for authorizing writes to Firestore using the same instance of Firebase as the application upon notification arrival. The following additions/changes were made to the Prisma application: - Added the Notification Service Extension `PrismaPushNotificationsExtension/NotificationsService.swift` - `didReceive` implements functionality for writing to Firestore, using the logs collection path passed in the notification payload. Because the main application and app extension run independently of each other, we needed to introduce an authorization mechanism that is checked in the function before actually doing any writes. - We also added a keychain access group, allowing for keychain sharing between the main app and the extension. We subsequently implemented the authorization function `authorizeAccessGroupForCurrentUser()` which checks for the shared access group instance for a user, documented in `PrismaStandard.swift`. - This function is called within both the Home view and the AccountOnboarding flow, ensuring that a user will always need to be authorized and logged in in order to receive background notification updates and have those writable to Firestore. ## 📚 Documentation *Please ensure that you properly document any additions in conformance to [Spezi Documentation Guide](https://github.com/StanfordSpezi/.github/blob/main/DOCUMENTATIONGUIDE.md).* *You can use this section to describe your solution, but we encourage contributors to document your reasoning and changes using in-line documentation.* ## ✅ Testing *Please ensure that the PR meets the testing requirements set by CodeCov and that new functionality is appropriately tested.* *This section describes important information about the tests and why some elements might not be testable.* ## 📝 Code of Conduct & Contributing Guidelines By submitting creating this pull request, you agree to follow our [Code of Conduct](https://github.com/CS342/.github/blob/main/CODE_OF_CONDUCT.md) and [Contributing Guidelines](https://github.com/CS342/.github/blob/main/CONTRIBUTING.md): - [ x] I agree to follow the [Code of Conduct](https://github.com/CS342/.github/blob/main/CODE_OF_CONDUCT.md) and [Contributing Guidelines](https://github.com/CS342/.github/blob/main/CONTRIBUTING.md).
- Loading branch information
1 parent
581e524
commit 4939632
Showing
13 changed files
with
555 additions
and
20 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
<plist version="1.0"> | ||
<dict> | ||
<key>NSExtension</key> | ||
<dict> | ||
<key>NSExtensionPointIdentifier</key> | ||
<string>com.apple.usernotifications.service</string> | ||
<key>NSExtensionPrincipalClass</key> | ||
<string>$(PRODUCT_MODULE_NAME).NotificationService</string> | ||
</dict> | ||
</dict> | ||
</plist> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
|
||
This source file is part of the Stanford Prisma Application based on the Stanford Spezi Template Application project | ||
|
||
SPDX-FileCopyrightText: 2023 Stanford University | ||
|
||
SPDX-License-Identifier: MIT |
66 changes: 66 additions & 0 deletions
66
PrismaPushNotificationsExtension/NotificationService.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
// | ||
// This source file is part of the Stanford Prisma Application based on the Stanford Spezi Template Application project. | ||
// | ||
// SPDX-FileCopyrightText: 2023 Stanford University | ||
// | ||
// SPDX-License-Identifier: MIT | ||
// | ||
// This file implements an extension to the Notification Service class, which is used to upload timestamps to | ||
// Firestore on receival of background notifications. | ||
// | ||
// Created by Bryant Jimenez on 2/1/24. | ||
// | ||
|
||
import Firebase | ||
import FirebaseAuth | ||
import FirebaseCore | ||
import FirebaseFirestore | ||
import UserNotifications | ||
|
||
class NotificationService: UNNotificationServiceExtension { | ||
var contentHandler: ((UNNotificationContent) -> Void)? | ||
var bestAttemptContent: UNMutableNotificationContent? | ||
|
||
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { | ||
FirebaseApp.configure() | ||
|
||
let accessGroup = "637867499T.edu.stanford.cs342.2024.behavior" | ||
do { | ||
try Auth.auth().useUserAccessGroup(accessGroup) | ||
} catch let error as NSError { | ||
print("Error changing user access group: %@", error) | ||
} | ||
|
||
self.contentHandler = contentHandler | ||
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) | ||
if let bestAttemptContent = bestAttemptContent { | ||
let path = request.content.userInfo["logs_path"] as? String ?? "" | ||
let receivedTimestamp = Date().toISOFormat(timezone: TimeZone(abbreviation: "UTC")) | ||
Firestore.firestore().document(path).setData(["received": receivedTimestamp], merge: true) | ||
contentHandler(bestAttemptContent) | ||
} | ||
} | ||
|
||
override func serviceExtensionTimeWillExpire() { | ||
// Called just before the extension will be terminated by the system. | ||
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used. | ||
if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent { | ||
contentHandler(bestAttemptContent) | ||
} | ||
} | ||
} | ||
|
||
extension Date { | ||
/// converts Date object to ISO Format string. Can optionally pass in a time zone to convert it to. | ||
/// If no timezone is passed, it converts the Date object using the local time zone. | ||
func toISOFormat(timezone: TimeZone? = nil) -> String { | ||
let formatter = ISO8601DateFormatter() | ||
formatter.formatOptions = [.withFullDate, .withTime, .withColonSeparatorInTime, .withFractionalSeconds] | ||
if let timezone = timezone { | ||
formatter.timeZone = timezone | ||
} else { | ||
formatter.timeZone = TimeZone.current | ||
} | ||
return formatter.string(from: self) | ||
} | ||
} |
10 changes: 10 additions & 0 deletions
10
PrismaPushNotificationsExtension/PrismaPushNotificationsExtension.entitlements
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
<plist version="1.0"> | ||
<dict> | ||
<key>keychain-access-groups</key> | ||
<array> | ||
<string>637867499T.edu.stanford.cs342.2024.behavior</string> | ||
</array> | ||
</dict> | ||
</plist> |
6 changes: 6 additions & 0 deletions
6
PrismaPushNotificationsExtension/PrismaPushNotificationsExtension.entitlements.license
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
|
||
This source file is part of the Stanford Prisma Application based on the Stanford Spezi Template Application project | ||
|
||
SPDX-FileCopyrightText: 2023 Stanford University | ||
|
||
SPDX-License-Identifier: MIT |