Commit c091e39943
Verified · cmc
Layout: unified · split
docs/superpowers/specs/2026-09-20-push-notifications-design.md +16 −5
| @@ -105,9 +105,14 @@ call rather than being duplicated; a repository root or an unknown kind | ||
| 105 | 105 | yields no route, and such a tap opens the app without navigating. |
| 106 | 106 | |
| 107 | 107 | `PushRouter` is owned by `gitbayApp` beside `SessionStore`. That |
| 108 | placement is the design: `ContentView` is `.id(account.id)`, so | |
| 109 | anything held inside it is destroyed by the account switch a | |
| 110 | cross-account tap performs. | |
| 108 | placement is the design: the `TabView` inside `ContentView` carries | |
| 109 | `.id(account.id)`, so a cross-account tap tears that subtree down and | |
| 110 | rebuilds it. A target held inside would go with it; held above, the | |
| 111 | rebuilt tree drains it. | |
| 112 | ||
| 113 | Note the `.id` is on the `TabView`, not on `ContentView` itself — | |
| 114 | `ContentView`'s own `@State` survives an account switch, which is why | |
| 115 | the Dashboard path has to be reset explicitly rather than by identity. | |
| 111 | 116 | |
| 112 | 117 | ## Registration |
| 113 | 118 | |
| @@ -225,8 +230,14 @@ an APNs token, and it does so itself. | ||
| 225 | 230 | - The **Push Notifications** capability on `org.gitbay.gitbay`, already |
| 226 | 231 | enabled in the developer portal, needs its matching entitlement in |
| 227 | 232 | the Xcode project. |
| 228 | - The **privacy nutrition label** gains the device token under | |
| 229 | Identifiers: not linked to the user, not used for tracking. | |
| 233 | - The **privacy nutrition label** does not gain the device token. It is | |
| 234 | per-app, it rotates, it is not the advertising identifier, and it is | |
| 235 | not used for tracking under any reading Apple's rules care about — | |
| 236 | nothing is shared with a data broker and nothing is linked to | |
| 237 | third-party data. Decided, not skipped; do not add it on a later pass | |
| 238 | without raising it again. Note the token *is* stored against the | |
| 239 | account (`push_devices.user_id`), so if it were ever declared it would | |
| 240 | be "linked to the user", not unlinked. | |
| 230 | 241 | - An App Store resubmission. If Apple asks what push is for, it is |
| 231 | 242 | activity on repositories the account follows. |
| 232 | 243 | |
gitbay/Push/PushRouter.swift +10 −4
| @@ -2,10 +2,16 @@ import Observation | ||
| 2 | 2 | |
| 3 | 3 | /// Carries a tapped notification's destination into the view tree. |
| 4 | 4 | /// |
| 5 | /// Owned by `gitbayApp`, above `ContentView` — which is `.id(account.id)` | |
| 6 | /// and is therefore destroyed and rebuilt by the account switch a | |
| 7 | /// cross-account tap performs. A target held inside that subtree would | |
| 8 | /// go with it; held here, the rebuilt tree drains it on appear. | |
| 5 | /// Owned by `gitbayApp`, above `ContentView`. The `TabView` inside | |
| 6 | /// `ContentView` carries `.id(account.id)`, so the account switch a | |
| 7 | /// cross-account tap performs tears that subtree down and rebuilds it. | |
| 8 | /// A target held inside would go with it; held here, the rebuilt tree | |
| 9 | /// drains it. | |
| 10 | /// | |
| 11 | /// The `.id` is on the `TabView`, not on `ContentView` itself, so | |
| 12 | /// `ContentView`'s own `@State` survives the switch — which is why the | |
| 13 | /// Dashboard path is reset explicitly in the drain rather than by | |
| 14 | /// identity. | |
| 9 | 15 | @Observable |
| 10 | 16 | @MainActor |
| 11 | 17 | final class PushRouter { |