Skip to content

v0.10.2 — Permission UX & TCC stability

Pre-release
Pre-release

Choose a tag to compare

@hoangperry hoangperry released this 26 May 11:29

Permission UX & TCC stability

A user-reported pain point: every Sparkle upgrade felt like macOS was clearing the app's Accessibility grant — translate hotkeys went silent until you manually re-granted, with no in-app surface telling you what happened.

v0.10.2 fixes the user experience and hardens the signature so the underlying TCC issue is much less likely to recur in the first place.

Added — Auto-recovery onboarding

  • Permission-loss detection on launchAppDelegate reads the persisted lastKnownAccessibilityGranted flag before refreshing the live grant, so the true→false transition that signals a TCC reset is detectable. When detected, the onboarding window pops automatically in recovery mode with copy that acknowledges you already did this once.
  • OnboardingMode.permissionRecovery swaps the title + intro: "Welcome back. macOS cleared the app's Accessibility grant after the recent update — translate hotkeys are silent until you re-grant. Click Request below, or Open Settings if the prompt does not appear."
  • Hotkeys still register in recovery mode so any grants that survived (e.g. Input Monitoring) keep working — the onboarding pops on top so the issue cannot be missed.

Hardened — Codesign stability

  • --identifier "app.lookerlab.translator" is now passed explicitly to the main-app codesign call instead of being inferred from CFBundleIdentifier. The value baked into the signature is byte-stable across releases. Verified post-build: v0.10.0, v0.10.1, and v0.10.2 all sign with byte-identical designated requirements, so TCC should persist the grant across upgrade.

Internal — PermissionManager testability

  • Constructor accepts injected accessibilityProbe / inputMonitoringProbe / requestAccessibilityAction / requestInputMonitoringAction closures so unit tests exercise grant-sync wiring without touching the real TCC database.
  • refresh() writes the live grant back to SettingsStore.lastKnownAccessibilityGranted (guarded by didSet so the 1-second polling loop does not spam UserDefaults).
  • refreshLater() settle delay bumped 1s → 2s — gives the TCC database room after the user clicks "Allow".

Tests

  • +11 new tests — 6 in PermissionManagerTests (grant-sync, init non-mutation, request action, nil-settings), 5 in LaunchRecoveryDecisionTreeTests (fresh / steady / revoked / never-granted / lost-then-regranted).
  • 388 tests pass (was 377).

Risk-free upgrade

UserDefaults adds one new key (translator.lastKnownAccessibilityGranted) defaulting to false on existing installs — so the recovery flow does not fire spuriously on the v0.10.1 → v0.10.2 upgrade itself. The first refresh writes the current live state forward; from v0.10.3 onwards the recovery signal is armed end-to-end.

Install

  • Sparkle — existing installs receive this automatically via the appcast at https://hoangperry.github.io/hp-translator/appcast.xml.
  • Fresh install — download Contextual-Mac-Translator-v0.10.2-macos-arm64.zip below, unzip, drag the .app to /Applications. Signed (Developer ID JP4XV8A9JF) + notarized + stapled.

Apple Silicon only. macOS 14 or later.