diff --git a/RevenueCat.xcodeproj/project.pbxproj b/RevenueCat.xcodeproj/project.pbxproj index 8593a14a59..3b6a9162b8 100644 --- a/RevenueCat.xcodeproj/project.pbxproj +++ b/RevenueCat.xcodeproj/project.pbxproj @@ -670,6 +670,9 @@ 57FFD2522922DBED00A9A878 /* MockStoreTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57FFD2502922DBED00A9A878 /* MockStoreTransaction.swift */; }; 6E38843A0CAFD551013D0A3F /* StoreProduct.swift in Sources */ = {isa = PBXBuildFile; fileRef = FECF627761D375C8431EB866 /* StoreProduct.swift */; }; 7706ED3E2C6E374D0004B9F9 /* ButtonStyles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7706ED3D2C6E374D0004B9F9 /* ButtonStyles.swift */; }; + 7707A94C2CAD93AC006E0313 /* PaywallButtonComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7707A94B2CAD93AC006E0313 /* PaywallButtonComponent.swift */; }; + 7707A94E2CAD94D2006E0313 /* ButtonComponentViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7707A94D2CAD94D2006E0313 /* ButtonComponentViewModel.swift */; }; + 7707A9502CAD9775006E0313 /* ButtonComponentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7707A94F2CAD9775006E0313 /* ButtonComponentView.swift */; }; 77372D992C6F8C7B008E59D3 /* AppUpdateWarningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77372D982C6F8C7B008E59D3 /* AppUpdateWarningView.swift */; }; 77791ECF2C6B852000BCEF03 /* SemanticVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77791ECE2C6B851F00BCEF03 /* SemanticVersion.swift */; }; 777FB4882C661C0600CD4749 /* SemanticVersionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 777FB4872C661C0600CD4749 /* SemanticVersionTests.swift */; }; @@ -1820,6 +1823,9 @@ 57FFD2502922DBED00A9A878 /* MockStoreTransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockStoreTransaction.swift; sourceTree = ""; }; 7706ED3D2C6E374D0004B9F9 /* ButtonStyles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonStyles.swift; sourceTree = ""; }; 7707A93B2CAD8AA2006E0313 /* Local.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Local.xcconfig; sourceTree = ""; }; + 7707A94B2CAD93AC006E0313 /* PaywallButtonComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaywallButtonComponent.swift; sourceTree = ""; }; + 7707A94D2CAD94D2006E0313 /* ButtonComponentViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonComponentViewModel.swift; sourceTree = ""; }; + 7707A94F2CAD9775006E0313 /* ButtonComponentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonComponentView.swift; sourceTree = ""; }; 77372D982C6F8C7B008E59D3 /* AppUpdateWarningView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppUpdateWarningView.swift; sourceTree = ""; }; 77607FD72CAD87D60066C23C /* Global.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Global.xcconfig; sourceTree = ""; }; 77791ECE2C6B851F00BCEF03 /* SemanticVersion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SemanticVersion.swift; sourceTree = ""; }; @@ -3914,6 +3920,15 @@ path = Security; sourceTree = ""; }; + 7707A94A2CAD936A006E0313 /* Button */ = { + isa = PBXGroup; + children = ( + 7707A94D2CAD94D2006E0313 /* ButtonComponentViewModel.swift */, + 7707A94F2CAD9775006E0313 /* ButtonComponentView.swift */, + ); + path = Button; + sourceTree = ""; + }; 887A5FBB2C1D036200E1A461 /* RevenueCatUIDev */ = { isa = PBXGroup; children = ( @@ -4270,6 +4285,7 @@ isa = PBXGroup; children = ( 88AD01012C740CF400AA1F2B /* PaywallComponentBase.swift */, + 7707A94B2CAD93AC006E0313 /* PaywallButtonComponent.swift */, 88EA80EE2C87D68F003E6675 /* PaywallComponentLocalization.swift */, 88B1BAD72C812D72001B7EE5 /* PaywallComponentPropertyTypes.swift */, 88AD01032C740CF400AA1F2B /* PaywallImageComponent.swift */, @@ -4284,6 +4300,7 @@ 88AD01352C74196600AA1F2B /* Components */ = { isa = PBXGroup; children = ( + 7707A94A2CAD936A006E0313 /* Button */, 2C2AEB0D2CA64DA900A50F38 /* Previews */, 88B1BAE62C813A3C001B7EE5 /* Image */, 88B1BAE22C813A3C001B7EE5 /* LinkButton */, @@ -5512,6 +5529,7 @@ B32B74FF26868AEB005647BF /* Package.swift in Sources */, 353756522C382BC700A1B8D6 /* PreferredLocalesProvider.swift in Sources */, 35D159CF2BC43B89004D8061 /* DiagnosticsSynchronizer.swift in Sources */, + 7707A94C2CAD93AC006E0313 /* PaywallButtonComponent.swift in Sources */, 578DAA482948EEAD001700FD /* Clock.swift in Sources */, 2DDF41B324F6F387005BC22D /* InAppPurchaseBuilder.swift in Sources */, 4F4FF3E12A3B731A0028018C /* ETagStrings.swift in Sources */, @@ -5975,6 +5993,7 @@ 887A60702C1D037000E1A461 /* PaywallTemplate.swift in Sources */, 887A60882C1D037000E1A461 /* MockPurchases.swift in Sources */, 887A60BC2C1D037000E1A461 /* Template5View.swift in Sources */, + 7707A9502CAD9775006E0313 /* ButtonComponentView.swift in Sources */, 887A60BE2C1D037000E1A461 /* PaywallFooterViewController.swift in Sources */, 887A608A2C1D037000E1A461 /* PurchaseHandler.swift in Sources */, 2D2AFE8D2C6A834D00D1B0B4 /* TestData.swift in Sources */, @@ -6065,6 +6084,7 @@ 887A60BF2C1D037000E1A461 /* PaywallViewController.swift in Sources */, 887A60772C1D037000E1A461 /* TemplateViewConfiguration+Images.swift in Sources */, 3537566A2C382C2800A1B8D6 /* ManageSubscriptionsViewModel.swift in Sources */, + 7707A94E2CAD94D2006E0313 /* ButtonComponentViewModel.swift in Sources */, 887A60842C1D037000E1A461 /* ConsistentPackageContentView.swift in Sources */, 887A60C42C1D037000E1A461 /* IconView.swift in Sources */, 887A60732C1D037000E1A461 /* ProcessedLocalizedConfiguration.swift in Sources */, diff --git a/RevenueCatUI/Templates/Components/Button/ButtonComponentView.swift b/RevenueCatUI/Templates/Components/Button/ButtonComponentView.swift new file mode 100644 index 0000000000..37870daed5 --- /dev/null +++ b/RevenueCatUI/Templates/Components/Button/ButtonComponentView.swift @@ -0,0 +1,73 @@ +// +// 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 +// +// ButtonComponentView.swift +// +// Created by Jay Shortway on 02/10/2024. + +import Foundation +import RevenueCat +import SwiftUI + +#if PAYWALL_COMPONENTS + +@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) +struct ButtonComponentView: View { + + private let viewModel: ButtonComponentViewModel + + internal init(viewModel: ButtonComponentViewModel) { + self.viewModel = viewModel + } + + var body: some View { + StackComponentView(viewModel: viewModel.stackViewModel) + } + +} + +#if DEBUG + +@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) +struct ButtonComponentView_Previews: PreviewProvider { + + static var previews: some View { + VStack { + ButtonComponentView( + // swiftlint:disable:next force_try + viewModel: try! .init( + component: .init( + stack: .init( + components: [ + PaywallComponent.text( + PaywallComponent.TextComponent( + textLid: "buttonText", + color: .init(light: "#000000") + ) + ) + ], + backgroundColor: nil + ) + ), + locale: Locale(identifier: "en_US"), + localizedStrings: [ + "buttonText": PaywallComponentsData.LocalizationData.string("Do something") + ], + offering: Offering(identifier: "", serverDescription: "", availablePackages: []) + ) + ) + } + .previewLayout(.fixed(width: 400, height: 400)) + .previewDisplayName("Default") + } +} + +#endif + +#endif diff --git a/RevenueCatUI/Templates/Components/Button/ButtonComponentViewModel.swift b/RevenueCatUI/Templates/Components/Button/ButtonComponentViewModel.swift new file mode 100644 index 0000000000..148dd779ef --- /dev/null +++ b/RevenueCatUI/Templates/Components/Button/ButtonComponentViewModel.swift @@ -0,0 +1,44 @@ +// +// 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 +// +// ButtonComponentViewModel.swift +// +// Created by Jay Shortway on 02/10/2024. +// +// swiftlint:disable missing_docs + +import Foundation +import RevenueCat + +#if PAYWALL_COMPONENTS + +@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) +public class ButtonComponentViewModel { + + private let component: PaywallComponent.ButtonComponent + let stackViewModel: StackComponentViewModel + + init( + component: PaywallComponent.ButtonComponent, + locale: Locale, + localizedStrings: PaywallComponent.LocalizationDictionary, + offering: Offering + ) throws { + self.component = component + self.stackViewModel = try StackComponentViewModel( + locale: locale, + component: component.stack, + localizedStrings: localizedStrings, + offering: offering + ) + } + +} + +#endif diff --git a/RevenueCatUI/Templates/Components/PaywallComponentViewModel.swift b/RevenueCatUI/Templates/Components/PaywallComponentViewModel.swift index 7aaac9e3d8..fc8d6c30e1 100644 --- a/RevenueCatUI/Templates/Components/PaywallComponentViewModel.swift +++ b/RevenueCatUI/Templates/Components/PaywallComponentViewModel.swift @@ -19,6 +19,7 @@ enum PaywallComponentViewModel { case spacer(SpacerComponentViewModel) case stack(StackComponentViewModel) case linkButton(LinkButtonComponentViewModel) + case button(ButtonComponentViewModel) } @@ -55,6 +56,15 @@ extension PaywallComponent { try LinkButtonComponentViewModel(component: component, localizedStrings: localizedStrings) ) + case .button(let component): + return .button( + try ButtonComponentViewModel( + component: component, + locale: locale, + localizedStrings: localizedStrings, + offering: offering + ) + ) } } diff --git a/RevenueCatUI/Templates/Components/TemplateComponentsView.swift b/RevenueCatUI/Templates/Components/TemplateComponentsView.swift index 40e319a7fb..6178218cbf 100644 --- a/RevenueCatUI/Templates/Components/TemplateComponentsView.swift +++ b/RevenueCatUI/Templates/Components/TemplateComponentsView.swift @@ -112,6 +112,8 @@ struct ComponentsView: View { StackComponentView(viewModel: viewModel) case .linkButton(let viewModel): LinkButtonComponentView(viewModel: viewModel) + case .button(let viewModel): + ButtonComponentView(viewModel: viewModel) } } } diff --git a/Sources/Paywalls/Components/PaywallButtonComponent.swift b/Sources/Paywalls/Components/PaywallButtonComponent.swift new file mode 100644 index 0000000000..aa8b0e696d --- /dev/null +++ b/Sources/Paywalls/Components/PaywallButtonComponent.swift @@ -0,0 +1,38 @@ +// +// 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 +// +// PaywallButtonComponent.swift +// +// Created by Jay Shortway on 02/10/2024. +// +// swiftlint:disable missing_docs + +import Foundation + +#if PAYWALL_COMPONENTS + +public extension PaywallComponent { + + struct ButtonComponent: PaywallComponentBase { + + let type: ComponentType + public let stack: PaywallComponent.StackComponent + + public init( + stack: PaywallComponent.StackComponent + ) { + self.type = .button + self.stack = stack + } + + } + +} + +#endif diff --git a/Sources/Paywalls/Components/PaywallComponentBase.swift b/Sources/Paywalls/Components/PaywallComponentBase.swift index e67a019b3f..0ec508a5a7 100644 --- a/Sources/Paywalls/Components/PaywallComponentBase.swift +++ b/Sources/Paywalls/Components/PaywallComponentBase.swift @@ -18,6 +18,7 @@ public enum PaywallComponent: PaywallComponentBase { case spacer(SpacerComponent) case stack(StackComponent) case linkButton(LinkButtonComponent) + case button(ButtonComponent) public enum ComponentType: String, Codable, Sendable { @@ -26,6 +27,7 @@ public enum PaywallComponent: PaywallComponentBase { case spacer case stack case linkButton = "link_button" + case button } @@ -65,6 +67,9 @@ extension PaywallComponent: Codable { case .linkButton(let component): try container.encode(ComponentType.linkButton, forKey: .type) try component.encode(to: encoder) + case .button(let component): + try container.encode(ComponentType.button, forKey: .type) + try component.encode(to: encoder) } } @@ -83,6 +88,8 @@ extension PaywallComponent: Codable { self = .stack(try StackComponent(from: decoder)) case .linkButton: self = .linkButton(try LinkButtonComponent(from: decoder)) + case .button: + self = .button(try ButtonComponent(from: decoder)) } }