mirror of
https://github.com/RevenueCat/purchases-ios
synced 2026-11-10 22:45:55 +05:45
### Motivation: Initially I wanted to make `SubscriberAttribute.asDictionary` work using `Codable`. However, this isn't the right refactor because we need the actual raw types to be encoded inside `UserDefaults`. At least this refactor provides several improvements: ### Changes: - `SubscriberAttribute` now has value semantics - ⚠️ `SubscriberAttribute.isSynced` used to be mutable, with reference semantics, which was dangerous - `SubscriberAttribute: Equatable` implementation is now automatic - Cleaned up `SubscriberAttribute.asDictionary` implementation, extracting keys into an `enum` - Moved `Dictionary` -> `SubscriberAttribute` creation to the type itself - ⚠️ `SubscriberAttribute.init(dictionary:)` returns an `Optional` instead of using force-unwrap, avoiding potential crashes - Added more serialization / deserialization tests - Removed `SubscriberAttributesMarshaller` and moved method into `SubscriberAttribute` extension - Moved `SubscriberAttributeDict` into `SubscriberAttribute.Dictionary`
109 lines
2.7 KiB
Swift
109 lines
2.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
|
|
//
|
|
// SubscriberAttribute.swift
|
|
//
|
|
// Created by Joshua Liebowitz on 7/1/21.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
struct SubscriberAttribute {
|
|
|
|
let setTime: Date
|
|
let key: String
|
|
let value: String
|
|
var isSynced: Bool
|
|
|
|
init(withKey key: String, value: String?, isSynced: Bool, setTime: Date) {
|
|
self.key = key
|
|
self.value = value ?? ""
|
|
self.isSynced = isSynced
|
|
self.setTime = setTime
|
|
}
|
|
|
|
init(withKey key: String, value: String?, dateProvider: DateProvider = DateProvider()) {
|
|
self.init(withKey: key, value: value, isSynced: false, setTime: dateProvider.now())
|
|
}
|
|
|
|
}
|
|
|
|
extension SubscriberAttribute {
|
|
|
|
init?(dictionary: [String: Any]) {
|
|
guard let key = dictionary[Key.key.rawValue] as? String,
|
|
let isSynced = (dictionary[Key.isSynced.rawValue] as? NSNumber)?.boolValue,
|
|
let setTime = dictionary[Key.setTime.rawValue] as? Date else {
|
|
return nil
|
|
}
|
|
|
|
let value = dictionary[Key.value.rawValue] as? String
|
|
|
|
self.init(withKey: key, value: value, isSynced: isSynced, setTime: setTime)
|
|
}
|
|
|
|
func asDictionary() -> [String: NSObject] {
|
|
return [Key.key.rawValue: self.key as NSString,
|
|
Key.value.rawValue: self.value as NSString,
|
|
Key.isSynced.rawValue: NSNumber(value: self.isSynced),
|
|
Key.setTime.rawValue: self.setTime as NSDate]
|
|
}
|
|
|
|
func asBackendDictionary() -> [String: Any] {
|
|
return [BackendKey.value.rawValue: self.value,
|
|
BackendKey.timestamp.rawValue: self.setTime.millisecondsSince1970AsUInt64()]
|
|
}
|
|
|
|
}
|
|
|
|
extension SubscriberAttribute: Equatable {}
|
|
|
|
extension SubscriberAttribute: CustomStringConvertible {
|
|
|
|
var description: String {
|
|
return "[SubscriberAttribute] key: \(self.key) value: \(self.value) setTime: \(self.setTime)"
|
|
}
|
|
|
|
}
|
|
|
|
extension SubscriberAttribute {
|
|
|
|
typealias Dictionary = [String: SubscriberAttribute]
|
|
|
|
}
|
|
|
|
extension SubscriberAttribute {
|
|
|
|
static func map(subscriberAttributes: SubscriberAttribute.Dictionary) -> [String: [String: Any]] {
|
|
return subscriberAttributes.mapValues { $0.asBackendDictionary() }
|
|
}
|
|
}
|
|
|
|
// MARK: - Private
|
|
|
|
extension SubscriberAttribute {
|
|
|
|
private enum Key: String {
|
|
|
|
case key
|
|
case value
|
|
case isSynced
|
|
case setTime
|
|
|
|
}
|
|
|
|
private enum BackendKey: String {
|
|
|
|
case value = "value"
|
|
case timestamp = "updated_at_ms"
|
|
|
|
}
|
|
|
|
}
|