Skip to content

Commit

Permalink
Improved JSON format for ButtonComponent codables (#4408)
Browse files Browse the repository at this point in the history
* Improved JSON format for ButtonComponent codables

* Fixed JSON decoding and also tests

* Lint
  • Loading branch information
joshdholtz authored Oct 25, 2024
1 parent 649a7a8 commit 5f9bd6c
Show file tree
Hide file tree
Showing 6 changed files with 315 additions and 27 deletions.
12 changes: 12 additions & 0 deletions RevenueCat.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
2C6CC1162B8D2B6900432E4D /* PurchasesSyncAttributesAndOfferingsIfNeededTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C6CC1152B8D2B6800432E4D /* PurchasesSyncAttributesAndOfferingsIfNeededTests.swift */; };
2C7F0AD32B8EEB4600381179 /* RateLimiter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C7F0AD22B8EEB4600381179 /* RateLimiter.swift */; };
2C7F0AD62B8EEF7B00381179 /* RateLimiterRests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C7F0AD42B8EEF0B00381179 /* RateLimiterRests.swift */; };
2C8EC6AF2CCBD34100D6CCF8 /* ButtonComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C8EC6AE2CCBD33E00D6CCF8 /* ButtonComponent.swift */; };
2CAB87F72CAAB13200247013 /* CornerBorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CAB87F62CAAB13200247013 /* CornerBorder.swift */; };
2CB8CF9327BF538F00C34DE3 /* PlatformInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CB8CF9227BF538F00C34DE3 /* PlatformInfo.swift */; };
2CC791552CC0452100FBE120 /* PurchaseButtonComponentViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CC791522CC0452100FBE120 /* PurchaseButtonComponentViewModel.swift */; };
Expand Down Expand Up @@ -1179,6 +1180,7 @@
2C6CC1152B8D2B6800432E4D /* PurchasesSyncAttributesAndOfferingsIfNeededTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PurchasesSyncAttributesAndOfferingsIfNeededTests.swift; sourceTree = "<group>"; };
2C7F0AD22B8EEB4600381179 /* RateLimiter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RateLimiter.swift; sourceTree = "<group>"; };
2C7F0AD42B8EEF0B00381179 /* RateLimiterRests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RateLimiterRests.swift; sourceTree = "<group>"; };
2C8EC6AE2CCBD33E00D6CCF8 /* ButtonComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonComponent.swift; sourceTree = "<group>"; };
2CAB87F62CAAB13200247013 /* CornerBorder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CornerBorder.swift; sourceTree = "<group>"; };
2CB8CF9227BF538F00C34DE3 /* PlatformInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlatformInfo.swift; sourceTree = "<group>"; };
2CC7914B2CC0452100FBE120 /* PackageComponentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PackageComponentView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2295,6 +2297,14 @@
path = TemplateComponentsViewPreviews;
sourceTree = "<group>";
};
2C8EC6AD2CCBD33800D6CCF8 /* Components */ = {
isa = PBXGroup;
children = (
2C8EC6AE2CCBD33E00D6CCF8 /* ButtonComponent.swift */,
);
path = Components;
sourceTree = "<group>";
};
2CC7913E2CC0450900FBE120 /* Recovered References */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -3683,6 +3693,7 @@
4FBBD4E22A620516001CBA21 /* Paywalls */ = {
isa = PBXGroup;
children = (
2C8EC6AD2CCBD33800D6CCF8 /* Components */,
4FE6FEE62AA940E300780B45 /* Events */,
4F6ABC7B2A81673F00250E63 /* PaywallCacheWarmingTests.swift */,
4F05876E2A5DE03F00E9A834 /* PaywallDataTests.swift */,
Expand Down Expand Up @@ -5911,6 +5922,7 @@
5712BE9229241F7900A83F15 /* TimingUtilTests.swift in Sources */,
57554CC1282AE1E3009A7E58 /* TestCase.swift in Sources */,
57A1772B276A726C0052D3A8 /* SetExtensionsTests.swift in Sources */,
2C8EC6AF2CCBD34100D6CCF8 /* ButtonComponent.swift in Sources */,
351B515A26D44B6200BD2BD7 /* MockAttributionFetcher.swift in Sources */,
5796A38C27D6BA1600653165 /* BackendLoginTests.swift in Sources */,
351B51BD26D450E800BD2BD7 /* EntitlementInfosTests.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ struct ButtonComponentView: View {
switch destination {
case .customerCenter:
showCustomerCenter = true
case .URL(let url, let method),
case .url(let url, let method),
.privacyPolicy(let url, let method),
.terms(let url, let method):
navigateToUrl(url: url, method: method)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public class ButtonComponentViewModel {
/// This way the view layer doesn't need to handle this error scenario.
internal enum Destination {
case customerCenter
case URL(url: URL, method: PaywallComponent.ButtonComponent.URLMethod)
case url(url: URL, method: PaywallComponent.ButtonComponent.URLMethod)
case privacyPolicy(url: URL, method: PaywallComponent.ButtonComponent.URLMethod)
case terms(url: URL, method: PaywallComponent.ButtonComponent.URLMethod)
}
Expand Down Expand Up @@ -66,9 +66,9 @@ public class ButtonComponentViewModel {
switch destination {
case .customerCenter:
self.action = .navigateTo(destination: .customerCenter)
case .URL(let urlLid, let method):
case .url(let urlLid, let method):
self.action = .navigateTo(
destination: .URL(url: try localizedStrings.urlFromLid(urlLid), method: method)
destination: .url(url: try localizedStrings.urlFromLid(urlLid), method: method)
)
case .privacyPolicy(let urlLid, let method):
self.action = .navigateTo(
Expand Down
135 changes: 113 additions & 22 deletions Sources/Paywalls/Components/PaywallButtonComponent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,6 @@ public extension PaywallComponent {

struct ButtonComponent: PaywallComponentBase {

// swiftlint:disable:next nesting
public enum Action: Codable, Sendable, Hashable, Equatable {
case restorePurchases
case navigateTo(destination: Destination)
case navigateBack
}

// swiftlint:disable:next nesting
public enum Destination: Codable, Sendable, Hashable, Equatable {
case customerCenter
case URL(urlLid: String, method: URLMethod)
case privacyPolicy(urlLid: String, method: URLMethod)
case terms(urlLid: String, method: URLMethod)
}

// swiftlint:disable:next nesting
public enum URLMethod: Codable, Sendable, Hashable, Equatable {
case inAppBrowser
case externalBrowser
case deepLink
}

let type: ComponentType
public let action: Action
public let stack: PaywallComponent.StackComponent
Expand All @@ -60,4 +38,117 @@ public extension PaywallComponent {

}

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
}

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) // Encode destination directly under action
}
}

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")
}
}
}

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)

// swiftlint:disable:next nesting
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"
}

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

}

#endif
2 changes: 1 addition & 1 deletion Sources/Paywalls/Components/PaywallStackComponent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public extension PaywallComponent {
public init(components: [T],
dimension: Dimension = .vertical(.center),
width: WidthSize? = nil,
spacing: CGFloat? = 0,
spacing: CGFloat? = nil,
backgroundColor: ColorInfo? = nil,
padding: Padding = .zero,
margin: Padding = .zero,
Expand Down
Loading

0 comments on commit 5f9bd6c

Please sign in to comment.