From e23bd97ea1b0c9b42b38c41895061c96f39bfdab Mon Sep 17 00:00:00 2001 From: Jamil Date: Thu, 13 Feb 2025 15:57:58 -0800 Subject: [PATCH] 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. --- .../Views/UpdateNotification.swift | 21 ++++++++++++++----- website/src/components/Changelog/Apple.tsx | 7 ++++++- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/swift/apple/FirezoneKit/Sources/FirezoneKit/Views/UpdateNotification.swift b/swift/apple/FirezoneKit/Sources/FirezoneKit/Views/UpdateNotification.swift index 8a01afa13..067d12375 100644 --- a/swift/apple/FirezoneKit/Sources/FirezoneKit/Views/UpdateNotification.swift +++ b/swift/apple/FirezoneKit/Sources/FirezoneKit/Views/UpdateNotification.swift @@ -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 diff --git a/website/src/components/Changelog/Apple.tsx b/website/src/components/Changelog/Apple.tsx index ebafa40c6..0613bef09 100644 --- a/website/src/components/Changelog/Apple.tsx +++ b/website/src/components/Changelog/Apple.tsx @@ -19,7 +19,12 @@ export default function Apple() { return ( {/* 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. */} - + + + Fixes a rare crash that could occur when dismissing the update + available notification. + + Fixes a minor memory leak that could occur after being unexpectedly