为什么在 Swift Widget 中不能使用 URLSession

问题描述

我从我的网站创建了带有温度的小部件。 在标准程序中一切正常,但在结果的 Widget 中我看到“0”(来自 var 的信息)

这里有错误代码 (DataProvider.swift)

import Foundation

class DataProvider {
    
    struct CurrencyJSON: Codable {
        var temp : String
    }
    static func getTemp() -> String {
        
    var dig : String = "0"
    print ("1")
    //on url this data: {temp:3.5},this is my site,i can do anything format,may be make format: 3.5 (no json)?
    URLSession.shared.dataTask(with: URL(string: "http://example.com/weather.PHP")! ) {(data,response,error) in
        guard let data = data else { return }
        do {
            let res = try JSONDecoder().decode(CurrencyJSON.self,from: data)
            dig=res.temp
            print ("\(dig)")
        } catch let error {
            print("\(error)")
        }
    }.resume()
    return dig
    }
    
}

在小部件上我看到“0”(来自:var dig : String =“0”),为什么?我哪里出错了?

这里是我的结构提供者:TimelineProvider:

struct Provider: TimelineProvider {
    func placeholder(in context: Context) -> SimpleEntry {
        SimpleEntry(date: Date(),myString: "...")
    }

    func getSnapshot(in context: Context,completion: @escaping (SimpleEntry) -> ()) {
        let entry = SimpleEntry(date: Date(),myString: "...")
        completion(entry)
    }

    func getTimeline(in context: Context,completion: @escaping (Timeline<Entry>) -> ()) {
        var entries: [SimpleEntry] = []
        print ("1")
        // Generate a timeline consisting of five entries an hour apart,starting from the current date.
        let currentDate = Date()
        for hourOffset in 0 ..< 5 {
            let entryDate = Calendar.current.date(byAdding: .second,value: hourOffset * 10,to: currentDate)!
            
            let entry = SimpleEntry(date: entryDate,myString: DataProvider.getTemp())
            entries.append(entry)
        }

        let timeline = Timeline(entries: entries,policy: .atEnd)
        completion(timeline)
    }
}

解决方法

URLSession.shared.dataTask异步,这意味着您需要另一种方式来返回数据。

为此,您可以在 getTemp 函数中提供一个完成处理程序:

static func getTemp(completion: @escaping (String) -> Void) { // add completion,don't return anything
    var dig: String = "0"
    URLSession.shared.dataTask(with: URL(string: "http://example.com/weather.php")!) { data,response,error in
        guard let data = data else { return }
        do {
            let res = try JSONDecoder().decode(CurrencyJSON.self,from: data)
            dig = res.temp
            print("\(dig)")
        } catch {
            print("\(error)")
        }
        completion(dig) // pass the value to the completion handler
    }.resume()
    // return dig // don't return here
}

如您所见,getTimeline 函数还有一个完成处理程序 - 这允许您在那里执行异步函数:

func getTimeline(in context: Context,completion: @escaping (Timeline<Entry>) -> ()) {
    DataProvider.getTemp { myString in
        var entries: [SimpleEntry] = []
        let currentDate = Date()
        for hourOffset in 0 ..< 5 {
            let entryDate = Calendar.current.date(byAdding: .second,value: hourOffset * 10,to: currentDate)!
            
            let entry = SimpleEntry(date: entryDate,myString: myString)
            entries.append(entry)
        }

        let timeline = Timeline(entries: entries,policy: .atEnd)
        completion(timeline)
    }
}