Skip to content

Commit

Permalink
Paywalls: extracted intro eligibility out of templates (#2901)
Browse files Browse the repository at this point in the history
This simplifies template views further. Before they needed to get the
`TrialOrIntroEligibilityChecker` environment and call the appropriate
method.

Now they just need to inject the new `IntroEligibilityViewModel`, and
extract either `.allEligibility` or `singleEligibility` based on the
type of template. Everything else is done automatically outside of
templates.
  • Loading branch information
NachoSoto committed Aug 7, 2023
1 parent 9cde8f6 commit a9c5875
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 28 deletions.
45 changes: 45 additions & 0 deletions RevenueCatUI/Data/IntroEligibilityViewModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//
// IntroEligibilityViewModel.swift
//
//
// Created by Nacho Soto on 7/26/23.
//

import RevenueCat
import SwiftUI

/// Holds the state for dynamically computed `IntroEligibilityStatus`
/// for single or multi-package templates, depending on `PackageConfiguration`.
@available(iOS 16.0, macOS 13.0, tvOS 16.0, *)
@MainActor
final class IntroEligibilityViewModel: ObservableObject {

typealias PackageConfiguration = TemplateViewConfiguration.PackageConfiguration

private let introEligibilityChecker: TrialOrIntroEligibilityChecker

init(introEligibilityChecker: TrialOrIntroEligibilityChecker) {
self.introEligibilityChecker = introEligibilityChecker
}

@Published
private(set) var allEligibility: [Package: IntroEligibilityStatus] = [:]
@Published
private(set) var singleEligibility: IntroEligibilityStatus?

}

@available(iOS 16.0, macOS 13.0, tvOS 16.0, *)
extension IntroEligibilityViewModel {

func computeEligibility(for packages: PackageConfiguration) async {
switch packages {
case let .single(package):
self.singleEligibility = await self.introEligibilityChecker.eligibility(for: package.content)

case let .multiple(_, _, packages):
self.allEligibility = await self.introEligibilityChecker.eligibility(for: packages.map(\.content))
}
}

}
12 changes: 9 additions & 3 deletions RevenueCatUI/PaywallView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,9 @@ struct LoadedOfferingPaywallView: View {
private let offering: Offering
private let paywall: PaywallData
private let mode: PaywallViewMode
private let introEligibility: TrialOrIntroEligibilityChecker

@StateObject
private var introEligibility: IntroEligibilityViewModel
@ObservedObject
private var purchaseHandler: PurchaseHandler

Expand All @@ -152,13 +153,18 @@ struct LoadedOfferingPaywallView: View {
self.offering = offering
self.paywall = paywall
self.mode = mode
self.introEligibility = introEligibility
self._introEligibility = .init(
wrappedValue: .init(introEligibilityChecker: introEligibility)
)
self.purchaseHandler = purchaseHandler
}

var body: some View {
let view = self.paywall
.createView(for: self.offering, mode: self.mode, locale: self.locale)
.createView(for: self.offering,
mode: self.mode,
introEligibility: self.introEligibility,
locale: self.locale)
.environmentObject(self.introEligibility)
.environmentObject(self.purchaseHandler)
.hidden(if: self.shouldHidePaywall)
Expand Down
13 changes: 2 additions & 11 deletions RevenueCatUI/Templates/MultiPackageBoldTemplate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,16 @@ import SwiftUI
struct MultiPackageBoldTemplate: TemplateViewType {

private let configuration: TemplateViewConfiguration

@EnvironmentObject
private var introEligibilityChecker: TrialOrIntroEligibilityChecker

@State
private var introEligibility: [Package: IntroEligibilityStatus] = [:]
private var introEligibility: IntroEligibilityViewModel

init(_ configuration: TemplateViewConfiguration) {
self.configuration = configuration
}

var body: some View {
MultiPackageTemplateContent(configuration: self.configuration,
introEligibility: self.introEligibility)
.task(id: self.configuration.packages) {
self.introEligibility = await self.introEligibilityChecker.eligibility(
for: self.configuration.packages.all.map(\.content)
)
}
introEligibility: self.introEligibility.allEligibility)
}

}
Expand Down
15 changes: 2 additions & 13 deletions RevenueCatUI/Templates/SinglePackageStandardTemplate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,16 @@ import SwiftUI
struct SinglePackageStandardTemplate: TemplateViewType {

private let configuration: TemplateViewConfiguration

@EnvironmentObject
private var introEligibilityChecker: TrialOrIntroEligibilityChecker

@State
private var introEligibility: IntroEligibilityStatus?
private var introEligibility: IntroEligibilityViewModel

init(_ configuration: TemplateViewConfiguration) {
self.configuration = configuration
}

var body: some View {
SinglePackageTemplateContent(configuration: self.configuration,
introEligibility: self.introEligibility)
.task(id: self.package) {
self.introEligibility = await self.introEligibilityChecker.eligibility(for: self.package)
}
}

private var package: Package {
return self.configuration.packages.single.content
introEligibility: self.introEligibility.singleEligibility)
}

}
Expand Down
8 changes: 7 additions & 1 deletion RevenueCatUI/Templates/TemplateViewType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,16 @@ private extension PaywallTemplate {
extension PaywallData {

@ViewBuilder
func createView(for offering: Offering, mode: PaywallViewMode, locale: Locale) -> some View {
func createView(for offering: Offering,
mode: PaywallViewMode,
introEligibility: IntroEligibilityViewModel,
locale: Locale) -> some View {
switch self.configuration(for: offering, mode: mode, locale: locale) {
case let .success(configuration):
Self.createView(template: self.template, configuration: configuration)
.task(id: offering) {
await introEligibility.computeEligibility(for: configuration.packages)
}
.background(
Rectangle()
.foregroundColor(
Expand Down

0 comments on commit a9c5875

Please sign in to comment.