fix(apple): Persist last notified version (#8122)

Notifications on Apple platforms are delivered with best-effort
reliability and are not guaranteed.

They can also be queued up by the system so that, for example, it's
possible to issue a notification, quit the app, and then upon the next
launch of the app, receive the notification.

In this second case, if the user dismissed the notification, we will
crash. This is because we only track the `lastNotifiedVersion` in the
`NotificationAdapter` instance object and don't persist it to disk, then
we assert the value not to be nil when saving the user's `dismiss`
action.

To fix this, we persist the `lastNotifiedVersion` to the `UserDefaults`
store and attempt to read this when the user is dismissing the
notification. If we can't read it for some reason, we still dismiss the
notification but won't prevent showing it again on the next update
check.

A minor bug is also fixed where the original author didn't correctly
call the function's `completionHandler`. Also, unused instance vars
`lastDismissedVersion` left over from the original author are removed as
well.
This commit is contained in:
Jamil
2025-02-13 15:57:58 -08:00
committed by GitHub
parent 39cbf60ec8
commit e23bd97ea1
2 changed files with 22 additions and 6 deletions

View File

@@ -115,8 +115,6 @@ class UpdateChecker {
}
private class NotificationAdapter: NSObject, UNUserNotificationCenterDelegate {
private var lastNotifiedVersion: SemanticVersion?
private var lastDismissedVersion: SemanticVersion?
static let notificationIdentifier = "UPDATE_CATEGORY"
static let dismissIdentifier = "DISMISS_ACTION"
@@ -155,7 +153,7 @@ private class NotificationAdapter: NSObject, UNUserNotificationCenterDelegate {
func showUpdateNotification(version: SemanticVersion) {
let content = UNMutableNotificationContent()
lastNotifiedVersion = version
setLastNotifiedVersion(version: version)
content.title = "Update Firezone"
content.body = "New version available"
content.sound = .default
@@ -177,8 +175,12 @@ private class NotificationAdapter: NSObject, UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
if response.actionIdentifier == NotificationAdapter.dismissIdentifier {
setLastDismissedVersion(version: lastNotifiedVersion!)
if response.actionIdentifier == NotificationAdapter.dismissIdentifier { // User dismissed this notification
if let lastNotifiedVersion = getLastNotifiedVersion() { // Don't notify them again for this version
setLastDismissedVersion(version: lastNotifiedVersion)
}
completionHandler()
return
}
@@ -210,13 +212,22 @@ private class NotificationAdapter: NSObject, UNUserNotificationCenterDelegate {
}
private let lastDismissedVersionKey = "lastDismissedVersion"
private let lastNotifiedVersionKey = "lastNotifiedVersion"
private func setLastDismissedVersion(version: SemanticVersion) {
UserDefaults.standard.setValue(version, forKey: lastDismissedVersionKey)
}
private func setLastNotifiedVersion(version: SemanticVersion) {
UserDefaults.standard.setValue(version, forKey: lastNotifiedVersionKey)
}
private func getLastDismissedVersion() -> SemanticVersion? {
return UserDefaults.standard.object(forKey: lastDismissedVersionKey) as? SemanticVersion
}
private func getLastNotifiedVersion() -> SemanticVersion? {
return UserDefaults.standard.object(forKey: lastNotifiedVersionKey) as? SemanticVersion
}
#endif

View File

@@ -19,7 +19,12 @@ export default function Apple() {
return (
<Entries downloadLinks={downloadLinks} title="macOS / iOS">
{/* When you cut a release, remove any solved issues from the "known issues" lists over in `client-apps`. This must not be done when the issue's PR merges. */}
<Unreleased></Unreleased>
<Unreleased>
<ChangeItem pull="8122">
Fixes a rare crash that could occur when dismissing the update
available notification.
</ChangeItem>
</Unreleased>
<Entry version="1.4.2" date={new Date("2025-02-13")}>
<ChangeItem pull="8104">
Fixes a minor memory leak that could occur after being unexpectedly