* This mostly works
* This mostly works
* Better tests and logic
* More tests
* Fixed onemore
* lint
* Fix tests
* Actually use this
* Fix for all quantity translation things
* More minor fixes
* Fix tests
## Rounds prices for certain locales.
The server now returns a list of storefront country codes where paywalls
should not display cents. This part of paywall data, in
`zero_decimal_place_countries`.
Prices are only truncated if the price ends in .00.
### Motivation
Template 5 but with multiple tiers 😊
### Description
Enabling Template 7 by changing `7_disabled` to `7`
- Changed a `header.jpg` to `header.heic` to try and fix issue with
weirdly failing snapshot UI test
- Changed visibility on `TestData` from `public` to `internal`
- Hides tier selector if only one tier
- Changed `color_overrides_by_tier` to `colors_by_tier` and
`image_overrides_by_tier` to `images_by_tier`
- The original implementation was going to default to existing color and
images configurations and then override by tier
- However, this turned out to be harder to implement on the
configuration side
- `Colors` got four new optional values
- `tier_control_background`
- `tier_control_foreground`
- `tier_control_selected_background`
- `tier_control_selected_foreground`
### Demo
#### Full Screen
https://github.com/RevenueCat/purchases-ios/assets/401294/e5317a53-428c-415e-a1a1-b4231d32a776
### Footer
https://github.com/RevenueCat/purchases-ios/assets/401294/8905b55e-0fa2-49f0-aa1c-d4c1e694fb5b
### Footer Condensed
https://github.com/RevenueCat/purchases-ios/assets/401294/311d78fb-bdeb-43cd-b601-9c60a8a54b6f
The paywalls examples weren't showing up when compiled in release mode.
Enabling it meant making a bunch of more internal things public via the
preprocessing script. `TestData` is no longer `#ifdef DEBUG`ed out, but
most of its data is still `#ifdef DEBUG`, and when it's compiled with an
`internal` modifier and thus nothing uses it, I'd expect what remains to
be stripped away during linking.
This wasn't fully required, but now we'll correctly encode
`stringRepresentation`.
Also fixed the math (dividing by 255 instead of 256), which was
technically producing slightly incorrect colors.
Also adds an overload for `UIColor` which allows us to use color
literals in Xcode:

See
https://docs.google.com/document/d/1oLZR77apAjZf04hDzlrJpPaFV0xhuQ1-hApsNBohhfA/edit
This simplifies `PaywallView`: the validated `PaywallData` is what's
always displayed, and a `DebugErrorView` containing an error description
will potentially explain why a default paywall is being displayed.
### Validations:
- [x] `Offering` contains a `PaywallData`
- [x] `PaywallLocalizedConfiguration` contains no unrecognized variables
- [x] `PaywallIcon`s are all recognized
- [x] `PaywallTemplate` is recognized
I've moved `PaywallViewMode` from `RevenueCatUI` to `RevenueCat`. This
uses the same approach as #2645. This method is `public` so it can be
used from `RevenueCatUI`, but the documentation stays its purpose.
The behavior is covered with integration tests and a snapshot test.
Depends on https://github.com/RevenueCat/khepri/pull/6364.
These match the new agreed upon variables.
- Improved the localized unit abbreviations
- Refactored `VariableDataProvider` so all the logic is now in `Package`
- Added new tests for `Package` variables
`PaywallView` and `PaywallViewController` (along with the extensions)
still require iOS 16, but this paves the way to support iOS 15 down the
road if we need to.
### Main Changes:
- Changed all `@available` annotations
- Added CI tests
- Replaced `ViewThatFits` with the "biggest" version
- Because of that, `scrollableIfNeeded` is always scrollable
- Double `Regex` implementation (keeping the modern one), so once we
drop support for iOS 15 we can just keep the modern one.
### TODO:
- [x] Add snapshots
- [x] Test paywalls
- [x] Updated APITester to verify this
The main change is that `VariableHandler` no longer cashes when trying
to determine price per month for non-subscriptions. I added a test to
cover this behavior.
This allows users to present a `PaywallView` and be notified of
purchasing events. Example:
```swift
VStack {
YourViews()
if !self.didPurchase {
PaywallView()
}
}
.onPurchaseCompleted { customerInfo in
print("Purchase completed: \(customerInfo.entitlements)")
self.didPurchase = true
}
```
Additionally, an explicit optional closure is added to
`.presentPaywallIfNecessary`:
```swift
YourApp()
.presentPaywallIfNecessary {
!$0.entitlements.active.keys.contains("entitlement_identifier")
} purchaseCompleted: { customerInfo in
print("Customer info unlocked entitlement: \(customerInfo.entitlements)")
}
```
These are now tested through new unit tests.
This could still use more polish, but some basic fixes:
- Fixed colors
- Added application name as a title
- Using `total_price_and_per_month` instead of `price_per_month` so it
also works with monthly subscriptions
- Added snapshot test for its dark mode version
- Improved `PaywallView` API: now there's only 2 constructors (with
optional `Mode` parameters):
- `PaywallView()`
- `PaywallView(offering:)`
- New `PaywallData.default` as a fallback when trying to present a
paywall with missing data (either because it failed to decode, or it's
missing)
- Extracted error state handling to `ErrorDisplay` (used by
`PaywallView` and `AsyncButton` now). It can optionally dismiss the
presenting sheet.
- Handling offering loading errors in `PaywallView`
- Improved `DebugErrorView` to allow displaying a fallback view:
```swift
DebugErrorView(
"Offering '\(offering.identifier)' has no configured paywall.\n" +
"The displayed paywall contains default configuration.\n" +
"This error will be hidden in production.",
releaseBehavior: .replacement(
AnyView(
LoadedOfferingPaywallView(
offering: offering,
paywall: .default,
mode: mode,
introEligibility: checker,
purchaseHandler: purchaseHandler
)
)
)
)
```
- Added `LoadingPaywallView` as a placeholder view during loading
- Improved `MultiPackageTemplate` and `RemoteImage` to fix some layout
issues during transitions
- Added transition animations between loading state and loaded paywall
- Added support for `PackageSetting.multiple`
- Changed `PackageConfiguration.multiple` to ensure at compile time that
the list of packages is not empty
- Renamed `Example1Template` to `SinglePackageTemplate`
- Extracted `RemoteImage`
- Added snapshot tests
<img width="196" alt="screenshot_2023-07-20_at_16 01 14"
src="https://github.com/RevenueCat/purchases-ios/assets/685609/c7d6ae58-a608-414a-b5b1-11638e1a2a67">