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 V2] Convert Codable structs to classes #4665

Merged
merged 4 commits into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -73,18 +73,31 @@ class IconComponentViewModel {

extension PresentedIconPartial: PresentedPartial {

static func combine(_ base: Self?, with other: Self?) -> Self {
static func combine(
_ base: PaywallComponent.PartialIconComponent?,
with other: PaywallComponent.PartialIconComponent?
) -> Self {

let visible = other?.visible ?? base?.visible
let baseUrl = other?.baseUrl ?? base?.baseUrl
let iconName = other?.iconName ?? base?.iconName
let formats = other?.formats ?? base?.formats
let size = other?.size ?? base?.size
let padding = other?.padding ?? base?.padding
let margin = other?.margin ?? base?.margin
let color = other?.color ?? base?.color
let iconBackground = other?.iconBackground ?? base?.iconBackground
Comment on lines +81 to +89
Copy link
Member Author

Choose a reason for hiding this comment

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

Had to do this because Swift compiler was timing out 🤷‍♂️


return .init(
visible: other?.visible ?? base?.visible,
baseUrl: other?.baseUrl ?? base?.baseUrl,
iconName: other?.iconName ?? base?.iconName,
formats: other?.formats ?? base?.formats,
size: other?.size ?? base?.size,
padding: other?.padding ?? base?.padding,
margin: other?.margin ?? base?.margin,
color: other?.color ?? base?.color,
iconBackground: other?.iconBackground ?? base?.iconBackground
visible: visible,
baseUrl: baseUrl,
iconName: iconName,
formats: formats,
size: size,
padding: padding,
margin: margin,
color: color,
iconBackground: iconBackground
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,19 +79,33 @@ class StackComponentViewModel {

extension PresentedStackPartial: PresentedPartial {

static func combine(_ base: Self?, with other: Self?) -> Self {
static func combine(
_ base: PaywallComponent.PartialStackComponent?,
with other: PaywallComponent.PartialStackComponent?
) -> Self {

let visible = other?.visible ?? base?.visible
let dimension = other?.dimension ?? base?.dimension
let size = other?.size ?? base?.size
let spacing = other?.spacing ?? base?.spacing
let backgroundColor = other?.backgroundColor ?? base?.backgroundColor
let padding = other?.padding ?? base?.padding
let margin = other?.margin ?? base?.margin
let shape = other?.shape ?? base?.shape
let border = other?.border ?? base?.border
let shadow = other?.shadow ?? base?.shadow

return .init(
visible: other?.visible ?? base?.visible,
dimension: other?.dimension ?? base?.dimension,
size: other?.size ?? base?.size,
spacing: other?.spacing ?? base?.spacing,
backgroundColor: other?.backgroundColor ?? base?.backgroundColor,
padding: other?.padding ?? base?.padding,
margin: other?.margin ?? base?.margin,
shape: other?.shape ?? base?.shape,
border: other?.border ?? base?.border,
shadow: other?.shadow ?? base?.shadow
visible: visible,
dimension: dimension,
size: size,
spacing: spacing,
backgroundColor: backgroundColor,
padding: padding,
margin: margin,
shape: shape,
border: border,
shadow: shadow
)
}

Expand Down
216 changes: 122 additions & 94 deletions Sources/Paywalls/Components/PaywallButtonComponent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@
//
// Created by Jay Shortway on 02/10/2024.
//
// swiftlint:disable missing_docs
// swiftlint:disable missing_docs nesting

import Foundation

#if PAYWALL_COMPONENTS

public extension PaywallComponent {

struct ButtonComponent: PaywallComponentBase {
final class ButtonComponent: PaywallComponentBase {

let type: ComponentType
public let action: Action
Expand All @@ -34,119 +34,147 @@ public extension PaywallComponent {
self.stack = stack
}

}

}

public extension PaywallComponent.ButtonComponent {

enum Action: Codable, Sendable, Hashable, Equatable {
case restorePurchases
case navigateBack
case navigateTo(destination: Destination)

// swiftlint:disable:next nesting
private enum CodingKeys: String, CodingKey {
case type
case destination
case url
case action
case stack
}

required public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.type = try container.decode(ComponentType.self, forKey: .type)
self.action = try container.decode(Action.self, forKey: .action)
self.stack = try container.decode(PaywallComponent.StackComponent.self, forKey: .stack)
}

public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(type, forKey: .type)
try container.encode(action, forKey: .action)
try container.encode(stack, forKey: .stack)
}

switch self {
case .restorePurchases:
try container.encode("restore_purchases", forKey: .type)
case .navigateBack:
try container.encode("navigate_back", forKey: .type)
case .navigateTo(let destination):
try container.encode("navigate_to", forKey: .type)
try destination.encode(to: encoder) // Encode destination directly under action
}
public func hash(into hasher: inout Hasher) {
hasher.combine(type)
hasher.combine(action)
hasher.combine(stack)
}

public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let type = try container.decode(String.self, forKey: .type)

switch type {
case "restore_purchases":
self = .restorePurchases
case "navigate_back":
self = .navigateBack
case "navigate_to":
let destination = try Destination(from: decoder) // Decode destination directly under action
self = .navigateTo(destination: destination)
default:
throw DecodingError.dataCorruptedError(forKey: .type,
in: container, debugDescription: "Invalid action type")
}
public static func == (lhs: ButtonComponent, rhs: ButtonComponent) -> Bool {
return lhs.type == rhs.type &&
lhs.action == rhs.action &&
lhs.stack == rhs.stack
}
}

enum Destination: Codable, Sendable, Hashable, Equatable {
case customerCenter
case privacyPolicy(urlLid: String, method: URLMethod)
case terms(urlLid: String, method: URLMethod)
case url(urlLid: String, method: URLMethod)
public enum Action: Codable, Sendable, Hashable, Equatable {
case restorePurchases
case navigateBack
case navigateTo(destination: Destination)

// swiftlint:disable:next nesting
private enum CodingKeys: String, CodingKey {
case destination
case url
}
private enum CodingKeys: String, CodingKey {
case type
case destination
}

public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)

switch self {
case .restorePurchases:
try container.encode("restore_purchases", forKey: .type)
case .navigateBack:
try container.encode("navigate_back", forKey: .type)
case .navigateTo(let destination):
try container.encode("navigate_to", forKey: .type)
try destination.encode(to: encoder)
}
}

switch self {
case .customerCenter:
try container.encode("customer_center", forKey: .destination)
case .terms(let urlLid, let method):
try container.encode("terms", forKey: .destination)
try container.encode(URLPayload(urlLid: urlLid, method: method), forKey: .url)
case .privacyPolicy(let urlLid, let method):
try container.encode("privacy_policy", forKey: .destination)
try container.encode(URLPayload(urlLid: urlLid, method: method), forKey: .url)
case .url(let urlLid, let method):
try container.encode("url", forKey: .destination)
try container.encode(URLPayload(urlLid: urlLid, method: method), forKey: .url)
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let type = try container.decode(String.self, forKey: .type)

switch type {
case "restore_purchases":
self = .restorePurchases
case "navigate_back":
self = .navigateBack
case "navigate_to":
let destination = try Destination(from: decoder)
self = .navigateTo(destination: destination)
default:
throw DecodingError.dataCorruptedError(
forKey: .type, in: container,
debugDescription: "Invalid action type"
)
}
}
}

public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let destination = try container.decode(String.self, forKey: .destination)

switch destination {
case "customer_center":
self = .customerCenter
case "terms":
let urlPayload = try container.decode(URLPayload.self, forKey: .url)
self = .terms(urlLid: urlPayload.urlLid, method: urlPayload.method)
case "privacy_policy":
let urlPayload = try container.decode(URLPayload.self, forKey: .url)
self = .privacyPolicy(urlLid: urlPayload.urlLid, method: urlPayload.method)
case "url":
let urlPayload = try container.decode(URLPayload.self, forKey: .url)
self = .url(urlLid: urlPayload.urlLid, method: urlPayload.method)
default:
throw DecodingError.dataCorruptedError(forKey: .destination,
in: container, debugDescription: "Invalid destination type")
public enum Destination: Codable, Sendable, Hashable, Equatable {
case customerCenter
case privacyPolicy(urlLid: String, method: URLMethod)
case terms(urlLid: String, method: URLMethod)
case url(urlLid: String, method: URLMethod)

private enum CodingKeys: String, CodingKey {
case destination
case url
}

public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)

switch self {
case .customerCenter:
try container.encode("customer_center", forKey: .destination)
case .terms(let urlLid, let method):
try container.encode("terms", forKey: .destination)
try container.encode(URLPayload(urlLid: urlLid, method: method), forKey: .url)
case .privacyPolicy(let urlLid, let method):
try container.encode("privacy_policy", forKey: .destination)
try container.encode(URLPayload(urlLid: urlLid, method: method), forKey: .url)
case .url(let urlLid, let method):
try container.encode("url", forKey: .destination)
try container.encode(URLPayload(urlLid: urlLid, method: method), forKey: .url)
}
}

public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let destination = try container.decode(String.self, forKey: .destination)

switch destination {
case "customer_center":
self = .customerCenter
case "terms":
let urlPayload = try container.decode(URLPayload.self, forKey: .url)
self = .terms(urlLid: urlPayload.urlLid, method: urlPayload.method)
case "privacy_policy":
let urlPayload = try container.decode(URLPayload.self, forKey: .url)
self = .privacyPolicy(urlLid: urlPayload.urlLid, method: urlPayload.method)
case "url":
let urlPayload = try container.decode(URLPayload.self, forKey: .url)
self = .url(urlLid: urlPayload.urlLid, method: urlPayload.method)
default:
throw DecodingError.dataCorruptedError(
forKey: .destination, in: container,
debugDescription: "Invalid destination type"
)
}
}
}
}

enum URLMethod: String, Codable, Sendable, Hashable, Equatable {
case inAppBrowser = "in_app_browser"
case externalBrowser = "external_browser"
case deepLink = "deep_link"
}
public enum URLMethod: String, Codable, Sendable, Hashable, Equatable {
case inAppBrowser = "in_app_browser"
case externalBrowser = "external_browser"
case deepLink = "deep_link"
}

private struct URLPayload: Codable, Hashable, Sendable {
let urlLid: String
let method: URLMethod
private struct URLPayload: Codable, Hashable, Sendable {
let urlLid: String
let method: URLMethod
}
}

}
Expand Down
Loading