Formerly via blocks, now with final classes.
App:
- ProfileProcessor
- AppTunnelProcessor
- Implemented by DefaultAppProcessor in app
- Implemented by MockAppProcessor in UILibrary (for previews)
Tunnel:
- PacketTunnelProcessor
- Implemented by DefaultTunnelProcessor
Simplify preferences model by doing a bulk load/save together with
load/save Profile. ModulePreferences is now a struct rather than an
ObservableObject, because it doesn't need ad hoc observation. It's just
a binding to ProfileEditor.preferences
Fix:
- Disable CloudKit in tunnel singleton of PreferencesManager
(.sharedForTunnel)
Additionally:
- Replace MainActor in PreferencesManager with Sendable (immutable)
- Replace MainActor from ProviderPreferencesRepository with Sendable
(syncs on NSManagedObjectContext)
- Drop ModuleMetadata for good
Replace favorites entities with a PreferencesManager, that returns
observables for:
- Module preferences (by module UUID)
- Provider preferences (by ProviderID)
Automate preferences availability in:
- Module views (empty for now)
- VPN server view (favorites)
Synchronize preferences by making this a CloudKit container. Preferences
are also available in the Tunnel by storing the container in the App
Group.
Clarify the use of contexts:
- **Production** (.shared)
- **Previews** (.mock → .forPreviews)
- ONLY use it in UILibrary for, well, previews
- This context has dumb profiles with UUIDs as names
- Registry is fake
- **UI Tests** (.forUITesting)
- Add new context for UI testing
- Selected based on command line arguments
- This context has mock data tuned for decent screenshots
- Registry is real
Share the same InAppProcessor in .shared and .forTesting contexts
because the app behavior was inconsistent regarding e.g. in-app
purchases.
Create UITesting target with:
- AppCommandLine/AppEnvironment: strongly typed refactoring of PP_*
environment values
- AccessibilityInfo: identifies and locates elements for UI testing
Make the app behave differently when launched with `.uiTesting`, and
expose the flag to SwiftUI via `.environment(\.isUITesting)` to:
- Use the mock AppContext
- Skip onboarding
Add PassepartoutUITests target with two screenshot tests:
- Connected screen
- Profile modal
Optimize ProfileManager in several ways:
- Refine control over objectWillChange
- Observe search separately
- Store subscriptions separately (local, remote, search)
- Fix multiple local updates on save/remove/foreground (updating
allProfiles manually)
- Update the library with more optimized NE reloads
- Cancel pending remote import before a new one
- Yield 100ms between imports
- Reorganize code
Extras:
- Only use background context in provider repositories
- Externalize tunnel receipt URL, do not hardcode BundleConfiguration
- Improve some logging
Self-reminder: NEVER use a Core Data background context to observe
changes in CloudKit containers. They just won't be notified (e.g. in
NSFetchedResultsController).
Fixes#857
- Drop the .importing / .imported steps
- Animate rows re-sorting during process
- Rephrase some strings better
- Test fake migration with launch argument
- Define separate IAPManager instances for app and tunnel (different
receipt URLs)
- Copy app receipt URL over to tunnel before install/connect
- Use AppTransaction to get original build number so that
FallbackReceiptReader is also much simpler now
Fixes#869
Restore .sharing feature:
- Merge "Apple TV" into "iCloud" section
- "Enabled", disabled if ineligible for .sharing
- "Apple TV", disabled if ineligible for .appleTV || !isShared
- Footer about TV restrictions
Paywalls:
- "Share on iCloud" if ineligible for .sharing
- "Drop TV restriction" if eligible for .sharing but not for .appleTV
- Applies to full version products (user level 2)
- Suggest Apple TV product
Restrictions:
- Toggle CloudKit sync on remote repository based on .sharing
eligibility
- Do not start tunnel on Apple TV if ineligible for .appleTV
Fixes:
- Incorrect zip() publishers in remote repository
- Resolve duplicates in Core Data, first profile wins sorted by
lastUpdate descending
- Reload receipt on OOB IAPManager events