Skip to content

Commit

Permalink
Offering: new getMetadataValue with Decodable type (#3373)
Browse files Browse the repository at this point in the history
This allows to easily extract data from `Offering.metadata` in a
structured way.

Example:
```swift
struct Data: Decodable {
    var number: Int
}

let elements: [Data] = offering.getMetadataValue(for: "elements") ?? []
let element: Data? = offering.getMetadataValue(for: "element")
```
  • Loading branch information
NachoSoto authored Nov 1, 2023
1 parent dd03843 commit 4344ea9
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 6 deletions.
25 changes: 21 additions & 4 deletions Sources/Purchasing/Offering.swift
Original file line number Diff line number Diff line change
Expand Up @@ -215,17 +215,34 @@ import Foundation

extension Offering {

/**
- Returns: the `metadata` value associated to `key` for the expected type,
or `default` if not found, or it's not the expected type.
*/
/// - Returns: The `metadata` value associated to `key` for the expected type,
/// or `default` if not found or it's not the expected type.
public func getMetadataValue<T>(for key: String, default: T) -> T {
guard let rawValue = self.metadata[key], let value = rawValue as? T else {
return `default`
}
return value
}

/// - Returns: The `metadata` value associated to `key` for the expected `Decodable` type,
/// or `nil` if not found.
/// - Throws: Error if the content couldn't be deserialized to the expected type.
/// - Note: This decodes JSON using `JSONDecoder.KeyDecodingStrategy.convertFromSnakeCase`.
public func getMetadataValue<T: Decodable>(for key: String) -> T? {
do {
guard let value = self.metadata[key] else { return nil }
let data = try JSONSerialization.data(withJSONObject: value)

return try JSONDecoder.default.decode(
T.self,
jsonData: data,
logErrors: true
)
} catch {
return nil
}
}

}

extension Offering: Identifiable {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import RevenueCat

var off: Offering!
func checkOfferingAPI() {
struct Data: Decodable {}

let ident: String = off.identifier
let sDesc: String = off.serverDescription
let aPacks: [Package] = off.availablePackages
Expand All @@ -33,10 +35,12 @@ func checkOfferingAPI() {
let metadataString: String = off.getMetadataValue(for: "", default: "")
let metadataInt: Int = off.getMetadataValue(for: "", default: 0)
let metadataOptionalInt: Int? = off.getMetadataValue(for: "", default: nil)
let metadataDecodable: Data? = off.getMetadataValue(for: "")
let _: PaywallData? = off.paywall

print(off!, ident, sDesc, aPacks, lPack!, annPack!, smPack!, thmPack!, twmPack!,
mPack!, wPack!, pPack!, package!, metadata, metadataString, metadataInt, metadataOptionalInt!)
mPack!, wPack!, pPack!, package!, metadata, metadataString, metadataInt, metadataOptionalInt!,
metadataDecodable!)
}

private func checkCreateOfferingAPI(package: Package) {
Expand Down
38 changes: 37 additions & 1 deletion Tests/UnitTests/Purchasing/OfferingsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,17 @@ class OfferingsTests: TestCase {
"array": ["five"],
"dictionary": [
"string": "five"
],
"elements": [
[
"number": 1
],
[
"number": 2
]
],
"element": [
"number": 3
]
]

Expand Down Expand Up @@ -212,7 +223,7 @@ class OfferingsTests: TestCase {
expect(offerings.current) == offerings["offering_a"]

let offeringA = try XCTUnwrap(offerings["offering_a"])
expect(offeringA.metadata).to(haveCount(6))
expect(offeringA.metadata).to(haveCount(8))
expect(offeringA.getMetadataValue(for: "int", default: 0)) == 5
expect(offeringA.getMetadataValue(for: "double", default: 0.0)) == 5.5
expect(offeringA.getMetadataValue(for: "boolean", default: false)) == true
Expand All @@ -225,6 +236,31 @@ class OfferingsTests: TestCase {

let wrongMetadataType = offeringA.getMetadataValue(for: "string", default: 5.5)
expect(wrongMetadataType) == 5.5

struct Data: Decodable, Equatable {
var number: Int
}

let elements: [Data]? = offeringA.getMetadataValue(for: "elements")
expect(elements) == [.init(number: 1), .init(number: 2)]

let element: Data? = offeringA.getMetadataValue(for: "element")
expect(element) == .init(number: 3)

let missing: Data? = offeringA.getMetadataValue(for: "missing")
expect(missing).to(beNil())

do {
let logger = TestLogHandler()

expect(offeringA.getMetadataValue(for: "dictionary") as Data?)
.to(beNil())

logger.verifyMessageWasLogged("Error deserializing `Data`",
level: .debug,
expectedCount: 1)
}

}

func testLifetimePackage() throws {
Expand Down

0 comments on commit 4344ea9

Please sign in to comment.