变量未正确保存

Variable is not saved correctly

在函数中,我将json字符串中的数据解析为字典,然后将其保存到变量中。但是当我尝试从另一个函数调用变量时,数据没有保存。

密码是:

    var usersData: [String: NSArray] = [:]

    @IBAction func buttonTapped(_ sender: UIButton) {
                
        if let url = URL(string: "https://medaljson.aadev151.repl.co/get-data") {
            URLSession.shared.dataTask(with: url) { data, response, error in
              if let data = data {
                 if let jsonString = String(data: data, encoding: .utf8) {
                    let data: Data? = jsonString.data(using: .utf8)
                    let json = (try? JSONSerialization.jsonObject(with: data!, options: [])) as? [String:NSArray]
                    
                    self.usersData = json!

                 }
               }
           }.resume()
        }
        
        if shouldPerformSegue(withIdentifier: "loginSegue", sender: nil) {
            performSegue(withIdentifier: "loginSegue", sender: nil)
        }
        
    }
    
    override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
        
        if identifier == "loginSegue" {
            
            print(self.usersData)    // [:]
            
            let name = nameTF.text ?? ""
            let surname = surnameTF.text ?? ""
            let ssa = ssaTF.text ?? ""
            
            if usersData[ssa] == nil {
                errorLabel.text = "SSA not found"
                return false
                
            } else if usersData[ssa]![0] as! String != name || usersData[ssa]![0] as! String != surname {
                errorLabel.text = "Name / Surname doesn't match"
                return false
                
            } else {
                return true
                
            }
            
        }
        return true
        
    }

我想知道如何解决问题。 感谢您的帮助:)

URLSession.shared.dataTask(with 调用完成 block/closure 异步 因为你更新闭包内的 self.usersData = json!self.userData 也会异步更新,当您调用 if shouldPerformSegue(withIdentifier: "loginSegue", sender: nil) { 作为 URLSession.shared.dataTask(with 的紧接下一条语句时( 同步 ),因此运行时甚至在关闭之前执行 if 语句和 performSegue(withIdentifier:被执行因此当 shouldPerformSegue(withIdentifier 被执行时你的数据没有更新

        if let url = URL(string: "https://medaljson.aadev151.repl.co/get-data") {
            URLSession.shared.dataTask(with: url) { data, response, error in
                if let data = data {
                    if let jsonString = String(data: data, encoding: .utf8) {
                        let data: Data? = jsonString.data(using: .utf8)
                        let json = (try? JSONSerialization.jsonObject(with: data!, options: [])) as? [String:NSArray]

                        self.usersData = json! //as mentioned by jnpdx in comment above, this statement is error prone. Because its out of scope of the question I havent updated it, if you need to know how to assign the value safely, lemme know
                        DispatchQueue.main.async {
                            if self.shouldPerformSegue(withIdentifier: "loginSegue", sender: nil) {
                                self.performSegue(withIdentifier: "loginSegue", sender: nil)
                            }
                        }
                    }
                }
            }.resume()
        }

编辑: 由于评论中不相关但重要的观点越来越多,我相信我在回答中也解决所有这些问题是唯一正确的,尽管它们并不严格与问题本身有关。上面的答案应该为 OP 问题提供正确的解释,尽管如此,让我更新一下以解决其他问题。

        if let url = URL(string: "https://medaljson.aadev151.repl.co/get-data") {
            URLSession.shared.dataTask(with: url) { data, response, error in
                if let data = data, let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
                    self.usersData = json

                    DispatchQueue.main.async {
                        if self.shouldPerformLoginSegue() {
                            self.performSegue(withIdentifier: "loginSegue", sender: nil)
                        }
                    }
                }
            }.resume()
        }
func shouldPerformLoginSegue() -> Bool {
        let name = nameTF.text ?? ""
        let surname = surnameTF.text ?? ""
        let ssa = ssaTF.text ?? ""

        if usersData[ssa] == nil {
            errorLabel.text = "SSA not found"
            return false

        } else if usersData[ssa]![0] as! String != name || usersData[ssa]![0] as! String != surname {
            errorLabel.text = "Name / Surname doesn't match"
            return false

        } else {
            return true

        }
    }

几件事,

  1. 您已经在使用 try? 因此您不需要包装 do catch 中的陈述,但我真的希望你知道 它的后果,如果 JSONSerialisation 由于任何原因结束 抛出错误,try? 将 return 值 nil,并且 因为您将 let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] 代码放入 if let 条件将永远不会被执行。

    这是一种处理方式,另一种是捕获错误并处理 适当地(根据你的用例)如果你决定去 该方法使用 try 代替。 try!还有另一种变体 您可以阅读并决定哪一个对您来说更有意义。

  2. 正如 Vadian 提到的,实际上没有必要使用任何 NS 集合类型,而是可以使用它们的 swift 对应类型,在 在这种情况下 Array,数组采用通用参数 Element 并且 因为在这种情况下它不能隐式推断 Element 的类型, 它要求您明确指定,因为我不完全确定 json 结构 我建议你可以使用 Array<Any> 但你的 shouldPerformSegue(withIdentifier 中的代码让我失望,我 不要认为你的代码可以处理数组,看起来你是 期待一本字典,因为我也无法推断类型 采摘[String: Any]随意适当设置

  3. 再次如 Vadian 指定的那样,您永远不应该手动调用 shouldPerformSegue(withIdentifier:,您可以覆盖它 显然可以 return 一个布尔值,指示是否 是否应该执行特定的 segue,但你不应该调用 手动。

    shouldPerformSegue(withIdentifier: 仅在以下情况下被调用 故事板 segues,当你打电话时不会被调用 performSegue(withIdentifier:manually/explicitly。阅读 link 了解更多信息。

    我知道你只想在某些情况下才执行 segue 满足条件,但显然当你打电话 performSegue(withIdentifier: iOS 假定您已经 执行了各种检查并且您想明确执行 segue 因此它不会调用 shouldPerformSegue(withIdentifier: 到 再检查一下。

    因此您需要在显式调用之前处理这些检查 performSegue(withIdentifier: 本身因此我添加了一个新的 实例方法 shouldPerformLoginSegue() -> Bool 告诉你 您是否应该执行特定的 loginSegue

  4. 在你的代码中 if let jsonString = String(data: data, encoding: .utf8) {let data: Data? = jsonString.data(using: .utf8) 是 完全没有必要,将数据转换为 JSON 毫无意义 字符串并再次将其重新转换为 Data 并将此数据传递给 JSONSerialization 您已经有来自数据任务的数据 响应,只需将其传递给 JSONSerialization 即可。

  5. 我目前实施的 shouldPerformLoginSegue 有很多副作用,如果您打算遵循 pure functional programmingconcern separation 但这里的想法只是为了演示如何将代码 shouldPerformSegue 移动到您自己的另一个实例函数,并在调用 performSegue

    之前将其用作检查