Could not cast value of type 'NSNull' to 'FIRTimestamp'
Tulleb opened this issue · comments
I am facing this error while trying to decode an object through FirebaseDecoder containing a timestamp:
Could not cast value of type 'NSNull' (0x1db8afc00) to 'FIRTimestamp' (0x1db8b1f20).
2022-01-04 10:54:09.430366-0300 Voila[8602:2189885] Could not cast value of type 'NSNull' (0x1db8afc00) to 'FIRTimestamp' (0x1db8b1f20).
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
This happens at the line try? values.decode(FirebaseFirestore.Timestamp.self, forKey: .lastUpdate)
from the User.swift
file, crashing when calling the decoded = value as! T
line 1234 from the Decoder.swift
file of this repository.
The lastUpdate is a Swift Date
object which is encoded using FieldValue.serverTimestamp()
.
The Firestore User object currently has a timestamp date properly filled:
Here are some extracts of my code:
NetworkManager.swift:
func loadUser(from uuid: String, completion: @escaping (Result<User, LoadingError>) -> Void) {
let docRef = database.collection("users").document(uuid)
docRef.getDocument { (document, error) in
if let error = error {
print("Error while fetching user: \(error)")
error.sendToCrashlytics()
completion(.failure(.unknown))
return
}
guard let document = document,
document.exists,
let data = document.data() else {
print("User \(uuid) does not exist")
completion(.failure(.invalidUUID))
return
}
do {
let user = try FirestoreDecoder().decode(User.self, from: data)
print("User received: \(user)")
completion(.success(user))
} catch {
print("Error while unboxing user \(uuid): \(error)")
error.sendToCrashlytics()
completion(.failure(.outOfDate))
}
}
}
func save(user: User, completion: ((SavingError?) -> Void)? = nil) {
print("Saving user remotely...")
let ref = database.document("users/\(user.uuid)")
let userDictionary = formatForFirebase(dictionary: user.dictionary, device: true)
ref.setData(userDictionary) { (error) in
if let error = error {
print("Error uploading user document \(user.uuid): \(error)")
error.sendToCrashlytics()
completion?(.unknown)
return
}
print("User successfully saved!")
completion?(nil)
}
}
func formatForFirebase(dictionary: [String: Any], lastUpdate: Bool = true, device: Bool = false) -> [String: Any] {
var bufferDictionary = dictionary
addMiscInformations(dictionary: &bufferDictionary, lastUpdate: lastUpdate, device: device)
return replaceNilWithDelete(from: bufferDictionary)
}
func addMiscInformations(dictionary: inout [String: Any], lastUpdate: Bool, device: Bool) {
if lastUpdate {
dictionary["lastUpdate"] = FieldValue.serverTimestamp()
}
if device {
dictionary["device"] = Device.current.dictionary
}
}
User.swift:
import Foundation
import FirebaseFirestore
final class User: Codable {
var uuid: String
var nickname: String?
[...]
var lastUpdate: Date
enum CodingKeys: String, CodingKey {
case uuid
case nickname
[...]
case lastUpdate
}
var dictionary: [String: Any] {
var dictionary: [String: Any] = [:]
dictionary[CodingKeys.uuid.rawValue] = uuid
dictionary[CodingKeys.nickname.rawValue] = nickname
[...]
return dictionary
}
init(uuid: String) {
self.uuid = uuid
nickname = nil
[...]
lastUpdate = Date()
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
uuid = try values.decode(String.self, forKey: .uuid)
nickname = try? values.decode(String.self, forKey: .nickname)
[...]
if let lastUpdateTimestamp = try? values.decode(FirebaseFirestore.Timestamp.self, forKey: .lastUpdate) {
lastUpdate = lastUpdateTimestamp.dateValue()
} else {
lastUpdate = Date()
}
}
}
Using Timestamp+Extension.swift alongside:
import Foundation
import FirebaseFirestore
import CodableFirebase
extension Timestamp: TimestampType {}
Extract of Podfile:
pod 'CodableFirebase', '~> 0.2'
pod 'Firebase/Analytics', '~> 8.6'
pod 'Firebase/Auth', '~> 8.6'
pod 'Firebase/Crashlytics', '~> 8.6'
pod 'Firebase/Firestore', '~> 8.6'
pod 'Firebase/Functions', '~> 8.6'
pod 'Firebase/Messaging', '~> 8.6'
pod 'Firebase/RemoteConfig', '~> 8.6'
pod 'Firebase/Storage', '~> 8.6'
pod 'FirebaseFirestoreSwift', '~> 8.6-beta'
Thank you a lot for your help on this.
I just saw that this lib was not supported anymore and is using force cast (reason of the crash).
I am moving to FirebaseFirestoreSwift (https://peterfriese.dev/firestore-codable-the-comprehensive-guide) which is the official Firebase library for Swift guys using Codable.