Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Paywalls: new PaywallFooterView to replace modes #3051

Merged
merged 9 commits into from
Aug 22, 2023

Conversation

joshdholtz
Copy link
Member

@joshdholtz joshdholtz commented Aug 22, 2023

Motivation

It was a little unclear how and when to use the different PaywallView modes (.fullscreen, .card, and .condensedCard). The actual functionality of the modes is great but they seemed like three different components in one view and it was just trying to do too much for one public API.

This change will...

  • Remove modes from being a public parameter in PaywallView and only allow it to be full screen
  • Create a new PaywallFooterView which will combine .footer (previously .card) and .condensedFooter (previously .condensedCard)
  • Removed the modes parameter from PaywallView

New PaywallFooterView

  • Wraps PaywallView and will only choose between .footer and .footerCondensed
  • Currently offers a condensed parameter but this might be switched to being a remote setting inside of PaywalData

New .paywallFooter() view modifier

  • Easy way for developers to place PaywallFooterView where it is supposed to be placed
  • This places the view in .safeAreaInset(edge: .bottom)
    • This works on any view but is ideal in ScrollView as it will automatically handle adjusting the scroll insets with the paywalls size

New footer structure and animation (with some hacks)

  • No parts of PaywallFooterView uses .overlay anymore
    • The whole grows in .condensedFooter when "All Plans" is toggled

Demo

My sample app

RocketSim_Recording_iPhone_14_Plus_2023-08-22_07.05.52.mp4

Simple sample app

RocketSim_Recording_iPhone_14_2023-08-22_07.45.07.mp4

RevenueCatUI/Modifiers/ViewExtensions.swift Show resolved Hide resolved
// Disabling in overlay mode here because of animation issues with ViewThatFits
// Scrolling is enabled in overlay mode in the packages
// Bonus is that CTA stays visible and doesn't scroll
.scrollableIfNecessary(enabled: configuration.mode == .fullScreen)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels gross. Not sure if this should be moved into its own logic somewhere so its more easily used by other templates? 🤔

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I don't think we can do this, this probably breaks with larger dynamic types.

}
// TODO: This is really bad but should be capped when in overlay mode so scrolls
.frame(maxHeight: configuration.mode == .fullScreen ? nil : 400)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably should have some height limit on the footer size so the custom paywall content can show 🤔

Comment on lines 102 to 92
/// Hack because scrollableIfNecessary() doesn't work on iOS 15 because of unknown reason
private var packagesScrollHack: some View {
Group {
if #available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, *) {
self.packages
// Another hack because bounce animation temporarily clips top package a little bits
.padding(.top, 5)
.scrollableIfNecessary()
} else {
ScrollView {
self.packages
}
}
}
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something with GeometryReader and changing of sizes caused issues on iOS 15 so this just wraps things in a scroll view

Happy to rename/move this or fix in GeometryReader but wanted to call this logic out


init(
offering: Offering?,
condensed: Bool = false,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Going with condensed: Bool for now to make it easier on which type of footer is wanted but this might get moved to a remote configuration in PaywallData

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nah this makes sense, I think this should be configured from the SDK as it depends on how it's used in the app.

@joshdholtz joshdholtz marked this pull request as ready for review August 22, 2023 13:44
@joshdholtz joshdholtz requested review from NachoSoto and a team August 22, 2023 13:44

static let defaultCornerRadius: CGFloat = 20

static let maxFooterHeight: CGFloat = 400
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving unlimited height for footer felt bad so just putting this at 400 for now? But not sold on this at all 🙃

@codecov
Copy link

codecov bot commented Aug 22, 2023

Codecov Report

❗ No coverage uploaded for pull request base (paywalls-rename-card-to-overlay@b813ae9). Click here to learn what that means.
The diff coverage is n/a.

❗ Current head ec9778f differs from pull request most recent head a649113. Consider uploading reports for the commit a649113 to get more accurate results

@@                        Coverage Diff                         @@
##             paywalls-rename-card-to-overlay    #3051   +/-   ##
==================================================================
  Coverage                                   ?   86.07%           
==================================================================
  Files                                      ?      225           
  Lines                                      ?    16138           
  Branches                                   ?        0           
==================================================================
  Hits                                       ?    13890           
  Misses                                     ?     2248           
  Partials                                   ?        0           

Copy link
Contributor

@NachoSoto NachoSoto left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't tested this, but assuming it hasn't broken any of the permutations (iOS / template / etc) this is awesome 👏🏻

  • Much better API
  • Simpler implementation
  • More discoverable
  • More intuitive to use

One thing is that we can make PaywallViewMode internal, and "hide" the parameter from the PaywallView public constructors, right?

@@ -12,10 +12,12 @@ enum Constants {

static let defaultAnimation: Animation = .easeInOut(duration: 0.2)
static let fastAnimation: Animation = .easeInOut(duration: 0.1)
static let displayAllPlansAnimation: Animation = .easeInOut(duration: 0.2)
static let toggleAllPlansAnimation: Animation = .spring(response: 0.35, dampingFraction: 0.7)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😍

Comment on lines 41 to 46
content
.onSizeChange(.vertical) { self.height = $0 }
.opacity(self.hide ? 0 : 1)
.frame(height: self.hide ? 0 : nil, alignment: .top)
.clipped()
.transition(.move(edge: .bottom))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is so much simpler

RevenueCatUI/Templates/Template2View.swift Show resolved Hide resolved
}
// Sets a max footer height so there is enough room for paywall content above
.frame(maxHeight: configuration.mode == .fullScreen ? nil : Constants.maxFooterHeight)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use .infinite? I think it accomplishes the same goal without hardcoding a magic number.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed this. We can add a parameter for the maximum height if users request it.

if #available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, *) {
self.packages
// Another hack because bounce animation temporarily clips top package a little bits
.padding(.top, 5)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:( let's look at a better way of doing this.

@available(iOS 15.0, macOS 12.0, tvOS 15.0, *)
@available(macOS, unavailable)
@available(tvOS, unavailable)
struct PresentingPaywallFooterModifier: ViewModifier {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be private.

@@ -49,6 +49,29 @@ struct App: View {
}
}

@ViewBuilder
var checkPaywallFooter: some View {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome

@@ -12,7 +12,7 @@ import SwiftUI
struct CustomPaywall: View {

var offering: Offering?
var mode: PaywallViewMode
var condensed: Bool
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice makes sense.

private static let modes: [PaywallViewMode] = [
.footer,
.condensedFooter
private static let modes: [Bool] = [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe

Suggested change
private static let modes: [Bool] = [
private static let condensedValues: [Bool] = [

@available(macOS, unavailable, message: "RevenueCatUI does not support macOS yet")
@available(tvOS, unavailable, message: "RevenueCatUI does not support tvOS yet")
@available(macCatalyst, unavailable, message: "RevenueCatUI does not support Catalyst yet")
public struct PaywallFooterView: View {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that I think about it, does this need to be public?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed.

Comment on lines 29 to 30
@State
private var error: NSError?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't used.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

private let introEligibility: TrialOrIntroEligibilityChecker?
private let purchaseHandler: PurchaseHandler?

@State
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be @State.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed.

// Sets a max footer height so there is enough room for paywall content above
.frame(maxHeight: configuration.mode == .fullScreen ? nil : Constants.maxFooterHeight)
.hideFooterContent(self.configuration,
hide: !self.configuration.mode.shouldDisplayPackages && !self.displayingAllPlans)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this change?

@NachoSoto NachoSoto force-pushed the paywalls-rename-card-to-overlay branch from 906ade4 to a030561 Compare August 22, 2023 21:46
@NachoSoto NachoSoto force-pushed the paywalls-footer-view branch 2 times, most recently from 6a91860 to a649113 Compare August 22, 2023 21:51
@available(macOS, unavailable, message: "RevenueCatUI does not support macOS yet")
@available(tvOS, unavailable, message: "RevenueCatUI does not support tvOS yet")
@available(macCatalyst, unavailable, message: "RevenueCatUI does not support Catalyst yet")
internal struct PaywallFooterView: View {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made this internal now.

Base automatically changed from paywalls-rename-card-to-overlay to paywalls August 22, 2023 22:02
@NachoSoto NachoSoto force-pushed the paywalls-footer-view branch from 29a23c5 to caabcd0 Compare August 22, 2023 22:05
@NachoSoto NachoSoto enabled auto-merge (squash) August 22, 2023 22:06
@NachoSoto NachoSoto disabled auto-merge August 22, 2023 22:11
@NachoSoto NachoSoto changed the title Paywalls: New PaywallFooterView to replace modes Paywalls: new PaywallFooterView to replace modes Aug 22, 2023
@NachoSoto NachoSoto merged commit 01fa3ce into paywalls Aug 22, 2023
@NachoSoto NachoSoto deleted the paywalls-footer-view branch August 22, 2023 23:27
NachoSoto added a commit that referenced this pull request Aug 24, 2023
## Motivation

It was a little unclear how and when to use the different `PaywallView`
modes (`.fullscreen`, `.card`, and `.condensedCard`). The actual
functionality of the modes is great but they seemed like three different
components in one view and it was just trying to do too much for one
public API.

This change will...
- Remove modes from being a public parameter in `PaywallView` and only
allow it to be full screen
- Create a new `PaywallFooterView` which will combine `.footer`
(previously `.card`) and `.condensedFooter` (previously
`.condensedCard`)
- Removed the `modes` parameter from `PaywallView`

### New `PaywallFooterView`

- Wraps `PaywallView` and will only choose between `.footer` and
`.footerCondensed`
- Currently offers a `condensed` parameter but this might be switched to
being a remote setting inside of `PaywalData`

### New `.paywallFooter()` view modifier

- Easy way for developers to place `PaywallFooterView` where it is
supposed to be placed
- This places the view in `.safeAreaInset(edge: .bottom)`
- This works on any view but is ideal in `ScrollView` as it will
automatically handle adjusting the scroll insets with the paywalls size

### New footer structure and animation (with some hacks)

- No parts of `PaywallFooterView` uses `.overlay` anymore
  - The whole grows in `.condensedFooter` when "All Plans" is toggled

## Demo

### My sample app


https://github.com/RevenueCat/purchases-ios/assets/401294/17c52bba-956d-404e-b991-a2272e7fc4f1

### Simple sample app 


https://github.com/RevenueCat/purchases-ios/assets/401294/4ea94156-8367-4dea-9640-0cce8ffa1deb

---------

Co-authored-by: NachoSoto <ignaciosoto90@gmail.com>
NachoSoto added a commit that referenced this pull request Aug 28, 2023
## Motivation

It was a little unclear how and when to use the different `PaywallView`
modes (`.fullscreen`, `.card`, and `.condensedCard`). The actual
functionality of the modes is great but they seemed like three different
components in one view and it was just trying to do too much for one
public API.

This change will...
- Remove modes from being a public parameter in `PaywallView` and only
allow it to be full screen
- Create a new `PaywallFooterView` which will combine `.footer`
(previously `.card`) and `.condensedFooter` (previously
`.condensedCard`)
- Removed the `modes` parameter from `PaywallView`

### New `PaywallFooterView`

- Wraps `PaywallView` and will only choose between `.footer` and
`.footerCondensed`
- Currently offers a `condensed` parameter but this might be switched to
being a remote setting inside of `PaywalData`

### New `.paywallFooter()` view modifier

- Easy way for developers to place `PaywallFooterView` where it is
supposed to be placed
- This places the view in `.safeAreaInset(edge: .bottom)`
- This works on any view but is ideal in `ScrollView` as it will
automatically handle adjusting the scroll insets with the paywalls size

### New footer structure and animation (with some hacks)

- No parts of `PaywallFooterView` uses `.overlay` anymore
  - The whole grows in `.condensedFooter` when "All Plans" is toggled

## Demo

### My sample app


https://github.com/RevenueCat/purchases-ios/assets/401294/17c52bba-956d-404e-b991-a2272e7fc4f1

### Simple sample app 


https://github.com/RevenueCat/purchases-ios/assets/401294/4ea94156-8367-4dea-9640-0cce8ffa1deb

---------

Co-authored-by: NachoSoto <ignaciosoto90@gmail.com>
NachoSoto added a commit that referenced this pull request Aug 31, 2023
## Motivation

It was a little unclear how and when to use the different `PaywallView`
modes (`.fullscreen`, `.card`, and `.condensedCard`). The actual
functionality of the modes is great but they seemed like three different
components in one view and it was just trying to do too much for one
public API.

This change will...
- Remove modes from being a public parameter in `PaywallView` and only
allow it to be full screen
- Create a new `PaywallFooterView` which will combine `.footer`
(previously `.card`) and `.condensedFooter` (previously
`.condensedCard`)
- Removed the `modes` parameter from `PaywallView`

### New `PaywallFooterView`

- Wraps `PaywallView` and will only choose between `.footer` and
`.footerCondensed`
- Currently offers a `condensed` parameter but this might be switched to
being a remote setting inside of `PaywalData`

### New `.paywallFooter()` view modifier

- Easy way for developers to place `PaywallFooterView` where it is
supposed to be placed
- This places the view in `.safeAreaInset(edge: .bottom)`
- This works on any view but is ideal in `ScrollView` as it will
automatically handle adjusting the scroll insets with the paywalls size

### New footer structure and animation (with some hacks)

- No parts of `PaywallFooterView` uses `.overlay` anymore
  - The whole grows in `.condensedFooter` when "All Plans" is toggled

## Demo

### My sample app


https://github.com/RevenueCat/purchases-ios/assets/401294/17c52bba-956d-404e-b991-a2272e7fc4f1

### Simple sample app 


https://github.com/RevenueCat/purchases-ios/assets/401294/4ea94156-8367-4dea-9640-0cce8ffa1deb

---------

Co-authored-by: NachoSoto <ignaciosoto90@gmail.com>
NachoSoto added a commit that referenced this pull request Sep 1, 2023
## Motivation

It was a little unclear how and when to use the different `PaywallView`
modes (`.fullscreen`, `.card`, and `.condensedCard`). The actual
functionality of the modes is great but they seemed like three different
components in one view and it was just trying to do too much for one
public API.

This change will...
- Remove modes from being a public parameter in `PaywallView` and only
allow it to be full screen
- Create a new `PaywallFooterView` which will combine `.footer`
(previously `.card`) and `.condensedFooter` (previously
`.condensedCard`)
- Removed the `modes` parameter from `PaywallView`

### New `PaywallFooterView`

- Wraps `PaywallView` and will only choose between `.footer` and
`.footerCondensed`
- Currently offers a `condensed` parameter but this might be switched to
being a remote setting inside of `PaywalData`

### New `.paywallFooter()` view modifier

- Easy way for developers to place `PaywallFooterView` where it is
supposed to be placed
- This places the view in `.safeAreaInset(edge: .bottom)`
- This works on any view but is ideal in `ScrollView` as it will
automatically handle adjusting the scroll insets with the paywalls size

### New footer structure and animation (with some hacks)

- No parts of `PaywallFooterView` uses `.overlay` anymore
  - The whole grows in `.condensedFooter` when "All Plans" is toggled

## Demo

### My sample app


https://github.com/RevenueCat/purchases-ios/assets/401294/17c52bba-956d-404e-b991-a2272e7fc4f1

### Simple sample app 


https://github.com/RevenueCat/purchases-ios/assets/401294/4ea94156-8367-4dea-9640-0cce8ffa1deb

---------

Co-authored-by: NachoSoto <ignaciosoto90@gmail.com>
NachoSoto added a commit that referenced this pull request Sep 6, 2023
## Motivation

It was a little unclear how and when to use the different `PaywallView`
modes (`.fullscreen`, `.card`, and `.condensedCard`). The actual
functionality of the modes is great but they seemed like three different
components in one view and it was just trying to do too much for one
public API.

This change will...
- Remove modes from being a public parameter in `PaywallView` and only
allow it to be full screen
- Create a new `PaywallFooterView` which will combine `.footer`
(previously `.card`) and `.condensedFooter` (previously
`.condensedCard`)
- Removed the `modes` parameter from `PaywallView`

### New `PaywallFooterView`

- Wraps `PaywallView` and will only choose between `.footer` and
`.footerCondensed`
- Currently offers a `condensed` parameter but this might be switched to
being a remote setting inside of `PaywalData`

### New `.paywallFooter()` view modifier

- Easy way for developers to place `PaywallFooterView` where it is
supposed to be placed
- This places the view in `.safeAreaInset(edge: .bottom)`
- This works on any view but is ideal in `ScrollView` as it will
automatically handle adjusting the scroll insets with the paywalls size

### New footer structure and animation (with some hacks)

- No parts of `PaywallFooterView` uses `.overlay` anymore
  - The whole grows in `.condensedFooter` when "All Plans" is toggled

## Demo

### My sample app


https://github.com/RevenueCat/purchases-ios/assets/401294/17c52bba-956d-404e-b991-a2272e7fc4f1

### Simple sample app 


https://github.com/RevenueCat/purchases-ios/assets/401294/4ea94156-8367-4dea-9640-0cce8ffa1deb

---------

Co-authored-by: NachoSoto <ignaciosoto90@gmail.com>
NachoSoto added a commit that referenced this pull request Sep 6, 2023
## Motivation

It was a little unclear how and when to use the different `PaywallView`
modes (`.fullscreen`, `.card`, and `.condensedCard`). The actual
functionality of the modes is great but they seemed like three different
components in one view and it was just trying to do too much for one
public API.

This change will...
- Remove modes from being a public parameter in `PaywallView` and only
allow it to be full screen
- Create a new `PaywallFooterView` which will combine `.footer`
(previously `.card`) and `.condensedFooter` (previously
`.condensedCard`)
- Removed the `modes` parameter from `PaywallView`

### New `PaywallFooterView`

- Wraps `PaywallView` and will only choose between `.footer` and
`.footerCondensed`
- Currently offers a `condensed` parameter but this might be switched to
being a remote setting inside of `PaywalData`

### New `.paywallFooter()` view modifier

- Easy way for developers to place `PaywallFooterView` where it is
supposed to be placed
- This places the view in `.safeAreaInset(edge: .bottom)`
- This works on any view but is ideal in `ScrollView` as it will
automatically handle adjusting the scroll insets with the paywalls size

### New footer structure and animation (with some hacks)

- No parts of `PaywallFooterView` uses `.overlay` anymore
  - The whole grows in `.condensedFooter` when "All Plans" is toggled

## Demo

### My sample app


https://github.com/RevenueCat/purchases-ios/assets/401294/17c52bba-956d-404e-b991-a2272e7fc4f1

### Simple sample app 


https://github.com/RevenueCat/purchases-ios/assets/401294/4ea94156-8367-4dea-9640-0cce8ffa1deb

---------

Co-authored-by: NachoSoto <ignaciosoto90@gmail.com>
NachoSoto added a commit that referenced this pull request Sep 6, 2023
## Motivation

It was a little unclear how and when to use the different `PaywallView`
modes (`.fullscreen`, `.card`, and `.condensedCard`). The actual
functionality of the modes is great but they seemed like three different
components in one view and it was just trying to do too much for one
public API.

This change will...
- Remove modes from being a public parameter in `PaywallView` and only
allow it to be full screen
- Create a new `PaywallFooterView` which will combine `.footer`
(previously `.card`) and `.condensedFooter` (previously
`.condensedCard`)
- Removed the `modes` parameter from `PaywallView`

### New `PaywallFooterView`

- Wraps `PaywallView` and will only choose between `.footer` and
`.footerCondensed`
- Currently offers a `condensed` parameter but this might be switched to
being a remote setting inside of `PaywalData`

### New `.paywallFooter()` view modifier

- Easy way for developers to place `PaywallFooterView` where it is
supposed to be placed
- This places the view in `.safeAreaInset(edge: .bottom)`
- This works on any view but is ideal in `ScrollView` as it will
automatically handle adjusting the scroll insets with the paywalls size

### New footer structure and animation (with some hacks)

- No parts of `PaywallFooterView` uses `.overlay` anymore
  - The whole grows in `.condensedFooter` when "All Plans" is toggled

## Demo

### My sample app


https://github.com/RevenueCat/purchases-ios/assets/401294/17c52bba-956d-404e-b991-a2272e7fc4f1

### Simple sample app 


https://github.com/RevenueCat/purchases-ios/assets/401294/4ea94156-8367-4dea-9640-0cce8ffa1deb

---------

Co-authored-by: NachoSoto <ignaciosoto90@gmail.com>
NachoSoto added a commit that referenced this pull request Sep 7, 2023
## Motivation

It was a little unclear how and when to use the different `PaywallView`
modes (`.fullscreen`, `.card`, and `.condensedCard`). The actual
functionality of the modes is great but they seemed like three different
components in one view and it was just trying to do too much for one
public API.

This change will...
- Remove modes from being a public parameter in `PaywallView` and only
allow it to be full screen
- Create a new `PaywallFooterView` which will combine `.footer`
(previously `.card`) and `.condensedFooter` (previously
`.condensedCard`)
- Removed the `modes` parameter from `PaywallView`

### New `PaywallFooterView`

- Wraps `PaywallView` and will only choose between `.footer` and
`.footerCondensed`
- Currently offers a `condensed` parameter but this might be switched to
being a remote setting inside of `PaywalData`

### New `.paywallFooter()` view modifier

- Easy way for developers to place `PaywallFooterView` where it is
supposed to be placed
- This places the view in `.safeAreaInset(edge: .bottom)`
- This works on any view but is ideal in `ScrollView` as it will
automatically handle adjusting the scroll insets with the paywalls size

### New footer structure and animation (with some hacks)

- No parts of `PaywallFooterView` uses `.overlay` anymore
  - The whole grows in `.condensedFooter` when "All Plans" is toggled

## Demo

### My sample app


https://github.com/RevenueCat/purchases-ios/assets/401294/17c52bba-956d-404e-b991-a2272e7fc4f1

### Simple sample app 


https://github.com/RevenueCat/purchases-ios/assets/401294/4ea94156-8367-4dea-9640-0cce8ffa1deb

---------

Co-authored-by: NachoSoto <ignaciosoto90@gmail.com>
NachoSoto added a commit that referenced this pull request Sep 7, 2023
## Motivation

It was a little unclear how and when to use the different `PaywallView`
modes (`.fullscreen`, `.card`, and `.condensedCard`). The actual
functionality of the modes is great but they seemed like three different
components in one view and it was just trying to do too much for one
public API.

This change will...
- Remove modes from being a public parameter in `PaywallView` and only
allow it to be full screen
- Create a new `PaywallFooterView` which will combine `.footer`
(previously `.card`) and `.condensedFooter` (previously
`.condensedCard`)
- Removed the `modes` parameter from `PaywallView`

### New `PaywallFooterView`

- Wraps `PaywallView` and will only choose between `.footer` and
`.footerCondensed`
- Currently offers a `condensed` parameter but this might be switched to
being a remote setting inside of `PaywalData`

### New `.paywallFooter()` view modifier

- Easy way for developers to place `PaywallFooterView` where it is
supposed to be placed
- This places the view in `.safeAreaInset(edge: .bottom)`
- This works on any view but is ideal in `ScrollView` as it will
automatically handle adjusting the scroll insets with the paywalls size

### New footer structure and animation (with some hacks)

- No parts of `PaywallFooterView` uses `.overlay` anymore
  - The whole grows in `.condensedFooter` when "All Plans" is toggled

## Demo

### My sample app


https://github.com/RevenueCat/purchases-ios/assets/401294/17c52bba-956d-404e-b991-a2272e7fc4f1

### Simple sample app 


https://github.com/RevenueCat/purchases-ios/assets/401294/4ea94156-8367-4dea-9640-0cce8ffa1deb

---------

Co-authored-by: NachoSoto <ignaciosoto90@gmail.com>
NachoSoto added a commit that referenced this pull request Sep 8, 2023
## Motivation

It was a little unclear how and when to use the different `PaywallView`
modes (`.fullscreen`, `.card`, and `.condensedCard`). The actual
functionality of the modes is great but they seemed like three different
components in one view and it was just trying to do too much for one
public API.

This change will...
- Remove modes from being a public parameter in `PaywallView` and only
allow it to be full screen
- Create a new `PaywallFooterView` which will combine `.footer`
(previously `.card`) and `.condensedFooter` (previously
`.condensedCard`)
- Removed the `modes` parameter from `PaywallView`

### New `PaywallFooterView`

- Wraps `PaywallView` and will only choose between `.footer` and
`.footerCondensed`
- Currently offers a `condensed` parameter but this might be switched to
being a remote setting inside of `PaywalData`

### New `.paywallFooter()` view modifier

- Easy way for developers to place `PaywallFooterView` where it is
supposed to be placed
- This places the view in `.safeAreaInset(edge: .bottom)`
- This works on any view but is ideal in `ScrollView` as it will
automatically handle adjusting the scroll insets with the paywalls size

### New footer structure and animation (with some hacks)

- No parts of `PaywallFooterView` uses `.overlay` anymore
  - The whole grows in `.condensedFooter` when "All Plans" is toggled

## Demo

### My sample app


https://github.com/RevenueCat/purchases-ios/assets/401294/17c52bba-956d-404e-b991-a2272e7fc4f1

### Simple sample app 


https://github.com/RevenueCat/purchases-ios/assets/401294/4ea94156-8367-4dea-9640-0cce8ffa1deb

---------

Co-authored-by: NachoSoto <ignaciosoto90@gmail.com>
NachoSoto added a commit that referenced this pull request Sep 14, 2023
## Motivation

It was a little unclear how and when to use the different `PaywallView`
modes (`.fullscreen`, `.card`, and `.condensedCard`). The actual
functionality of the modes is great but they seemed like three different
components in one view and it was just trying to do too much for one
public API.

This change will...
- Remove modes from being a public parameter in `PaywallView` and only
allow it to be full screen
- Create a new `PaywallFooterView` which will combine `.footer`
(previously `.card`) and `.condensedFooter` (previously
`.condensedCard`)
- Removed the `modes` parameter from `PaywallView`

### New `PaywallFooterView`

- Wraps `PaywallView` and will only choose between `.footer` and
`.footerCondensed`
- Currently offers a `condensed` parameter but this might be switched to
being a remote setting inside of `PaywalData`

### New `.paywallFooter()` view modifier

- Easy way for developers to place `PaywallFooterView` where it is
supposed to be placed
- This places the view in `.safeAreaInset(edge: .bottom)`
- This works on any view but is ideal in `ScrollView` as it will
automatically handle adjusting the scroll insets with the paywalls size

### New footer structure and animation (with some hacks)

- No parts of `PaywallFooterView` uses `.overlay` anymore
  - The whole grows in `.condensedFooter` when "All Plans" is toggled

## Demo

### My sample app


https://github.com/RevenueCat/purchases-ios/assets/401294/17c52bba-956d-404e-b991-a2272e7fc4f1

### Simple sample app 


https://github.com/RevenueCat/purchases-ios/assets/401294/4ea94156-8367-4dea-9640-0cce8ffa1deb

---------

Co-authored-by: NachoSoto <ignaciosoto90@gmail.com>
NachoSoto added a commit that referenced this pull request Sep 14, 2023
## Motivation

It was a little unclear how and when to use the different `PaywallView`
modes (`.fullscreen`, `.card`, and `.condensedCard`). The actual
functionality of the modes is great but they seemed like three different
components in one view and it was just trying to do too much for one
public API.

This change will...
- Remove modes from being a public parameter in `PaywallView` and only
allow it to be full screen
- Create a new `PaywallFooterView` which will combine `.footer`
(previously `.card`) and `.condensedFooter` (previously
`.condensedCard`)
- Removed the `modes` parameter from `PaywallView`

### New `PaywallFooterView`

- Wraps `PaywallView` and will only choose between `.footer` and
`.footerCondensed`
- Currently offers a `condensed` parameter but this might be switched to
being a remote setting inside of `PaywalData`

### New `.paywallFooter()` view modifier

- Easy way for developers to place `PaywallFooterView` where it is
supposed to be placed
- This places the view in `.safeAreaInset(edge: .bottom)`
- This works on any view but is ideal in `ScrollView` as it will
automatically handle adjusting the scroll insets with the paywalls size

### New footer structure and animation (with some hacks)

- No parts of `PaywallFooterView` uses `.overlay` anymore
  - The whole grows in `.condensedFooter` when "All Plans" is toggled

## Demo

### My sample app


https://github.com/RevenueCat/purchases-ios/assets/401294/17c52bba-956d-404e-b991-a2272e7fc4f1

### Simple sample app 


https://github.com/RevenueCat/purchases-ios/assets/401294/4ea94156-8367-4dea-9640-0cce8ffa1deb

---------

Co-authored-by: NachoSoto <ignaciosoto90@gmail.com>
NachoSoto added a commit that referenced this pull request Sep 15, 2023
## Motivation

It was a little unclear how and when to use the different `PaywallView`
modes (`.fullscreen`, `.card`, and `.condensedCard`). The actual
functionality of the modes is great but they seemed like three different
components in one view and it was just trying to do too much for one
public API.

This change will...
- Remove modes from being a public parameter in `PaywallView` and only
allow it to be full screen
- Create a new `PaywallFooterView` which will combine `.footer`
(previously `.card`) and `.condensedFooter` (previously
`.condensedCard`)
- Removed the `modes` parameter from `PaywallView`

### New `PaywallFooterView`

- Wraps `PaywallView` and will only choose between `.footer` and
`.footerCondensed`
- Currently offers a `condensed` parameter but this might be switched to
being a remote setting inside of `PaywalData`

### New `.paywallFooter()` view modifier

- Easy way for developers to place `PaywallFooterView` where it is
supposed to be placed
- This places the view in `.safeAreaInset(edge: .bottom)`
- This works on any view but is ideal in `ScrollView` as it will
automatically handle adjusting the scroll insets with the paywalls size

### New footer structure and animation (with some hacks)

- No parts of `PaywallFooterView` uses `.overlay` anymore
  - The whole grows in `.condensedFooter` when "All Plans" is toggled

## Demo

### My sample app


https://github.com/RevenueCat/purchases-ios/assets/401294/17c52bba-956d-404e-b991-a2272e7fc4f1

### Simple sample app 


https://github.com/RevenueCat/purchases-ios/assets/401294/4ea94156-8367-4dea-9640-0cce8ffa1deb

---------

Co-authored-by: NachoSoto <ignaciosoto90@gmail.com>
NachoSoto added a commit that referenced this pull request Sep 15, 2023
**This is an automatic release.**
### New Features
#### ✨ Introducing RevenueCatUI 📱

RevenueCat's Paywalls allow you to to remotely configure your entire
paywall view without any code changes or app updates.
Our paywall templates use native code to deliver smooth, intuitive
experiences to your customers when you’re ready to deliver them an
Offering; and you can use our Dashboard to pick the right template and
configuration to meet your needs.

To use RevenueCat Paywalls on iOS, simply:

1. Create a Paywall on the Dashboard for the `Offering` you intend to
serve to your customers
2. Add the `RevenueCatUI` SPM dependency to your project
3. `import RevenueCatUI` at the point in the user experience when you
want to display a paywall:

```swift
import RevenueCatUI
import SwiftUI

struct YourApp: View {

    var body: some View {
        YourContent()
            .presentPaywallIfNeeded(
                requiredEntitlementIdentifier: "pro",
                purchaseCompleted: { customerInfo in
                    print("Purchase completed: \(customerInfo)")
                },
                restoreCompleted: { customerInfo in
                    print("Purchases restored: \(customerInfo)")
                }
            )
    }

}
```

You can find more information in [our
documentation](https://rev.cat/paywalls).

<details>

<summary>List of changes</summary>
* NachoSoto: `Paywalls`: renamed `PaywallEvent.view` to `.impression`
(#3212)
* NachoSoto: `Paywalls`: loading indicator for in-progress purchases
(#3217)
*  NachoSoto: `Paywalls`: fixed template 4 bottom padding (#3211)
* NachoSoto: `Paywalls`: only pre-warm images/intro-eligibility for
`Offerings.current` (#3210)
* NachoSoto: `Paywalls`: fixed mock intro eligibility on snapshot tests
(#3205)
*  NachoSoto: `Paywalls`: fixed SimpleApp release build (#3203)
*  NachoSoto: `Paywalls`: improved `DebugErrorView` layout (#3204)
* NachoSoto: `Paywalls`: refactored `PurchaseHandler` extracting
protocol (#3196)
*  NachoSoto: `Paywalls`: automatically flush events (#3177)
* NachoSoto: `Paywalls`: fixed `TemplateBackgroundImageView` aspect
ratio (#3201)
*  NachoSoto: `Paywalls`: fixed broken layout on template 4 (#3202)
*  NachoSoto: `Paywalls`: events unit and integration tests (#3169)
*  NachoSoto: `Paywalls`: send events to `Purchases` (#3164)
*  NachoSoto: `Paywalls`: convert empty images into `nil` (#3195)
*  NachoSoto: `Paywalls`: new `onRestoreCompleted` handler (#3190)
* NachoSoto: `Paywalls`: fixed `IntroEligibilityViewModel` data lifetime
(#3194)
* NachoSoto: `Paywalls`: test plan for running non-snapshot tests
(#3188)
*  NachoSoto: `Paywalls`: polish template 4 (#3183)
* NachoSoto: `Paywalls`: fixed data flow resulting in multiple
`PurchaseHandler` instances (#3187)
* Cesar de la Vega: `Paywalls`: update `blurred_background_image` key in
`PaywallData` test fixture (#3186)
*  NachoSoto: `Paywalls`: added `Purchases.track(paywallEvent:)` (#3160)
* NachoSoto: `Paywalls`: don't apply dark appearance with no dark mode
colors (#3184)
* NachoSoto: `Paywalls`: fixed template 2 + `.condensedFooter` + iPad
(#3185)
* NachoSoto: `Paywalls`: new `{{ sub_duration_in_months }}` variable
(#3173)
*  NachoSoto: `Paywalls`: created `PaywallEventsManager` (#3159)
* NachoSoto: `Paywalls`: implemented `PostPaywallEventsOperation`
(#3158)
* NachoSoto: `Paywalls`: new `{{ sub_relative_discount }}` variable
(#3131)
*  Charlie Chapman: `Paywalls`: improved `FooterView` (#3171)
* NachoSoto: `Paywalls`: fixed `FooterView` horizontal centering (#3172)
*  NachoSoto: `Paywalls`: created `PaywallEventStore` (#3157)
*  NachoSoto: `Paywalls`: add `PaywallEvent` model (#3156)
*  NachoSoto: `Paywalls`: add `PaywallData.revision` (#3155)
* NachoSoto: `Paywalls`: support fuzzy-Locale search in `iOS 15` (#3162)
* NachoSoto: `PaywallData`: added `@NonEmptyString` to `subtitle` and
`offerName` (#3150)
* NachoSoto: `Paywalls`: add paywall for Load Shedder integration tests
(#3151)
* NachoSoto: `Paywalls`: fixed error view being displayed on release
builds (#3141)
* NachoSoto: `Paywalls`: improved `{{ total_price_and_per_month }}` to
include period (#3136)
* NachoSoto: `Paywalls`: `{{ price_per_period }}` now takes
`SubscriptionPeriod.value` into account (#3133)
*  NachoSoto: `Paywalls`: add Arabic to SimpleApp for testing (#3132)
* NachoSoto: `Paywalls`: update snapshot generation with new separate
git repo (#3116)
*  NachoSoto: `Paywalls`: add support for CTA button gradients (#3121)
*  NachoSoto: `Paywalls`: template 5 (#3095)
* NachoSoto: `Paywalls`: replaced submodule with `gitignore`d reference
(#3125)
* NachoSoto: `Catalyst`: fixed a couple of Catalyst build warnings
(#3120)
* NachoSoto: `Paywalls`: reference test snapshots from submodule (#3115)
*  NachoSoto: `Paywalls`: removed `presentedPaywallViewMode` (#3109)
* NachoSoto: `Paywalls`: remove duplicate `RevenueCat` scheme to fix
Carthage (#3105)
*  NachoSoto: `Paywalls`: fixed iOS 12 build (#3104)
*  NachoSoto: `Paywalls`: fixed template 2 inconsistent spacing (#3091)
*  NachoSoto: `Paywalls`: improved test custom paywall (#3089)
*  NachoSoto: `Paywalls`: avoid warming up cache multiple times (#3068)
*  NachoSoto: `Paywalls`: added all localization (#3080)
* NachoSoto: `Paywalls`: temporarily disable `PaywallTemplate.template4`
(#3088)
*  NachoSoto: `Paywalls`: enabled `Catalyst` support (#3087)
*  NachoSoto: `Paywalls`: iPad polish (#3061)
*  NachoSoto: `Paywalls`: added MIT license to all headers (#3084)
* NachoSoto: `Paywalls`: improved unselected package background color
(#3079)
*  NachoSoto: `Paywalls`: handle already purchased state (#3046)
* NachoSoto: `Paywalls`: only dismiss `PaywallView` when explicitly
presenting it with `.presentPaywallIfNeeded` (#3075)
* NachoSoto: `Paywalls`: add support for generating snapshots on CI
(#3055)
* NachoSoto: `Paywalls`: removed unnecessary `PaywallFooterView` (#3064)
* Josh Holtz: `Paywalls`: new `PaywallFooterView` to replace `modes`
(#3051)
*  Josh Holtz: `Paywalls`: rename card to footer (#3049)
* NachoSoto: `Paywalls`: changed `total_price_and_per_month` to include
period (#3044)
* NachoSoto: `Paywalls`: internal documentation for implementing
templates (#3053)
*  NachoSoto: `Paywalls`: finished `iOS 15` support (#3043)
* NachoSoto: `Paywalls`: validate `PaywallData` to ensure displayed data
is always correct (#3019)
* NachoSoto: `Paywalls`: fixed `total_price_and_per_month` for custom
monthly packages (#3027)
*  NachoSoto: `Paywalls`: tweaking colors on template 2&3 (#3011)
*  NachoSoto: `Paywalls`: changed snapshots to scale 1 (#3016)
* NachoSoto: `Paywalls`: replaced `defaultLocale` with
`preferredLocales` (#3003)
* NachoSoto: `Paywalls`: improved `PaywallDisplayMode.condensedCard`
layout (#3001)
*  NachoSoto: `Paywalls`: `.card` and `.condensedCard` modes (#2995)
*  NachoSoto: `Paywalls`: prevent multiple concurrent purchases (#2991)
*  NachoSoto: `Paywalls`: improved variable warning (#2984)
*  NachoSoto: `Paywalls`: fixed horizontal padding on template 1 (#2987)
* NachoSoto: `Paywalls`: changed `FooterView` to always use `text1`
color (#2992)
*  NachoSoto: `Paywalls`: retry test failures (#2985)
* NachoSoto: `Paywalls`: send presented `PaywallViewMode` with purchases
(#2859)
*  NachoSoto: `Paywalls`: added support for custom fonts (#2988)
* NachoSoto: `Paywalls`: improved template 2 unselected packages (#2982)
* Josh Holtz: `Paywalls`: fix template 2 selected text offer details
color (#2975)
*  NachoSoto: `Paywalls`: warm-up image cache (#2978)
*  NachoSoto: `Paywalls`: extracted `PaywallCacheWarming` (#2977)
*  NachoSoto: `Paywalls`: fixed color in template 3 (#2980)
*  NachoSoto: `Paywalls`: improved default template (#2973)
*  NachoSoto: `Paywalls`: added links to documentation (#2974)
*  NachoSoto: `Paywalls`: updated template names (#2971)
*  NachoSoto: `Paywalls`: updated variable names (#2970)
* NachoSoto: `Paywalls`: added JSON debug screen to
`debugRevenueCatOverlay` (#2972)
*  NachoSoto: `Paywalls`: multi-package horizontal template  (#2949)
*  NachoSoto: `Paywalls`: fixed template 3 icon aspect ratio (#2969)
*  NachoSoto: `Paywalls`: iOS 17 tests on CI (#2955)
*  NachoSoto: `Paywalls`: deploy `debug` sample app (#2966)
*  NachoSoto: `Paywalls`: sort offerings list in sample app (#2965)
*  NachoSoto: `Paywalls`: initial iOS 15 support (#2933)
* NachoSoto: `Paywalls`: changed default `PaywallData` to display
available packages (#2964)
*  NachoSoto: `Paywalls`: changed `offerDetails` to be optional (#2963)
*  NachoSoto: `Paywalls`: markdown support (#2961)
*  NachoSoto: `Paywalls`: updated icon set to match frontend (#2962)
*  NachoSoto: `Paywalls`: added support for `PackageType.custom` (#2959)
* NachoSoto: `Paywalls`: fixed `tvOS` compilation by making it
explicitly unavailable (#2956)
* NachoSoto: `Paywalls`: fix crash when computing localization with
duplicate packages (#2958)
*  NachoSoto: `Paywalls`: UIKit `PaywallViewController` (#2934)
* NachoSoto: `Paywalls`: `presentPaywallIfNecessary` ->
`presentPaywallIfNeeded` (#2953)
* NachoSoto: `Paywalls`: added support for custom and lifetime products
(#2941)
* NachoSoto: `Paywalls`: changed `SamplePaywallsList` to work offline
(#2937)
* NachoSoto: `Paywalls`: fixed header image mask on first template
(#2936)
*  NachoSoto: `Paywalls`: new `subscription_duration` variable (#2942)
* NachoSoto: `Paywalls`: removed `mode` parameter from
`presentPaywallIfNecessary` (#2940)
*  NachoSoto: `Paywalls`: improved `RemoteImage` error layout (#2939)
* NachoSoto: `Paywalls`: added default close button when using
`presentPaywallIfNecessary` (#2935)
* NachoSoto: `Paywalls`: added ability to preview templates in a
`.sheet` (#2938)
*  NachoSoto: `Paywalls`: avoid recomputing variable `Regex` (#2944)
*  NachoSoto: `Paywalls`: improved `FooterView` scaling (#2948)
* NachoSoto: `Paywalls`: added ability to calculate and localize
subscription discounts (#2943)
*  NachoSoto: `Offering`: improved description (#2912)
*  NachoSoto: `Paywalls`: fixed `FooterView` color in template 1 (#2951)
*  NachoSoto: `Paywalls`: fixed `View.scrollableIfNecessary` (#2947)
* NachoSoto: `Paywalls`: improved `IntroEligibilityStateView` to avoid
layout changes (#2946)
* NachoSoto: `Paywalls`: updated offerings snapshot with new asset base
URL (#2950)
* NachoSoto: `Paywalls`: extracted `TemplateBackgroundImageView` (#2945)
*  NachoSoto: `Paywalls`: more polish from design feedback (#2932)
*  NachoSoto: `Paywalls`: more unit tests for purchasing state (#2931)
*  NachoSoto: `Paywalls`: new `.onPurchaseCompleted` modifier (#2930)
* NachoSoto: `Paywalls`: fixed `LoadingPaywallView` displaying a
progress view (#2929)
* NachoSoto: `Paywalls`: added default template to `SamplePaywallsList`
(#2928)
*  NachoSoto: `Paywalls`: added a few more logs (#2927)
*  NachoSoto: `Paywalls` added individual previews for templates (#2924)
*  NachoSoto: `Paywalls`: improved default paywall configuration (#2926)
* NachoSoto: `Paywalls`: moved purchasing state to `PurchaseHandler`
(#2923)
*  NachoSoto: `Paywalls`: updated Integration Test snapshot (#2921)
* NachoSoto: `Paywalls`: pre-warm intro eligibility in background thread
(#2925)
*  NachoSoto: `Paywalls`: removed "couldn't find package" log (#2922)
* NachoSoto: `Paywalls`: SimpleApp reads API key from Xcode Cloud
environment (#2919)
* NachoSoto: `Paywalls`: improved template accessibility support (#2920)
* NachoSoto: `Paywalls`: work around SwiftUI bug to allow embedding
`PaywallView` inside `NavigationStack` (#2918)
*  NachoSoto: `Paywalls`: some basic polish from design feedback (#2917)
* NachoSoto: `Paywalls`: added `OfferingsList` to preview all paywalls
(#2916)
* NachoSoto: `Paywalls`: fixed tappable area for a couple of buttons
(#2915)
*  NachoSoto: `Paywalls`: new `text1` and `text2` colors (#2903)
* NachoSoto: `Paywalls`: updated multi-package bold template design
(#2908)
*  NachoSoto: `Paywalls`: added sample paywalls to `SimpleApp` (#2907)
*  NachoSoto: `Paywalls`: one package with features template (#2902)
*  NachoSoto: `Paywalls`: initial support for icons (#2882)
* NachoSoto: `Paywalls`: extracted intro eligibility out of templates
(#2901)
*  NachoSoto: `Paywalls`: changed `subtitle` to be optional (#2900)
* NachoSoto: `Paywalls`: added "features" to `LocalizedConfiguration`
(#2899)
* NachoSoto: `Paywalls`: fixed `{{ total_price_and_per_month }}` (#2881)
*  NachoSoto: `Paywalls`: updated template names (#2878)
*  NachoSoto: `Paywalls`: added accent colors (#2883)
* NachoSoto: `Paywalls`: changed images representation to an object
(#2875)
*  NachoSoto: `Paywalls`: added `offerName` parameter (#2877)
*  NachoSoto: `Paywalls`: new `{{ period }}` variable (#2876)
*  NachoSoto: `Paywalls`: disabled `PaywallViewMode`s for now (#2874)
* NachoSoto: `Paywalls`: added new `defaultPackage` configuration
(#2871)
*  NachoSoto: `Paywalls`: fixed tests on CI (#2872)
* NachoSoto: `Paywalls`: pre-fetch intro eligibility for paywalls
(#2860)
*  Andy Boedo: `Paywalls`: clean up the error view (#2873)
* NachoSoto: `Paywalls`: new API for easily displaying `PaywallView`
with just one line (#2869)
*  NachoSoto: `Paywalls`: handle missing paywalls gracefully (#2855)
* NachoSoto: `Paywalls`: temporarily disable non-fullscreen
`PaywallView`s (#2868)
* NachoSoto: `Paywalls`: added test to ensure package selection
maintains order (#2853)
* NachoSoto: `Paywalls`: added new `blurredBackgroundImage`
configuration (#2852)
*  NachoSoto: `Paywalls`: fuzzy `Locale` lookups (#2847)
*  NachoSoto: `Paywalls`: basic localization support (#2851)
*  NachoSoto: `Paywalls`: added `FooterView` (#2850)
*  NachoSoto: `Paywalls`: multi-package template (#2840)
*  NachoSoto: `Paywalls`: disable animations during unit tests (#2848)
* NachoSoto: `Paywalls`: `TrialOrIntroEligibilityChecker.eligibility(for
packages:)` (#2846)
* NachoSoto: `Paywalls`: added new `total_price_and_per_month` variable
(#2845)
*  NachoSoto: `Paywalls`: extracted `PurchaseButton` (#2839)
*  NachoSoto: `Paywalls`: extracted `IntroEligibilityStateView` (#2837)
* NachoSoto: `Paywalls`: support for multiple `PaywallViewMode`s (#2834)
* NachoSoto: `Paywalls`: add support for multiple images in template
configuration (#2832)
* NachoSoto: `Paywalls`: extracted configuration processing into a new
`TemplateViewConfiguration` (#2830)
* NachoSoto: `Paywalls`: improved support for dynamic type with
snapshots (#2827)
* NachoSoto: `Paywalls`: disable `macOS`/`macCatalyst`/`watchOS` for now
(#2821)
* NachoSoto: `Paywalls`: using new color information in template (#2823)
*  NachoSoto: `Paywalls`: set up CI tests and API Tester (#2816)
*  NachoSoto: `Paywalls`: added support for decoding colors (#2822)
* NachoSoto: `Paywalls`: ignore empty strings in
`LocalizedConfiguration` (#2818)
*  NachoSoto: `Paywalls`: updated `PaywallData` field names (#2817)
*  NachoSoto: `Paywalls`: added support for purchasing (#2812)
* NachoSoto: `Paywalls`: added tests for `PackageType` filtering (#2810)
* Andy Boedo: `Paywalls`: changed variable handling to use Swift `Regex`
(#2811)
*  NachoSoto: `Paywalls`: added `price` variable (#2809)
*  NachoSoto: `Paywalls`: determine intro eligibility (#2808)
*  NachoSoto: `Paywalls`: added header image to configuration (#2800)
*  NachoSoto: `Paywalls`: added `packages` to configuration (#2798)
* NachoSoto: `Paywalls`: add support for displaying
`StoreProductDiscount`s (#2796)
*  NachoSoto: `Paywalls`: added support for variables (#2793)
* NachoSoto: `Paywalls`: using `PaywallData` and setting up basic
template loading (#2781)
*  NachoSoto: `Paywalls`: initial configuration types (#2780)
*  NachoSoto: `Paywalls`: initial `RevenueCatUI` target setup (#2776)

</details>

### Other Changes

* `Debug`: add `Offering` metadata to debug screen (#3137) via NachoSoto
(@NachoSoto)
* `TestStoreProduct`: new `locale` parameter (#3134) via NachoSoto
(@NachoSoto)
* `Integration Tests`: fixed more flaky failures (#3218) via NachoSoto
(@NachoSoto)

---------

Co-authored-by: NachoSoto <ignaciosoto90@gmail.com>
Co-authored-by: NachoSoto <NachoSoto@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants