Commit Graph

73 Commits

Author SHA1 Message Date
Davide b45f9c23fe
Rearrange targets for unit testing (#900)
- Move and rename entities
- Split protocols and default extensions
- Move in-app entities to CommonIAP target
2024-11-20 18:05:47 +01:00
Davide 74ed93a966
Check provider requirements before connecting (#896)
E.g. interactive login must be presented afterwards.
2024-11-19 20:51:55 +01:00
Davide 89d7af4df7
Rethink eligibility checks (#889)
- Allow unrestricted save, but show PurchaseRequiredButton
- Warn however about paid features (FIXME)
- Redesign features in paywall
- Strip already eligible features from paywall
- List required features in restricted alert
- Localize feature descriptions
- Review propagation of paywall modifiers/reasons

Extra:

- Move more domain entities from UILibrary to CommonLibrary
- Default on-demand policy to .any (free feature)
- Fix modals not reappearing after closing with gesture
- Extend UILibrary start-up assertions
2024-11-18 17:43:01 +01:00
Davide 8e7f3e80ce
Update library with provider in modules (#887)
Move provider metadata from profile to module.
2024-11-17 22:27:02 +01:00
Davide d3e344670b
Resolve excessive profile reloads (#883)
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
2024-11-17 11:34:43 +01:00
Davide 962ffdf678
Sort servers by .serverId in repository 2024-11-15 00:24:36 +01:00
Davide 0a51d1a1f6
Strip package dependencies (#862)
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
2024-11-13 21:05:53 +01:00
Davide 008b78cc7c
Upgrade to Xcode 16 (#858)
- 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
2024-11-13 12:07:30 +01:00
Davide 54f4364c33
Split test jobs (#855)
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`
2024-11-12 18:35:44 +01:00
Davide e514ade036
Add logic to migrate v2 profiles (#854)
Will add UI separately.

Fixes #642
2024-11-12 16:42:19 +01:00
Davide 21340e9f56
Rewrite AppContext event handlers (#839)
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
2024-11-10 17:51:28 +01:00
Davide e07833b2a4
Revisit in-app eligibility for iCloud sharing (#837)
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
2024-11-09 15:20:59 +01:00
Davide 7c27125dd7
Decouple library from PassepartoutKit implementations (#834)
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.
2024-11-08 12:37:09 +01:00
Davide 68df6066ba
Improve configuration on app launch/active (#821)
- 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
2024-11-06 18:42:42 +01:00
Davide d8c4e87239
Refactor in-app entities for StoreKit/Kvitto integration (#820)
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
2024-11-06 13:20:12 +01:00
Davide 1cb46e066c
Encapsulate behavior on app active (#812)
Implement inside AppContext.
2024-11-05 10:41:02 +01:00
Davide bba661f104
Implement TV profile expiration (#811)
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.
2024-11-05 10:03:54 +01:00
Davide a22584c630
Fine-tune profile management with additional attributes (#807)
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)
2024-11-03 23:35:45 +01:00
Davide 2790a2aac2
Merge AppLibrary into CommonLibrary (#806)
CommonLibrary barely used as standalone, almost always together with
AppLibrary.
2024-11-03 13:16:13 +01:00
Davide 070b661c43
Refactor library targets (#799)
Renames:

- AppUI → UILibrary
- AppUIPlatform → AppUI (conditional umbrella) + AppUI[Main|TV]
- APILibrary/CommonLibrary/UtilsLibrary → Common[API|Library|Utils]

Dependencies:

- AppUI → UILibrary
- UILibrary → AppLibrary, CommonAPI
- AppLibrary → CommonLibrary, CommonUtils
2024-11-02 10:11:59 +01:00
Davide 72e784272a
Create basic UI for TV app (#798)
Start with the profile tab. Left to do: search and settings.

Fixes and refactoring:

- Listen to changes in current profile in ExtendedTunnel
- Externalize style from TunnelToggleButton and ConnectionStatusText
(renamed from View)
- Add ThemeCountryText for convenience
2024-11-02 08:41:32 +01:00
Davide 357c505cc0
Refactor AppUI/AppUIMain to accomodate TV (#797)
- Move InteractiveView to AppUI for use in TV, with
OpenVPNCredentialsView
- Move non-UI entities to AppLibrary (IAP, ExtendedTunnel,
ProfileProcessor)
- Take API out of CommonLibrary (tunnel extension does not need it)
- Reorganize theme views/modifiers into separate files
2024-11-01 23:32:35 +01:00
Davide ca18aadddf
Allow interactive credentials with providers (#796)
Fixes #795
2024-11-01 20:01:18 +01:00
Davide 80dd6dc779
Render country flags with Emojis (#787)
Revert FlagKit just introduced in #786
2024-10-31 01:15:07 +01:00
Davide dcdb03a735
Improve country flags (#786)
Leverage the convenience of FlagKit. Update credits too.
2024-10-30 19:11:58 +01:00
Davide bb8c760278
OpenVPN OTP is persisted with the password (#785)
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.
2024-10-30 17:07:59 +01:00
Davide aadc46e98f
Bundle API in app rather than library (#782)
Add script to speed up API update.
2024-10-30 11:46:24 +01:00
Davide f48349cde9
Fix duplicated message on import error 2024-10-29 16:30:43 +01:00
Davide 944d6f8c28
Refactor AppUI for TV target (#775)
Split AppUI into AppUI and AppUIMain to allow for a new, simplified
AppUITV target tailored for the Apple TV.

As a PoC, present a view with a list of the shared profiles.
2024-10-29 14:30:41 +01:00
Davide f8773cacf1
Filter countries by selected category (#767)
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.
2024-10-28 20:53:35 +01:00
Davide 7ede841346
Present error alert on missing provider (#766) 2024-10-28 20:30:22 +01:00
Davide 0ec06c2c65
Import OpenVPN configuration (#765)
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.
2024-10-28 20:07:19 +01:00
Davide 639dee55ee
Replace 'lastUpdated' with 'lastUpdate' 2024-10-28 17:16:13 +01:00
Davide 0d383ec792
Store providers to Core Data (#763)
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
2024-10-28 16:57:23 +01:00
Davide 61e8d8e2f7
Optimize redraws in provider servers views (#760) 2024-10-26 20:28:02 +02:00
Davide 3abde3851a
Improve some things about providers (#757)
- 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
2024-10-25 11:38:27 +02:00
Davide 2c2b3f063a
Multi-configuration providers (#756) 2024-10-25 10:54:28 +02:00
Davide 5fcb710d5e
Allow creation of OpenVPN provider module (#755)
Behind paywall as per #712 

Fixes #712
2024-10-23 23:19:53 +02:00
Davide 1a82aea612
Optionally apply filters based on server selection (#754)
Restore passing selectedEntity, but add filtersWithSelection to decide
whether to filter servers based on selection.

Partially revert 91a3e42533
2024-10-23 22:57:30 +02:00
Davide 295ba4d662
Quick selection of provider server (#750)
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
2024-10-23 17:17:20 +02:00
Davide 39bdf145e8
Resolve issues when toggling/switching profiles (#747)
- 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
2024-10-22 13:03:34 +02:00
Davide 2155fe1892
Optimize updates in NEProfileRepository (#742)
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
2024-10-19 18:22:27 +02:00
Davide 5d2e24792c
Rewrite provider views (#740)
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.
2024-10-18 18:12:28 +02:00
Davide 0221aea6b6
Move some profile integrity checks to library (#739)
They really belong to Profile.Builder
2024-10-16 09:50:26 +02:00
Davide 9656e5ed29
Filter servers with category/country of selected server (#735)
And use Task for blocking actions in filters.
2024-10-15 23:48:27 +02:00
Davide ed28126cf7
Rework OpenVPN view with provider modifiers (#733)
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
2024-10-15 21:34:02 +02:00
Davide 87c7d63678
Redo provider managers lifecycle (#732)
Update library with more efficient choices for interacting with the
providers API.

Fixes #731
2024-10-13 11:36:34 +02:00
Davide a5d4f6aee5
Unify library iOS target to 15 2024-10-12 15:51:00 +02:00
Davide 9769a151db
Provider configuration is persisted into module (#730)
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.
2024-10-12 13:19:46 +02:00
Davide 208198c3f8
Create OpenVPN module without a configuration (#729)
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.
2024-10-11 19:11:42 +02:00