了解Firebase观察功能

我一直在与团队一起开发健身应用程序。 该应用程序将允许您启动或加入团队,让您为团队创建健身挑战,然后通过这些挑战跟踪您的进度。 我们正在使用Firebase来存储我们所有的数据库信息,而我一直是编码Firebase管理器的主要人员。

我认为关于Firebase的最酷的事情是如何从数据库中检索数据。 通常,使用API​​可以使用附加的“ GET”方法设置URLRequest,然后取回所请求数据的单个实例。 在Firebase中,您不仅可以发送请求并返回单个响应。 不好了。 在Firebase中,您要将侦听器附加到数据库中的端点。 该侦听器正在观察该端点,并在端点内的数据更改时随时报告。

Google在其文档中的声明方式如下:

Firebase数据将写入FIRDatabase引用,并通过将异步侦听器附加到该引用来进行检索。 数据的初始状态会触发一次侦听器,并且在数据更改时会再次触发。

我要说的是要提出这个主题,因为我有一些很棒的新技巧可以显示。 但是,为什么我真正谈论Firebase的原因是因为我在代码中做了一些超级头脑。 这就是很多编码的方式,对吗? 您尝试了一些,但没有用,后来当您找出原因时,您会说“哦,对,那很愚蠢”。

因此,希望您能从我的错误中获得一些见识。 我的FirebaseManager类具有一个功能:

  static func fetchAllTeams(completion:@escaping([Team])-> Void){//获取所有团队,并在完成后以数组形式返回它们 
var团队= [Team]()
dataRef.child(“ teams”)。observe(.value,其中:{(快照)在
让teamDict = snapshot.value为? [字符串:任何]
如果让teamDict = teamDict {
对于teamDict {
让teamValues = team.value为? [String:Any] ?? [:]
让team = Team(id:team.key,dict:teamValues)
team.append(团队)
}
}
完成(团队)
})
  } 

很酷 然后,在我的“ TeamsViewController”中,我有另一个函数(称为“获取团队数据”),该函数调用该函数并将其用于填充保存UITableView数据的数组。

  private func getAllTeams(用户:用户,完成:@escaping()->无效){//获取数据库中存在的所有团队,按字母顺序对它们进行排序,然后将它们设置为与TeamsVC可用的allTeams数组相等 
FirebaseManager.fetchAllTeams {(团队)在
self.myTeams.removeAll()
self.publicTeams.removeAll()
self.filteredTeams.removeAll()
对于团队中的团队{
如果让teamID = team.id {
如果user.teamIDs.contains(teamID){
打印(“添加我的团队”)
self.myTeams.append(团队)
}其他{
打印(“ APPEND Public TEAMS”)
self.publicTeams.append(团队)
}
}
}
self.myTeams = self.myTeams.sorted {$ 0.name.lowercased()
$ 1.name.lowercased()}
self.publicTeams = self.publicTeams.sorted {$ 0.name.lowercased()<$ 1.name.lowercased()}
self.filteredTeams = self.publicTeams
完成()
}
  } 

很棒的是,因为这是在Firebase中设置为“观察者”,所以如果我在另一个视图中将团队添加到数据库中,则不必再次调用此函数。 我的Firebase观察者将看到数据已更改,然后调用在.observe函数中定义的闭包。 请记住这一点,因为这是我接下来要讲的地方:设置观察者时,如果观察者看到所观察数据的任何变化,它将调用闭包—这是您定义的函数。

所以我有另一个UIViewController,我称之为TeamDetailVC。 这显示了团队中的挑战和团队成员。 问题是,当我向团队添加新挑战时,我的ChallengesTableView会复制新挑战。 这是我的FirebaseManager函数:

 静态函数fetchChallenge(withChallengeIDhallengeID:字符串,完成:@escaping(Challenge)-> Void){ 
dataRef.child(“ challenges”)。child(challengeID).observe(.value,其中:{{snapshot)in
如果让challengeDict = snapshot.value为? [String:任何] {
让Challenge = Challenge(id:ChallengeID,dict:
ChallengeDict)
完成(挑战)
}其他{
打印(“未完成”)
}
})
}

这就是我在TableView中调用该函数的方式:

  func getTeamChallenges(对于团队团队:团队?,完成时间:@escaping()->无效){ 
如果让ChallengeList = team?.challengeIDs {
self.teamChallenges.removeAll()
for ChallengeList.enumerated()中的(index,ChallengeID){
FirebaseManager.fetchChallenge(withChallengeID:ChallengeID,complete:{(challenge)in
self.teamChallenges.append(挑战)
完成()
})
}
}
}

您在这里看到问题了吗? 当我向数据库添加挑战时,我在FirebaseManager中的“观察者”看到了更改,因此它将触发我在.observe闭包中为其创建的功能。 然后,我的闭包获取数据,创建Challenge类的实例,然后通过转义的闭包将其发送出去。 当在我的getTeamChallenges方法中调用此转义关闭时,唯一发生的是我的teamChallenges数组被附加了新的Challenge。 teamChallenges.removeAll()从未被调用。 我建立的循环都不会被调用。 将tableView数据重新加载到完成中的其他位置,并使用以前填充的数组进行加载,仅这次是在其末尾添加了额外的“新挑战”。

那么如何解决呢? 好吧,Firebase还有另一个名为.observeSingleEvent的功能。 这更类似于URLRequest的工作方式,在这里我们只获取单个数据实例。 然后,我只需要确保每次出现TeamDetailVC时都调用我的“ getTeamChallenges”函数。 另外,我可以更改FirebaseManager函数,使其接受“挑战ID”数组,然后自行返回挑战数组。 我认为我更喜欢第二种选择,尽管肯定会涉及更多。 一旦可以使用,我将发布解决方案! (这里是)