From 90699cc751dc1dd1ec5c77dcf0de7acd6bc9e32e Mon Sep 17 00:00:00 2001 From: Josh Holtz Date: Sun, 5 Jan 2025 09:17:28 -0600 Subject: [PATCH 1/2] Support custom fonts with UIConfig --- RevenueCat.xcodeproj/project.pbxproj | 44 +------- .../Button/ButtonComponentView.swift | 1 + .../V2/Components/ComponentsView.swift | 4 - .../Components/Image/ImageComponentView.swift | 4 + .../Image/ImageComponentViewModel.swift | 8 +- .../LinkButton/LinkButtonComponentView.swift | 37 ------ .../LinkButtonComponentViewModel.swift | 38 ------- .../Package/PackageComponentView.swift | 1 + .../PurchaseButtonComponentView.swift | 1 + .../Spacer/SpacerComponentView.swift | 31 ------ .../Spacer/SpacerComponentViewModel.swift | 32 ------ .../Components/Stack/StackComponentView.swift | 3 +- .../Components/Text/TextComponentView.swift | 105 +++++++++++++++++- .../Text/TextComponentViewModel.swift | 17 ++- .../Templates/V2/PaywallsV2View.swift | 7 +- .../Templates/V2/Previews/PreviewMock.swift | 21 ++++ .../FallbackComponentPreview.swift | 3 +- .../PaywallComponentTypeTransformers.swift | 24 +++- .../PaywallComponentViewModel.swift | 2 - .../ViewModelHelpers/UIConfigProvider.swift | 45 ++++++++ .../ViewModelHelpers/ViewModelFactory.swift | 48 +++++--- .../Common/PaywallComponentBase.swift | 14 --- .../PaywallLinkButtonComponent.swift | 34 ------ .../Components/PaywallSpacerComponent.swift | 27 ----- 24 files changed, 258 insertions(+), 293 deletions(-) delete mode 100644 RevenueCatUI/Templates/V2/Components/LinkButton/LinkButtonComponentView.swift delete mode 100644 RevenueCatUI/Templates/V2/Components/LinkButton/LinkButtonComponentViewModel.swift delete mode 100644 RevenueCatUI/Templates/V2/Components/Spacer/SpacerComponentView.swift delete mode 100644 RevenueCatUI/Templates/V2/Components/Spacer/SpacerComponentViewModel.swift create mode 100644 RevenueCatUI/Templates/V2/ViewModelHelpers/UIConfigProvider.swift delete mode 100644 Sources/Paywalls/Components/PaywallLinkButtonComponent.swift delete mode 100644 Sources/Paywalls/Components/PaywallSpacerComponent.swift diff --git a/RevenueCat.xcodeproj/project.pbxproj b/RevenueCat.xcodeproj/project.pbxproj index 00e00d0d9b..c7013ef8df 100644 --- a/RevenueCat.xcodeproj/project.pbxproj +++ b/RevenueCat.xcodeproj/project.pbxproj @@ -13,6 +13,7 @@ 03A98D302D240C41009BCA61 /* UIConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03A98D2F2D240C3E009BCA61 /* UIConfig.swift */; }; 03A98D322D2441B8009BCA61 /* PaywallDataDecodingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03A98D312D2441B2009BCA61 /* PaywallDataDecodingTests.swift */; }; 03A98D362D244329009BCA61 /* UIConfigDecodingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03A98D352D244321009BCA61 /* UIConfigDecodingTests.swift */; }; + 03A98D382D2AC63B009BCA61 /* UIConfigProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03A98D372D2AC637009BCA61 /* UIConfigProvider.swift */; }; 1E2F910B2CC8FE5600BDB016 /* ContactSupportUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E2F910A2CC8FE5600BDB016 /* ContactSupportUtilities.swift */; }; 1E2F911B2CC918ED00BDB016 /* ContactSupportUtilitiesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E2F911A2CC918ED00BDB016 /* ContactSupportUtilitiesTests.swift */; }; 1E2F91722CCFA98C00BDB016 /* WebRedemptionStrings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E2F91712CCFA98C00BDB016 /* WebRedemptionStrings.swift */; }; @@ -901,22 +902,16 @@ 887A634E2C1D17EF00E1A461 /* OHHTTPStubsSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 887A634D2C1D17EF00E1A461 /* OHHTTPStubsSwift */; }; 887A63502C1D17EF00E1A461 /* SnapshotTesting in Frameworks */ = {isa = PBXBuildFile; productRef = 887A634F2C1D17EF00E1A461 /* SnapshotTesting */; }; 887E7B592C13CD2C002977DE /* PurchasesAreCompletedBy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 887E7B582C13CD2C002977DE /* PurchasesAreCompletedBy.swift */; }; - 8893C9B52C7D1F090060B030 /* PaywallLinkButtonComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8893C9B42C7D1F090060B030 /* PaywallLinkButtonComponent.swift */; }; 88A543DF2C37A45B0039C6A5 /* TemplatePackageSetting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88A543DE2C37A45B0039C6A5 /* TemplatePackageSetting.swift */; }; 88A543E12C37A4820039C6A5 /* TemplateView+MultiTier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88A543E02C37A4820039C6A5 /* TemplateView+MultiTier.swift */; }; 88A543E32C37A4970039C6A5 /* Template7View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88A543E22C37A4970039C6A5 /* Template7View.swift */; }; 88A543E52C37A4AF0039C6A5 /* ConsistentTierContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88A543E42C37A4AF0039C6A5 /* ConsistentTierContentView.swift */; }; 88A543E72C37A4C40039C6A5 /* TierSelectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88A543E62C37A4C40039C6A5 /* TierSelectorView.swift */; }; 88AD010F2C740CF400AA1F2B /* PaywallImageComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88AD01032C740CF400AA1F2B /* PaywallImageComponent.swift */; }; - 88AD01122C740CF400AA1F2B /* PaywallSpacerComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88AD01062C740CF400AA1F2B /* PaywallSpacerComponent.swift */; }; 88AD01132C740CF400AA1F2B /* PaywallTextComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88AD01072C740CF400AA1F2B /* PaywallTextComponent.swift */; }; 88AD4C482C24E8EA00943C3E /* ExternalPurchaseAndRestoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88AD4C472C24E8EA00943C3E /* ExternalPurchaseAndRestoreTests.swift */; }; 88B1BAEE2C813A3C001B7EE5 /* TextComponentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88B1BADA2C813A3C001B7EE5 /* TextComponentView.swift */; }; 88B1BAF02C813A3C001B7EE5 /* TextComponentViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88B1BADB2C813A3C001B7EE5 /* TextComponentViewModel.swift */; }; - 88B1BAF22C813A3C001B7EE5 /* SpacerComponentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88B1BADD2C813A3C001B7EE5 /* SpacerComponentView.swift */; }; - 88B1BAF42C813A3C001B7EE5 /* SpacerComponentViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88B1BADE2C813A3C001B7EE5 /* SpacerComponentViewModel.swift */; }; - 88B1BAF62C813A3C001B7EE5 /* LinkButtonComponentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88B1BAE02C813A3C001B7EE5 /* LinkButtonComponentView.swift */; }; - 88B1BAF82C813A3C001B7EE5 /* LinkButtonComponentViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88B1BAE12C813A3C001B7EE5 /* LinkButtonComponentViewModel.swift */; }; 88B1BAFA2C813A3C001B7EE5 /* PaywallsV2View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88B1BAE32C813A3C001B7EE5 /* PaywallsV2View.swift */; }; 88B1BAFC2C813A3C001B7EE5 /* ImageComponentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88B1BAE42C813A3C001B7EE5 /* ImageComponentView.swift */; }; 88B1BAFE2C813A3C001B7EE5 /* ImageComponentViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88B1BAE52C813A3C001B7EE5 /* ImageComponentViewModel.swift */; }; @@ -1248,6 +1243,7 @@ 03A98D2F2D240C3E009BCA61 /* UIConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIConfig.swift; sourceTree = ""; }; 03A98D312D2441B2009BCA61 /* PaywallDataDecodingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaywallDataDecodingTests.swift; sourceTree = ""; }; 03A98D352D244321009BCA61 /* UIConfigDecodingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIConfigDecodingTests.swift; sourceTree = ""; }; + 03A98D372D2AC637009BCA61 /* UIConfigProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIConfigProvider.swift; sourceTree = ""; }; 1E2F910A2CC8FE5600BDB016 /* ContactSupportUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactSupportUtilities.swift; sourceTree = ""; }; 1E2F911A2CC918ED00BDB016 /* ContactSupportUtilitiesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactSupportUtilitiesTests.swift; sourceTree = ""; }; 1E2F91712CCFA98C00BDB016 /* WebRedemptionStrings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebRedemptionStrings.swift; sourceTree = ""; }; @@ -2173,22 +2169,16 @@ 887A63212C1D174200E1A461 /* RevenueCatUITestsDev.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RevenueCatUITestsDev.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 887A63482C1D17A200E1A461 /* RevenueCatUIDev.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; name = RevenueCatUIDev.xctestplan; path = RevenueCatUI/RevenueCatUIDev.xctestplan; sourceTree = SOURCE_ROOT; }; 887E7B582C13CD2C002977DE /* PurchasesAreCompletedBy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PurchasesAreCompletedBy.swift; sourceTree = ""; }; - 8893C9B42C7D1F090060B030 /* PaywallLinkButtonComponent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PaywallLinkButtonComponent.swift; sourceTree = ""; }; 88A543DE2C37A45B0039C6A5 /* TemplatePackageSetting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TemplatePackageSetting.swift; path = RevenueCatUI/Views/TemplatePackageSetting.swift; sourceTree = SOURCE_ROOT; }; 88A543E02C37A4820039C6A5 /* TemplateView+MultiTier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TemplateView+MultiTier.swift"; sourceTree = ""; }; 88A543E22C37A4970039C6A5 /* Template7View.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Template7View.swift; sourceTree = ""; }; 88A543E42C37A4AF0039C6A5 /* ConsistentTierContentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConsistentTierContentView.swift; sourceTree = ""; }; 88A543E62C37A4C40039C6A5 /* TierSelectorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TierSelectorView.swift; sourceTree = ""; }; 88AD01032C740CF400AA1F2B /* PaywallImageComponent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PaywallImageComponent.swift; sourceTree = ""; }; - 88AD01062C740CF400AA1F2B /* PaywallSpacerComponent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PaywallSpacerComponent.swift; sourceTree = ""; }; 88AD01072C740CF400AA1F2B /* PaywallTextComponent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PaywallTextComponent.swift; sourceTree = ""; }; 88AD4C472C24E8EA00943C3E /* ExternalPurchaseAndRestoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ExternalPurchaseAndRestoreTests.swift; path = Templates/ExternalPurchaseAndRestoreTests.swift; sourceTree = ""; }; 88B1BADA2C813A3C001B7EE5 /* TextComponentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextComponentView.swift; sourceTree = ""; }; 88B1BADB2C813A3C001B7EE5 /* TextComponentViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextComponentViewModel.swift; sourceTree = ""; }; - 88B1BADD2C813A3C001B7EE5 /* SpacerComponentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpacerComponentView.swift; sourceTree = ""; }; - 88B1BADE2C813A3C001B7EE5 /* SpacerComponentViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpacerComponentViewModel.swift; sourceTree = ""; }; - 88B1BAE02C813A3C001B7EE5 /* LinkButtonComponentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LinkButtonComponentView.swift; sourceTree = ""; }; - 88B1BAE12C813A3C001B7EE5 /* LinkButtonComponentViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LinkButtonComponentViewModel.swift; sourceTree = ""; }; 88B1BAE32C813A3C001B7EE5 /* PaywallsV2View.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PaywallsV2View.swift; sourceTree = ""; }; 88B1BAE42C813A3C001B7EE5 /* ImageComponentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageComponentView.swift; sourceTree = ""; }; 88B1BAE52C813A3C001B7EE5 /* ImageComponentViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageComponentViewModel.swift; sourceTree = ""; }; @@ -2513,9 +2503,7 @@ 2C7457472CEA66AB004ACE52 /* ComponentsView.swift */, 7707A94A2CAD936A006E0313 /* Button */, 88B1BAE62C813A3C001B7EE5 /* Image */, - 88B1BAE22C813A3C001B7EE5 /* LinkButton */, 2CC791542CC0452100FBE120 /* Packages */, - 88B1BADF2C813A3C001B7EE5 /* Spacer */, 88B1BAEA2C813A3C001B7EE5 /* Stack */, 88B1BADC2C813A3C001B7EE5 /* Text */, 778360772CCA85D1000785B8 /* StickyFooter */, @@ -2539,6 +2527,7 @@ 2C7457452CEA653A004ACE52 /* ViewModelHelpers */ = { isa = PBXGroup; children = ( + 03A98D372D2AC637009BCA61 /* UIConfigProvider.swift */, 2C74574A2CEA6B80004ACE52 /* LocalizationProvider.swift */, 2C08B3162CDA443A0024857B /* ViewModelFactory.swift */, 2C8EC71F2CCF275E00D6CCF8 /* PresentedPartials.swift */, @@ -4752,9 +4741,7 @@ 2CC791612CC0493600FBE120 /* Common */, 7707A94B2CAD93AC006E0313 /* PaywallButtonComponent.swift */, 88AD01032C740CF400AA1F2B /* PaywallImageComponent.swift */, - 88AD01062C740CF400AA1F2B /* PaywallSpacerComponent.swift */, 88E679462C7503C1007E69D5 /* PaywallStackComponent.swift */, - 8893C9B42C7D1F090060B030 /* PaywallLinkButtonComponent.swift */, 88AD01072C740CF400AA1F2B /* PaywallTextComponent.swift */, 2C2AEB3A2CA7209F00A50F38 /* PaywallPackageComponent.swift */, 2C2AEB3E2CA7235300A50F38 /* PaywallPurchaseButtonComponent.swift */, @@ -4785,24 +4772,6 @@ path = Text; sourceTree = ""; }; - 88B1BADF2C813A3C001B7EE5 /* Spacer */ = { - isa = PBXGroup; - children = ( - 88B1BADD2C813A3C001B7EE5 /* SpacerComponentView.swift */, - 88B1BADE2C813A3C001B7EE5 /* SpacerComponentViewModel.swift */, - ); - path = Spacer; - sourceTree = ""; - }; - 88B1BAE22C813A3C001B7EE5 /* LinkButton */ = { - isa = PBXGroup; - children = ( - 88B1BAE02C813A3C001B7EE5 /* LinkButtonComponentView.swift */, - 88B1BAE12C813A3C001B7EE5 /* LinkButtonComponentViewModel.swift */, - ); - path = LinkButton; - sourceTree = ""; - }; 88B1BAE62C813A3C001B7EE5 /* Image */ = { isa = PBXGroup; children = ( @@ -5924,7 +5893,6 @@ 2C74578A2CEE314F004ACE52 /* Background.swift in Sources */, 35AAEB492BBB17B500A12548 /* DiagnosticsEvent.swift in Sources */, 4F6EEBD92A38ED76007FD783 /* FakeSigning.swift in Sources */, - 8893C9B52C7D1F090060B030 /* PaywallLinkButtonComponent.swift in Sources */, 2DDF41AE24F6F37C005BC22D /* InAppPurchase.swift in Sources */, B32B750126868C1D005647BF /* EntitlementInfo.swift in Sources */, 57ABA76D28F08DDA003D9181 /* Either.swift in Sources */, @@ -6042,7 +6010,6 @@ 4F6ABC7E2A81700900250E63 /* PaywallsStrings.swift in Sources */, 575137CF27F50D2F0064AB2C /* HTTPResponseBody.swift in Sources */, 573E7F092819B989007C9128 /* StoreKitWorkarounds.swift in Sources */, - 88AD01122C740CF400AA1F2B /* PaywallSpacerComponent.swift in Sources */, 5791FCF32992D3EC00F1FEDA /* SigningStrings.swift in Sources */, 1E473B662AC42D34008B07F9 /* StoreMessagesHelper.swift in Sources */, B3AA6236268A81C700894871 /* EntitlementInfos.swift in Sources */, @@ -6541,7 +6508,6 @@ 2C91068E2CE2481A00189565 /* SizeModifier.swift in Sources */, 887A60B82C1D037000E1A461 /* Template1View.swift in Sources */, 887A60C62C1D037000E1A461 /* LoadingPaywallView.swift in Sources */, - 88B1BAF22C813A3C001B7EE5 /* SpacerComponentView.swift in Sources */, 77791ECF2C6B852000BCEF03 /* SemanticVersion.swift in Sources */, 2CAB87F72CAAB13200247013 /* Shape.swift in Sources */, 887A60C72C1D037000E1A461 /* PackageButtonStyle.swift in Sources */, @@ -6569,6 +6535,7 @@ 887A60BE2C1D037000E1A461 /* PaywallFooterViewController.swift in Sources */, 887A608A2C1D037000E1A461 /* PurchaseHandler.swift in Sources */, 2D2AFE8D2C6A834D00D1B0B4 /* TestData.swift in Sources */, + 03A98D382D2AC63B009BCA61 /* UIConfigProvider.swift in Sources */, FDAD6AC92D132E6500FB047E /* CustomerCenterStoreKitUtilities.swift in Sources */, 887A60C92C1D037000E1A461 /* PurchaseButton.swift in Sources */, 88B1BAF02C813A3C001B7EE5 /* TextComponentViewModel.swift in Sources */, @@ -6607,7 +6574,6 @@ 887A60BD2C1D037000E1A461 /* TemplateViewType.swift in Sources */, 887A606D2C1D037000E1A461 /* Localization.swift in Sources */, 887A60CA2C1D037000E1A461 /* RemoteImage.swift in Sources */, - 88B1BAF42C813A3C001B7EE5 /* SpacerComponentViewModel.swift in Sources */, 887A607B2C1D037000E1A461 /* Bundle+Extensions.swift in Sources */, 353756662C382C2800A1B8D6 /* CustomerCenterError.swift in Sources */, 887A60CF2C1D037000E1A461 /* View+PresentPaywallFooter.swift in Sources */, @@ -6664,7 +6630,6 @@ 35C200AF2C39252D00B9778B /* FeedbackSurveyData.swift in Sources */, 353FDC0D2CA41CB20055F328 /* SubscriptionPeriod+Extensions.swift in Sources */, 3551E39D2C4A6A1400D27C25 /* TintedProgressView.swift in Sources */, - 88B1BAF82C813A3C001B7EE5 /* LinkButtonComponentViewModel.swift in Sources */, 887A60BA2C1D037000E1A461 /* Template3View.swift in Sources */, 887A607D2C1D037000E1A461 /* ImageLoader.swift in Sources */, 4D6F4BD02CF69DE400353AF6 /* ForegroundColorScheme.swift in Sources */, @@ -6690,7 +6655,6 @@ 887A606F2C1D037000E1A461 /* PaywallData+Validation.swift in Sources */, 88A543DF2C37A45B0039C6A5 /* TemplatePackageSetting.swift in Sources */, 3546355D2C391F38001D7E85 /* PromotionalOfferViewModel.swift in Sources */, - 88B1BAF62C813A3C001B7EE5 /* LinkButtonComponentView.swift in Sources */, 35F249CA2C493D970058993A /* LoadPromotionalOfferUseCase.swift in Sources */, 887A60762C1D037000E1A461 /* TemplateViewConfiguration+Extensions.swift in Sources */, 887A607A2C1D037000E1A461 /* Variables.swift in Sources */, diff --git a/RevenueCatUI/Templates/V2/Components/Button/ButtonComponentView.swift b/RevenueCatUI/Templates/V2/Components/Button/ButtonComponentView.swift index 94597f9086..c77308ce51 100644 --- a/RevenueCatUI/Templates/V2/Components/Button/ButtonComponentView.swift +++ b/RevenueCatUI/Templates/V2/Components/Button/ButtonComponentView.swift @@ -155,6 +155,7 @@ fileprivate extension ButtonComponentViewModel { let stackViewModel = try factory.toStackViewModel( component: component.stack, localizationProvider: localizationProvider, + uiConfigProvider: .init(uiConfig: PreviewUIConfig.make()), offering: offering ) diff --git a/RevenueCatUI/Templates/V2/Components/ComponentsView.swift b/RevenueCatUI/Templates/V2/Components/ComponentsView.swift index d8c4dd13fb..96e4a6e4a9 100644 --- a/RevenueCatUI/Templates/V2/Components/ComponentsView.swift +++ b/RevenueCatUI/Templates/V2/Components/ComponentsView.swift @@ -41,12 +41,8 @@ struct ComponentsView: View { TextComponentView(viewModel: viewModel) case .image(let viewModel): ImageComponentView(viewModel: viewModel) - case .spacer(let viewModel): - SpacerComponentView(viewModel: viewModel) case .stack(let viewModel): StackComponentView(viewModel: viewModel, onDismiss: onDismiss) - case .linkButton(let viewModel): - LinkButtonComponentView(viewModel: viewModel) case .button(let viewModel): ButtonComponentView(viewModel: viewModel, onDismiss: onDismiss) case .package(let viewModel): diff --git a/RevenueCatUI/Templates/V2/Components/Image/ImageComponentView.swift b/RevenueCatUI/Templates/V2/Components/Image/ImageComponentView.swift index 4dbaef3472..760e4a9520 100644 --- a/RevenueCatUI/Templates/V2/Components/Image/ImageComponentView.swift +++ b/RevenueCatUI/Templates/V2/Components/Image/ImageComponentView.swift @@ -117,6 +117,7 @@ struct ImageComponentView_Previews: PreviewProvider { locale: Locale.current, localizedStrings: [:] ), + uiConfigProvider: .init(uiConfig: PreviewUIConfig.make()), component: .init( source: .init( light: .init( @@ -145,6 +146,7 @@ struct ImageComponentView_Previews: PreviewProvider { locale: Locale.current, localizedStrings: [:] ), + uiConfigProvider: .init(uiConfig: PreviewUIConfig.make()), component: .init( source: .init( light: .init( @@ -173,6 +175,7 @@ struct ImageComponentView_Previews: PreviewProvider { locale: Locale.current, localizedStrings: [:] ), + uiConfigProvider: .init(uiConfig: PreviewUIConfig.make()), component: .init( source: .init( light: .init( @@ -204,6 +207,7 @@ struct ImageComponentView_Previews: PreviewProvider { locale: Locale.current, localizedStrings: [:] ), + uiConfigProvider: .init(uiConfig: PreviewUIConfig.make()), component: .init( source: .init( light: .init( diff --git a/RevenueCatUI/Templates/V2/Components/Image/ImageComponentViewModel.swift b/RevenueCatUI/Templates/V2/Components/Image/ImageComponentViewModel.swift index 9e1708fd99..8644b0db69 100644 --- a/RevenueCatUI/Templates/V2/Components/Image/ImageComponentViewModel.swift +++ b/RevenueCatUI/Templates/V2/Components/Image/ImageComponentViewModel.swift @@ -24,13 +24,19 @@ class ImageComponentViewModel { private var introOfferEligibilityContext: IntroOfferEligibilityContext private let localizationProvider: LocalizationProvider + private let uiConfigProvider: UIConfigProvider private let component: PaywallComponent.ImageComponent private let imageInfo: PaywallComponent.ThemeImageUrls private let presentedOverrides: PresentedOverrides? - init(localizationProvider: LocalizationProvider, component: PaywallComponent.ImageComponent) throws { + init( + localizationProvider: LocalizationProvider, + uiConfigProvider: UIConfigProvider, + component: PaywallComponent.ImageComponent + ) throws { self.localizationProvider = localizationProvider + self.uiConfigProvider = uiConfigProvider self.component = component if let overrideSourceLid = component.overrideSourceLid { diff --git a/RevenueCatUI/Templates/V2/Components/LinkButton/LinkButtonComponentView.swift b/RevenueCatUI/Templates/V2/Components/LinkButton/LinkButtonComponentView.swift deleted file mode 100644 index d2d5317335..0000000000 --- a/RevenueCatUI/Templates/V2/Components/LinkButton/LinkButtonComponentView.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// 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 { - - private let viewModel: LinkButtonComponentViewModel - - internal init(viewModel: LinkButtonComponentViewModel) { - self.viewModel = viewModel - } - - var url: URL { - viewModel.url - } - - var body: some View { - EmptyView() - Link(destination: url) { - TextComponentView(viewModel: viewModel.textComponentViewModel) - .cornerRadius(25) - } - } - -} - -#endif diff --git a/RevenueCatUI/Templates/V2/Components/LinkButton/LinkButtonComponentViewModel.swift b/RevenueCatUI/Templates/V2/Components/LinkButton/LinkButtonComponentViewModel.swift deleted file mode 100644 index 7b0c2ccbc1..0000000000 --- a/RevenueCatUI/Templates/V2/Components/LinkButton/LinkButtonComponentViewModel.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// LinkButtonComponentView.swift -// -// -// Created by James Borthwick on 2024-08-21. -// -// swiftlint:disable missing_docs - -import Foundation -import RevenueCat -import SwiftUI - -#if PAYWALL_COMPONENTS - -@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) -public class LinkButtonComponentViewModel { - - let textComponentViewModel: TextComponentViewModel - private let component: PaywallComponent.LinkButtonComponent - - public var url: URL { - component.url - } - public var textComponent: PaywallComponent.TextComponent { - component.textComponent - } - - init(component: PaywallComponent.LinkButtonComponent, - localizationProvider: LocalizationProvider - ) throws { - self.component = component - self.textComponentViewModel = try TextComponentViewModel(localizationProvider: localizationProvider, - component: component.textComponent) - } - -} - -#endif diff --git a/RevenueCatUI/Templates/V2/Components/Packages/Package/PackageComponentView.swift b/RevenueCatUI/Templates/V2/Components/Packages/Package/PackageComponentView.swift index 9d3ce2fc07..380ea04bc5 100644 --- a/RevenueCatUI/Templates/V2/Components/Packages/Package/PackageComponentView.swift +++ b/RevenueCatUI/Templates/V2/Components/Packages/Package/PackageComponentView.swift @@ -204,6 +204,7 @@ fileprivate extension PackageComponentViewModel { let stackViewModel = try factory.toStackViewModel( component: component.stack, localizationProvider: localizationProvider, + uiConfigProvider: .init(uiConfig: PreviewUIConfig.make()), offering: offering ) diff --git a/RevenueCatUI/Templates/V2/Components/Packages/PurchaseButton/PurchaseButtonComponentView.swift b/RevenueCatUI/Templates/V2/Components/Packages/PurchaseButton/PurchaseButtonComponentView.swift index b2b4d6a964..6a7e788013 100644 --- a/RevenueCatUI/Templates/V2/Components/Packages/PurchaseButton/PurchaseButtonComponentView.swift +++ b/RevenueCatUI/Templates/V2/Components/Packages/PurchaseButton/PurchaseButtonComponentView.swift @@ -152,6 +152,7 @@ fileprivate extension PurchaseButtonComponentViewModel { let stackViewModel = try factory.toStackViewModel( component: component.stack, localizationProvider: localizationProvider, + uiConfigProvider: .init(uiConfig: PreviewUIConfig.make()), offering: offering ) diff --git a/RevenueCatUI/Templates/V2/Components/Spacer/SpacerComponentView.swift b/RevenueCatUI/Templates/V2/Components/Spacer/SpacerComponentView.swift deleted file mode 100644 index f7b737edb3..0000000000 --- a/RevenueCatUI/Templates/V2/Components/Spacer/SpacerComponentView.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// 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 viewModel: SpacerComponentViewModel - - var body: some View { - Spacer() - } - -} - -#endif diff --git a/RevenueCatUI/Templates/V2/Components/Spacer/SpacerComponentViewModel.swift b/RevenueCatUI/Templates/V2/Components/Spacer/SpacerComponentViewModel.swift deleted file mode 100644 index eee5b04fa8..0000000000 --- a/RevenueCatUI/Templates/V2/Components/Spacer/SpacerComponentViewModel.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// 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. -// swiftlint:disable missing_docs - -import Foundation -import RevenueCat -import SwiftUI - -#if PAYWALL_COMPONENTS - -@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) -public class SpacerComponentViewModel { - - private let component: PaywallComponent.SpacerComponent - - init(component: PaywallComponent.SpacerComponent) { - self.component = component - } - -} - -#endif diff --git a/RevenueCatUI/Templates/V2/Components/Stack/StackComponentView.swift b/RevenueCatUI/Templates/V2/Components/Stack/StackComponentView.swift index 130dc0c2cf..b839169309 100644 --- a/RevenueCatUI/Templates/V2/Components/Stack/StackComponentView.swift +++ b/RevenueCatUI/Templates/V2/Components/Stack/StackComponentView.swift @@ -449,7 +449,8 @@ fileprivate extension StackComponentViewModel { component: component, packageValidator: validator, offering: offering, - localizationProvider: localizationProvider + localizationProvider: localizationProvider, + uiConfigProvider: .init(uiConfig: PreviewUIConfig.make()) ) } diff --git a/RevenueCatUI/Templates/V2/Components/Text/TextComponentView.swift b/RevenueCatUI/Templates/V2/Components/Text/TextComponentView.swift index 6bf877b223..548aea969c 100644 --- a/RevenueCatUI/Templates/V2/Components/Text/TextComponentView.swift +++ b/RevenueCatUI/Templates/V2/Components/Text/TextComponentView.swift @@ -50,7 +50,7 @@ struct TextComponentView: View { Group { if style.visible { Text(.init(style.text)) - .font(style.fontSize) + .font(style.font) .fontWeight(style.fontWeight) .fixedSize(horizontal: false, vertical: true) .multilineTextAlignment(style.textAlignment) @@ -70,6 +70,7 @@ struct TextComponentView: View { #if DEBUG +// swiftlint:disable type_body_length @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) struct TextComponentView_Previews: PreviewProvider { @@ -84,6 +85,7 @@ struct TextComponentView_Previews: PreviewProvider { "id_1": .string("Hello, world") ] ), + uiConfigProvider: .init(uiConfig: PreviewUIConfig.make()), component: .init( text: "id_1", color: .init(light: .hex("#000000")) @@ -94,7 +96,100 @@ struct TextComponentView_Previews: PreviewProvider { .previewLayout(.sizeThatFits) .previewDisplayName("Default") - // Default + // Custom Font + VStack { + TextComponentView( + // swiftlint:disable:next force_try + viewModel: try! .init( + localizationProvider: .init( + locale: Locale.current, + localizedStrings: [ + "id_1": .string("Hello, world") + ] + ), + uiConfigProvider: .init(uiConfig: PreviewUIConfig.make()), + component: .init( + text: "id_1", + color: .init(light: .hex("#000000")), + fontSize: .headingXXL + ) + ) + ) + + TextComponentView( + // swiftlint:disable:next force_try + viewModel: try! .init( + localizationProvider: .init( + locale: Locale.current, + localizedStrings: [ + "id_1": .string("Hello, world") + ] + ), + uiConfigProvider: .init(uiConfig: PreviewUIConfig.make( + fonts: [ + "primary": .init(ios: .name("Chalkduster")) + ] + )), + component: .init( + text: "id_1", + fontName: "primary", + color: .init(light: .hex("#000000")), + fontSize: .headingXXL + ) + ) + ) + + TextComponentView( + // swiftlint:disable:next force_try + viewModel: try! .init( + localizationProvider: .init( + locale: Locale.current, + localizedStrings: [ + "id_1": .string("Hello, world") + ] + ), + uiConfigProvider: .init(uiConfig: PreviewUIConfig.make( + fonts: [ + "primary": .init(ios: .name("Chalkduster")) + ] + )), + component: .init( + text: "id_1", + fontName: "This font name is not configured", + color: .init(light: .hex("#000000")), + fontSize: .headingXXL + ) + ) + ) + + TextComponentView( + // swiftlint:disable:next force_try + viewModel: try! .init( + localizationProvider: .init( + locale: Locale.current, + localizedStrings: [ + "id_1": .string("Hello, world") + ] + ), + uiConfigProvider: .init(uiConfig: PreviewUIConfig.make( + fonts: [ + "primary": .init(ios: .name("This Font Does Not Exist")) + ] + )), + component: .init( + text: "id_1", + fontName: "primary", + color: .init(light: .hex("#000000")), + fontSize: .headingXXL + ) + ) + ) + } + .previewRequiredEnvironmentProperties() + .previewLayout(.sizeThatFits) + .previewDisplayName("Custom Font") + + // Gradient TextComponentView( // swiftlint:disable:next force_try viewModel: try! .init( @@ -104,6 +199,7 @@ struct TextComponentView_Previews: PreviewProvider { "id_1": .string("Hello, world") ] ), + uiConfigProvider: .init(uiConfig: PreviewUIConfig.make()), component: .init( text: "id_1", color: PaywallComponent.ColorScheme( @@ -136,6 +232,7 @@ struct TextComponentView_Previews: PreviewProvider { "id_1": .string("Hello, world") ] ), + uiConfigProvider: .init(uiConfig: PreviewUIConfig.make()), component: .init( text: "id_1", fontName: nil, @@ -169,6 +266,7 @@ struct TextComponentView_Previews: PreviewProvider { "id_1": .string("Hello, world") ] ), + uiConfigProvider: .init(uiConfig: PreviewUIConfig.make()), component: .init( text: "id_1", color: .init(light: .hex("#000000")), @@ -210,6 +308,7 @@ struct TextComponentView_Previews: PreviewProvider { "id_2": .string("Showing medium condition") ] ), + uiConfigProvider: .init(uiConfig: PreviewUIConfig.make()), component: .init( text: "id_1", color: .init(light: .hex("#000000")), @@ -240,6 +339,7 @@ struct TextComponentView_Previews: PreviewProvider { "id_2": .string("SHOULDN'T SHOW MEDIUM") ] ), + uiConfigProvider: .init(uiConfig: PreviewUIConfig.make()), component: .init( text: "id_1", color: .init(light: .hex("#000000")), @@ -271,6 +371,7 @@ struct TextComponentView_Previews: PreviewProvider { ) ] ), + uiConfigProvider: .init(uiConfig: PreviewUIConfig.make()), component: .init( text: "id_1", color: .init(light: .hex("#000000")) diff --git a/RevenueCatUI/Templates/V2/Components/Text/TextComponentViewModel.swift b/RevenueCatUI/Templates/V2/Components/Text/TextComponentViewModel.swift index 07a20e96a1..92ecaf11e7 100644 --- a/RevenueCatUI/Templates/V2/Components/Text/TextComponentViewModel.swift +++ b/RevenueCatUI/Templates/V2/Components/Text/TextComponentViewModel.swift @@ -20,13 +20,19 @@ import SwiftUI class TextComponentViewModel { private let localizationProvider: LocalizationProvider + private let uiConfigProvider: UIConfigProvider private let component: PaywallComponent.TextComponent private let text: String private let presentedOverrides: PresentedOverrides? - init(localizationProvider: LocalizationProvider, component: PaywallComponent.TextComponent) throws { + init( + localizationProvider: LocalizationProvider, + uiConfigProvider: UIConfigProvider, + component: PaywallComponent.TextComponent + ) throws { self.localizationProvider = localizationProvider + self.uiConfigProvider = uiConfigProvider self.component = component self.text = try localizationProvider.localizedStrings.string(key: component.text) @@ -51,9 +57,10 @@ class TextComponentViewModel { with: self.presentedOverrides ) let partial = localizedPartial?.partial - let text = localizedPartial?.text ?? self.text + let fontFamily = self.uiConfigProvider.getFontFamily(for: partial?.fontName ?? self.component.fontName) + let style = TextComponentStyle( visible: partial?.visible ?? true, text: Self.processText( @@ -61,7 +68,7 @@ class TextComponentViewModel { packageContext: packageContext, locale: self.localizationProvider.locale ), - fontFamily: partial?.fontName ?? self.component.fontName, + fontFamily: fontFamily, fontWeight: partial?.fontWeight ?? self.component.fontWeight, color: partial?.color ?? self.component.color, backgroundColor: partial?.backgroundColor ?? self.component.backgroundColor, @@ -158,7 +165,7 @@ struct TextComponentStyle { let text: String let fontWeight: Font.Weight let color: PaywallComponent.ColorScheme - let fontSize: Font + let font: Font let horizontalAlignment: Alignment let textAlignment: TextAlignment let backgroundStyle: BackgroundStyle? @@ -185,7 +192,7 @@ struct TextComponentStyle { self.color = color // WIP: Take into account the fontFamily mapping - self.fontSize = fontSize.font + self.font = fontSize.makeFont(familyName: fontFamily) self.textAlignment = horizontalAlignment.textAlignment self.horizontalAlignment = horizontalAlignment.frameAlignment diff --git a/RevenueCatUI/Templates/V2/PaywallsV2View.swift b/RevenueCatUI/Templates/V2/PaywallsV2View.swift index 09952a0a47..93203fc195 100644 --- a/RevenueCatUI/Templates/V2/PaywallsV2View.swift +++ b/RevenueCatUI/Templates/V2/PaywallsV2View.swift @@ -80,6 +80,7 @@ struct PaywallsV2View: View { componentsConfig: componentsConfig, componentsLocalizations: paywallComponents.data.componentsLocalizations, defaultLocale: paywallComponents.data.defaultLocale, + uiConfig: paywallComponents.uiConfig, offering: offering, introEligibilityChecker: introEligibilityChecker, showZeroDecimalPlacePrices: showZeroDecimalPlacePrices @@ -177,6 +178,7 @@ fileprivate extension PaywallsV2View { componentsConfig: PaywallComponentsData.PaywallComponentsConfig, componentsLocalizations: [PaywallComponent.LocaleID: PaywallComponent.LocalizationDictionary], defaultLocale: String, + uiConfig: UIConfig, offering: Offering, introEligibilityChecker: TrialOrIntroEligibilityChecker, showZeroDecimalPlacePrices: Bool @@ -188,11 +190,14 @@ fileprivate extension PaywallsV2View { ) do { + let uiConfigProvider = UIConfigProvider(uiConfig: uiConfig) + let factory = ViewModelFactory() let root = try factory.toRootViewModel( componentsConfig: componentsConfig, offering: offering, - localizationProvider: localizationProvider + localizationProvider: localizationProvider, + uiConfigProvider: uiConfigProvider ) // WIP: Maybe re-enable this later or add some warnings diff --git a/RevenueCatUI/Templates/V2/Previews/PreviewMock.swift b/RevenueCatUI/Templates/V2/Previews/PreviewMock.swift index c7e86c26ce..036db7f89d 100644 --- a/RevenueCatUI/Templates/V2/Previews/PreviewMock.swift +++ b/RevenueCatUI/Templates/V2/Previews/PreviewMock.swift @@ -21,6 +21,27 @@ import SwiftUI // swiftlint:disable identifier_name +enum PreviewUIConfig { + + static func make( + colors: [String: PaywallComponent.ColorInfo] = [:], + fonts: [String: UIConfig.FontsConfig] = [:] + ) -> UIConfig { + return .init( + app: .init( + colors: colors, + fonts: fonts + ), + localizations: [:], + variableConfig: .init( + variableCompatibilityMap: [:], + functionCompatibilityMap: [:] + ) + ) + } + +} + @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) struct PreviewRequiredEnvironmentProperties: ViewModifier { diff --git a/RevenueCatUI/Templates/V2/Previews/TemplateComponentsViewPreviews/FallbackComponentPreview.swift b/RevenueCatUI/Templates/V2/Previews/TemplateComponentsViewPreviews/FallbackComponentPreview.swift index 7825338bc4..d94bf390a2 100644 --- a/RevenueCatUI/Templates/V2/Previews/TemplateComponentsViewPreviews/FallbackComponentPreview.swift +++ b/RevenueCatUI/Templates/V2/Previews/TemplateComponentsViewPreviews/FallbackComponentPreview.swift @@ -121,7 +121,8 @@ struct FallbackComponentPreview_Previews: PreviewProvider { component: component, packageValidator: packageValidator, offering: offering, - localizationProvider: localizationProvider + localizationProvider: localizationProvider, + uiConfigProvider: .init(uiConfig: PreviewUIConfig.make()) ) } diff --git a/RevenueCatUI/Templates/V2/ViewModelHelpers/PaywallComponentTypeTransformers.swift b/RevenueCatUI/Templates/V2/ViewModelHelpers/PaywallComponentTypeTransformers.swift index 6f7b265256..0c883546f3 100644 --- a/RevenueCatUI/Templates/V2/ViewModelHelpers/PaywallComponentTypeTransformers.swift +++ b/RevenueCatUI/Templates/V2/ViewModelHelpers/PaywallComponentTypeTransformers.swift @@ -18,8 +18,8 @@ import SwiftUI extension PaywallComponent.FontSize { - var font: Font { - return Font(self.uiFont) + func makeFont(familyName: String?) -> Font { + return Font(self.makeUIFont(familyName: familyName)) } private var textStyle: UIFont.TextStyle { @@ -36,7 +36,8 @@ extension PaywallComponent.FontSize { } } - private var uiFont: UIFont { + // swiftlint:disable cyclomatic_complexity + private func makeUIFont(familyName: String?) -> UIFont { let fontSize: CGFloat switch self { case .headingXXL: fontSize = 40 @@ -51,8 +52,21 @@ extension PaywallComponent.FontSize { case .bodyS: fontSize = 13 } - // Create a UIFont and apply dynamic type scaling - let baseFont = UIFont.systemFont(ofSize: fontSize, weight: .regular) + // Create the base font, with fallback to the system font + let baseFont: UIFont + if let familyName = familyName { + if let customFont = UIFont(name: familyName, size: fontSize) { + baseFont = customFont + } else { + // Log a warning about the missing custom font + Logger.warning("Custom font '\(familyName)' could not be loaded. Falling back to system font.") + baseFont = UIFont.systemFont(ofSize: fontSize, weight: .regular) + } + } else { + baseFont = UIFont.systemFont(ofSize: fontSize, weight: .regular) + } + + // Apply dynamic type scaling return UIFontMetrics(forTextStyle: self.textStyle).scaledFont(for: baseFont) } diff --git a/RevenueCatUI/Templates/V2/ViewModelHelpers/PaywallComponentViewModel.swift b/RevenueCatUI/Templates/V2/ViewModelHelpers/PaywallComponentViewModel.swift index a294f86c38..2b19f0e24f 100644 --- a/RevenueCatUI/Templates/V2/ViewModelHelpers/PaywallComponentViewModel.swift +++ b/RevenueCatUI/Templates/V2/ViewModelHelpers/PaywallComponentViewModel.swift @@ -22,9 +22,7 @@ enum PaywallComponentViewModel { case root(RootViewModel) case text(TextComponentViewModel) case image(ImageComponentViewModel) - case spacer(SpacerComponentViewModel) case stack(StackComponentViewModel) - case linkButton(LinkButtonComponentViewModel) case button(ButtonComponentViewModel) case package(PackageComponentViewModel) case purchaseButton(PurchaseButtonComponentViewModel) diff --git a/RevenueCatUI/Templates/V2/ViewModelHelpers/UIConfigProvider.swift b/RevenueCatUI/Templates/V2/ViewModelHelpers/UIConfigProvider.swift new file mode 100644 index 0000000000..3b726a2546 --- /dev/null +++ b/RevenueCatUI/Templates/V2/ViewModelHelpers/UIConfigProvider.swift @@ -0,0 +1,45 @@ +// +// 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 +// +// UIConfigProvider.swift +// +// Created by Josh Holtz on 1/5/25. + +import Foundation +import RevenueCat + +#if PAYWALL_COMPONENTS + +struct UIConfigProvider { + + private let uiConfig: UIConfig + + init(uiConfig: UIConfig) { + self.uiConfig = uiConfig + } + + func getFontFamily(for name: String?) -> String? { + guard let name, let fontInfo = self.uiConfig.app.fonts[name]?.ios else { + return nil + } + + switch fontInfo { + case .name(let fontFamily): + return fontFamily + case .googleFonts: + // Not supported on this platform (yet) + return nil + @unknown default: + return nil + } + } + +} + +#endif diff --git a/RevenueCatUI/Templates/V2/ViewModelHelpers/ViewModelFactory.swift b/RevenueCatUI/Templates/V2/ViewModelHelpers/ViewModelFactory.swift index f147c93921..4d7bc6d08f 100644 --- a/RevenueCatUI/Templates/V2/ViewModelHelpers/ViewModelFactory.swift +++ b/RevenueCatUI/Templates/V2/ViewModelHelpers/ViewModelFactory.swift @@ -24,11 +24,13 @@ struct ViewModelFactory { func toRootViewModel( componentsConfig: PaywallComponentsData.PaywallComponentsConfig, offering: Offering, - localizationProvider: LocalizationProvider + localizationProvider: LocalizationProvider, + uiConfigProvider: UIConfigProvider ) throws -> RootViewModel { let rootStackViewModel = try toStackViewModel( component: componentsConfig.stack, localizationProvider: localizationProvider, + uiConfigProvider: uiConfigProvider, offering: offering ) @@ -36,6 +38,7 @@ struct ViewModelFactory { let stackViewModel = try toStackViewModel( component: $0.stack, localizationProvider: localizationProvider, + uiConfigProvider: uiConfigProvider, offering: offering ) @@ -56,42 +59,46 @@ struct ViewModelFactory { component: PaywallComponent, packageValidator: PackageValidator, offering: Offering, - localizationProvider: LocalizationProvider + localizationProvider: LocalizationProvider, + uiConfigProvider: UIConfigProvider ) throws -> PaywallComponentViewModel { switch component { case .text(let component): return .text( - try TextComponentViewModel(localizationProvider: localizationProvider, component: component) + try TextComponentViewModel( + localizationProvider: localizationProvider, + uiConfigProvider: uiConfigProvider, + component: component + ) ) case .image(let component): return .image( - try ImageComponentViewModel(localizationProvider: localizationProvider, component: component) - ) - case .spacer(let component): - return .spacer( - SpacerComponentViewModel(component: component) + try ImageComponentViewModel( + localizationProvider: localizationProvider, + uiConfigProvider: uiConfigProvider, + component: component + ) ) case .stack(let component): let viewModels = try component.components.map { component in - try self.toViewModel(component: component, - packageValidator: packageValidator, - offering: offering, - localizationProvider: localizationProvider) + try self.toViewModel( + component: component, + packageValidator: packageValidator, + offering: offering, + localizationProvider: localizationProvider, + uiConfigProvider: uiConfigProvider + ) } return .stack( try StackComponentViewModel(component: component, viewModels: viewModels) ) - case .linkButton(let component): - return .linkButton( - try LinkButtonComponentViewModel(component: component, - localizationProvider: localizationProvider) - ) case .button(let component): let stackViewModel = try toStackViewModel( component: component.stack, localizationProvider: localizationProvider, + uiConfigProvider: uiConfigProvider, offering: offering ) @@ -107,6 +114,7 @@ struct ViewModelFactory { let stackViewModel = try toStackViewModel( component: component.stack, localizationProvider: localizationProvider, + uiConfigProvider: uiConfigProvider, offering: offering ) @@ -125,6 +133,7 @@ struct ViewModelFactory { let stackViewModel = try toStackViewModel( component: component.stack, localizationProvider: localizationProvider, + uiConfigProvider: uiConfigProvider, offering: offering ) @@ -135,6 +144,7 @@ struct ViewModelFactory { let stackViewModel = try toStackViewModel( component: component.stack, localizationProvider: localizationProvider, + uiConfigProvider: uiConfigProvider, offering: offering ) @@ -150,6 +160,7 @@ struct ViewModelFactory { func toStackViewModel( component: PaywallComponent.StackComponent, localizationProvider: LocalizationProvider, + uiConfigProvider: UIConfigProvider, offering: Offering ) throws -> StackComponentViewModel { let viewModels = try component.components.map { component in @@ -157,7 +168,8 @@ struct ViewModelFactory { component: component, packageValidator: packageValidator, offering: offering, - localizationProvider: localizationProvider + localizationProvider: localizationProvider, + uiConfigProvider: uiConfigProvider ) } diff --git a/Sources/Paywalls/Components/Common/PaywallComponentBase.swift b/Sources/Paywalls/Components/Common/PaywallComponentBase.swift index 3c03129888..07612c7573 100644 --- a/Sources/Paywalls/Components/Common/PaywallComponentBase.swift +++ b/Sources/Paywalls/Components/Common/PaywallComponentBase.swift @@ -15,9 +15,7 @@ public enum PaywallComponent: PaywallComponentBase { case text(TextComponent) case image(ImageComponent) - case spacer(SpacerComponent) case stack(StackComponent) - case linkButton(LinkButtonComponent) case button(ButtonComponent) case package(PackageComponent) case purchaseButton(PurchaseButtonComponent) @@ -27,9 +25,7 @@ public enum PaywallComponent: PaywallComponentBase { case text case image - case spacer case stack - case linkButton = "link_button" case button case package case purchaseButton = "purchase_button" @@ -65,15 +61,9 @@ extension PaywallComponent: Codable { case .image(let component): try container.encode(ComponentType.image, forKey: .type) try component.encode(to: encoder) - case .spacer(let component): - try container.encode(ComponentType.spacer, forKey: .type) - try component.encode(to: encoder) case .stack(let component): try container.encode(ComponentType.stack, forKey: .type) try component.encode(to: encoder) - 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) @@ -142,12 +132,8 @@ extension PaywallComponent: Codable { return .text(try TextComponent(from: decoder)) case .image: return .image(try ImageComponent(from: decoder)) - case .spacer: - return .spacer(try SpacerComponent(from: decoder)) case .stack: return .stack(try StackComponent(from: decoder)) - case .linkButton: - return .linkButton(try LinkButtonComponent(from: decoder)) case .button: return .button(try ButtonComponent(from: decoder)) case .package: diff --git a/Sources/Paywalls/Components/PaywallLinkButtonComponent.swift b/Sources/Paywalls/Components/PaywallLinkButtonComponent.swift deleted file mode 100644 index e024f2c75d..0000000000 --- a/Sources/Paywalls/Components/PaywallLinkButtonComponent.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// PaywallLinkButtonComponent.swift -// -// -// Created by James Borthwick on 2024-08-21. -// -// swiftlint:disable missing_docs - -import Foundation - -#if PAYWALL_COMPONENTS - -public extension PaywallComponent { - - struct LinkButtonComponent: PaywallComponentBase { - - let type: ComponentType - public let url: URL - public let textComponent: PaywallComponent.TextComponent - - public init( - url: URL, - textComponent: PaywallComponent.TextComponent - ) { - self.type = .linkButton - self.url = url - self.textComponent = textComponent - } - - } - -} - -#endif diff --git a/Sources/Paywalls/Components/PaywallSpacerComponent.swift b/Sources/Paywalls/Components/PaywallSpacerComponent.swift deleted file mode 100644 index d134040487..0000000000 --- a/Sources/Paywalls/Components/PaywallSpacerComponent.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -// File.swift -// -// -// Created by Josh Holtz on 6/12/24. -// - -import Foundation -// swiftlint:disable missing_docs - -#if PAYWALL_COMPONENTS - -public extension PaywallComponent { - - struct SpacerComponent: PaywallComponentBase { - - let type: ComponentType - - public init() { - self.type = .spacer - } - - } - -} - -#endif From 168ba6f759b0406cb757aa424f572e1cc697969c Mon Sep 17 00:00:00 2001 From: Josh Holtz Date: Sun, 5 Jan 2025 09:26:26 -0600 Subject: [PATCH 2/2] Added log to google fonts --- .../Templates/V2/ViewModelHelpers/UIConfigProvider.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/RevenueCatUI/Templates/V2/ViewModelHelpers/UIConfigProvider.swift b/RevenueCatUI/Templates/V2/ViewModelHelpers/UIConfigProvider.swift index 3b726a2546..df069b4b2f 100644 --- a/RevenueCatUI/Templates/V2/ViewModelHelpers/UIConfigProvider.swift +++ b/RevenueCatUI/Templates/V2/ViewModelHelpers/UIConfigProvider.swift @@ -34,6 +34,7 @@ struct UIConfigProvider { return fontFamily case .googleFonts: // Not supported on this platform (yet) + Logger.warning("Google Fonts are not supported on this platform") return nil @unknown default: return nil