这是我的服务:
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()
}
}
过去几年语法方面发生了一些变化。
总之,问题中的代码正在尝试 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。