注册后领域不包含用户

Realm doesn't contain user after register

我正在使用 mongodb+realm 开发 swift 应用程序。在我的注册函数中,有时返回领域时它不包含任何用户(但创建的用户显示在集群中)。所以我在 hasSignedConsent 中得到了一个零异常。我错过了什么吗?

func signup() {
    app.emailPasswordAuth.registerUser(email: email!, password: password!, completion: { [weak self](error) in
            DispatchQueue.main.async {
                self!.setLoading(false)
                guard error == nil else {
                    print("Signup failed: \(error!)")
                    self!.errorLabel.text = "Signup failed: \(error!.localizedDescription)"
                    return
                }
                print("Signup successful!")
                self!.errorLabel.text = "Signup successful! Signing in..."
                self!.login()
            }
        })
    }
func login(){
    setLoading(true)
    app.login(credentials: Credentials.emailPassword(email: email!, password: password!)) { result in
        DispatchQueue.main.async {
            self.setLoading(false)
            switch result {
            case .failure(let error):
                print("Login failed: \(error)")
                self.errorLabel.text = "Login failed: \(error.localizedDescription)"
                return
            case .success(let user):
                print("Login succeeded!")
                self.setLoading(true)
                var configuration = user.configuration(partitionValue: "user=\(user.id)")
                configuration.objectTypes = RealmManager.OBJECT_TYPES
                Realm.asyncOpen(configuration: configuration) { [weak self](result) in
                    self!.setLoading(false)
                    switch result {
                    case .failure(let error):
                        fatalError("Failed to open realm: \(error)")
                    case .success(let userRealm):
                        RealmManager.shared.setRealm(realm: userRealm, handler:{
                            if RealmManager.shared.hasSignedConsent(realmA: userRealm){
                                self?.scheduleReminders()
                                self?.goToViewController(storyboardID: "Main", viewcontrollerID: "home")
                            }
                            else{
                                self?.goToViewController(storyboardID: "Onboarding", viewcontrollerID: "consentVC")
                            }
                        })    
                    }
                }
            }
        }
    }
func hasSignedConsent(realmA: Realm) -> Bool {
        let user = realmA.objects(User.self).first
        return user!.hasSignedConsent
    }
func setLoading(_ loading: Bool) {
        activityIndicator.isHidden = !loading
        if loading {
            activityIndicator.startAnimating()
            errorLabel.text = ""
        } else {
            activityIndicator.stopAnimating()
        }
        emailTextField.isEnabled = !loading
        passwordTextField.isEnabled = !loading
        signupLoginButton.isEnabled = !loading
    }

这是我添加新用户时触发的触发器:

这是 createNewUserDocument 函数:

exports = async function createNewUserDocument({user}){
  const cluster = context.services.get("mongodb-atlas");
  const users = cluster.db("mseasedb").collection("User");
  return users.insertOne({
    _id: user.id,
    _partition: `user=${user.id}`,
    name: user.data.email,
    profilePicture: null,
    hasSignedConsent: false,
    gender: '',
    birthday: '',
    typeOfMS: '',
    diagnosisDate: '',
    treatmentBeginningDate: '',
    mascot: 'Drummer',
    canReadPartitions: [`user=${user.id}`],
    canWritePartitions: [`user=${user.id}`],
  });
};

这完全是瞎猜,但我会猜测并说至少部分问题是由在异步调用中嵌入异步调用引起的。

总结一下,现有代码就是这样做的

func signup() {
    app.emailPasswordAuth.registerUser { 
            DispatchQueue.main.async {            1
               app.login {
                    DispatchQueue.main.async {    2
                         Realm.asyncOpen {        3

你唯一需要 DispatchQueue.main.async 的时候是你想要更新 UI - 否则不需要它,因为网络都在后台线程上发生。

这个模式可能会解决这个问题(这是一个总结,不是 copy/paste)

app.emailPasswordAuth.registerUser {
    app.login {
       switch result {
       case .failure(let error):
           print("Login failed: \(error.localizedDescription)")
       case .success(let user):
           print("Successfully logged in as user \(user)")
           // Remember to dispatch to main if you are doing anything on the 
           //  UI thread. Like this to add the id to a textField
           DispatchQueue.main.async {
               self.someTextField.stringValue = user.id
           }
           Realm.asyncOpen(configuration: configuration) { (result) in
              switch result {
              case .failure(let error):
                 print("Failed to open realm: \(error.localizedDescription)")
                 // Handle error...
              case .success(let realm):
                // Realm opened - do realm stuff
           

如果您注意到,只有一个 DispatchQueue 用于更新 UI - 这些调用中没有嵌入其他网络代码

我终于找到解决办法了! (感谢 Mongodb community forums 的安德鲁)

基本上,问题是因为触发器是异步的。 我的案例的解决方案之一是将 user 对象拆分为 2 个不同的对象(userprofile):

import RealmSwift

class Profile: Object {
    @objc dynamic var _id: ObjectId = ObjectId.generate()
    @objc dynamic var _partition : String = ""

    @objc dynamic var name: String = ""
    @objc dynamic var profilePicture: Data? = nil
    @objc dynamic var hasSignedConsent: Bool = false

    @objc dynamic var gender: String = ""
    @objc dynamic var birthday: String = ""
    @objc dynamic var typeOfMS: String = ""
    @objc dynamic var diagnosisDate: String = ""
    @objc dynamic var treatmentBeginningDate: String = ""

    @objc dynamic var mascot: String = "Drummer"

    override static func primaryKey() -> String? {
        return "_id"
    }

    convenience init(email: String){
         self.init()
         self._partition = RealmManager.shared.getPartitionValue()
         self.name = email
     }

    func getPretestData()->(gender: String, birthday: String, typeOfMS: String, diagnosisDate: String, treatmentBeginningDate: String){
         return (gender: gender, birthday: birthday, typeOfMS: typeOfMS, diagnosisDate: diagnosisDate, treatmentBeginningDate: treatmentBeginningDate)
     }
 }

‌‌

import RealmSwift

class User: Object {
     @objc dynamic var _id: String = ""
     @objc dynamic var _partition: String = ""

     override static func primaryKey() -> String? {
        return "_id"
     }
}

注册后我就在 Realm 中创建了个人资料。