Firebase —初始获取期间数据不同步

退出了一家主要使用专有技术的大公司之后,我获得了所有自由,可以尝试各种出色的工具来为自己的公司构建产品。 我想在这里分享我的学习历程(在所有方面,而不仅仅是编码),我欢迎大家的评论和讨论,因为这也是我的学习方式。

最近,我一直在iOS上使用Firebase。 对于大多数事情来说,这都是令人惊奇的,我特别喜欢脱机功能。 基本上,它使您的应用程序可以脱机运行,并且在您恢复网络后,数据将立即同步到db。 它还“聪明地”(稍后Database.database().isPersistence首先从缓存中查找数据以提高加载性能,而仅用一行代码Database.database().isPersistence获得全部Database.database().isPersistence 。 一切工作正常,除了在初始访存时遇到神秘的数据不同步问题。

如果进行“如何刷新Firebase缓存”或“ Firebase中的缓存中的过时数据”之类的搜索,您将得到诸如此类,此类或此类的零碎讨论。 它们中的许多有用,但没有一个能提供完整的图片。 我真的想知道发生了什么,所以我做了很多实验,让我在这里总结一下自己的理解。 (TL; DR;)

有2种主要的Firebase API用于检索数据:

  • ref.observe(.value, with: { // completion block }
  • ref.observeSingleEvent(of: .value, with: { // completion block }

第一个API是事件监听器。 它返回初始结果,并监听ref更改。 第二个API不监听更改,它只检索一次数据,因此它似乎是用于初始提取的正确API。 但是不幸的是,如果持续打开它就无法工作。

无论调用多少次, observeSingleEvent都将仅返回本地缓存的数据。 如果您的本地数据超过默认的最大10MB,问题将变得更加严重,Firebase将启动逐出缓存的数据(似乎是随机的)。 因此,如果您离线或关闭应用程序并重新启动,则仅通过使用observeSingleEvent获取数据可能会丢失一些数据。

这看起来像是一个常见问题,我希望有一些简单的解决方案,但令人惊讶的是,我确实找不到解决该问题的好方法。 经过大量的阅读和实验后,我提供了一些替代方案来解决或缓解此问题,并让我与您分享。

ref.observe 始终使用 ref.observe

这是最推荐的方法,也是Google小组的Firebase工程师推荐的方法。 使用observe将使本地数据保持同步,如果使用它来获取数据,它将首先从本地获取数据,并最终与服务器同步(具有一个或多个回调)。 但是,使用它初始获取时有两个问题:

  1. 您无法确定何时从初始服务器状态获取数据。 如果您真的想立即进入服务器状态,请停止加载微调器,而不是让数据不断更新和重新加载,则不能这样做。
  2. 它不适用于列表数据。 对于列表数据,您可能会使用.childAdded.childRemoved因为.value仅在更改时为您提供整个Blob数据。 它效率不高,大多数时候不是很有用。
    由于您不知道它何时确切返回初始服务器状态,因此,如果您只想将其用于初始提取,则无法确定何时删除.value侦听器。

ALT2 —调用 ref.keepSynced(true)

它的作用是使ref本地数据保持同步,即使您没有附加任何侦听器也是如此。 您获取了初始数据之后,这可能会有所帮助,基本上,如果您调用了keepSynced则可以使用observeSingleEvent来获取相应ref的最新数据。 但是由于当数据完全同步时它不提供回调,因此您将遇到与ALT1类似的问题。 您可以做的一件事是使应用程序休眠几秒钟,并希望所有数据同步。 (多数情况下应该这样,否则您一开始会加载太多数据)

该API也有一些缺点。 1)。 如果不需要,始终保持加载数据效率不高; 2)。 keepSynced(true) ,本地缓存限制将不适用,我不确定这是否是您想要的。

ALT3-一种结合了以上内容的hack(基于这篇 文章

根据我的实验,这是始终保持一致的方式:

  • 第一次调用ref.keepSynced(true)
  • 然后对ref进行虚拟写入,然后在完成回调中进行一次提取,即
  ref.keepSynced(true) 
ref.child(“ dummy”)。setValue(“ dummy”,{(错误,dummyRef)在
如果让错误==错误{
//处理错误
}
//删除哑数值
dummyRef.remove()
//如果需要,删除保持同步
ref.keepSynced(false)
//执行抓取
ref.observeSingleEvent(of:.value,with:{snapshot in
//在完成块中完成您的工作
})
})

也许在内部在同步完成之前不会触发写完成回调,但这完全是凭经验的,因此没有保证。

现在,我主要是使用这种黑客程序来进行初始抓取,到目前为止,该应用程序很高兴。 如果您遇到类似我的问题,希望能对您有所帮助…

聚苯乙烯
1.有些使用runTransaction进行runTransaction也可以检索同步的数据,但我的实验并非如此。
2. Google可能会将重点转移到构建FireStore(仍处于Beta版中),我将尝试是否仍然存在该问题。

更新(11/27/2017):
我在新的FireStore中尝试了相同的方案,没有这样的问题,太好了!