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
Finalize migration flow:
- Add entry to "Add" menu
- Suggest to migrate old profiles when there are no profiles
- Add informational message
- Keep included profiles on top
- Allow deletion of migratable profiles
- Fix duplicated Form in preview
- Rename views and models
Improve some Theme modifiers:
- Empty message with full screen option
- Progress modifier with custom view
- Confirmation dialog with custom message
For some reason, Table doesn't seem to inherit the environment in some
cases. Reapply environment to each TableColumn (only Theme is required).
Work around what clearly seems to be a SwiftUI bug.
Fixes#872
- 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
Otherwise, it would never import remote profiles w/o a fingerprint.
Scenarios (must test in #570):
- No local profile → Import
- Local profile has no fingerprint → Import
- Local profile has fingerprint
- Remote profile has no fingerprint → Skip
- Remote profile has same fingerprint → Skip
- Remote profile has different fingerprint → Import
The Library package offers the PassepartoutImplementations target for
OpenVPN/OpenSSL and WireGuard/Go, but it doesn't need it itself. Only
the main app does, so move the dependency there.
On the other side, drop the potentially problematic AppUI meta target.
Move platform filters to the Xcode project.
Indirectly fixes a crash with Xcode 16 Previews on iOS (forced to use
legacy previews before):
https://forums.developer.apple.com/forums/thread/756681
- Address further restrictions on actor-isolation by using `nonisolated`
on:
- Combine subjects
- Core Data context/controller
- Blocks
- In previews using inline `@State`, create a custom view instead
- Use `@retroactive` in l10n extensions
- Fix compile error in WireGuardKit
Move Core Data tests out of the Library package so that we can still use
the more efficient `swift test` for most tests.
Create a PassepartoutTests target only for tests that require
`xcodebuild`, like Core Data tests.
Eventually:
- PRs only run SwiftPM tests
- Releases run ALL tests with `scan` before `gym`
- Do not observe tunnel in grid/list
- Only observe .$currentProfile for grid selection
- Move row tunnel updates to MarkerView
- Debug InstalledProfileView
Regression in #839 due to how NSFetchedResultsController was refactored.
Duplicated entities were not excluded from mapping.
Could "crash" the app with these easy steps:
- Pick a profile
- Unshare the profile on iOS
- Unshare the profile on macOS
- Re-share the profile on iCloud on both iOS and macOS
- Save the profile simultaneously on iOS/macOS
- Assertion failure due to duplicates in
ProfileManager.reloadRemoteProfiles() → "Remote repository must not have
duplicates"
Loading remote profiles before local profiles may cause duplicated NE
managers. This happened because if local profiles are empty, any remote
profile is imported regardless of their former existence in the local
store. The importer just doesn't know.
Therefore, revisit the sequence of AppContext registrations:
- First off
- Skip Tunnel prepare() because NEProfileRepository.fetch() does it
already
- NE is both Tunnel and ProfileRepository, so calling tunnel.prepare()
loads local NE profiles twice
- onLaunch() - **run this once and before anything else**
- Read local profiles
- Reload in-app receipt
- Observe in-app eligibility → Triggers onEligibleFeatures()
- Observe profile save → Triggers onSaveProfile()
- Fetch providers index
- onForeground()
- Read local profiles
- Read remote profiles, and toggle CloudKit sync based on eligibility
- onEligibleFeatures()
- Read remote profiles, and toggle CloudKit sync based on eligibility
- onSaveProfile()
- Reconnect if necessary