如何快速解码来自 Firebase Firestore 的时间戳

Posted

技术标签:

【中文标题】如何快速解码来自 Firebase Firestore 的时间戳【英文标题】:How to decode a TimeStamp from the firebase Firestore in swift 【发布时间】:2019-09-16 01:54:43 【问题描述】:

我想通过解析来自 firestore 数据库的响应来解码 TimeStamp(由 FirebaseFirestore.Timestamp 快速定义)。

以下从服务器解析的代码让编译器告诉我:

实例方法 'decode(_:forKey:)' 要求 'Timestamp' 符合 到“可解码”

 created = try container.decode(FirebaseFirestore.Timestamp.self, forKey: .created)

我也无法使用以下行进行编码(在本地保存或发送到服务器):

try container.encode(created, forKey: .created)

编译器说:

无法将“时间戳”类型的值转换为预期的参数类型 '字符串'

下面是完整的复制粘贴

此外,时间戳似乎是字典,而不是整数,因为当我尝试将时间戳解码为整数时,我得到了错误:

Expected to decode Int but found a dictionary instead.

但我们都知道 [String:Any](即字典)无法解码。

import FirebaseFirestore

class SomeClassToParseFromFirestoresDatabase: Codable


  var created = FirebaseFirestore.Timestamp.init(date: Date())

  private enum CodingKeys: String, CodingKey
  
    case created
  

  func encode(to encoder: Encoder) throws
  
    var container = encoder.container(keyedBy: CodingKeys.self)

    do
    
      try container.encode(created, forKey: .created)
    
    catch let error
    
      print("error encoding to server or locally: \(error) ")
    
  


  required init(from decoder: Decoder) throws
  
    let container = try decoder.container(keyedBy: CodingKeys.self)


    do
    
      created = try container.decode(FirebaseFirestore.Timestamp.self, forKey: .created)
    
    catch 
    
      print("error getting 'created' from server: \(error) ")
    
  


下面是一个示例,说明如何解析来自 https.callable firestore 函数(仅返回 JSON)的响应 - 并使用自定义响应类来解析响应中的时间戳(并将时间戳存储在类中)

func getChatUsers( _ done: @escaping (ChatUsersResponse) -> ())
  
    let response     = ChatUsersResponse()
    response.success = true

    let functions    = Functions.functions()

    functions.httpsCallable("getChatUsers").call
     (result, error) in

      if let error = error as NSError?
      
        response.success = false
        response.message = error.localizedDescription

        done(response)
      
      else if let result = result,
        let data = result.data as? [String:Any],
        let users = data["users"]
      
        do
        
          let nsdata     = try JSONSerialization.data(withJSONObject: users, options: .prettyPrinted)
          **response.users = try JSONDecoder().decode([SomeClassToParseFromFirestoresDatabase].self, from:nsdata)**
          done(response)
        
        catch let error
                  
          response.success = false
          response.message = error.localizedDescription
          done(response)
          return
        
      
      else
      
        response.success = false
        response.message = "Server responded with no error, but no users either"
        done(response)
      
    
  

【问题讨论】:

【参考方案1】:

有点不清楚问题中的代码在做什么,但也许如果我们只是简化流程,它会有所帮助。

这是一个将 Firestore 时间戳写入 'timestamp' 集合的函数,每个文档都有一个唯一的 documentID 和一个 'stamp' 的子字段

func writeTimestampAction() 
    let now = Date()
    let stamp = Timestamp(date: now)

    let docRef = self.db.collection("timestamps").document()
    docRef.setData( [
        "stamp": stamp
    ])

然后是一个函数,用于从该集合中读取所有时间戳,并以 yyyy-mm-dd 格式将它们输出到控制台。

func readTimestampAction() 
    self.db.collection("timestamps").getDocuments()  (querySnapshot, err) in
        if let err = err 
            print("Error getting documents: \(err)")
         else 
            for document in querySnapshot!.documents 
                if let stamp = document.get("stamp") 
                    let title = document.documentID
                    let ts = stamp as! Timestamp
                    let aDate = ts.dateValue()
                    let formatter = DateFormatter()
                    formatter.dateFormat = "yyyy-MM-dd HH:mm:ss ZZZ"
                    let formattedTimeZoneStr = formatter.string(from: aDate)
                    print(title, formattedTimeZoneStr)

                
            
        
    

编辑

他是一个可以通过 Firestore 快照的活动类

class ActivityClass 
    var activity_name = ""
    var activity_date: Timestamp?

    convenience init(withDoc: QueryDocumentSnapshot) 
        self.init()
        if let stamp = withDoc.get("stamp") 
            self.activity_date = stamp as? Timestamp
        
    

当您从 Firestore 检索数据时,只需执行此操作

for document in querySnapshot!.documents 
   let myActivity = ActivityClass(withDoc: document)
   //do something with myActivity

【讨论】:

谢谢。我想做相反的事情(从 https 可调用响应中将 firestore 时间戳读入本地类) - 我更新了我的帖子 @aman 与什么相反?这个答案演示了如何同时 write 时间戳以及 read 时间戳?如果时间戳存在,只需使用答案的第二部分,一旦您使用let ts = stamp as! Timestamp 读取时间戳,将其存储在您的类中。 对不起,我现在明白了。我想我不明白如何翻译你写的东西 - 它将文档解析为 Swift 的原生 Codable 协议 @aman 我想我的问题是你为什么要使用可编码协议?为什么不直接将 Firestore 返回的快照传递给班级呢?这可能使代码更易于阅读,更易于扩展和维护。我用一个建议更新了我的答案。也许这不是你想去的方向,但它很干净。 @aman 你有没有想过这个问题?我还需要解码 Firestore 时间戳,但无法弄清楚。似乎互联网上没有其他人正在尝试这样做。【参考方案2】:

我需要做两件事。

第一件事是通过使用 timeStamp 的“toString()”在服务器端“设置”我的日期 - 尽管默认情况下它看起来像一个字符串,但时间戳不是一个字符串,并且客户端解析一个空字典。

然后我能够获取该字符串并将其转换为具有以下日期格式的日期

  let d = try container.decode(String.self, forKey: .created)
  //example: Fri Apr 26 2019 17:25:22
  let index = d.index(d.endIndex, offsetBy: -15) 
  let abc = d[..<index]

  let dateFormatter        = DateFormatter()
  dateFormatter.dateFormat = "EEE MMM dd yyyy HH:mm:ss"


  if let d = dateFormatter.date(from:String(abc))
  
    created = d
  

【讨论】:

以上是关于如何快速解码来自 Firebase Firestore 的时间戳的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Firebase 实时数据库中解码这个 json?

使用 swift Codable 以值作为键来解码 JSON

从Firebase本地实时存储Room DB上的数据

将firebase oobCode解码为服务器端的用户

相同的 Flutter Web 构建使用 `dhttp` 在本地运行,但使用 `firebase serve` 或 `firebase deploy` 失败

如何从 Codable 结构中捕获 init(来自解码器:解码器)中的错误?