【问题标题】:Swift 3.0 iOS10 Passing Async data through a segue sender for FirebaseSwift 3.0 iOS10 通过 Firebase 的 segue 发送方传递异步数据
【发布时间】:2017-04-27 02:44:17
【问题描述】:

我有一个收集用户名和密码的登录页面。提交时,它会发送到数据库以检索我们的服务器访问密钥。我通过使用 session.dataTask 的异步 JSON POST 来做到这一点。当我检索 JSON 对象时,我会从中解析出密钥。我想将它传递到下一页,检索一个 firebase 令牌,然后将这两条数据发送回服务器以进行数据库存储。我创建了一个“为 segue 做准备”函数,它收集变量并将其传递给下一页上的变量。我相信我没有正确设置事件序列,或者数据没有从异步容器中取出。有人可以看看这两个文件,看看我哪里弄错了吗?

这是我在调用 REST Web 服务后想要离开的第一页...

loginVC.swift:

import UIKit

class LoginVC: UIViewController {

    @IBOutlet weak var username: UITextField!
    @IBOutlet weak var password: UITextField!
    @IBOutlet weak var validationBox: UITextView!
    @IBAction func logInAction(_ sender: UIButton) {
        guard let user = username.text, !user.isEmpty else {
            validationBox.text = "Please enter valid credentials"
            return
        }
        guard let pass = password.text, !pass.isEmpty else {
            validationBox.text = "Please enter valid credentials"
            return
        }

        let params = ["sUser": username.text!, "sPass": password.text!]

        let url = URL(string: "restWebServiceURL")!
        let session = URLSession.shared
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        do {
            request.httpBody = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
        } catch let error {
            print(error.localizedDescription)
        }
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
        request.addValue("application/json", forHTTPHeaderField: "Accept")

        let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in

            guard error == nil else { return }
            guard let data = data else { return }

            do {
                if let parsedJSON = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
                    let parsedData = parsedJSON["d"] as! [String:Any]
                    let key = parsedData["key"] as! String
                    DispatchQueue.main.async {
                        print(key)
                        self.performSegue(withIdentifier: "FirebaseVC", sender: key)
                    }

                }
            } catch let error {
                print(error.localizedDescription)
            }
        })
        task.resume()
    }
    func sayHello() {
        print("Hello!")
    }
    func sayGoodbye() {
        print("Goodbye!")
    }



    override func viewDidLoad() {
        super.viewDidLoad()
        validationBox.text = "Ready..."
        func prepare(for segue: UIStoryboardSegue, sender: Any?) {
            if let FirebaseInit = segue.destination as? FirebaseVC {
                if let sKey = sender as? String {
                    print("prepare - " + sKey)
                    FirebaseInit.sessionKey = sKey
                }
            }
        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


}

这里是我想要去接收数据访问密钥的页面...

FirebaseVC.swift:

import UIKit

class FirebaseVC: UIViewController {

    private var _sessionKey = String()

    var sessionKey : String {
        get { return _sessionKey }
        set { _sessionKey = newValue }
    }

    @IBOutlet weak var sessionKeyTestBox: UITextView!

    override func viewDidLoad() {
        super.viewDidLoad()
        print(_sessionKey)

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }



}

请随意提出将数据传递到下一页的更好方法。谢谢...

【问题讨论】:

  • PS:我们不会在提交时说 swift,这并没有错,但它不存在,无论如何我想邀请您考虑使用一些设计模式,例如单例:逻辑非常简单,您可以使用从服务器获取的数据实例化一个类,并在需要时调用该对象,并且由于一个用户可以一次连接(单例将保证该类只有一个实例)
  • 我不喜欢使用单例,这只是我个人的看法....我会将if let FirebaseInit = segue.destination as? FirebaseVC { 更改为if (segue.destination.isKind(of: FirebaseVC.self)) { let vc = segue.destination as! FireBaseVC 但它们几乎相同...你能设置一个prepareForSegue 内部的断点,并确保您的 sKey 正确转换为字符串?
  • 好吧,我使用该模型构建了 Android 应用程序和 Web 应用程序应用程序,所以我一点也不反对。你能提供一些指导吗?
  • @JacobBoyd 当我提交时(按登录)xcode 在 self.performSegue(withIdentifier: "FirebaseVC", sender: key) 处显示断点
  • 我禁用了那个断点,它执行 segue 并转到下一页但没有数据传递...

标签: ios json swift asynchronous uistoryboardsegue


【解决方案1】:

事实证明,我的假设是正确的,即事件链已关闭。按照@achrefGassoumi 建议的模型,我将数据任务移至此处的单例服务:

import Foundation

struct CallWebService {

    static let sharedInstance = CallWebService()

    func logInToCaduceus(u: String, p: String, completion: @escaping (_ sKey: String) -> ()) {
        let params = ["sUser": u, "sPass": p]

        let url = URL(string: "https://telemed.caduceususa.com/ws/telemed.asmx/telemedLogin")!
        let session = URLSession.shared
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        do {
            request.httpBody = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
        } catch let error {
            print(error.localizedDescription)
        }
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
        request.addValue("application/json", forHTTPHeaderField: "Accept")

        let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in

            guard error == nil else { return }
            guard let data = data else { return }

            do {
                if let parsedJSON = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
                    let parsedData = parsedJSON["d"] as! [String:Any]
                    let key = parsedData["key"] as! String
                    DispatchQueue.main.async {
                        completion(key)
                    }
                }
            } catch let error {
                print(error.localizedDescription)
            }
        })
        task.resume()

    }

}

