Home>

I am developing an app using Swift UI.

DispatchGroupTo usewhileThe same value to get from the statementwhileI want to use it as the value of the condition of whether to loop the statement.


For example, if you call the method in the code below as follows:
makeGetCallDateOverSetTemp (start_date: "2020-11-01", set_temp: 100)

  • 1st loop->totalTemp = 20 (The temperature of 2020-11-01 is 20 ℃)
  • 2nd loop->totalTemp = 50 (Temperature of 2020-11-02 is 30 ℃)
  • 3rd loop->totalTemp = 80 (Temperature of 2020-11-03 is 30 ℃)
  • 4th loop->totalTemp = 105 (The temperature of 2020-11-04 is 25 ℃)

Fifth, the loop stops because the totalTemp, which is the condition of the while statement, exceeds 100 passed to the second argument.

I want to make a move.

Corresponding source code

AppState.swift

@Published var weatherInfos: [WeatherInfos]?

func makeGetCallDateOverSetTemp (start_date: String, set_temp: Int) {
    let start_date = self.dateFromString (string: start_date, format: "yyyy/MM/dd")
    var addDays = 0
    var totalTemp: Float = 0.0
    let group = DispatchGroup ()
    // Set up the URL request
    while Float (set_temp)<totalTemp {
        let start_date = Calendar.current.date (byAdding: .day, value: addDays, to: start_date)
        let url_start_date = self.stringFromDate (date: start_date !, format: "yyyy-MM-dd")
        let endpoint: String = "https://sample.com/api/weather/?start_date=\(url_start_date)"
        addDays + = 1
        guard let url = URL (string: endpoint) else {
            print ("Error: cannot create URL")
            continue
        }
        var urlRequest = URLRequest (url: url)
        urlRequest.addValue ("token xxxxxxxxxxx", forHTTPHeaderField: "authorization")
        // set up the session
        let config = URLSessionConfiguration.default
        let session = URLSession (configuration: config)
        // make the request
        group.enter ()
        let task = session.dataTask (with: urlRequest) {(data, response, error) in
            guard error == nil else {
                print ("error calling GET")
                return
            }
            // make sure we got data
            guard let responseData = data else {
                print ("Error: did not receive data")return
            }
            // check for any errors
            defer {group.leave ()}
            // parse the result as JSON, since that's what the API provides
            DispatchQueue.main.async {
                do {
                    self.weatherInfos = try JSONDecoder (). Decode ([WeatherInfos] .self, from: responseData)
                    for info in self.weatherInfos! {
                        totalTemp + = info.temp
                    }
                } catch {
                    print ("Error: did not decode")
                    return
                }
            }
        }
        task.resume ()
    }
    group.notify (queue: .main) {
    print (url_start_date)
    }
}
func stringFromDate (date: Date, format: String)->String {
    let formatter: DateFormatter = DateFormatter ()
    formatter.calendar = Calendar (identifier: .gregorian)
    formatter.dateFormat = format
    return formatter.string (from: date)
}
func dateFromString (string: String, format: String)->Date {
    let formatter: DateFormatter = DateFormatter ()
    formatter.calendar = Calendar (identifier: .gregorian)
    formatter.dateFormat = format
    return formatter.date (from: string) ?? Date ()
}

jsonModel.swift

struct WeatherInfos: Codable, Identifiable {
    var id: Int
    var temp: Float
}

Postscript

func getTemperature (date: String)->Double? {
        let semaphore = DispatchSemaphore (value: 0)
        let endpoint: String = "https://sample.com/api/weather_ave_day/?date=\(date)"
        let url = URL (string: endpoint)
        var urlRequest = URLRequest (url: url!)
        urlRequest.addValue ("token xxxxxxxxxxxxxxxxx", forHTTPHeaderField: "authorization")// set up the session
        let config = URLSessionConfiguration.default
        let session = URLSession (configuration: config)
        var temperature: Double = 0.0
        let task = session.dataTask (with: urlRequest) {(data, response, error) in
            guard error == nil else {
                print ("error calling GET")
                return
            }
            // make sure we got data
            guard let responseData = data else {
                print ("Error: did not receive data")
                return
            }
            DispatchQueue.main.async {
                do {
                    self.weatherInfos = try JSONDecoder (). Decode ([WeatherInfos] .self, from: responseData)
                    for info in self.weatherAveInfos! {
                        temperature + = Double (info.ave_temp)
                    }
                    print ("weatherInfos: \ (self.weatherInfos as Any)") // 11.9
                } catch {
                    print ("Error: did not decode")
                    return
                }
            }
            semaphore.signal ()
        }
        task.resume ()
        semaphore.wait ()
        print ("returnValue: \ (temperature as Any)") // 0.0
        return temperature
    }

Method call

Button (action: {
                appState.getTemperature (date: "2020-11-11")
                }
        ) {
           Text ("get")
          }


Execution result

returnValue: 0.0
weatherInfos: Optional ([Sample.WeatherInfos (id: 6, temp: 11.9)])
Supplementary information (FW/tool version, etc.)

Xcode: Version 12.0.1

  • Answer # 1

    It's better not to do many things at once with one method.

    I would create a method to get the temperature at a certain date and time synchronously.
    In other words

    func getTemperature (on: Date)->Double? {
      let semaphore = DispatchSemaphore (value: 0)
      let session = // Create and configure URLSession
      var temperature: Double?
      let task = session.dataTask (with: urlRequest) {(data, response, error) in
        // Various
        temperature = temperature
        semaphore.signal ()
      }
      semaphore.wait ()
      return temperature
    }


    Like this.

    Then the loop is simply

    var totalTemperature = Double (0)
    while totalTemperature<p></pre>
    <p><br />
    All you have to do is</p>

  • Answer # 2

    <pre><code>func getTemperatureAsync (date: String) { var totalTemp: Double = 0.0 while totalTemp<100 { DispatchQueue.global (). async {// Since it is async here, it will be asynchronous processing totalTemp + = self.getTemperature (date: date) } } }