소스 검색

Merge pull request #477 from nightscout/onboarding-permissions

Improve Onboarding Notification Permission Handling
Sam King 1 년 전
부모
커밋
07fe3bc26a

+ 6 - 0
Trio/Sources/Localizations/Main/Localizable.xcstrings

@@ -31272,6 +31272,9 @@
         }
       }
     },
+    "After completing onboarding, a red banner will appear on Trio's main screen to guide you to the iOS Settings app, where you can enable notifications." : {
+
+    },
     "Agree and continue" : {
       "extractionState" : "manual",
       "localizations" : {
@@ -154817,6 +154820,9 @@
         }
       }
     },
+    "Notifications for “Trio” are Disabled" : {
+
+    },
     "Notifications give you important Trio information without requiring you to open the app." : {
       "localizations" : {
         "bg" : {

+ 1 - 0
Trio/Sources/Modules/Onboarding/OnboardingStateModel.swift

@@ -142,6 +142,7 @@ extension Onboarding {
         // MARK: - Permission Requests
 
         var hasNotificationsGranted = false
+        var shouldDisplayCustomNotificationAlert: Bool = false
 
         var shouldDisplayBluetoothRequestAlert: Bool = false
         var hasBluetoothGranted = false

+ 17 - 4
Trio/Sources/Modules/Onboarding/View/OnboardingRootView.swift

@@ -310,7 +310,7 @@ struct OnboardingStepContent: View {
                         case .targetBehavior:
                             AlgorithmSettingsSubstepView(state: state, substep: currentTargetBehaviorSubstep)
                         case .notifications:
-                            NotificationPermissionStepView()
+                            NotificationPermissionStepView(state: state, currentStep: $currentStep)
                         case .bluetooth:
                             BluetoothPermissionStepView(
                                 state: state,
@@ -547,10 +547,23 @@ struct OnboardingNavigationButtons: View {
 
         case .notifications:
             currentTargetBehaviorSubstep = .halfBasalTarget
+
             if let next = currentStep.next {
-                DispatchQueue.main.async {
-                    state.notificationsManager.requestNotificationPermissions { granted in
-                        state.hasNotificationsGranted = granted
+                state.notificationsManager.getNotificationSettings { notificationSettings in
+                    switch notificationSettings.authorizationStatus {
+                    case .notDetermined:
+                        state.notificationsManager.requestNotificationPermissions { granted in
+                            state.hasNotificationsGranted = granted
+                            currentStep = next
+                        }
+                    case .denied:
+                        state.shouldDisplayCustomNotificationAlert = true
+                    case .authorized,
+                         .ephemeral,
+                         .provisional:
+                        currentStep = next
+                        break
+                    @unknown default:
                         currentStep = next
                     }
                 }

+ 30 - 0
Trio/Sources/Modules/Onboarding/View/OnboardingSteps/NotificationPermissionStepView.swift

@@ -8,6 +8,9 @@ import SwiftUI
 import UserNotifications
 
 struct NotificationPermissionStepView: View {
+    @Bindable var state: Onboarding.StateModel
+    var currentStep: Binding<OnboardingStep>
+
     var body: some View {
         VStack(alignment: .leading, spacing: 20) {
             Text("Allow Notifications")
@@ -64,5 +67,32 @@ struct NotificationPermissionStepView: View {
                 .foregroundColor(Color.secondary)
                 .padding(.top)
         }.padding(.horizontal)
+            .background(
+                SystemAlert(
+                    isPresented: $state.shouldDisplayCustomNotificationAlert,
+                    title: String(localized: "Notifications for “Trio” are Disabled"),
+                    message: String(
+                        localized: "After completing onboarding, a red banner will appear on Trio's main screen to guide you to the iOS Settings app, where you can enable notifications."
+                    ),
+                    allowTitle: String(localized: "Got it!"),
+                    denyTitle: String(localized: "Cancel"),
+                    onAllow: {
+                        DispatchQueue.main.async {
+                            state.shouldDisplayCustomNotificationAlert = false
+                            if let next = currentStep.wrappedValue.next {
+                                currentStep.wrappedValue = next
+                            }
+                        }
+                    },
+                    onDeny: {
+                        DispatchQueue.main.async {
+                            state.shouldDisplayCustomNotificationAlert = false
+                            if let next = currentStep.wrappedValue.next {
+                                currentStep.wrappedValue = next
+                            }
+                        }
+                    }
+                )
+            )
     }
 }

+ 18 - 18
Trio/Sources/Services/UserNotifications/UserNotificationsManager.swift

@@ -9,6 +9,7 @@ import UIKit
 import UserNotifications
 
 protocol UserNotificationsManager {
+    func getNotificationSettings(completionHandler: @escaping (UNNotificationSettings) -> Void)
     func requestNotificationPermissions(completion: @escaping (Bool) -> Void)
 }
 
@@ -61,7 +62,7 @@ final class BaseUserNotificationsManager: NSObject, UserNotificationsManager, In
 
     @Persisted(key: "UserNotificationsManager.snoozeUntilDate") private var snoozeUntilDate: Date = .distantPast
 
-    private let center = UNUserNotificationCenter.current()
+    private let notificationCenter = UNUserNotificationCenter.current()
     private var lifetime = Lifetime()
 
     private let viewContext = CoreDataStack.shared.persistentContainer.viewContext
@@ -77,7 +78,7 @@ final class BaseUserNotificationsManager: NSObject, UserNotificationsManager, In
 
     init(resolver: Resolver) {
         super.init()
-        center.delegate = self
+        notificationCenter.delegate = self
         injectServices(resolver)
 
         coreDataPublisher =
@@ -132,7 +133,7 @@ final class BaseUserNotificationsManager: NSObject, UserNotificationsManager, In
     private func addAppBadge(glucose: Int?) {
         guard let glucose = glucose, settingsManager.settings.glucoseBadge else {
             DispatchQueue.main.async {
-                self.center.setBadgeCount(0) { error in
+                self.notificationCenter.setBadgeCount(0) { error in
                     guard let error else {
                         return
                     }
@@ -150,7 +151,7 @@ final class BaseUserNotificationsManager: NSObject, UserNotificationsManager, In
         }
 
         DispatchQueue.main.async {
-            self.center.setBadgeCount(badge) { error in
+            self.notificationCenter.setBadgeCount(badge) { error in
                 guard let error else {
                     return
                 }
@@ -386,18 +387,17 @@ final class BaseUserNotificationsManager: NSObject, UserNotificationsManager, In
         return body
     }
 
-//    private func requestNotificationPermissionsIfNeeded() {
-//        center.getNotificationSettings { settings in
-//            debug(.service, "UNUserNotificationCenter.authorizationStatus: \(String(describing: settings.authorizationStatus))")
-//            if ![.authorized, .provisional].contains(settings.authorizationStatus) {
-//                self.requestNotificationPermissions()
-//            }
-//        }
-//    }
+    func getNotificationSettings(completionHandler: @escaping (UNNotificationSettings) -> Void) {
+        notificationCenter.getNotificationSettings { settings in
+            DispatchQueue.main.async {
+                completionHandler(settings)
+            }
+        }
+    }
 
     func requestNotificationPermissions(completion: @escaping (Bool) -> Void) {
         debug(.service, "requestNotificationPermissions")
-        center.requestAuthorization(options: [.badge, .sound, .alert]) { granted, error in
+        notificationCenter.requestAuthorization(options: [.badge, .sound, .alert]) { granted, error in
             if granted {
                 debug(.service, "requestNotificationPermissions was granted")
                 DispatchQueue.main.async {
@@ -432,8 +432,8 @@ final class BaseUserNotificationsManager: NSObject, UserNotificationsManager, In
             .title : (identifier == .alertMessageNotification ? alertIdentifier + content.body : alertIdentifier)
         if deleteOld {
             DispatchQueue.main.async {
-                self.center.removeDeliveredNotifications(withIdentifiers: [alertIdentifier])
-                self.center.removePendingNotificationRequests(withIdentifiers: [alertIdentifier])
+                self.notificationCenter.removeDeliveredNotifications(withIdentifiers: [alertIdentifier])
+                self.notificationCenter.removePendingNotificationRequests(withIdentifiers: [alertIdentifier])
             }
         }
         if alertPermissionsChecker.notificationsDisabled {
@@ -444,7 +444,7 @@ final class BaseUserNotificationsManager: NSObject, UserNotificationsManager, In
 
         let request = UNNotificationRequest(identifier: alertIdentifier, content: content, trigger: trigger)
         DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
-            self.center.add(request) { error in
+            self.notificationCenter.add(request) { error in
                 if let error = error {
                     warning(.service, "Unable to addNotificationRequest", error: error)
                     return
@@ -563,8 +563,8 @@ extension BaseUserNotificationsManager: pumpNotificationObserver {
     func pumpRemoveNotification() {
         let identifier: Identifier = .pumpNotification
         DispatchQueue.main.async {
-            self.center.removeDeliveredNotifications(withIdentifiers: [identifier.rawValue])
-            self.center.removePendingNotificationRequests(withIdentifiers: [identifier.rawValue])
+            self.notificationCenter.removeDeliveredNotifications(withIdentifiers: [identifier.rawValue])
+            self.notificationCenter.removePendingNotificationRequests(withIdentifiers: [identifier.rawValue])
         }
     }
 }