全局缓存服务器数据并刷新视图

 收藏

我正在构建一个使用Firebase进行身份验证和数据库功能的应用程序。用户注册后,将为该用户存储数据库记录,其中包含一些基本信息,例如名字,姓氏等。

用户使用其凭据登录后,我想设置一个全局变量(也许是userDefaults?),其中包含该特定用户的用户数据。否则,每次我想用用户的名字填写标签时,我都必须获取用户数据。

我设法在登录时设置了userdefaults并在UIlables中使用了此信息。但是,当我让用户对其数据进行更改(其中一些更改对于应用程序的功能很重要)时,我可以更新服务器,并且用户使用默认值,但应用程序本身无法使用正确的数据进行更新。它将旧数据保留在(例如)UIlables中。

我希望对管理此类情况的最佳工作流程有更多的了解。

When opening the app, i have a tabBarController set as rootviewcontroller. In the load of tabbarcontroller I have the following code retrieving the user data from firebase and saving it to userdefaults:

guard let uid = Auth.auth().currentUser?.uid else { return }
            Database.database().reference().child("users").child(uid).observeSingleEvent(of: .value, with: { (snapshot) in
                print(snapshot.value ?? "")


              guard let dictionary = snapshot.value as? [String: Any] else { return }
                let firstname = dictionary["First name"] as? String
                let lastname = dictionary["Last name"] as? String

                print("first name is: " + firstname!)

                UserDefaults.standard.set(firstname, forKey: "userFirstName")

                print(UserDefaults.standard.value(forKey: "userFirstName"))


                self.setupViewControllers()


            }

然后,我继续在tabBarController中加载所有视图控制器:

self.setupViewControllers()

在此过程中,这些视图控制器中的标签将用userdefaults数据填充。

这是一个用userDefaults填充标签,但在更改userdefaults时不更新标签的示例:

    let welcomeLabel: UILabel = {
        let label = UILabel()  
        let attributedText = NSMutableAttributedString(string: "Welcome ")
        attributedText.append(NSAttributedString(string: "\(UserDefaults.standard.string(forKey: "userFirstName")!)"))
        label.attributedText = attributedText
        label.font = UIFont.systemFont(ofSize: 30, weight: .bold)
        return label
    }()

这是我用来更新名字的功能(通过用户填写的文本字段):

    @objc func updateName() {
        guard let uid = Auth.auth().currentUser?.uid else { return }
        Database.database().reference().child("users").child(uid).updateChildValues(["First name" : updateNameField.text ?? ""])

        UserDefaults.standard.set(updateNameField.text, forKey: "userFirstName")

        print(UserDefaults.standard.value(forKey: "userFirstName"))
    }
回复
  • 红颜暗 回复

    So you'll have to organize things first. In a new file define constants such as below. These constant will be accessible in global scope unless private

    Constants.swift

    private let storedusername = "usname"
        private let storedName = "uname"
        private let displaypic = "udp"
        private let aboutme = "udesc"
    
        var myusername : String {
            get {
                return (UserDefaults.standard.string(forKey: storedusername)!)
            } set {
                UserDefaults.standard.set(newValue, forKey: storedusername)
            }
        }
    
        var myname : String {
            get {
                return (UserDefaults.standard.string(forKey: storedName)!)
            } set {
                UserDefaults.standard.set(newValue, forKey: storedName)
            }
        }
    
        var myProfileImage : Data {
            get {
                return (UserDefaults.standard.data(forKey: displaypic)!)
            } set {
                UserDefaults.standard.set(newValue, forKey: displaypic)
            }
        }
    
        var myAboutMe : String? {
            get {
                return (UserDefaults.standard.string(forKey: aboutme)!)
            } set {
                UserDefaults.standard.set(newValue, forKey: aboutme)
            }
        }
    

    Now the next time you want to save anything in UserDefaults, you'll just do the following anywhere throughout your code base :

    myusername = "@CVEIjk"
    

    要检索它,只需调用它:

    print(myusername)
    

    重要的提示 -

    1. 永远记得初始化它们。您可以在用户注册时执行此操作。一旦他们填写了详细信息并点击Submit,就将其保存到这些变量中。那不会造成不必要的崩溃。
    2. 您必须将它们保存在执行有关数据库中这些节点的更新的每个位置。

    Now, the refreshing views part. I am taking a scenario where your ProfileView.swift has the view and user goes to EditProfile.swift for updating the content.

    You initialize all your observers the place where the update will have the immediate effect. Because the view immediately after the update matters. The rest will be called through the getter of the aboutme

    ProfileView.swift

    func openEditView() {
    
            NotificationCenter.default.addObserver(self, selector: #selector(fetchUserDetails), name: Notification.Name("update"), object: nil)
    
            //setting this right before the segue will create an observer specifically and exclusively for updates. Hence you don't have to worry about the extra observers.
    
            perform(segue: With Identifier:)// Goes to the editProfile page
        }
    

    此函数将首先在viewDidLoad()中调用。此时,您需要确保拥有所有数据,否则将不会产生任何值。但是,如果您在用户注册时存储所有内容,那将是安全的。

    @objc func fetchUserDetails() {
            if uid != nil {
                if myname.count > 0 { // This will check if the variable has anything in the memory or not. Dont confuse this with [Array].count
                    self.nameLabel = myname
                }
            }
        }
    

    此函数还充当ab观察者方法。因此,发布通知后,它们可以再次运行。

    现在,EditProfile.swift

    In the block where you are updating the server, save the values and then create a Notification.post and put this method right before you dismiss(toViewController:)

       func updateUserCacheData(name: String, username: String, aboutme: String, ProfilePhoto: UIImage? = nil) {
            DispatchQueue.global().async {
                myname = name
                myusername = username
                myAboutMe = aboutme
                if self.newImage != nil {
                    myProfileImage = self.newImage!.jpegData(compressionQuality: 1)!
                }
                DispatchQueue.main.async {
                    NotificationCenter.default.post(name: .refreshProfileViews, object: nil)
                }
            }
        }
    
        func updateToServerAndBackToProfileView() {
            self.updateUserCacheData(name: iname!, username: iusername, aboutme: iaboutme!)
            self.dismiss(animated: true, completion: nil)
            }
        }
    

    As long as this goes back to ProfileView, your views will be instantly refreshed. You can keep an observer wherever you view will be first displayed after the dismiss. the rest will fetch updated content always. Also, don't forget to deinit your Observer in ProfileView

    //This code is in ProfileView.swift
    deinit {
            NotificationCenter.default.removeObserver(self, name: Notification.Name("update"), object: nil)
        }
    

    另外,如果内容可能为空,则只需使用空内容对其进行初始化。例如,如果用户在注册时未选择添加Aboutme,则只需输入

    `myaboutme = ""`
    

    这将为您创建一个安全的环境,并且您已经做好了准备。