mirror of
https://github.com/RevenueCat/purchases-ios
synced 2026-11-11 22:55:55 +05:45
* Add checkpoint public API surface * Rename checkpoint paywall result to outcome * Add checkpoint API compile tests * Add checkpoint sources to Xcode project * Hide checkpoint Objective-C API behind flag * Align checkpoint API models with Android * Move checkpoint public API to RevenueCatUI * Export flagged checkpoint Objective-C API * Type checkpoint parameters in core * Fix checkpoint project membership and lint * Own checkpoint API models in core * Simplify checkpoint completion listener * Allow core to construct checkpoint results * Fix checkpoint test project membership * Centralize Foundation number classification * Share checkpoint models with Objective-C * Preserve JSON number classification behavior * Organize checkpoint API sources * Simplify checkpoint API storage * Restrict checkpoint outcome construction
69 lines
2.5 KiB
Swift
69 lines
2.5 KiB
Swift
//
|
|
// Value+JSON.swift
|
|
//
|
|
// Created by Antonio Pallares.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
/// Production JSON → `Value` parser. Converts the predicate JSON extracted
|
|
/// from the SDK artifact into the engine's typed `Value` tree. Used by
|
|
/// `RulesEngine.evaluate`; failures surface as `RulesEngine.EvaluationError.parse`.
|
|
extension RulesEngine.Value {
|
|
|
|
/// Parse a JSON string into a `Value`. `JSONSerialization` returns
|
|
/// numbers as `NSNumber`, so we use `CFNumber` type metadata to
|
|
/// distinguish booleans from ints from doubles — without that, a JSON
|
|
/// `true` and a JSON `1` both round-trip to `NSNumber` and lose their
|
|
/// type intent.
|
|
static func fromJSONString(_ input: String) throws -> RulesEngine.Value {
|
|
guard let data = input.data(using: .utf8) else {
|
|
throw RulesEngine.EvaluationError.parse(message: "non-UTF8 input")
|
|
}
|
|
let json: Any
|
|
do {
|
|
json = try JSONSerialization.jsonObject(with: data, options: [.fragmentsAllowed])
|
|
} catch {
|
|
throw RulesEngine.EvaluationError.parse(message: error.localizedDescription)
|
|
}
|
|
return try Self.fromJSONObject(json)
|
|
}
|
|
|
|
/// Recursively convert a value produced by `JSONSerialization` (the
|
|
/// `Any` is one of `NSNull`, `NSNumber`, `String`, `[Any]`, or
|
|
/// `[String: Any]`). Throws `RulesEngine.EvaluationError.parse` if it
|
|
/// encounters anything else.
|
|
static func fromJSONObject(_ object: Any) throws -> RulesEngine.Value {
|
|
if object is NSNull {
|
|
return .null
|
|
}
|
|
if let number = object as? NSNumber {
|
|
switch number.jsonNumberKind {
|
|
case .boolean:
|
|
return .bool(number.boolValue)
|
|
case .integer:
|
|
return .int(number.int64Value)
|
|
case .floatingPoint:
|
|
return .float(number.doubleValue)
|
|
}
|
|
}
|
|
if let string = object as? String {
|
|
return .string(string)
|
|
}
|
|
if let array = object as? [Any] {
|
|
return .array(try array.map(Self.fromJSONObject))
|
|
}
|
|
if let dict = object as? [String: Any] {
|
|
var result: [String: RulesEngine.Value] = [:]
|
|
result.reserveCapacity(dict.count)
|
|
for (key, value) in dict {
|
|
result[key] = try Self.fromJSONObject(value)
|
|
}
|
|
return .object(result)
|
|
}
|
|
throw RulesEngine.EvaluationError.parse(
|
|
message: "unexpected JSONSerialization output of type \(type(of: object))"
|
|
)
|
|
}
|
|
}
|