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
Move the following dependencies:
- OpenVPN/OpenSSL
- WireGuard/Go
up the chain until the main App/Tunnel targets, so that UILibrary and
CommonLibrary can abstract from these unnecessary details. Instead, give
module views access to generic implementations via Registry.
Incidentally, this fixes an issue preventing TV previews from working
due to OpenSSL linkage.
- Centralize context initialization/refresh in platform-specific app
delegates
- Prevent multiple calls to .onApplicationActive()
- Simplify local/remote profile fingerprint comparison
- Revert to always replacing Core Data entities
- The remote store somehow ended up having duplicates, which caused
repeated imports of remote profiles due to randomly different
fingerprints
- Optimize reload of in-app receipt
Refactoring:
- Get receipts from StoreKit Transaction.currentEntitlements
- Search for the originally purchased build in the local receipt anyway
(Kvitto)
- Fall back to release receipt (Kvitto), if any, for feature eligibility
in TestFlight builds
- Parse and verify expiration date in subscriptions
- Decouple in-app identifier composition from BundleConfiguration
- Fix user level features only applied when a receipt was not found
Testing:
- Add StoreKit configuration
- Fake purchases with PP_FAKE_IAP
- Fake user level with PP_USER_LEVEL
Then for reactive receipt reload, detect app activation differently:
- iOS/tvOS on .scenePhase
- macOS on launch and NSWorkspace.didActivateApplicationNotification
As to features:
- Credit former "Full version" purchasers with all current AND future
features, except the Apple TV
Based on in-app eligibility, expire TV profiles after 10 minutes.
Refactor/redesign general sections and offer .sharing feature for free,
it makes it simpler to focus on Apple TV product.
Additions to the domain:
- Update rather than replace existing Core Data profile
- Attach ProfileAttributes to Profile.userInfo
- Store one-off `fingerprint` UUID on each save
With the above in place, fix and improve ProfileManager to:
- Use `fingerprint` to compare local/remote profiles in history and thus
avoid local re-import of shared profiles
- Use `deletingRemotely` to delete local profiles when removed from the
remote repository (default false)
- Use `isIncluded` filter to exclude certain profiles from the local
repository (default nil)
Encode OpenVPN password + OTP in tunnel rather than in the app.
Encoding them upfront in the app ends up persisting the profile with the
combined password. Update the library with a new OTP field in
OpenVPN.Credentials, so that the password encoding is performed [on the
fly in the
tunnel](https://github.com/passepartoutvpn/passepartoutkit-source/pull/398).
Similar to how provider modules are generated.
This is likely a regression caused by migrating to NEProfileRepository,
because starting a connection causes the profile to be saved to NE with
the encoded password. Later, the profile is restored from NE and
therefore contains the encoded password.
Countries are filtered through the latest servers list. If a country is
chosen, the countries picker only lists the currently selected country,
because there are no servers from other countries.
Update the library to prefetch the available countries per category.
At module creation time, choose whether to use a provider or import a
configuration file. After the import, the provider picker is hidden for
mutual exclusion.
For clarity, refactor the configuration part of OpenVPNView into a
ConfigurationView subview.
Also, improve filters by constraining related fields:
- Pick countries from the filtered category
- Pick presets from those available in the currently filtered servers
Closes#705
- iOS: Add category name to clarify servers context
- iOS: Show "No servers" when list is empty
- macOS: Show "Connect" in server selector when presenting from home
- Add last update to issue report
- Refactor provider strings
First of all, add country flags assets. Then, present provider server
selector:
- From installed profile view, specifically from a button with the flag
of the current country
- From profile context menu
- On toggle profile when no server is selected
Closes#711
- Drop logic behind connection button tasks, let the library handle
concurrency
- Drop AppContext observation of saved profiles for reconnection, let
save() actively decide
- NETunnelStrategy and NETunnelManagerRepository are now a single entity
- Avoid flickering when toggling same profile
Currently, NEProfileRepository decodes profiles from ALL NE managers on
any update. This is undesirable considering that:
- Profiles are only _added_ by the app
- Externally, profiles can only be _removed_
Therefore:
- Observe the initial managers to decode the initial profiles from them
- Publish values manually on save/delete (to ProfileManager eventually)
- Observe the subsequent updates for when a profile is removed
externally, i.e. its ID doesn't appear in managers
Fixes#741
Resolve some flickering and state inconsistency due to overextended
observation of VPNProviderManager. Narrow down its scope to
VPNProviderServerView.
The downside of that, for now, is that servers are loaded "lazily late",
but this flow will make region selection from home easier.
Finally, show filters in popover on iPad.
Improve rendering and work around some SwiftUI bugs, e.g. with .menu
Picker on iOS (use .navigationLink instead).
Here goes the hierarchy bottom-up:
- ProviderPicker: a Picker wrapper built around ProviderManager
- ProviderContentModifier: adds a ProviderPicker on top and replaces the
content with a set of provider selectors when a provider is selected
- VPNProviderContentModifier: wrapper for ProviderContentModifier that
adds a VPN server selector
- OpenVPNView: provides a view of specific OpenVPN settings, and adds a
credentials selector to the provider/server selectors provided by
VPNProviderContentModifier
When e.g. a OpenVPNModule is created without a configuration and a
provider/server is then selected, the ProfileProcessor class serializes
the profile with the provider configuration injected. When the module is
re-edited, we can see the provider server configuration in the module
after selecting "None" as provider.
Instead, validate the provider modules in ProfileProcessor, but generate the provider configuration on the fly in the tunnel.
Update library to allow optional VPN configurations. This in turn allows
a module to be used with a provider, where the configuration is
generated on the fly.
- [x] NE managers were not deleted when unable to be decoded to a
profile
- [x] Keychain items were not deleted on profile removal
- [x] Perform clean-up on app launch
- [x] Perform clean-up on app active
Prematurely merged as #727 then reverted, this is the complete PR.
Profiles are being maintained in two places:
- Core Data
- NetworkExtension
Core Data is redundant for local profiles, so make NetworkExtension the
only source of truth.