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

Paywalls: added support for custom fonts #2988

Merged
merged 3 commits into from
Aug 8, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
1 change: 1 addition & 0 deletions RevenueCatUI/Data/TemplateViewConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ struct TemplateViewConfiguration {
let packages: PackageConfiguration
let configuration: PaywallData.Configuration
let colors: PaywallData.Configuration.Colors
let fonts: PaywallFontProvider
let assetBaseURL: URL

}
Expand Down
1 change: 1 addition & 0 deletions RevenueCatUI/Helpers/PreviewHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ struct PreviewableTemplate<T: TemplateViewType>: View {
self.configuration = offering.paywall!.configuration(
for: offering,
mode: .fullScreen,
fonts: DefaultPaywallFontProvider(),
locale: .current
)
self.presentInSheet = presentInSheet
Expand Down
102 changes: 102 additions & 0 deletions RevenueCatUI/PaywallFontProvider.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
//
// PaywallFontProvider.swift
//
//
// Created by Nacho Soto on 8/8/23.
//

import SwiftUI

/// A type that returns a font for a given `Font.TextStyle`.
///
/// You can use one of the provided implementations, or make your own:
/// - ``DefaultPaywallFontProvider``
/// - ``CustomPaywallFontProvider``
public protocol PaywallFontProvider {

/// - Returns: Desired `Font` for the given `Font.TextStyle`.
@available(iOS 15.0, macOS 12.0, tvOS 15.0, *)
func font(for textStyle: Font.TextStyle) -> Font

}

/// Default ``PaywallFontProvider`` which uses the system default font
/// supporting dynamic type.
open class DefaultPaywallFontProvider: PaywallFontProvider {

// swiftlint:disable:next missing_docs
public init() {}

@available(iOS 15.0, macOS 12.0, tvOS 15.0, *)
// swiftlint:disable:next cyclomatic_complexity missing_docs
open func font(for textStyle: Font.TextStyle) -> Font {
switch textStyle {
case .largeTitle: return .largeTitle
case .title: return .title
case .title2: return .title2
case .title3: return .title3
case .headline: return .headline
case .subheadline: return .subheadline
case .body: return .body
case .callout: return .callout
case .footnote: return .footnote
case .caption: return .caption
case .caption2: return .caption2
@unknown default: return .body
}
}

}

#if canImport(UIKit)

/// A ``PaywallFontProvider`` implementation that allows you to provide a custom
/// font name, and it will automatically scale up based on the size category.
open class CustomPaywallFontProvider: PaywallFontProvider {

private let fontName: String

/// Creates a ``CustomPaywallFontProvider`` with a name.
public init(fontName: String) {
self.fontName = fontName
}

@available(iOS 15.0, macOS 12.0, tvOS 15.0, *)
// swiftlint:disable:next missing_docs
open func font(for textStyle: Font.TextStyle) -> Font {
return Font.custom(self.fontName,
size: UIFont.preferredFont(forTextStyle: textStyle.style).pointSize,
relativeTo: textStyle)
}

}

#endif

// MARK: - Private

#if canImport(UIKit)

@available(iOS 15.0, macOS 12.0, tvOS 15.0, *)
private extension Font.TextStyle {

var style: UIFont.TextStyle {
switch self {
case .largeTitle: return .largeTitle
case .title: return .title1
case .title2: return .title2
case .title3: return .title3
case .headline: return .headline
case .subheadline: return .subheadline
case .body: return .body
case .callout: return .callout
case .footnote: return .footnote
case .caption: return .caption1
case .caption2: return .caption2
@unknown default: return .body
}
}

}

#endif
24 changes: 22 additions & 2 deletions RevenueCatUI/PaywallView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import SwiftUI
public struct PaywallView: View {

private let mode: PaywallViewMode
private let fonts: PaywallFontProvider
private let introEligibility: TrialOrIntroEligibilityChecker?
private let purchaseHandler: PurchaseHandler?

Expand All @@ -26,10 +27,14 @@ public struct PaywallView: View {
/// an error will be displayed.
/// - Warning: `Purchases` must have been configured prior to displaying it.
/// If you want to handle that, you can use ``init(offering:mode:)`` instead.
public init(mode: PaywallViewMode = .default) {
public init(
mode: PaywallViewMode = .default,
fonts: PaywallFontProvider = DefaultPaywallFontProvider()
) {
self.init(
offering: nil,
mode: mode,
fonts: fonts,
introEligibility: .default(),
purchaseHandler: .default()
)
Expand All @@ -39,10 +44,15 @@ public struct PaywallView: View {
/// - Note: if `offering` does not have a current paywall, or it fails to load due to invalid data,
/// a default paywall will be displayed.
/// - Warning: `Purchases` must have been configured prior to displaying it.
public init(offering: Offering?, mode: PaywallViewMode = .default) {
public init(
offering: Offering,
mode: PaywallViewMode = .default,
fonts: PaywallFontProvider = DefaultPaywallFontProvider()
) {
self.init(
offering: offering,
mode: mode,
fonts: fonts,
introEligibility: .default(),
purchaseHandler: .default()
)
Expand All @@ -51,13 +61,15 @@ public struct PaywallView: View {
init(
offering: Offering?,
mode: PaywallViewMode = .default,
fonts: PaywallFontProvider = DefaultPaywallFontProvider(),
introEligibility: TrialOrIntroEligibilityChecker?,
purchaseHandler: PurchaseHandler?
) {
self._offering = .init(initialValue: offering)
self.introEligibility = introEligibility
self.purchaseHandler = purchaseHandler
self.mode = mode
self.fonts = fonts
}

// swiftlint:disable:next missing_docs
Expand All @@ -73,6 +85,7 @@ public struct PaywallView: View {
if let checker = self.introEligibility, let purchaseHandler = self.purchaseHandler {
if let offering = self.offering {
self.paywallView(for: offering,
fonts: self.fonts,
checker: checker,
purchaseHandler: purchaseHandler)
.transition(Self.transition)
Expand Down Expand Up @@ -104,6 +117,7 @@ public struct PaywallView: View {
@ViewBuilder
private func paywallView(
for offering: Offering,
fonts: PaywallFontProvider,
checker: TrialOrIntroEligibilityChecker,
purchaseHandler: PurchaseHandler
) -> some View {
Expand All @@ -112,6 +126,7 @@ public struct PaywallView: View {
offering: offering,
paywall: paywall,
mode: self.mode,
fonts: fonts,
introEligibility: checker,
purchaseHandler: purchaseHandler
)
Expand All @@ -127,6 +142,7 @@ public struct PaywallView: View {
offering: offering,
paywall: .createDefault(with: offering.availablePackages),
mode: self.mode,
fonts: fonts,
introEligibility: checker,
purchaseHandler: purchaseHandler
)
Expand All @@ -147,6 +163,7 @@ struct LoadedOfferingPaywallView: View {
private let offering: Offering
private let paywall: PaywallData
private let mode: PaywallViewMode
private let fonts: PaywallFontProvider

@StateObject
private var introEligibility: IntroEligibilityViewModel
Expand All @@ -160,12 +177,14 @@ struct LoadedOfferingPaywallView: View {
offering: Offering,
paywall: PaywallData,
mode: PaywallViewMode,
fonts: PaywallFontProvider,
introEligibility: TrialOrIntroEligibilityChecker,
purchaseHandler: PurchaseHandler
) {
self.offering = offering
self.paywall = paywall
self.mode = mode
self.fonts = fonts
self._introEligibility = .init(
wrappedValue: .init(introEligibilityChecker: introEligibility)
)
Expand All @@ -176,6 +195,7 @@ struct LoadedOfferingPaywallView: View {
let view = self.paywall
.createView(for: self.offering,
mode: self.mode,
fonts: self.fonts,
introEligibility: self.introEligibility,
locale: self.locale)
.environmentObject(self.introEligibility)
Expand Down
19 changes: 9 additions & 10 deletions RevenueCatUI/Templates/Template1View.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import SwiftUI
@available(tvOS, unavailable)
struct Template1View: TemplateViewType {

private let configuration: TemplateViewConfiguration
let configuration: TemplateViewConfiguration
private var localization: ProcessedLocalizedConfiguration

@EnvironmentObject
Expand Down Expand Up @@ -34,14 +34,14 @@ struct Template1View: TemplateViewType {
introEligibility: self.introEligibility,
foregroundColor: self.configuration.colors.text1Color
)
.font(self.configuration.mode.offerDetailsFont)
.font(self.font(for: self.configuration.mode.offerDetailsFont))
.multilineTextAlignment(.center)

self.button
.padding(.horizontal)

if case .fullScreen = self.configuration.mode {
FooterView(configuration: self.configuration.configuration,
FooterView(configuration: self.configuration,
color: self.configuration.colors.callToActionBackgroundColor,
purchaseHandler: self.purchaseHandler)
}
Expand All @@ -55,7 +55,7 @@ struct Template1View: TemplateViewType {

Group {
Text(.init(self.localization.title))
.font(self.configuration.mode.titleFont)
.font(self.font(for: self.configuration.mode.titleFont))
.fontWeight(.heavy)
.padding(
self.configuration.mode.displaySubtitle
Expand All @@ -65,7 +65,7 @@ struct Template1View: TemplateViewType {

if self.configuration.mode.displaySubtitle, let subtitle = self.localization.subtitle {
Text(.init(subtitle))
.font(self.configuration.mode.subtitleFont)
.font(self.font(for: self.configuration.mode.subtitleFont))
}
}
.padding(.horizontal, 20)
Expand Down Expand Up @@ -112,10 +112,9 @@ struct Template1View: TemplateViewType {
private var button: some View {
PurchaseButton(
package: self.configuration.packages.single.content,
colors: self.configuration.colors,
localization: self.localization,
configuration: self.configuration,
introEligibility: self.introEligibility,
mode: self.configuration.mode,
purchaseHandler: self.purchaseHandler
)
}
Expand All @@ -142,15 +141,15 @@ private extension PaywallViewMode {
}
}

var titleFont: Font {
var titleFont: Font.TextStyle {
switch self {
case .fullScreen: return .largeTitle
case .card: return .title
case .banner: return .headline
}
}

var subtitleFont: Font {
var subtitleFont: Font.TextStyle {
switch self {
case .fullScreen: return .subheadline
case .card, .banner: return .callout
Expand All @@ -164,7 +163,7 @@ private extension PaywallViewMode {
}
}

var offerDetailsFont: Font {
var offerDetailsFont: Font.TextStyle {
switch self {
case .fullScreen: return .callout
case .card, .banner: return .caption
Expand Down
15 changes: 7 additions & 8 deletions RevenueCatUI/Templates/Template2View.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import SwiftUI
@available(tvOS, unavailable)
struct Template2View: TemplateViewType {

private let configuration: TemplateViewConfiguration
let configuration: TemplateViewConfiguration
private var localization: [Package: ProcessedLocalizedConfiguration]

@State
Expand Down Expand Up @@ -41,7 +41,7 @@ struct Template2View: TemplateViewType {
.padding(.horizontal)

if case .fullScreen = self.configuration.mode {
FooterView(configuration: self.configuration.configuration,
FooterView(configuration: self.configuration,
color: self.configuration.colors.text1Color,
purchaseHandler: self.purchaseHandler)
}
Expand All @@ -61,13 +61,13 @@ struct Template2View: TemplateViewType {

Text(.init(self.selectedLocalization.title))
.foregroundColor(self.configuration.colors.text1Color)
.font(.largeTitle.bold())
.font(self.font(for: .largeTitle).bold())

Spacer()

Text(.init(self.selectedLocalization.subtitle ?? ""))
.foregroundColor(self.configuration.colors.text1Color)
.font(.title3)
.font(self.font(for: .title3))

Spacer()

Expand Down Expand Up @@ -108,9 +108,9 @@ struct Template2View: TemplateViewType {
alignment: Self.packageButtonAlignment
)
.fixedSize(horizontal: false, vertical: true)
.font(.body)
.font(self.font(for: .body))
}
.font(.body.weight(.medium))
.font(self.font(for: .body).weight(.medium))
.padding()
.multilineTextAlignment(.leading)
.frame(maxWidth: .infinity, alignment: Self.packageButtonAlignment)
Expand Down Expand Up @@ -162,10 +162,9 @@ struct Template2View: TemplateViewType {
private var subscribeButton: some View {
PurchaseButton(
package: self.selectedPackage,
colors: self.configuration.colors,
localization: self.selectedLocalization,
configuration: self.configuration,
introEligibility: self.introEligibility[self.selectedPackage],
mode: self.configuration.mode,
purchaseHandler: self.purchaseHandler
)
}
Expand Down
Loading