質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

2回答

1713閲覧

DispatchGroupを利用するwhile文の条件の設定の仕方

tomaa

総合スコア84

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2020/11/19 05:17

編集2020/11/19 08:47

前提・実現したいこと

SwiftUIを利用してアプリを開発しています。

DispatchGroupを利用するwhile文から取得する値を、同じwhile文をloopさせるかどうかの条件の値として利用したいです。


例えば、下記のコードに載せているメソッドを以下のように呼んだ場合:
makeGetCallDateOverSetTemp(start_date:"2020-11-01", set_temp:100)

  • 1回目 loop -> totalTemp = 20 (2020-11-01の気温は20℃)
  • 2回目 loop -> totalTemp = 50 (2020-11-02の気温は30℃)
  • 3回目 loop -> totalTemp = 80 (2020-11-03の気温は30℃)
  • 4回目 loop -> totalTemp = 105 (2020-11-04の気温は25℃)

5番目は、while文の条件としているtotalTempが、2番目の引数に渡した100を超えるため、loopが止まる

という動きにしたいです。

該当のソースコード

AppState.swift

swift

1@Published var weatherInfos:[WeatherInfos]? 2 3 4func makeGetCallDateOverSetTemp(start_date:String, set_temp:Int){ 5 6 let start_date = self.dateFromString(string: start_date, format: "yyyy/MM/dd") 7 var addDays = 0 8 var totalTemp:Float = 0.0 9 10 let group = DispatchGroup() 11 12 // Set up the URL request 13 while Float(set_temp) < totalTemp { 14 15 let start_date = Calendar.current.date(byAdding: .day, value: addDays, to: start_date) 16 let url_start_date = self.stringFromDate(date: start_date!, format: "yyyy-MM-dd") 17 18 let endpoint: String = "https://sample.com/api/weather/?start_date=(url_start_date)" 19 addDays += 1 20 21 guard let url = URL(string: endpoint) else { 22 print("Error: cannot create URL") 23 continue 24 } 25 var urlRequest = URLRequest(url: url) 26 urlRequest.addValue("token xxxxxxxxxxx", forHTTPHeaderField: "authorization") 27 // set up the session 28 let config = URLSessionConfiguration.default 29 let session = URLSession(configuration: config) 30 // make the request 31 group.enter() 32 let task = session.dataTask(with: urlRequest) {(data, response, error) in 33 guard error == nil else { 34 print("error calling GET") 35 return 36 } 37 // make sure we got data 38 guard let responseData = data else { 39 print("Error: did not receive data") 40 return 41 } 42 // check for any errors 43 defer { group.leave()} 44 // parse the result as JSON, since that's what the API provides 45 DispatchQueue.main.async { 46 do{ 47 self.weatherInfos = try JSONDecoder().decode([WeatherInfos].self, from: responseData) 48 for info in self.weatherInfos!{ 49 totalTemp += info.temp 50 } 51 }catch{ 52 print("Error: did not decode") 53 return 54 } 55 } 56 } 57 task.resume() 58 } 59 group.notify(queue: .main){ 60 print(url_start_date) 61 } 62} 63 64func stringFromDate(date: Date, format: String) -> String { 65 let formatter: DateFormatter = DateFormatter() 66 formatter.calendar = Calendar(identifier: .gregorian) 67 formatter.dateFormat = format 68 return formatter.string(from: date) 69} 70 71func dateFromString(string: String, format: String) -> Date { 72 let formatter: DateFormatter = DateFormatter() 73 formatter.calendar = Calendar(identifier: .gregorian) 74 formatter.dateFormat = format 75 return formatter.date(from: string) ?? Date() 76}

jsonModel.swift

swift

1struct WeatherInfos:Codable,Identifiable { 2 var id: Int 3 var temp: Float 4}

追記

swift

1 func getTemperature(date: String) -> Double? { 2 let semaphore = DispatchSemaphore(value: 0) 3 4 let endpoint: String = "https://sample.com/api/weather_ave_day/?date=(date)" 5 6 let url = URL(string: endpoint) 7 8 var urlRequest = URLRequest(url: url!) 9 urlRequest.addValue("token xxxxxxxxxxxxxxxxx", forHTTPHeaderField: "authorization") 10 // set up the session 11 let config = URLSessionConfiguration.default 12 let session = URLSession(configuration: config) 13 14 var temperature: Double = 0.0 15 16 let task = session.dataTask(with: urlRequest) {(data, response, error) in 17 guard error == nil else { 18 print("error calling GET") 19 return 20 } 21 // make sure we got data 22 guard let responseData = data else { 23 print("Error: did not receive data") 24 return 25 } 26 DispatchQueue.main.async { 27 do{ 28 self.weatherInfos = try JSONDecoder().decode([WeatherInfos].self, from: responseData) 29 for info in self.weatherAveInfos!{ 30 temperature += Double(info.ave_temp) 31 } 32 33 print("weatherInfos:(self.weatherInfos as Any)") // 11.9 34 }catch{ 35 print("Error: did not decode") 36 return 37 } 38 } 39 semaphore.signal() 40 } 41 task.resume() 42 semaphore.wait() 43 44 print("returnValue:(temperature as Any)") // 0.0 45 return temperature 46 }

メソッドの呼び出し

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

実行結果

console

1returnValue:0.0 2weatherInfos:Optional([Sample.WeatherInfos(id: 6, temp: 11.9)])

補足情報(FW/ツールのバージョンなど)

Xcode:Version 12.0.1

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

いろいろなことを一つのメソッドでいっぺんにしない方がいいです。

僕なら同期的にある日時の気温を取得するメソッドを作りますね。
つまり、

swift

1func getTemperature(on: Date) -> Double? { 2 let semaphore = DispatchSemaphore(value: 0) 3 4 let session = // URLSessionの作成と設定 5 6 var temperature: Double? 7 8 let task = session.dataTask(with: urlRequest) {(data, response, error) in 9 // いろいろ 10 11 temperature = 気温 12 13 semaphore.signal() 14 } 15 16 semaphore.wait() 17 18 return temperature 19}

こういうの。

そうするとループは単純に

swift

1var totalTemperature = Double(0) 2 3while totalTemperature < limit { 4 5 let date = //日付処理 6 7 totalTemperature += getTemperature(on: date) ?? 0.0 8 // ↑このエラー処理(nilの時の処理)はダメな見本です。 9 // 実際はちゃんとしてください。 10} 11

とするだけで済みます。

投稿2020/11/19 06:52

MasakiHori

総合スコア3384

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tomaa

2020/11/19 08:09 編集

回答していただきありがとうございます。回答を参考にしまずは、同期的に温度を取得するメソッドを作成しました(質問に追記として記載)。 しかし、私が修正したコードでは同期的に動かない状況です。 回答していただいた内容では、`task`が実行されていなかったので、それらしい場所で`task.resume()`をしています。 他に不備等ありますでしょうか?
MasakiHori

2020/11/19 08:16

DispatchQueue.main.async を DispatchQueue.main.sync (同期実行)にしてください。
tomaa

2020/11/19 08:23

DispatchQueue.main.sync (同期実行)で試してみました。 メソッドを呼ぶと、アプリが固まってしまします。エラー等は出ず、その後の動作は何もできなくなります。
MasakiHori

2020/11/19 08:36

もしかしてこのメソッド(makeGetCallDateOverSetTemp)はメインスレッド上から呼び出してますか? メインスレッド上でスレッドをブロックするのは全くよろしくないのでやめましょう。 このメソッドをDispatchQueue.global().asyncなどを用いて非同期に呼び出してください。 あるいはこのメソッド内での処理をDispatchQueue.global().asyncで行い即座に呼び出し元に処理を返してください。
tomaa

2020/11/19 08:48

今は、動作の確認のため`getTemperature ()`を単体で動かしています。 期待通りの動作する事を確認した後に、`while`文の中で使用するつもりです。 質問に呼び出し元のコードを追記しました。 この場合も、メインスレッド上で呼び出している事になりますでしょうか?
MasakiHori

2020/11/19 08:57

Target-Actionはすべてメインスレッド上で処理されます。 テストということでしたらgetTemperatureをラップする func getTemperatureAsync(date: String) { DispatchQueue.global().async { self.getTemperature(date: date) } } を作ってこちらをアクションに指定すれば動くと思われます。
tomaa

2020/11/19 10:05

func getTemperatureAsync(date: String) { DispatchQueue.global().async { self.getTemperature(date: date) } } の方法をとり、`getTemperature`の動作の確認がとれました。 その後、今回の目的である`while`文に使用しようとしましたが、こちらでも複数回の非同期処理(DispatchQueue.global().async)を行う必要があり(syncにするとアプリがクラッシュする)、totalTempがずっと0.0という条件となってしまいます。 回答欄にwhile文使用のコードを記載しましたが、修正できる方法はありますでしょうか?
MasakiHori

2020/11/20 01:35

関数内の”すべて"の処理をDispatchQueue.global().async{} のクロージャ内に記述してください。 変なところだけ非同期にしないでください。
MasakiHori

2020/11/20 01:46

非同期処理のゆるーいイメージとしては、与えられた処理を0秒後に実行されるタイマーにセットして処理を戻す、です。 タイマーを起動するためにも時間は必要ですので0秒後であってもすぐに処理が始まるわけではありません。 処理から戻った時点ではタイマーがまだ動いていないかもしれませんし、与えた処理が完了していないかもしれません。 つまり一般的にはasyncから処理が戻ってきた時点では与えた処理は終わっていません。 これを決して忘れないでください。
tomaa

2020/11/20 06:02

関数内の”すべて"の処理をDispatchQueue.global().async{} のクロージャ内に記述し、期待どおりの動きとなりました。 長い時間、ご丁寧に教えていただきありがとうございました。 おかげで非同期処理の知識が深まり、DispatchSemaphoreの使い方を知る事ができました。 DispatchSemaphoreは非同期処理を同期的に行う場面で活用できそうです。
guest

0

func getTemperatureAsync(date: String) { var totalTemp:Double = 0.0 while totalTemp < 100 { DispatchQueue.global().async { //ここでasyncとするため非同期処理となる totalTemp += self.getTemperature(date: date) } } }

投稿2020/11/19 10:00

tomaa

総合スコア84

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問