Important
Please support the developer! Flow is an excellent focus timer built with care. If you find it useful, buy it. This project exists for educational purposes and personal use.
Patches Flow to enable Pro features, disable telemetry, and preserve user data from the App Store installation.
| Flow Version | macOS | Status | Date |
|---|---|---|---|
| 4.6.1 | Tahoe 26.x | Working | 2026-02 |
| 4.7.0 | Tahoe 26.x | Working | 2026-03 |
Note
Flow is distributed exclusively via the Mac App Store. Apple does not
provide an archive of previous versions, so only the current release
can be tested at any given time. If Flow updates and the patch breaks,
run just probe (or flow-patcher probe) to diagnose the issue.
just patch # patches /Applications/Flow.app by default
open ~/Applications/Flow.app # launch the patched copyThe original App Store installation is never modified. The patcher copies
Flow.app to ~/Applications/, applies the patch there, and migrates your
data from the sandboxed container.
# Default (assumes /Applications/Flow.app):
just patch
# Or specify source explicitly:
just patch APP=/path/to/Flow.app
# Without just:
uv run flow-patcher patch /Applications/Flow.appThis will:
- Copy
Flow.appto~/Applications/ - Compile and inject
FlowPatch.dylib - Ad-hoc re-sign the app
- Migrate CoreData, preferences, and settings from the sandboxed container
just restore # removes ~/Applications/Flow.app
uv run flow-patcher restore Flow.appThis deletes the patched copy from ~/Applications/. The original in
/Applications/ is untouched.
FlowPatch.dylib is loaded at launch via an injected LC_LOAD_DYLIB command.
It hooks several runtime entry points:
| Hook | Effect |
|---|---|
NSUserDefaults.boolForKey: |
Returns YES for isProSubscriptionActive |
NSUserDefaults.objectForKey: |
Intercepts reads of RevenueCat's purchaserInfo cache and injects a fake pro entitlement before the SDK parses it |
NSUserDefaults.set*ForKey: |
Forces YES on writes to the pro key; injects entitlement into purchaserInfo writes; blocks deletion of the pro key |
RCEntitlementInfo.isActive |
Returns YES |
NSPersistentContainer.loadPersistentStores |
Injects NSPersistentHistoryTrackingKey to prevent CoreData read-only mode |
Firebase (FIRAnalytics, GDTCORTransport, …) |
No-ops event logging, heartbeats, and data transport (deferred to post-launch so Firestore can initialize) |
NSApplicationWillTerminateNotification |
Forces clean exit after 100ms to bypass gRPC 10-second shutdown hang |
flow_patcher/
__init__.py
cli.py Command-line interface (patch / restore / probe)
inject.py Mach-O LC_LOAD_DYLIB injector
patch_dylib.m ObjC dylib source (the actual hooks)
probe.py Flow.app compatibility checker
frida/
script.js Frida hook used during initial reverse engineering
diag.js Diagnostic disassembler for identifying call sites
example.py Frida loader script
tests/
conftest.py Pytest fixtures
test_cli.py CLI command and helper tests
test_cli_probe.py CLI probe command tests
test_dylib.m Standalone dylib tests
test_dylib_hooks.py Tests for dylib hooks
test_inject.py Mach-O injector tests (synthetic binaries)
test_integration.py Integration tests with live Flow.app
test_probe.py Probe logic tests
compatibility.json Known-good configurations for probe
EXECUTION_FLOW.md Detailed binary execution flow documentation
justfile Task runner shortcuts
pyproject.toml uv/pip project metadata
README.md This file
Check compatibility with your installed version of Flow before patching:
# Human-readable check
just probe # or: uv run flow-patcher probe /Applications/Flow.app
# JSON output
just probe --json
# Save current compatibility baseline
just probe --saveThis verifies:
- Mach-O header padding available for injection
- Existence of required ObjC classes (RevenueCat, Firebase, etc.)
- Expected selectors for swizzling
# Run checks (lint, format, mypy, tests)
just check
# Fix lint and format issues
just fix
# Run unit tests (fast, no Flow.app required)
just test
# Run integration tests (requires /Applications/Flow.app)
just test-integration- The patched app runs unsandboxed — it reads preferences and CoreData from
~/Library/instead of~/Library/Containers/. Data is migrated automatically on first patch. - Re-running
just patchre-copies from the original and re-applies everything. Your settings in~/Library/Preferences/are preserved. - If Flow updates in the App Store, just run
just patchagain.
- Fragility: The patch injects a fake entitlement into RevenueCat's cached JSON blob. If RevenueCat changes their schema (e.g., renaming the "subscriber" key), the patch may silently fail to unlock Pro features.
- Updates: This patch is version-specific. While it works for minor updates, major Flow updates might change class names or obfuscation, requiring updates to
flow-patcher. Always runjust probebefore patching a new version.