Commit 4f441dbc04
Verified · cmc
Layout: unified · split
gitbay/Push/AppDelegate.swift +36 −2
| @@ -14,6 +14,19 @@ final class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCent | ||
| 14 | 14 | weak var router: PushRouter? |
| 15 | 15 | weak var session: SessionStore? |
| 16 | 16 | |
| 17 | /// A tap that arrived before `gitbayApp` wired this delegate up. | |
| 18 | /// | |
| 19 | /// This is the common case, not an edge one: tapping a notification | |
| 20 | /// while the app is not running launches it, and iOS can deliver | |
| 21 | /// the tap before SwiftUI's root `.task` has run. Held as the raw | |
| 22 | /// `userInfo` rather than a resolved `PushTarget`, because resolving | |
| 23 | /// one needs `session.accounts` — precisely what is missing at that | |
| 24 | /// moment. | |
| 25 | /// | |
| 26 | /// One slot, newest wins. A queue would open several screens in | |
| 27 | /// sequence, which is not what a tap asks for. | |
| 28 | private var pendingUserInfo: [AnyHashable: Any]? | |
| 29 | ||
| 17 | 30 | func application( |
| 18 | 31 | _ application: UIApplication, |
| 19 | 32 | didFinishLaunchingWithOptions options: [UIApplication.LaunchOptionsKey: Any]? = nil |
| @@ -50,8 +63,29 @@ final class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCent | ||
| 50 | 63 | _ center: UNUserNotificationCenter, |
| 51 | 64 | didReceive response: UNNotificationResponse |
| 52 | 65 | ) async { |
| 53 | guard let session, let router, | |
| 54 | let payload = PushPayload(userInfo: response.notification.request.content.userInfo), | |
| 66 | deliver(response.notification.request.content.userInfo) | |
| 67 | } | |
| 68 | ||
| 69 | /// Drains a tap that arrived before the wiring did. Called from | |
| 70 | /// `gitbayApp`'s root task, once `session` and `router` are set. | |
| 71 | func deliverPendingNotification() { | |
| 72 | guard let userInfo = pendingUserInfo else { return } | |
| 73 | // Cleared whatever the resolve decides: a payload for an | |
| 74 | // account the user has since signed out of resolves to no | |
| 75 | // target, and must not sit here waiting to fire later. | |
| 76 | pendingUserInfo = nil | |
| 77 | deliver(userInfo) | |
| 78 | } | |
| 79 | ||
| 80 | /// The one resolve-and-deliver path, so a buffered tap and a live | |
| 81 | /// one cannot diverge. Parks the payload if the wiring is not up | |
| 82 | /// yet; `deliverPendingNotification` comes back for it. | |
| 83 | private func deliver(_ userInfo: [AnyHashable: Any]) { | |
| 84 | guard let session, let router else { | |
| 85 | pendingUserInfo = userInfo | |
| 86 | return | |
| 87 | } | |
| 88 | guard let payload = PushPayload(userInfo: userInfo), | |
| 55 | 89 | let target = PushTarget(payload: payload, accounts: session.accounts) |
| 56 | 90 | else { return } |
| 57 | 91 | router.pending = target |
gitbay/gitbayApp.swift +3
| @@ -40,6 +40,9 @@ struct gitbayApp: App { | ||
| 40 | 40 | session.willRemoveAccount = { [weak registrar] account in |
| 41 | 41 | await registrar?.deregister(account) |
| 42 | 42 | } |
| 43 | // A tap that launched the app reaches the delegate | |
| 44 | // before this runs; it parked the payload. | |
| 45 | appDelegate.deliverPendingNotification() | |
| 43 | 46 | await registrar.registerIfAlreadyAuthorized() |
| 44 | 47 | } |
| 45 | 48 | } |