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

Adds scaffolding for the ButtonComponent. #4348

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
20 changes: 20 additions & 0 deletions RevenueCat.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 */; };
Expand Down Expand Up @@ -1820,6 +1823,9 @@
57FFD2502922DBED00A9A878 /* MockStoreTransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockStoreTransaction.swift; sourceTree = "<group>"; };
7706ED3D2C6E374D0004B9F9 /* ButtonStyles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonStyles.swift; sourceTree = "<group>"; };
7707A93B2CAD8AA2006E0313 /* Local.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Local.xcconfig; sourceTree = "<group>"; };
7707A94B2CAD93AC006E0313 /* PaywallButtonComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaywallButtonComponent.swift; sourceTree = "<group>"; };
7707A94D2CAD94D2006E0313 /* ButtonComponentViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonComponentViewModel.swift; sourceTree = "<group>"; };
7707A94F2CAD9775006E0313 /* ButtonComponentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonComponentView.swift; sourceTree = "<group>"; };
77372D982C6F8C7B008E59D3 /* AppUpdateWarningView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppUpdateWarningView.swift; sourceTree = "<group>"; };
77607FD72CAD87D60066C23C /* Global.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Global.xcconfig; sourceTree = "<group>"; };
77791ECE2C6B851F00BCEF03 /* SemanticVersion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SemanticVersion.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3914,6 +3920,15 @@
path = Security;
sourceTree = "<group>";
};
7707A94A2CAD936A006E0313 /* Button */ = {
isa = PBXGroup;
children = (
7707A94D2CAD94D2006E0313 /* ButtonComponentViewModel.swift */,
7707A94F2CAD9775006E0313 /* ButtonComponentView.swift */,
);
path = Button;
sourceTree = "<group>";
};
887A5FBB2C1D036200E1A461 /* RevenueCatUIDev */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -4270,6 +4285,7 @@
isa = PBXGroup;
children = (
88AD01012C740CF400AA1F2B /* PaywallComponentBase.swift */,
7707A94B2CAD93AC006E0313 /* PaywallButtonComponent.swift */,
88EA80EE2C87D68F003E6675 /* PaywallComponentLocalization.swift */,
88B1BAD72C812D72001B7EE5 /* PaywallComponentPropertyTypes.swift */,
88AD01032C740CF400AA1F2B /* PaywallImageComponent.swift */,
Expand All @@ -4284,6 +4300,7 @@
88AD01352C74196600AA1F2B /* Components */ = {
isa = PBXGroup;
children = (
7707A94A2CAD936A006E0313 /* Button */,
2C2AEB0D2CA64DA900A50F38 /* Previews */,
88B1BAE62C813A3C001B7EE5 /* Image */,
88B1BAE22C813A3C001B7EE5 /* LinkButton */,
Expand Down Expand Up @@ -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 */,
Expand Down Expand Up @@ -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 */,
Expand Down Expand Up @@ -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 */,
Expand Down
65 changes: 65 additions & 0 deletions RevenueCatUI/Templates/Components/Button/ButtonComponentView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//
// 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 {
EmptyView()
JayShortway marked this conversation as resolved.
Show resolved Hide resolved
TextComponentView(viewModel: viewModel.textViewModel)
}

}

#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(
text: .init(
textLid: "buttonText",
color: .init(light: "#000000")
)
),
localizedStrings: [
"buttonText": PaywallComponentsData.LocalizationData.string("Do something")
]
)
)
}
.previewLayout(.fixed(width: 400, height: 400))
.previewDisplayName("Default")
}
}

#endif

#endif
Original file line number Diff line number Diff line change
@@ -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
//
// ButtonComponentViewModel.swift
//
// Created by Jay Shortway on 02/10/2024.

import Foundation
import RevenueCat

#if PAYWALL_COMPONENTS

@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
class ButtonComponentViewModel {

private let component: PaywallComponent.ButtonComponent
let textViewModel: TextComponentViewModel

init(
component: PaywallComponent.ButtonComponent,
localizedStrings: PaywallComponent.LocalizationDictionary
) throws {
self.component = component
self.textViewModel = try TextComponentViewModel(
localizedStrings: localizedStrings,
component: component.text
)
}

}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ enum PaywallComponentViewModel {
case spacer(SpacerComponentViewModel)
case stack(StackComponentViewModel)
case linkButton(LinkButtonComponentViewModel)
case button(ButtonComponentViewModel)

}

Expand Down Expand Up @@ -55,6 +56,13 @@ extension PaywallComponent {
try LinkButtonComponentViewModel(component: component,
localizedStrings: localizedStrings)
)
case .button(let component):
return .button(
try ButtonComponentViewModel(
component: component,
localizedStrings: localizedStrings
)
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ struct ComponentsView: View {
StackComponentView(viewModel: viewModel)
case .linkButton(let viewModel):
LinkButtonComponentView(viewModel: viewModel)
case .button(let viewModel):
ButtonComponentView(viewModel: viewModel)
}
}
}
Expand Down
38 changes: 38 additions & 0 deletions Sources/Paywalls/Components/PaywallButtonComponent.swift
Original file line number Diff line number Diff line change
@@ -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 text: PaywallComponent.TextComponent
Copy link
Member

Choose a reason for hiding this comment

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

This looks similar to the LinkButton where it has aTextComponent inside of it (which makes sense) but I totally forgot to delete the LinkComponent and mention when why it was build that way 😅 It was built for very early demo purpose and just reused TextComponent because easy.

However, I think we are going to be leaning towards more of an protocol style instead of composition for components that have similar things here.

I started doing this with the StackableComponent in https://github.com/RevenueCat/purchases-ios/pull/4324/files#diff-ec1da30bf93d1075c4c1985847a4762e27386eb3a6cb81a81dee288d50ba2fcc

So here, we could create a new protocol called like TextableComponent (names are hard???) that have the properties that are similar between TextComponent and ButtonComponent.

My reason for this is:

Copy link
Member Author

Choose a reason for hiding this comment

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

That makes sense! However, are we going to allow people to give buttons arbitrary content, like SwiftUI and Jetpack Compose do? What about icons/images?

Copy link
Member

Choose a reason for hiding this comment

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

This is... a great question 🤔 In the current spec, the button only has text. However...

Maybe a ButtonComponent should really be a container/stack and hold a list of components... and then the dashboard component selector can give options for like TextButtonComponent or IconButtonComponent that adds a button with those sub components in it already as a template?

Does that make sense? If so... we should bring this up to the Dan and Barbara 😇

Copy link
Member Author

Choose a reason for hiding this comment

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

Yea that does make sense! And then things like "leading icon" vs "trailing icon" are just the same (horizontal) stack, but in a different order. I'll bring it up!

Maybe a ButtonComponent should really be a container/stack

Yea, or it could have a single StackComponent child?

Copy link
Member Author

Choose a reason for hiding this comment

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

Maybe making buttons behave like stacks (in whatever implementation) might be good to do regardless, to not limit our future selves. 🤔


public init(
text: PaywallComponent.TextComponent
) {
self.type = .button
self.text = text
}

}

}

#endif
7 changes: 7 additions & 0 deletions Sources/Paywalls/Components/PaywallComponentBase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand All @@ -26,6 +27,7 @@ public enum PaywallComponent: PaywallComponentBase {
case spacer
case stack
case linkButton = "link_button"
case button

}

Expand Down Expand Up @@ -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)
}
}

Expand All @@ -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))
}
}

Expand Down