I'm trying to save a firebase model to user defaults but the coding/decoding of the id field fails.
struct DeviceModel: Identifiable, Codable, Hashable {
@DocumentID var id: String?
var fcmToken: String
var userId: String
@ServerTimestamp var expiresOn: Timestamp?
enum CodingKeys: String, CodingKey {
case id
case fcmToken = "token"
case expiresOn = "expires_on"
case userId = "user_id"
}
init(id: String? = nil,
fcmToken: String,
userId: String,
expiresOn: Timestamp? = Timestamp()) {
self.id = id
self.fcmToken = fcmToken
self.userId = userId
self.expiresOn = expiresOn
}
}
UserDefault extension
extension UserDefaults {
func decode<T: Decodable>(_ type: T.Type, forKey defaultName: String) throws -> T {
// try! JSONDecoder().decode(T.self, from: data(forKey: defaultName) ?? .init())
try! Firestore.Decoder().decode(T.self, from: data(forKey: defaultName) ?? .init())
}
func encode<T: Encodable>(_ value: T, forKey defaultName: String) throws {
// try! set(JSONEncoder().encode(value), forKey: defaultName)
try! set(Firestore.Encoder().encode(value), forKey: defaultName)
}
}
At first i tried with JSONEncoder and got an error saying that @DocumentID should be encoded/decoded using firestore encoder/decoder.
Fatal error: 'try!' expression unexpectedly raised an error:
FirebaseFirestoreSwift.FirestoreEncodingError.encodingIsNotSupported("DocumentID values can only be encoded with Firestore.Encoder")
After that, i use them as above but now i get
2023-04-18 10:52:19.298750+0300 Voxic[49960:1461668] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to insert non-property list object {
"expires_on" = "<FIRTimestamp: seconds=1685555885 nanoseconds=220984000>";
token = "<token>";
"user_id" = 41giBVERER34QNPFZmlB3;
} for key localDeviceKey'
One thing i find strange is that the user_id
is not printed as a string in the above error. It might be just that, a printing issue though.
Any thoughts? Thank you
Try using a Custom encode function & Custom init(from:) function
struct DeviceModel: Identifiable, Codable, Hashable {
@DocumentID var id: String?
var fcmToken: String
var userId: String
@ServerTimestamp var expiresOn: Timestamp?
enum CodingKeys: String, CodingKey {
case id
case fcmToken = "token"
case expiresOn = "expires_on"
case userId = "user_id"
}
init(id: String? = nil,
fcmToken: String,
userId: String,
expiresOn: Timestamp? = Timestamp()) {
self.id = id
self.fcmToken = fcmToken
self.userId = userId
self.expiresOn = expiresOn
}
// Custom init(from:) function that decodes the id property using the Firestore.Decoder
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
fcmToken = try container.decode(String.self, forKey: .fcmToken)
userId = try container.decode(String.self, forKey: .userId)
expiresOn = try container.decode(Timestamp?.self, forKey: .expiresOn)
// Decode the DocumentID property using the Firestore.Decoder
if let idContainer = try? decoder.container(keyedBy: CodingKeys.self),
let id = try? idContainer.decode(String.self, forKey: .id) {
self.id = id
}
}
// Custom encode function that encodes the DocumentID property using the Firestore.Encoder
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(fcmToken, forKey: .fcmToken)
try container.encode(userId, forKey: .userId)
try container.encode(expiresOn, forKey: .expiresOn)
// Encode the DocumentID property using the Firestore.Encoder
if let id = id {
let firestoreEncoder = Firestore.Encoder()
let something = try firestoreEncoder.encode(["id":id])
if let mapId = something["id"] as? String {
try container.encode(mapId, forKey: .id)
}
}
}
}
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments