fix: stop the permission-required screen flashing at launch when access is granted - #54
Merged
Merged
Conversation
…s granted FamilyControls loads authorization asynchronously and reports a stale `.notDetermined` for the first moments after a cold launch, even for a user who granted Screen Time access. `RootView` read that stale value once at init and only corrected it on the scene becoming active, so it rendered `ScreenTimeAccessRequiredView` for a frame or two before flipping to `MainView` — the visible flicker. Distinguish "not yet resolved" from "resolved and not approved": - `ScreenTimeAuthorization` gains `hasResolvedStatus` and `resolveAtLaunch()`, which re-reads the status while it stays `.notDetermined`, giving the framework a brief bounded window to settle before giving up. A definitive `.approved`/`.denied` resolves immediately (no wait, no splash). - `RootDestination` routes a not-yet-approved, not-yet-resolved status to a new `.resolvingAuthorization` case instead of the access-required screen. - `RootView` shows a neutral `LaunchResolvingView` (the app logo) while resolving and calls `resolveAtLaunch()` in `.task` at launch. `.approved` is still trusted immediately — stale reads under-report as `.notDetermined`, they never falsely approve — so granted access goes straight to `MainView` with no flash. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…mined Treat .notDetermined as .main instead of holding on a resolving screen, so the common launch (access granted, reported as a stale .notDetermined while FamilyControls loads) shows the real app with no flicker. Only .denied now routes to the access-required screen; a genuinely revoked user may see .main briefly before the status settles to .denied — an accepted rare-case flicker. Removes the now-unneeded resolving machinery: the .resolvingAuthorization case, LaunchResolvingView, and ScreenTimeAuthorization.hasResolvedStatus. resolveAtLaunch() stays to settle a revoked user's .denied promptly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When launching the app with Screen Time access already granted, the
ScreenTimeAccessRequiredViewflashes briefly before landing onMainView.Root cause
FamilyControls loads authorization asynchronously and reports a stale
.notDeterminedfor the first moments after a cold launch — even for a user who granted access (Apple Developer Forums 747915).RootViewcaptured that stale value once at init (ScreenTimeAuthorization.init) and only corrected it when the scene became active, soRootDestination.resolvemapped the transient non-.approvedstatus to.screenTimeAccessRequiredfor a frame or two before flipping to.main— the visible flicker.Onboarding only completes on
.approved, so a post-onboarding.notDeterminedat launch is almost always this stale value settling to.approved.Fix
Treat
.notDeterminedas.main. Only.deniedroutes to.screenTimeAccessRequired:RootDestination.resolvereturns.mainfor every post-onboarding status except.denied. The overwhelmingly common launch (access already granted, momentarily reported as a stale.notDetermined) shows the real app immediately, with no flash of the access-required screen. A genuinely revoked user may see.mainbriefly at launch before the status settles to.deniedand flips them to the access-required screen — a rare case where a momentary flicker is acceptable.ScreenTimeAuthorization.resolveAtLaunch()stays: it re-reads while the status is.notDetermined(bounded ~500ms) so a revoked user's.deniedsurfaces promptly rather than waiting for the next foreground..resolvingAuthorizationcase,LaunchResolvingView, andScreenTimeAuthorization.hasResolvedStatus.Test plan
RootDestinationTests— non-.deniedpost-onboarding statuses (.approved,.notDetermined) route to.main;.deniedroutes to the access-required screen; onboarding-incomplete always routes to onboarding.ScreenTimeAuthorizationTests—resolveAtLaunchsettles past a stale launch-time.notDeterminedto a revoked user's real.denied; gives up gracefully when the status never settles.🤖 Generated with Claude Code