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

Paywall Components Initial Commit #4224

Merged
merged 66 commits into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from 55 commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
9f4419a
Paywall components POC
joshdholtz Jun 13, 2024
7d46bb2
Added toggle
joshdholtz Jun 18, 2024
1dd97de
Game controller support
joshdholtz Jul 3, 2024
b70eef9
Paywalls components/file split (#4200)
jamesrb1 Aug 21, 2024
f3bd65a
Initial Stack Component
jamesrb1 Aug 20, 2024
31af70c
v, h and z stacks
jamesrb1 Aug 20, 2024
ddc22d5
StackComponentView uses ComponentsView
jamesrb1 Aug 20, 2024
2a7b7a4
Stack spacing support
jamesrb1 Aug 20, 2024
bf3ce3d
background stack color
jamesrb1 Aug 20, 2024
5fd18f9
Proper color implementation
jamesrb1 Aug 20, 2024
84ac29a
Add vertical alignment; extend horizontal alignment; add associated v…
jamesrb1 Aug 20, 2024
a73c42d
Default alignments
jamesrb1 Aug 20, 2024
381b96b
typo
jamesrb1 Aug 20, 2024
9c754ae
Indentation
jamesrb1 Aug 21, 2024
c2f461b
Update text font/size display
jamesrb1 Aug 21, 2024
232f392
initial link button
jamesrb1 Aug 21, 2024
39d87fe
Building it out
jamesrb1 Aug 21, 2024
f048085
Custom fonts not supported for now
jamesrb1 Aug 21, 2024
4f3784e
zstack alignment
jamesrb1 Aug 21, 2024
380a8e6
Working out the demo paywall….
jamesrb1 Aug 21, 2024
6ede294
Decodable Dimension
jamesrb1 Aug 22, 2024
38c11e3
Code ordering
jamesrb1 Aug 22, 2024
714f483
Decoding paywall components response, almost working
jamesrb1 Aug 22, 2024
3574199
Show component paywalls!
jamesrb1 Aug 22, 2024
a35aa7c
Todo encoder
jamesrb1 Aug 22, 2024
4fb295b
encoding
jamesrb1 Aug 23, 2024
5622854
Re-enable test component view
jamesrb1 Aug 23, 2024
edd55f5
Allow external modification of RemoteImage
jamesrb1 Aug 23, 2024
77b3468
Support for top-to-bottom gradients with arbitrary stops; corner radi…
jamesrb1 Aug 23, 2024
0888b94
ifdef out latest additions
jamesrb1 Aug 26, 2024
495b5fd
Tidy individual component views
jamesrb1 Aug 26, 2024
6730f0d
Tidy TemplateComponentsView, ComponentsView
jamesrb1 Aug 26, 2024
bdb3d80
More tidying; complitation fixes
jamesrb1 Aug 26, 2024
c8a4460
Missing ifdef
jamesrb1 Aug 26, 2024
52f2902
Tidy PaywallComponentBase
jamesrb1 Aug 26, 2024
a05fb2b
enum spaces
jamesrb1 Aug 26, 2024
1bad03d
Spacing, lint enablement
jamesrb1 Aug 26, 2024
86a9f36
Spacing, lint enablement
jamesrb1 Aug 26, 2024
3a60e57
Fix compilation issues
jamesrb1 Aug 26, 2024
eab0c8d
Missing from prior commit
jamesrb1 Aug 26, 2024
72ad780
ComponentsView lint
jamesrb1 Aug 26, 2024
ad1dd81
Lint
jamesrb1 Aug 26, 2024
833ae17
StackComponent lint
jamesrb1 Aug 26, 2024
de4fe87
PaywallComponent lint
jamesrb1 Aug 26, 2024
80e0e81
PaywallLinkButtonComponent lint
jamesrb1 Aug 26, 2024
56a7175
TextComponent lint
jamesrb1 Aug 26, 2024
8c291b5
Docs
jamesrb1 Aug 26, 2024
61727a8
lint
jamesrb1 Aug 26, 2024
7d23d2f
lint
jamesrb1 Aug 26, 2024
742fc32
Code order
jamesrb1 Aug 26, 2024
1062ab7
Delete extra line
jamesrb1 Aug 26, 2024
bb3bf8c
Update PaywallsTester - SK config.xcscheme
jamesrb1 Aug 26, 2024
dc4fdb4
Merge branch 'main' of https://github.com/RevenueCat/purchases-ios in…
jamesrb1 Aug 26, 2024
1553917
Remove TiersComponentView from project file.
jamesrb1 Aug 27, 2024
95f4056
vision os check
jamesrb1 Aug 27, 2024
ee0ffbc
Remove paywall components home in legacy paywalls
jamesrb1 Aug 27, 2024
1614d80
Remove unused file
jamesrb1 Aug 27, 2024
7222042
Add ability to show struct-created component paywalls
jamesrb1 Aug 27, 2024
b5ff52c
Remove unused PurchaseButton init
jamesrb1 Aug 27, 2024
48e50ac
Restore fade animation for images
jamesrb1 Aug 27, 2024
5fa016d
No more hard coded strings for component types
jamesrb1 Aug 28, 2024
dba53ef
Move #import SwiftUI behind a flag
jamesrb1 Aug 28, 2024
1a5961b
Single line protocol definition
jamesrb1 Aug 28, 2024
76f1ddb
Make sample URLs vars
jamesrb1 Aug 28, 2024
116dc9a
typo
jamesrb1 Aug 28, 2024
8d0bbc0
Lint
jamesrb1 Aug 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions RevenueCat.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions RevenueCatUI/Data/PaywallTemplate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ enum PaywallTemplate: String {
case template4 = "4"
case template5 = "5"
case template7 = "7"
#if PAYWALL_COMPONENTS
case templateComponents = "components"
#endif

}

Expand Down
3 changes: 3 additions & 0 deletions RevenueCatUI/Data/TemplateViewConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ struct TemplateViewConfiguration {
let fonts: PaywallFontProvider
let assetBaseURL: URL
let showZeroDecimalPlacePrices: Bool
#if PAYWALL_COMPONENTS
let components: PaywallComponent.Data?
#endif

}

Expand Down
36 changes: 36 additions & 0 deletions RevenueCatUI/PaywallView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -219,13 +219,48 @@ public struct PaywallView: View {
}

@ViewBuilder
// swiftlint:disable:next function_body_length
private func paywallView(
for offering: Offering,
activelySubscribedProductIdentifiers: Set<String>,
fonts: PaywallFontProvider,
checker: TrialOrIntroEligibilityChecker,
purchaseHandler: PurchaseHandler
) -> some View {

#if PAYWALL_COMPONENTS
if let componentData = offering.paywallComponentsData {
TemplateComponentsView(paywallComponentsData: componentData)
} else {
Comment on lines +232 to +234
Copy link
Member

Choose a reason for hiding this comment

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

Reviewer note: this will be reworked in future PR with proper loading and validation ☝️


let (paywall, displayedLocale, template, error) = offering.validatedPaywall(locale: self.locale)

let paywallView = LoadedOfferingPaywallView(
offering: offering,
activelySubscribedProductIdentifiers: activelySubscribedProductIdentifiers,
paywall: paywall,
template: template,
mode: self.mode,
fonts: fonts,
displayCloseButton: self.displayCloseButton,
introEligibility: checker,
purchaseHandler: purchaseHandler,
locale: displayedLocale
)

if let error {
DebugErrorView(
"\(error.description)\n" +
"You can fix this by editing the paywall in the RevenueCat dashboard.\n" +
"The displayed paywall contains default configuration.\n" +
"This error will be hidden in production.",
replacement: paywallView
Comment on lines +252 to +257
Copy link
Member

Choose a reason for hiding this comment

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

[Not for this PR]
I was about to comment that we should move this to one of the strings files, and then I realized that that's part of the main SDK. Maybe we should start thinking about extracting a Core framework to be used by both RevenueCat and RevenueCatUI (we actually used to have one a few years back)

)
} else {
paywallView
}
}
#else
let (paywall, displayedLocale, template, error) = offering.validatedPaywall(locale: self.locale)

let paywallView = LoadedOfferingPaywallView(
Expand All @@ -252,6 +287,7 @@ public struct PaywallView: View {
} else {
paywallView
}
#endif
}

// MARK: -
Expand Down
53 changes: 53 additions & 0 deletions RevenueCatUI/Templates/Components/ImageComponentView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//
// Copyright RevenueCat Inc. All Rights Reserved.
//
// Licensed under the MIT License (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://opensource.org/licenses/MIT
//
// ImageComponentView.swift
//
// Created by Josh Holtz on 6/11/24.

import Foundation
import RevenueCat
import SwiftUI

#if PAYWALL_COMPONENTS

@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
struct ImageComponentView: View {

let locale: Locale
let component: PaywallComponent.ImageComponent

var cornerRadius: CGFloat {
component.cornerRadius
}

var gradientColors: [Color] {
component.gradientColors.compactMap { try? $0.toColor() }
}

var body: some View {
RemoteImage(url: component.url) { image in
image
.resizable()
.aspectRatio(contentMode: .fit)
.overlay(
LinearGradient(
gradient: Gradient(colors: gradientColors),
startPoint: .top,
endPoint: .bottom
)
)
.cornerRadius(cornerRadius)
}
.clipped()
}

}

#endif
33 changes: 33 additions & 0 deletions RevenueCatUI/Templates/Components/LinkButtonComponentView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// LinkButtonComponentView.swift
//
//
// Created by James Borthwick on 2024-08-21.
//

import Foundation
import RevenueCat
import SwiftUI

#if PAYWALL_COMPONENTS

@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
struct LinkButtonComponentView: View {

let locale: Locale
let component: PaywallComponent.LinkButtonComponent

var url: URL {
component.url
}

var body: some View {
Link(destination: url) {
TextComponentView(locale: locale, component: component.textComponent)
.cornerRadius(25)
}
}

}

#endif
32 changes: 32 additions & 0 deletions RevenueCatUI/Templates/Components/SpacerComponentView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// Copyright RevenueCat Inc. All Rights Reserved.
//
// Licensed under the MIT License (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://opensource.org/licenses/MIT
//
// SpacerComponentView.swift
//
// Created by James Borthwick on 2024-08-19.

import Foundation
import RevenueCat
import SwiftUI

#if PAYWALL_COMPONENTS

@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
struct SpacerComponentView: View {

let locale: Locale
let component: PaywallComponent.SpacerComponent

var body: some View {
Spacer()
}

}

#endif
71 changes: 71 additions & 0 deletions RevenueCatUI/Templates/Components/StackComponentView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//
// Copyright RevenueCat Inc. All Rights Reserved.
//
// Licensed under the MIT License (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://opensource.org/licenses/MIT
//
// StackComponentView.swift
//
// Created by James Borthwick on 2024-08-20.

import RevenueCat
import SwiftUI

#if PAYWALL_COMPONENTS

@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
struct StackComponentView: View {

let component: PaywallComponent.StackComponent

var dimension: PaywallComponent.StackComponent.Dimension {
component.dimension
}
var components: [PaywallComponent] {
component.components
}

var spacing: CGFloat? {
component.spacing
}

var backgroundColor: Color {
if let lightColor = component.backgroundColor?.light {
return (try? PaywallColor(stringRepresentation: lightColor).underlyingColor) ?? Color.clear
}
return Color.clear
}

let locale: Locale

init(component: PaywallComponent.StackComponent, locale: Locale) {
self.component = component
self.locale = locale
}

var body: some View {
switch dimension {
case .vertical(let horizontalAlignment):
VStack(alignment: horizontalAlignment.stackAlignment, spacing: spacing) {
ComponentsView(locale: locale, components: components)
}
.background(backgroundColor)
case .horizontal(let verticalAlignment):
HStack(alignment: verticalAlignment.stackAlignment, spacing: spacing) {
ComponentsView(locale: locale, components: components)
}
.background(backgroundColor)
case .zlayer(let alignment):
ZStack(alignment: alignment.stackAlignment) {
ComponentsView(locale: locale, components: components)
}
.background(backgroundColor)
}
}

}

#endif
82 changes: 82 additions & 0 deletions RevenueCatUI/Templates/Components/TemplateComponentsView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//
// File.swift
//
//
// Created by Josh Holtz on 6/11/24.
//

import RevenueCat
import SwiftUI

#if PAYWALL_COMPONENTS
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
struct TemplateComponentsView: View {

@Environment(\.locale)
var locale

let paywallComponentsData: PaywallComponentsData

init(paywallComponentsData: PaywallComponentsData) {
self.paywallComponentsData = paywallComponentsData
}

var body: some View {
VStack(spacing: 0) {
ComponentsView(
locale: self.locale,
components: paywallComponentsData.componentsConfig.components
)
}
.edgesIgnoringSafeArea(.top)
}

}

func getLocalization(_ locale: Locale, _ displayString: DisplayString) -> String {
if let found = displayString.value[locale.identifier] {
return found
}

return displayString.value.values.first!
}

@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
struct ComponentsView: View {

let locale: Locale
let components: [PaywallComponent]

init(locale: Locale, components: [PaywallComponent]) {
self.locale = locale
self.components = components
}

var body: some View {
self.layoutComponents(self.components)
}

@ViewBuilder
func layoutComponents(_ layoutComponentsArray: [PaywallComponent]) -> some View {
ForEach(Array(layoutComponentsArray.enumerated()), id: \.offset) { _, item in
switch item {
case .text(let component):
TextComponentView(locale: locale, component: component)
case .image(let component):
ImageComponentView(locale: locale, component: component)
case .spacer(let component):
SpacerComponentView(
locale: locale,
component: component
)
case .stack(let component):
StackComponentView(component: component, locale: locale)
case .linkButton(let component):
LinkButtonComponentView(locale: locale, component: component)
}
}
}

}

#endif
Loading