那么我的两个控制器是这样的:

登录VC

import UIKit

class LoginVC: UIViewController {

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if (segue.destination.isKind(of: FirebaseVC.self)) {
            let vc = segue.destination as! FirebaseVC
            if let sKey = sender as? String {
                vc.sessionKey = sKey
            }
        }
    }

    @IBOutlet weak var username: UITextField!
    @IBOutlet weak var password: UITextField!
    @IBOutlet weak var validationBox: UITextView!
    @IBAction func logInAction(_ sender: UIButton) {
        guard let user = username.text, !user.isEmpty else {
            validationBox.text = "Please enter valid credentials"
            return
        }
        guard let pass = password.text, !pass.isEmpty else {
            validationBox.text = "Please enter valid credentials"
            return
        }

        CallWebService.sharedInstance.logInToCaduceus(u: username.text!, p: password.text!, completion: {(sessionKey: String) -> Void in
                print(sessionKey)
                self.performSegue(withIdentifier: "FirebaseVC", sender: sessionKey)
            }
        )

    }

    override func viewDidLoad() {
        super.viewDidLoad()
        //validationBox.textAlignment = .center
        validationBox.text = "Ready..."
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


}

和接收 FirebaseVC

import UIKit

class FirebaseVC: UIViewController {

    private var _sessionKey = String()

    var sessionKey : String {
        get { return _sessionKey }
        set { _sessionKey = newValue }
    }

    @IBOutlet weak var sessionKeyTestBox: UITextView!

    override func viewDidLoad() {
        super.viewDidLoad()
        sessionKeyTestBox.text = _sessionKey
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

}

请原谅我的(非 swift)Javascript 术语,但实际上我将数据调用移到了服务中,然后使用完成方法在服务中放置了一个回调方法,以确保 performSegue 在收到数据之前不会触发并解析出来。因此,当我将登录表单数据提交到服务器时,segue 在异步调用完成之前不会触发。

【讨论】:

  • 伟大的工作,很高兴听到这个:),我有以下评论: - 您可以添加一个模型层,例如您希望在其他 VC 中访问的数据实例化的类,也许 - “确保在接收到数据之前 performSegue 不会触发”并不是真正的问题,因为如果你添加一个模型层,就像我告诉你的那样,当你从类创建的对象不是 nil 时,登录成功你可以继续申请。 - Singleton 是 swift 中非常需要的设计模式之一,所以我想邀请您继续阅读它。
  • 您可以将 appDelegate 中的数据添加到该模型吗?
  • 它只是 MVC,据我所知你不能:你只需要从 appDelegate 获取视图控制器中的变量在视图控制器中初始化你的模型类(在它上面创建一个对象)你想要的数据。如果你正确地实现了 MVC,你可以在未来轻松地维护或更新你的代码,它会更容易理解。
  • 我不想让我的 appDelegate 成为一个 sharedInstance。
  • 据我所知,我只是创建了一个单例类并像在 JS 中使用 sessionStorage 一样使用它。这是一个公平的假设吗?如果设备重新启动或应用程序被强制关闭,课程是否会继续存在? (如本地存储)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-05
相关资源
最近更新 更多