- 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
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
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
- Use StoreKit views when available
- Offer one-time purchase
- Recurring subscriptions for all features
- Restore purchases
Remove .siri (Shortcuts), now free.
Closes#819Closes#469
The condition came from v2, but the flow was different. Drop the
condition because it would always fail in TestFlight for macOS, where
sandbox and release receipts have the same URL.
- 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.