Firebase实时数据库:用用户邮箱/用户名替代AutoId存数据的问题
解决Firebase Realtime Database用邮箱作为节点标识的崩溃问题
嘿,我一眼就揪出你代码崩溃的原因了!从崩溃日志里的InvalidPathValidation异常就能看出来——Firebase Realtime Database的节点路径不允许包含.、#、$、[、]这些特殊字符,而邮箱地址里的.是标配,直接把邮箱当child路径肯定会触发验证失败。
下面给你几个靠谱的解决方案,按需选就行:
方案1:转义邮箱中的特殊字符
把邮箱里的禁用字符替换成Firebase允许的字符,存数据时转义,取数据时再转回来就行。比如把.换成_,其他禁用字符也做对应替换:
// 写一个邮箱转义函数 func sanitizeEmailForFirebase(_ email: String) -> String { var sanitized = email sanitized = sanitized.replacingOccurrences(of: ".", with: "_") sanitized = sanitized.replacingOccurrences(of: "#", with: "__") sanitized = sanitized.replacingOccurrences(of: "$", with: "___") sanitized = sanitized.replacingOccurrences(of: "[", with: "____") sanitized = sanitized.replacingOccurrences(of: "]", with: "_____") return sanitized } // 修改你的addedJob函数 func addedJob (){ // 先校验输入不为空,避免空值崩溃 guard let email = txtEmail.text, !email.isEmpty, let shipper = txtShip.text, !shipper.isEmpty, let consignee = txtCon.text, !consignee.isEmpty, let collectionDate = txtCol.text, !collectionDate.isEmpty, let deliveryDate = txtDel.text, !deliveryDate.isEmpty, let freight = txtFreight.text, !freight.isEmpty, let reference = txtRef.text, !reference.isEmpty, let pod = txtPod.text, !pod.isEmpty else { // 这里可以加个提示,比如弹窗告诉用户输入不能为空 return } let sanitizedEmail = sanitizeEmailForFirebase(email) // 不需要再存AutoId的key了,因为我们用转义后的邮箱当节点标识 let job = ["shipper": shipper, "consignee": consignee, "email": email, // 存原始邮箱方便后续展示 "collection date": collectionDate, "delivery date": deliveryDate, "freight": freight, "reference": reference, "pod": pod] ref.child(sanitizedEmail).setValue(job) }
后续要根据邮箱查询数据时,记得先把输入的邮箱用同一个sanitizeEmailForFirebase函数处理,再去数据库里找对应的节点。
方案2:使用邮箱的哈希值
如果不想处理转义/反转义的逻辑,还可以直接对邮箱做哈希处理(比如SHA256),哈希后的字符串没有特殊字符,而且每个邮箱对应唯一的哈希值,完全符合Firebase的路径规则:
import CryptoKit // 哈希邮箱的函数 func hashEmailForFirebase(_ email: String) -> String { let emailData = Data(email.utf8) let sha256Hash = SHA256.hash(data: emailData) // 把哈希值转成十六进制字符串 return sha256Hash.compactMap { String(format: "%02x", $0) }.joined() } // 修改addedJob函数 func addedJob (){ guard let email = txtEmail.text, !email.isEmpty else { return } let hashedEmail = hashEmailForFirebase(email) // 构造job数据(同样建议先校验所有输入非空) let job = ["shipper": txtShip.text!, "consignee": txtCon.text!, "email": email, "collection date": txtCol.text!, "delivery date": txtDel.text!, "freight": txtFreight.text!, "reference": txtRef.text!, "pod": txtPod.text!] ref.child(hashedEmail).setValue(job) }
这个方案的好处是不用维护转义规则,缺点是哈希后的字符串不可读,但不影响功能使用。
额外提醒
不管用哪种方案,都要先校验用户输入的邮箱不为空,不然空字符串作为路径也会触发同样的崩溃。另外,建议对所有输入框的内容做非空校验,避免因用户未输入导致的nil崩溃。
内容的提问来源于stack exchange,提问作者mollywm
相关产品推荐
相关产品推荐

