【问题标题】:transfer data with protocol使用协议传输数据
【发布时间】:2021-04-13 18:04:10
【问题描述】:

我尝试解码天气 api

这是我的结构类weatherModal:

import Foundation

struct WeatherModel:Decodable{
    var main:Main?
}

struct Main:Decodable {
    var temp : Double?
    var feels_like : Double?
    var temp_min:Double?
    var temp_max:Double?
    var pressure , humidity: Int?

}

我正在尝试学习协议。所以这是一个 make api 调用管理器类:

protocol WeatherManagerProtocol:AnyObject {
    func weatherData(weatherData:WeatherModel)
}
class WeatherManager{
    var weather : WeatherModel?
    
    weak var delegate :WeatherManagerProtocol?
    
    public func callWeather(city:String) {
    
        let url = "http://api.openweathermap.org/data/2.5/weather?q=\(city)&appid=1234"

        URLSession.shared.dataTask(with:URL(string: url)!) { (data, response, err) in
            if err != nil {
                print(err!.localizedDescription)          
            } else {
                do {
                    self.weather = try JSONDecoder().decode(WeatherModel.self, from: data!)
                    self.delegate?.weatherData(weatherData: self.weather!)
                } catch {
                    print(error.localizedDescription)
                }
            }          
        }.resume()    
    }
}

在我的ViewController 中,我想做的是用户在textfield 上写下城市名称,如果用户单击进程按钮,则打印有关天气的信息。

class ViewController: UIViewController {

    @IBOutlet weak var textField: UITextField!
    var weatherManager = WeatherManager()
    var data : WeatherModel?
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        weatherManager.delegate = self
        
        // Do any additional setup after loading the view.
    }

    @IBAction func processButtonClicked(_ sender: Any) {
        if textField.text != "" {
           weatherManager.callWeather(city: textField.text ?? "nil")
           print(data?.main?.humidity) // it print nil
            
        } else{
           print("empty")
        }
    }

    extension ViewController: WeatherManagerProtocol{
        func weatherData(weatherData: WeatherModel) {
            self.data = weatherData
            print(self.data.main)
            // in here I can show my data
    }
}

当我点击进程按钮时,它总是打印 nil。为什么 ?我做错了什么?

【问题讨论】:

  • 我改进了您的代码格式(缩进,Xcode 可以自动为您执行此操作),对于未来的问题,请确保您也这样做,这样您的代码将更容易被我们所有人阅读将获得更多、更快、更好的答案
  • 异步方法就像打电话订购披萨一样。一旦你打完电话,你就没有披萨了。你可能在未来的某个时候得到一个披萨。
  • 谢谢,我会注意的。

标签: ios swift swift-protocols


【解决方案1】:

您似乎不明白您自己的代码应该如何工作。您设置的协议和委托模式的整个想法是“信号”在这样的路径上通过天气管理器往返:

  1. 你(ViewController)说weatherManager.callWeather

  2. 天气管理员负责一些网络。

  3. 天气管理器调用它自己的代理weatherData

  4. 你(ViewController)那个委托,所以你的weatherData被调用了,这就是你可以print的地方。

这就是信号路径:

@IBAction func processButtonClicked(_ sender: Any) {
    weatherManager.callWeather(city: textField.text ?? "nil") // < TO wm
}
func weatherData(weatherData: WeatherModel) { // < FROM wm
    // can print `weatherData` here
}

您不能通过尝试在其他任何地方打印天气数据来短路此路径。留在路上。你不能把它变成一个“线性”的简单路径;它是异步的

如果您确实希望它看起来更像“线性”简单路径,请使用完成处理程序而不是委托回调。这就是我在同一个实验版本中所做的,所以我的视图控制器代码如下所示:

self.jsonTalker.fetchJSON(zip:self.currentZip) { result in
    DispatchQueue.main.async {
        // and now `result` contains the weather data, or an error

更好的是,使用 Combine 框架(或等到 Swift 6 实现 async/await)。

【讨论】:

  • 我应该补充一点:完成处理程序将是比您的协议和委托更好的架构,因为如果用户更改文本字段并快速连续点击按钮两次怎么办?您可能会完全不同步:您的 weatherData 被调用,但您无法知道对应的城市。
【解决方案2】:

您调用以URLSession.shared.dataTask 开头的weatherManager.callWeather。任务异步执行,但callWeather 立即返回。所以,当你打印data?.main?.humidity时,任务还没有完成,data仍然是nil。任务完成后,您调用weatherData 并将响应分配给data

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-15
    • 1970-01-01
    • 2013-07-28
    相关资源
    最近更新 更多