mirror of
https://github.com/caoer/CodableFirebase.git
synced 2026-04-01 22:35:55 +08:00
add deferred to timestamp type, other types WIP
This commit is contained in:
@@ -13,6 +13,8 @@ public enum DateDecodingStrategy {
|
||||
/// Defer to `Date` for decoding. This is the default strategy.
|
||||
case deferredToDate
|
||||
|
||||
case deferredToTimestamp
|
||||
|
||||
/// Decode the `Date` as a UNIX timestamp from a JSON number.
|
||||
case secondsSince1970
|
||||
|
||||
@@ -43,13 +45,17 @@ public enum DataDecodingStrategy {
|
||||
}
|
||||
|
||||
public enum FirestoreTypeDecodingStrategy {
|
||||
case byProtocol
|
||||
case deferredToPtotocol
|
||||
case custom((_ value: Any) throws -> Any)
|
||||
}
|
||||
|
||||
|
||||
extension CodingUserInfoKey {
|
||||
public static let dateDecodingStrategy: CodingUserInfoKey = CodingUserInfoKey(rawValue: "dateDecodingStrategy")!
|
||||
|
||||
public static let dataDecodingStrategy: CodingUserInfoKey = CodingUserInfoKey(rawValue: "dataDecodingStrategy")!
|
||||
|
||||
public static let firestoreTypeDecodingStrategy: CodingUserInfoKey = CodingUserInfoKey(rawValue: "firestoreTypeDecodingStrategy")!
|
||||
}
|
||||
|
||||
extension Dictionary where Key == CodingUserInfoKey, Value == Any {
|
||||
@@ -60,4 +66,12 @@ extension Dictionary where Key == CodingUserInfoKey, Value == Any {
|
||||
var dataDecodingStrategy: DataDecodingStrategy? {
|
||||
return self[.dataDecodingStrategy] as? DataDecodingStrategy
|
||||
}
|
||||
|
||||
var firestoreTypeDecodingStrategy: FirestoreTypeDecodingStrategy {
|
||||
if let strategy = self[.firestoreTypeDecodingStrategy] as? FirestoreTypeDecodingStrategy {
|
||||
return strategy
|
||||
}
|
||||
|
||||
return FirestoreTypeDecodingStrategy.deferredToPtotocol
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1104,6 +1104,9 @@ extension _FirebaseDecoder {
|
||||
}
|
||||
|
||||
switch options {
|
||||
case .deferredToTimestamp:
|
||||
let timestamp = value as! TimestampType
|
||||
return timestamp.dateValue()
|
||||
case .deferredToDate:
|
||||
self.storage.push(container: value)
|
||||
let date = try Date(from: self)
|
||||
@@ -1219,7 +1222,13 @@ extension _FirebaseDecoder {
|
||||
decoded = decimal as! T
|
||||
}
|
||||
else if userInfo.skipFirestoreTypes && (T.self is FirestoreDecodable.Type) {
|
||||
decoded = value as! T
|
||||
let strategy = userInfo.firestoreTypeDecodingStrategy
|
||||
switch strategy {
|
||||
case .deferredToPtotocol:
|
||||
decoded = value as! T
|
||||
case .custom(let decodeFunc):
|
||||
decoded = try decodeFunc(value) as! T
|
||||
}
|
||||
}
|
||||
else {
|
||||
self.storage.push(container: value)
|
||||
|
||||
@@ -13,6 +13,8 @@ public enum DateEncodingStrategy {
|
||||
/// Defer to `Date` for choosing an encoding. This is the default strategy.
|
||||
case deferredToDate
|
||||
|
||||
case deferredToTimestamp((Date) -> TimestampType)
|
||||
|
||||
/// Encode the `Date` as a UNIX timestamp (as a JSON number).
|
||||
case secondsSince1970
|
||||
|
||||
@@ -46,11 +48,19 @@ public enum DataEncodingStrategy {
|
||||
case custom((Data, Encoder) throws -> Void)
|
||||
}
|
||||
|
||||
public enum FirestoreTypeEncodingStrategy {
|
||||
case deferredToPtotocol
|
||||
case custom((_ value: Any) throws -> Any)
|
||||
}
|
||||
|
||||
extension CodingUserInfoKey {
|
||||
public static let dateEncodingStrategy: CodingUserInfoKey = CodingUserInfoKey(rawValue: "dateEncodingStrategy")!
|
||||
|
||||
public static let dataEncodingStrategy: CodingUserInfoKey = CodingUserInfoKey(rawValue: "dataEncodingStrategy")!
|
||||
|
||||
public static let skipFirestoreTypes: CodingUserInfoKey = CodingUserInfoKey(rawValue: "skipFirestoreTypes")!
|
||||
|
||||
public static let firestoreTypeEncodingStrategy: CodingUserInfoKey = CodingUserInfoKey(rawValue: "firestoreTypeEncodingStrategy")!
|
||||
}
|
||||
|
||||
extension Dictionary where Key == CodingUserInfoKey, Value == Any {
|
||||
@@ -68,4 +78,12 @@ extension Dictionary where Key == CodingUserInfoKey, Value == Any {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var firestoreTypeEncodingStrategy: FirestoreTypeEncodingStrategy {
|
||||
if let strategy = self[.firestoreTypeEncodingStrategy] as? FirestoreTypeEncodingStrategy {
|
||||
return strategy
|
||||
}
|
||||
|
||||
return FirestoreTypeEncodingStrategy.deferredToPtotocol
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,6 +306,8 @@ extension _FirebaseEncoder {
|
||||
guard let options = userInfo.dateEncodingStrategy else { return date as NSDate }
|
||||
|
||||
switch options {
|
||||
case .deferredToTimestamp(let converter):
|
||||
return converter(date) as! NSObject
|
||||
case .deferredToDate:
|
||||
// Must be called with a surrounding with(pushedKey:) call.
|
||||
try date.encode(to: self)
|
||||
@@ -377,10 +379,18 @@ extension _FirebaseEncoder {
|
||||
} else if T.self == Decimal.self || T.self == NSDecimalNumber.self {
|
||||
return (value as! NSDecimalNumber)
|
||||
} else if userInfo.skipFirestoreTypes && (value is FirestoreEncodable) {
|
||||
guard let value = value as? NSObject else {
|
||||
let target: Any
|
||||
switch userInfo.firestoreTypeDecodingStrategy {
|
||||
case .deferredToPtotocol:
|
||||
target = value
|
||||
case .custom(let encodeFunc):
|
||||
target = try encodeFunc(value)
|
||||
}
|
||||
|
||||
guard let result = target as? NSObject else {
|
||||
throw DocumentReferenceError.typeIsNotNSObject
|
||||
}
|
||||
return value
|
||||
return result
|
||||
}
|
||||
|
||||
// The value should request a container from the _FirebaseEncoder.
|
||||
|
||||
@@ -26,9 +26,11 @@ public protocol TimestampType: FirestoreDecodable, FirestoreEncodable {
|
||||
}
|
||||
|
||||
open class FirestoreDecoder {
|
||||
public init() {}
|
||||
public init(userInfo: [CodingUserInfoKey: Any] = [.skipFirestoreTypes: true]) {
|
||||
self.userInfo = userInfo
|
||||
}
|
||||
|
||||
open var userInfo: [CodingUserInfoKey : Any] = [.skipFirestoreTypes: true]
|
||||
public let userInfo: [CodingUserInfoKey: Any]
|
||||
|
||||
open func decode<T : Decodable>(_ type: T.Type, from container: [String: Any]) throws -> T {
|
||||
let decoder = _FirebaseDecoder(referencing: container,
|
||||
|
||||
@@ -9,10 +9,12 @@
|
||||
import Foundation
|
||||
|
||||
open class FirestoreEncoder {
|
||||
public init() {}
|
||||
|
||||
open var userInfo: [CodingUserInfoKey : Any] = [.skipFirestoreTypes: true]
|
||||
public init(userInfo: [CodingUserInfoKey: Any] = [.skipFirestoreTypes: true]) {
|
||||
self.userInfo = userInfo
|
||||
}
|
||||
|
||||
public let userInfo: [CodingUserInfoKey: Any]
|
||||
|
||||
open func encode<Value : Encodable>(_ value: Value) throws -> [String: Any] {
|
||||
let topLevel = try encodeToTopLevelContainer(value)
|
||||
switch topLevel {
|
||||
|
||||
@@ -128,14 +128,36 @@ class TestCodableFirestore: XCTestCase {
|
||||
XCTAssertEqual((try? FirestoreEncoder().encode(val)) as NSDictionary?, ["value": val.value])
|
||||
XCTAssertEqual(try? FirestoreDecoder().decode(TopLevelWrapper<DocumentReference>.self, from: ["value": val.value]), val)
|
||||
}
|
||||
|
||||
|
||||
func testEncodingTimestamp() {
|
||||
let timestamp = Timestamp(date: Date())
|
||||
let wrapper = TopLevelWrapper(timestamp)
|
||||
XCTAssertEqual((try? FirestoreEncoder().encode(wrapper)) as NSDictionary?, ["value": timestamp])
|
||||
XCTAssertEqual(try? FirestoreDecoder().decode(TopLevelWrapper<Timestamp>.self, from: ["value": timestamp]), wrapper)
|
||||
}
|
||||
|
||||
|
||||
func testCustomEncodingTimestamp() {
|
||||
let date = Date()
|
||||
let timestamp = Timestamp(date: date)
|
||||
|
||||
// encode date to Timestamp
|
||||
let encodeWrapper = TopLevelWrapper(date)
|
||||
let encodeResult = (try? FirestoreEncoder(userInfo: [CodingUserInfoKey.dateEncodingStrategy: DateEncodingStrategy.deferredToTimestamp({ date in
|
||||
return Timestamp(date: date)
|
||||
})])
|
||||
.encode(encodeWrapper)) as NSDictionary?
|
||||
XCTAssertEqual(encodeResult, ["value": timestamp])
|
||||
|
||||
// decode timestamp to date
|
||||
let decodeWrapper = TopLevelWrapper(timestamp)
|
||||
let decoder = FirestoreDecoder(userInfo: [
|
||||
CodingUserInfoKey.dateDecodingStrategy: DateDecodingStrategy.deferredToTimestamp
|
||||
])
|
||||
|
||||
let decodeResult = (try? decoder.decode(TopLevelWrapper<Timestamp>.self, from: ["value": timestamp]))
|
||||
XCTAssertEqual(decodeResult, decodeWrapper)
|
||||
}
|
||||
|
||||
private func _testEncodeFailure<T : Encodable>(of value: T) {
|
||||
do {
|
||||
let _ = try FirestoreEncoder().encode(value)
|
||||
@@ -214,17 +236,21 @@ fileprivate class DocumentReference: NSObject, DocumentReferenceType {}
|
||||
// MARK: - Timestamp
|
||||
fileprivate class Timestamp: NSObject, TimestampType {
|
||||
let date: Date
|
||||
|
||||
|
||||
required init(date: Date) {
|
||||
self.date = date
|
||||
}
|
||||
|
||||
|
||||
func dateValue() -> Date {
|
||||
return date
|
||||
}
|
||||
|
||||
|
||||
override func isEqual(_ object: Any?) -> Bool {
|
||||
guard let other = object.flatMap({ $0 as? Timestamp }) else { return false }
|
||||
return date == other.date
|
||||
}
|
||||
}
|
||||
|
||||
extension Date: FirestoreEncodable {
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user