⚠️ Waiting for #4188 to be merged first (because needs all the tests to
pass)
## Motivation
Fix cases where paywall was showing multiple languages at once and
random languages.
## Description
👉 For a demo of the problem and solution, look in Slack in the paywalls
or SDK channel for the latest Loom that I posted
### Flow of paywall localization logic (since it can be confusing)
1.
[PaywallView](https://github.com/RevenueCat/purchases-ios/pull/4158/files#diff-ea75fe7280803e15b975d702a16c6851ffc2bbed01b7b196bf0fb80d75b4b332)
starts with `Locale.current` but then calls
`offering.validatedPaywall()` which will return the new
`displayedLocale: Locale` variable
2.
[PaywallData+Validation](https://github.com/RevenueCat/purchases-ios/pull/4158/files#diff-452790cf5287a44f75a4b2260254db2281a066faeea2b3e441a007ec60522b6b)
returns `displayedLocale` if its a valid paywall, otherwise it return
`Locale.current` for fallback paywall
3.
[PaywallData+Localization](https://github.com/RevenueCat/purchases-ios/pull/4158/files#diff-e620baead94f7acad56d931c485d7691c4f909be3fe625e104528a2702cca2a0)
has a lot of the new logic
- New `locale: Locale` computer property that it determines the locale
from the `localizedConfiguration` or `localizedConfigurationsByTier`
- New ` defaultLocalizedConfiguration` that uses the developer set
`defaultLocale` (from `PaywallData`) to find the default localization
- Updated `localizedConfiguration<Value>()` to take a new
`defaultLocalization` to put in its priority (and log it if its used).
If for some reason that's not found, it will use the legacy
`fallbackLocalization` (which is the first localization it finds or
english)
4. After all of that, the `displayedLocale` is passed into the actually
SwiftUI `LoadedOfferingPaywallView` to show this locale everywhere
### Problem 1 - Inconsistent languages displayed at once
The user defined localizations and the pre-localized content in the SDK
(like links in the footer and package duration names) could should
different localizations. The choice of which locale was happening in two
different spots.
Since the SDK has all possible localizations that the dashboard offers,
the locale SDK displays from the dashboard will be chosen for the
pre-localized content.
### Problem 2 - Showing random languages if not supported
We decided early on to not use a `default_locale` but that has hurt us.
The solution to use was use `localizations.first` to show the first
locale in the list and the intention that this was the first locale the
user did in the paywall. That was not the case, however. The order of
the localizations list was indeterministic so it should should a random
language (even to the point where different random languages would show
the the same user).
We are now introducing a `default_locale` that the developer can set in
the paywall. If the locale the user is requesting is not translated by
the developer, it will use the `default_locale`
### Problem 3 - Footer not using correct localization from the bundle
There were times where some parts of the footer itself (mainly the
restore button) would be a different language than the terms of service
and privacy policy buttons. This was due to a code issue where we were
using `Bundle.module`. Instead, we needed to specify a localization
bundle specifically that we wanted to use.
### 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
`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
- 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">
### Changes:
- Created `TemplateViewConfiguration` to encapsulate all processed
configuration for a paywall
- Created `TemplateViewConfiguration.PackageSetting` and
`TemplateViewConfiguration.PackageConfiguration` to represent paywalls
that support 1 or N packages
- Taken all common logic out of `Example1Template`
- Added new `PaywallViewMode` for upcoming "ramps" views