Files
purchases-ios/Sources/Networking/Responses/PaywallComponentsData.swift
T
Josh Holtz 91e695869a [Paywalls V2] Add root paywall background (color and image) (#4502)
* Added root paywall background

* Added some previews

* Undo comment about fallback

* Fix some lint

* Fit lint... again

* Added backgroud style to text and stack and fixed preview
2024-11-21 10:46:15 -06:00

213 lines
6.7 KiB
Swift

//
// 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
//
// PaywallComponentsData.swift
//
// Created by Josh Holtz on 11/11/24.
// swiftlint:disable identifier_name missing_docs
import Foundation
#if PAYWALL_COMPONENTS
public struct PaywallComponentsData: Codable, Equatable, Sendable {
public struct ComponentsConfig: Codable, Equatable, Sendable {
public var base: PaywallComponentsConfig
public init(base: PaywallComponentsConfig) {
self.base = base
}
}
public struct PaywallComponentsConfig: Codable, Equatable, Sendable {
public var stack: PaywallComponent.StackComponent
public let stickyFooter: PaywallComponent.StickyFooterComponent?
public var background: PaywallComponent.Background
public init(
stack: PaywallComponent.StackComponent,
stickyFooter: PaywallComponent.StickyFooterComponent?,
background: PaywallComponent.Background
) {
self.stack = stack
self.stickyFooter = stickyFooter
self.background = background
}
}
public enum LocalizationData: Codable, Equatable, Sendable {
case string(String), image(PaywallComponent.ThemeImageUrls)
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let stringValue = try? container.decode(String.self) {
self = .string(stringValue)
} else if let imageValue = try? container.decode(PaywallComponent.ThemeImageUrls.self) {
self = .image(imageValue)
} else {
throw DecodingError.typeMismatch(
LocalizationData.self,
DecodingError.Context(codingPath: decoder.codingPath,
debugDescription: "Wrong type for LocalizationData")
)
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .string(let stringValue):
try container.encode(stringValue)
case .image(let imageValue):
try container.encode(imageValue)
}
}
}
public var templateName: String
/// The base remote URL where assets for this paywall are stored.
public var assetBaseURL: URL
/// The revision identifier for this paywall.
public var revision: Int {
get { return self._revision }
set { self._revision = newValue }
}
public var componentsConfig: ComponentsConfig
public var componentsLocalizations: [PaywallComponent.LocaleID: PaywallComponent.LocalizationDictionary]
public var defaultLocale: String
@DefaultDecodable.Zero
internal private(set) var _revision: Int = 0
public var errorInfo: [String: EquatableError]?
private enum CodingKeys: String, CodingKey {
case templateName
case componentsConfig
case componentsLocalizations
case defaultLocale
case assetBaseURL = "assetBaseUrl"
case _revision = "revision"
}
public init(templateName: String,
assetBaseURL: URL,
componentsConfig: ComponentsConfig,
componentsLocalizations: [PaywallComponent.LocaleID: PaywallComponent.LocalizationDictionary],
revision: Int,
defaultLocaleIdentifier: String) {
self.templateName = templateName
self.assetBaseURL = assetBaseURL
self.componentsConfig = componentsConfig
self.componentsLocalizations = componentsLocalizations
self._revision = revision
self.defaultLocale = defaultLocaleIdentifier
}
}
extension PaywallComponentsData {
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
var errors: [String: EquatableError] = [:]
do {
templateName = try container.decode(String.self, forKey: .templateName)
} catch {
errors["templateName"] = .init(error)
templateName = ""
}
do {
assetBaseURL = try container.decode(URL.self, forKey: .assetBaseURL)
} catch {
errors["assetBaseURL"] = .init(error)
assetBaseURL = URL(string: "https://example.com")!
}
do {
componentsConfig = try container.decode(ComponentsConfig.self, forKey: .componentsConfig)
} catch {
errors["componentsConfig"] = .init(error)
componentsConfig = ComponentsConfig(base: PaywallComponentsConfig(
stack: .init(components: []),
stickyFooter: nil,
background: .color(.init(light: .hex("#ffffff")))
))
}
do {
componentsLocalizations = try container.decode(
[PaywallComponent.LocaleID: PaywallComponent.LocalizationDictionary].self,
forKey: .componentsLocalizations
)
} catch {
errors["componentsLocalizations"] = .init(error)
componentsLocalizations = [:]
}
do {
defaultLocale = try container.decode(String.self, forKey: .defaultLocale)
} catch {
errors["defaultLocale"] = .init(error)
defaultLocale = "en"
}
do {
_revision = try container.decode(Int.self, forKey: ._revision)
} catch {
errors["_revision"] = .init(error)
_revision = 0
}
if !errors.isEmpty {
errorInfo = errors
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(templateName, forKey: .templateName)
try container.encode(assetBaseURL, forKey: .assetBaseURL)
try container.encode(componentsConfig, forKey: .componentsConfig)
try container.encode(componentsLocalizations, forKey: .componentsLocalizations)
try container.encode(defaultLocale, forKey: .defaultLocale)
try container.encode(_revision, forKey: ._revision)
}
}
extension PaywallComponentsData {
public struct EquatableError: Equatable, Sendable {
let description: String
init(_ error: Error) {
self.description = String(describing: error)
}
public static func == (lhs: EquatableError, rhs: EquatableError) -> Bool {
return lhs.description == rhs.description
}
}
}
#endif