`RemoteImage` now uses two images: a high res image (as before), and a
new low res image which will be used if it is available before the high
res image has been loaded.
To enable this, it now uses two `ImageLoader` instances, one for to
fetch the low res image, a second for high res image.
It assumes that the low res image url is the same as the high res image
url, with a suffix appended (default `_low_res`) to the file name, eg
`/images/myImage.jpg` is assumed to have a low res equivalent at
`/images/myImage_low_res.jpg`.
It requests the high and low res images concurrently, and displays an
error when both requests fail. A low res image will never replace a high
res image even if it is received last.
Here is a video of it loading a "low res" image, following by high res
image. For this video the low-res image was intentionally set to a
completely different image to make the change easy to see. If you move
the slider manually you can control the speed.
https://github.com/RevenueCat/purchases-ios/assets/109382862/97521803-5c12-43d0-92a8-596b23cdcff6
---------
Co-authored-by: Josh Holtz <me@joshholtz.com>
`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