Firebase 实时数据库观察方法在启动搜索查询后返回 Null

问题描述 投票:0回答:1

这是我的服务:

final class UserDataService: Sendable {

    static let shared = UserDataService()

    private let databaseRef = Database.database(url: "https://catogram-58487-default-rtdb.europe-west1.firebasedatabase.app").reference()

    func observeUser(with uid: String) {
        let path = "Users/" + uid
        let query = databaseRef.child(path)
        query.observe(.value) { snapshot in
            print("\nObserved path: ", path)
            print("Observed user snapshot: ", snapshot)
        }
    }

    func searchUserWith(username: String) async throws {
        let usersRef = self.databaseRef.child("Users")
        let startString = username
        let endString = username + "\\uf8ff"
        let query = usersRef.queryOrdered(byChild: "username")
            .queryStarting(atValue: startString)
            .queryEnding(atValue: endString)
        let data = try await query.getData()
    // Doesn't do anything cause it's unrelated. 
    // Just firing this query will trigger the issue.
    }
}

observeUser
方法中,您可以看到两个打印语句,以下是它们在应用程序启动时的输出:

Observed path:  Users/Oi8HDhspOWYsgs4gE9zMnbkK65j2
Observed user snapshot:  Snap (Oi8HDhspOWYsgs4gE9zMnbkK65j2) {
    birthday = "12/11/2016";
    email = "[email protected]";
    gender = Male;
    joinDate = "1718197814.370859";
    name = "Test User";
    posts = 0;
    userID = Oi8HDhspOWYsgs4gE9zMnbkK65j2;
    username = testusername02;
}

observeUser
方法不断观察指定路径上的用户数据,并在每次检测到任何变化时返回数据。不知何故,来自
searchUserWith
的查询使
observeUser
返回 Null,就好像它正在观察的路径上的数据被删除一样。 以下是触发搜索查询时
observeUser
打印出来的内容:

Observed path:  Users/Oi8HDhspOWYsgs4gE9zMnbkK65j2
Observed user snapshot:  Snap (Oi8HDhspOWYsgs4gE9zMnbkK65j2) <null>

无论我做什么,Firebase 的

.observe
.getData()
方法都将为此路径返回 Null,直到我重新启动应用程序。 我制作了一个单独的“观察”UIButton,以便我可以在搜索查询后重复触发观察方法并查看其输出,执行搜索后它总是返回 Null。

有趣的是,如果搜索结果包括当前登录的用户(我正在观察的用户),应用程序将继续正常运行。

这是我的用户节点的安全规则:

"rules": {
    "Users": {
      ".indexOn": "username",
      ".read": true,
      ".write": true,
    },
}

这是我用来创建最小可重复示例的其他代码:

class ViewController: UIViewController {

    private lazy var searchButton: UIButton = {
        let button = UIButton()
        button.translatesAutoresizingMaskIntoConstraints = false
        button.setTitle("Search", for: .normal)
        button.backgroundColor = .systemGreen
        button.tintColor = .white
        button.layer.cornerRadius = 15
        button.layer.cornerCurve = .continuous
        button.addTarget(self, action: #selector(triggerSearchMethod), for: .touchUpInside)
        return button
    }()

    private lazy var observe: UIButton = {
        let button = UIButton()
        button.translatesAutoresizingMaskIntoConstraints = false
        button.setTitle("Observe", for: .normal)
        button.backgroundColor = .systemGreen
        button.tintColor = .white
        button.layer.cornerRadius = 15
        button.layer.cornerCurve = .continuous
        button.addTarget(self, action: #selector(observeUser), for: .touchUpInside)
        return button
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .systemBackground
        setupCurrentUserListener()
        setupSearchButton()
        setupObserveButton()
    }

    private func setupSearchButton() {
        view.addSubview(searchButton)
        NSLayoutConstraint.activate([
            searchButton.heightAnchor.constraint(equalToConstant: 40),
            searchButton.widthAnchor.constraint(equalToConstant: 70),
            searchButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            searchButton.centerYAnchor.constraint(equalTo: view.centerYAnchor)
        ])
    }

    private func setupObserveButton() {
        view.addSubview(observe)
        NSLayoutConstraint.activate([
            observe.topAnchor.constraint(equalTo: searchButton.bottomAnchor, constant: 20),
            observe.heightAnchor.constraint(equalToConstant: 40),
            observe.widthAnchor.constraint(equalToConstant: 70),
            observe.centerXAnchor.constraint(equalTo: view.centerXAnchor),
        ])
    }

   private func setupCurrentUserListener() {
        let currentUserID = AuthenticationService.shared.getCurrentUserUID()
        UserDataService.shared.observeUser(with: currentUserID)
    }

    @objc func triggerSearchMethod() {
        print("search tapped")
        Task {
            try await UserDataService.shared.searchUserWith(username: "Username")
        }
    }

    @objc func observeUser() {
        setupCurrentUserListener()
    }
}

swift firebase firebase-realtime-database
1个回答
0
投票

过去几年语法方面发生了一些变化。

总之,问题中的代码正在尝试 Firebase 不直接支持的部分字符串查询。解决方案是在两个字符串(包含这两个字符串)之间生成一个查询,从输入字符串开始,并以由 unicode 表中非常靠前的字符终止的相同字符串结束。

例如,如果用户查询以

Ap
开头的所有字符串,则查询将为
queryStarting(Ap)
...
queryEnding(Ap{f8ff})
,其中 f8ff 是 unicode 表中较高的 unicode 缩放器。这会导致

Apatite
Apex
Apple
Application
etc

但这不起作用

let startString = username
let endString = username + "\\uf8ff"

unicode 缩放字符需要格式化为“\u{F8FF}”

let startString = username
let endString = username + "\u{F8FF}"

请注意,这个“问题”纯粹与语言语法相关。在斯威夫特:

let x = "\\uf8ff"
let y = "\u{F8FF}"
print(x)
print(y)

结果

\uf8ff


而“\uf8ff”是 json 的正确语法,我相信也适用于 Android。

© www.soinside.com 2019 - 2024. All rights reserved.