I'm monitoring the state of some data from several UIViewControllers
. When the UIViewController
deinit happens, I delete the specific observer with the handle. Now I noticed that after removing query observer from one UIViewController
, in the future query observer on another UIViewController
is not called, although the data is changing. Please tell me how it can be fixed?
open func observeUserFriendshipEnd(_ observer: FirebaseObserverDelegate, isObserve: Bool, friendID: String, endHandler: ((_ removedFriendModel: FriendModel) -> Void)?, fail: ((_ error: Error) -> Void)?) {
let observerID = observer.observerID
let realmManager = RealmManager()
guard let currentUserID = realmManager.getCurrentUser()?.id else { return }
let query = Database.database().reference().child(FriendsPaths.MainGateways.friends.description).child(currentUserID).child(FriendsPaths.SubGateways.userFriends.description).queryOrdered(byChild: "friendID").queryEqual(toValue: friendID)
if !isObserve {
guard let handle = self.userFriendshipEndObservers[observerID] else { return }
query.removeObserver(withHandle: handle)
self.userFriendshipEndObservers[observerID] = nil
// system
removeObserverModel(observerID, handle: handle)
return
}
DispatchQueue.global(qos: .background).async {
var isContinue = true
self.queue.sync {
if self.userFriendshipEndObservers[observerID] != nil {
isContinue = false
}
}
guard isContinue else { return }
var handle: UInt = 0
handle = query.observe(.childRemoved, with: { (snap) in
if snap.value is NSNull {
return
}
guard let dict = snap.value as? [String : Any] else { return }
guard let removedFriendModel = Mapper<FriendModel>().map(JSON: dict) else { return }
let friendRealmManager = FriendRealmManager()
friendRealmManager.removeFriend(removedFriendModel.friendID)
if removedFriendModel.friendID == friendID {
endHandler?(removedFriendModel)
}
}, withCancel: { (error) in
fail?(error)
})
self.queue.sync {
self.userFriendshipEndObservers[observerID] = handle
self.addObserver(observerID, handle: handle, query: query, ref: nil)
}
}
}
Update: I output to logs, handles, and everything is correctly added and deleted. For example ProfileViewController had 33 handle, ChatViewController had 85 handle, after ChatViewController deinit, removed observer with 85 handle, it turns out there was observer with 33 handle, but it is not called.
Update 1 how to know: - how many observers has firebase reference?
Update 2
class ProfileUserFormVC: FormViewController {
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
addObservers()
}
deinit {
removeObservers()
NotificationCenter.default.removeObserver(self)
}
}
// MARK: - Obsevers
extension ProfileUserFormVC {
private func addObservers() {
guard let userID = self.userID else { return }
MatchObserverManager.shared.observeNewMatchWithUser(self, isObserve: true, userID: userID, completion: { [weak self] (match) in
// do something
}) { (error) in
}
MatchObserverManager.shared.observeUserMatchDeleting(self, isObserve: true, userID: userID, completion: { [weak self] (removedMatch) in
// do something
}, fail: nil)
FriendsObserver.shared.observeUserFriendshipEnd(self, isObserve: true, friendID: userID, endHandler: { [weak self] (removedFriend) in
DispatchQueue.main.async {
// do something with ui
}
}, fail: nil)
}
private func removeObservers() {
guard let userID = user?.id else { return }
MatchObserverManager.shared.observeNewMatchWithUser(self, isObserve: false, userID: userID, completion: nil, fail: nil)
MatchObserverManager.shared.observeUserMatchDeleting(self, isObserve: false, userID: userID, completion: nil, fail: nil)
FriendsObserver.shared.observeUserFriendshipEnd(self, isObserve: false, friendID: userID, endHandler: nil, fail: nil)
}
}
Managers
class FriendsObserver: FirebaseObserver {
static let shared = FriendsObserver()
private override init() {
super.init()
}
// MARK: - Queues
private let queue = DispatchQueue(label: "com.myapp.FriendsObserver.queue")
// MARK: - Data
private var userFriendshipEndObservers = [String : UInt]()
open func observeUserFriendshipEnd(_ observer: FirebaseObserverDelegate, isObserve: Bool, friendID: String, endHandler: ((_ removedFriendModel: FriendModel) -> Void)?, fail: ((_ error: Error) -> Void)?) {
let observerID = observer.observerID
let realmManager = RealmManager()
guard let currentUserID = realmManager.getCurrentUser()?.id else { return }
let query = Database.database().reference().child(FriendsPaths.MainGateways.friends.description).child(currentUserID).child(FriendsPaths.SubGateways.userFriends.description).queryOrdered(byChild: "friendID").queryEqual(toValue: friendID)
if !isObserve {
guard let handle = self.userFriendshipEndObservers[observerID] else { return }
query.removeObserver(withHandle: handle)
self.userFriendshipEndObservers[observerID] = nil
// system
removeObserverModel(observerID, handle: handle)
return
}
DispatchQueue.global(qos: .background).async {
var isContinue = true
self.queue.sync {
if self.userFriendshipEndObservers[observerID] != nil {
isContinue = false
}
}
guard isContinue else { return }
var handle: UInt = 0
handle = query.observe(.childRemoved, with: { (snap) in
if snap.value is NSNull {
return
}
guard let dict = snap.value as? [String : Any] else { return }
guard let removedFriendModel = Mapper<FriendModel>().map(JSON: dict) else { return }
let friendRealmManager = FriendRealmManager()
friendRealmManager.removeFriend(removedFriendModel.friendID)
if removedFriendModel.friendID == friendID {
endHandler?(removedFriendModel)
}
}, withCancel: { (error) in
fail?(error)
})
self.queue.sync {
self.userFriendshipEndObservers[observerID] = handle
self.addObserver(observerID, handle: handle, query: query, ref: nil)
}
}
}
}
Update 3 I did a quick change, although it's not right to do it, but I returned the handle to the handler after it was received, then I assigned a variable to the ViewController, and during deinit I also deleted the observer. I got the same result. That is, if I delete one observer with handle, then the second one does not work. Before that, my code worked in its original form.
open func observeUserFriendshipEnd(_ observer: FirebaseObserverDelegate, isObserve: Bool, friendID: String, handle: UInt?, handleHandler: ((_ handle: UInt) -> Void)?, endHandler: ((_ removedFriendModel: FriendModel) -> Void)?, fail: ((_ error: Error) -> Void)?) {
let observerID = observer.observerID
let realmManager = RealmManager()
guard let currentUserID = realmManager.getCurrentUser()?.id else { return }
let query = Database.database().reference().child(FriendsPaths.MainGateways.friends.description).child(currentUserID).child(FriendsPaths.SubGateways.userFriends.description).queryOrdered(byChild: "friendID").queryEqual(toValue: friendID)
if !isObserve {
guard let _handle = handle else { return }
query.removeObserver(withHandle: _handle)
return
}
DispatchQueue.global(qos: .background).async {
var isContinue = true
self.queue.sync {
if self.userFriendshipEndObservers[observerID] != nil {
isContinue = false
}
}
guard isContinue else { return }
var handle: UInt = 0
handle = query.observe(.childRemoved, with: { (snap) in
if snap.value is NSNull {
return
}
guard let dict = snap.value as? [String : Any] else { return }
guard let removedFriendModel = Mapper<FriendModel>().map(JSON: dict) else { return }
let friendRealmManager = FriendRealmManager()
friendRealmManager.removeFriend(removedFriendModel.friendID)
if removedFriendModel.friendID == friendID {
endHandler?(removedFriendModel)
}
}, withCancel: { (error) in
fail?(error)
})
handleHandler?(handle)
self.queue.sync {
self.userFriendshipEndObservers[observerID] = handle
self.addObserver(observerID, handle: handle, query: query, ref: nil)
}
}
}
It was my mistake in the code, I changed FriendsObserver and now everything works.
class FriendsObserver: FirebaseObserver {
static let shared = FriendsObserver()
private override init() {
super.init()
}
// MARK: - Queues
private let queue = DispatchQueue(label: "com.myapp.FriendsObserver.queue")
// MARK: - Data
private var userFriendshipStartObservers = [String : DatabaseHandle]()
private var userFriendshipEndObservers = [String : DatabaseHandle]()
open func observeSpecificUserFriendshipStart(_ observer: FirebaseObserverDelegate, isObserve: Bool, userID: String, startHandler: ((_ friend: FriendModel) -> Void)?, fail: ((_ error: Error) -> Void)?) {
let observerID = observer.observerID
let realmManager = RealmManager()
let timestamp = Date().currentTimestamp
guard let currentUserID = realmManager.getCurrentUser()?.id else { return }
let query = Database.database().reference().child(FriendsPaths.MainGateways.friends.description).child(currentUserID).child(FriendsPaths.SubGateways.userFriends.description).queryOrdered(byChild: "friendID").queryEqual(toValue: userID)
if !isObserve {
guard let handle = userFriendshipStartObservers[observerID] else { return }
query.removeObserver(withHandle: handle)
userFriendshipStartObservers[observerID] = nil
// system
removeObserverModel(observerID, handle: handle)
return
}
DispatchQueue.global(qos: .background).async {
var isContinue = true
self.queue.sync {
if self.userFriendshipStartObservers[observerID] != nil {
isContinue = false
}
}
guard isContinue else { return }
var handle: DatabaseHandle = 0
handle = query.observe(.childAdded, with: { (snapshot) in
guard snapshot.exists() else { return }
guard let dict = snapshot.value as? [String : Any] else { return }
guard let friendModel = Mapper<FriendModel>().map(JSON: dict) else { return }
guard timestamp < friendModel.friendshipTimeStamp else { return }
if friendModel.friendID == userID {
startHandler?(friendModel)
}
}, withCancel: { (error) in
fail?(error)
})
self.queue.sync {
self.userFriendshipStartObservers[observerID] = handle
self.addObserver(observerID, handle: handle, query: query, ref: nil)
}
}
}
/// Only one observer on one object
open func observeUserFriendshipEnd(_ observer: FirebaseObserverDelegate, isObserve: Bool, friendID: String, endHandler: ((_ removedFriendModel: FriendModel) -> Void)?, fail: ((_ error: Error) -> Void)?) {
let observerID = observer.observerID
let realmManager = RealmManager()
guard let currentUserID = realmManager.getCurrentUser()?.id else { return }
let query = Database.database().reference().child(FriendsPaths.MainGateways.friends.description).child(currentUserID).child(FriendsPaths.SubGateways.userFriends.description).queryOrdered(byChild: "friendID").queryEqual(toValue: friendID)
if !isObserve {
guard let handle = userFriendshipEndObservers[observerID] else { return }
query.removeObserver(withHandle: handle)
userFriendshipEndObservers[observerID] = nil
// system
removeObserverModel(observerID, handle: handle)
return
}
DispatchQueue.global(qos: .background).async {
var isContinue = true
self.queue.sync {
if self.userFriendshipEndObservers[observerID] != nil {
isContinue = false
}
}
guard isContinue else { return }
var handle: DatabaseHandle = 0
handle = query.observe(.childRemoved, with: { (snap) in
guard snap.exists() else { return }
guard let dict = snap.value as? [String : Any] else { return }
guard let removedFriendModel = Mapper<FriendModel>().map(JSON: dict) else { return }
let friendRealmManager = FriendRealmManager()
friendRealmManager.removeFriend(removedFriendModel.friendID)
if removedFriendModel.friendID == friendID {
endHandler?(removedFriendModel)
}
}, withCancel: { (error) in
fail?(error)
})
self.queue.sync {
self.userFriendshipEndObservers[observerID] = handle
self.addObserver(observerID, handle: handle, query: query, ref: nil)
}
}
}
}