When Firebase query removes Observer with handle, other observers are not called

281 Views Asked by At

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)
        }
    }
}
1

There are 1 best solutions below

0
Alexander Khitev On BEST ANSWER

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)
            }
        }
    }

}