首页 > 解决方案 > 我想在视图控制器之间快速传递一个用户对象类型的实例

问题描述

我有一个用户对象,我从 firebase 获取它的值,我想基本上在我所有的其他视图控制器中传递这个对象。我正在使用情节提要,我查找了如何执行此操作,我发现我可以覆盖 prepare 方法,但我没有成功,因为我不知道如何调用该方法,或者如果它曾经被调用过,它只是没用。然后我发现你可以将一个 vc 分配给另一个视图控制器并像这样传递数据,但我遇到了一个问题:

在 HomeViewController 中,我有这个从 firebase 获取数据并将其分配给用户的方法:

extension HomeViewController {
public func AssignValueToUserObject() {
    guard let uid = Auth.auth().currentUser?.uid else {
        print("Could not get user id")
        return
    }
    
    Database.database().reference().child("users").child(uid).observeSingleEvent(of: .value, with: { [self] snapshot in
        if let dictionary = snapshot.value as? [String: AnyObject] {
            
            user.first_name = dictionary["first_name"] as? String
            user.last_name = dictionary["last_name"] as? String
            user.email = dictionary["email"] as? String
            user.profile_picture = dictionary["profile_picture"] as? String
        }
    }, withCancel: nil)
} // End AssignValueToUserObject Method
} // End extension

这就是我在 HomeViewController 中将该用户对象复制到我的 ProfileViewController 中的内容:

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    title = "Home"
    checkIfUserIsLoggedIn()
    copyData()
}

func checkIfUserIsLoggedIn() {
    // Check if user is logged in
    if Auth.auth().currentUser == nil {
        // User is not logged in, send user to login screen
        let loginVC = storyboard?.instantiateViewController(identifier: "login")
        loginVC?.modalPresentationStyle = .fullScreen
        present(loginVC!, animated: false)
    }
    
    // User is logged in, fetch their info
    AssignValueToUserObject()

    } // End checkIfUserIsLoggedIn method

// Copy user from Home to Profile
func copyData() {
    let vc = storyboard?.instantiateViewController(identifier: "profile") as? ProfileViewController
    vc?.user = user
}

调试后,我发现了一个大问题,即在 AssignValueToUserObject 方法中将值分配给用户之前调用了复制方法,这对我来说绝对没有意义。

我在复制方法之前调用了assign方法,那它是如何工作的?经过一番研究,我发现它与完成处理有关,但我就是不明白。

标签: iosswiftxcodefirebase

解决方案


如评论中所述,使用异步函数,您不能立即期望返回值。处理此问题的一种常见方法是使用回调函数或完成处理程序。

我包括一个非常基本的例子。请注意,我现在没有进行任何错误处理——您希望将其构建得更健壮,但这至少得到了这个概念:

extension HomeViewController {
    public func assignValueToUserObject(completion: @escaping () -> Void) { //completion handler gets passed as an parameter
        guard let uid = Auth.auth().currentUser?.uid else {
            print("Could not get user id")
            return
        }
        
        Database.database().reference().child("users").child(uid).observeSingleEvent(of: .value, with: { [self] snapshot in
            if let dictionary = snapshot.value as? [String: AnyObject] {
                
                user.first_name = dictionary["first_name"] as? String
                user.last_name = dictionary["last_name"] as? String
                user.email = dictionary["email"] as? String
                user.profile_picture = dictionary["profile_picture"] as? String
                completion() //call once the action is done
            }
        }, withCancel: nil)
    } // End AssignValueToUserObject Method
} // End extension
override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    title = "Home"
    checkIfUserIsLoggedIn()
    //don't call copy here anymore
}
func checkIfUserIsLoggedIn() {
    // Check if user is logged in
    if Auth.auth().currentUser == nil {
        // User is not logged in, send user to login screen
        let loginVC = storyboard?.instantiateViewController(identifier: "login")
        loginVC?.modalPresentationStyle = .fullScreen
        present(loginVC!, animated: false)
    }
    
    // User is logged in, fetch their info
    assignValueToUserObject(completion: {
      self.copyData() //only copy once the completion handler is run
    })
    
} // End checkIfUserIsLoggedIn method

更新,展示了一种使用单例来监控不同视图控制器中的用户值的方法:

import Combine

struct User {
    var id : UUID //whatever properties your user model has
}

class UserManager {
    @Published var user : User?
    static public var shared = UserManager()
    private init() {
        
    }
    
    func login() {
        //do your firebase call here and set `user` when done in the completion handler
        self.user = User(id: UUID())
    }
}

class HomeViewController : UIViewController {
    private var userManager = UserManager.shared
    private var cancellable : AnyCancellable?
    
    init() {
        super.init(nibName: nil, bundle: nil)
        setupUserLink() //make sure this gets called in whatever initializer is used
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setupUserLink() //make sure this gets called in whatever initializer is used
    }
    
    func setupUserLink() {
        cancellable = userManager.$user.compactMap { $0 }.sink { user in
            print(user.id.uuidString) //do something with the user value -- assign it to a variable, a control, etc
        }
    }
}

class ProfileViewController : UIViewController {
    //do the same pattern as in HomeViewController, setting up the user link to be monitored
}

推荐阅读