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

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

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

JSON(JavaScript Object Notation)は軽量なデータ記述言語の1つである。構文はJavaScriptをベースとしていますが、JavaScriptに限定されたものではなく、様々なソフトウェアやプログラミング言語間におけるデータの受け渡しが行えるように設計されています。

非同期処理

非同期処理とは一部のコードを別々のスレッドで実行させる手法です。アプリケーションのパフォーマンスを向上させる目的でこの手法を用います。

Swift

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

クロージャ

クロージャは、プログラミング言語における関数オブジェクトの一種です。 引数以外の変数を実行時の環境ではなく、 自身が定義された環境において解決することを特徴とします。

Q&A

解決済

1回答

1088閲覧

非同期処理終了を取得したい

hodoru3sei

総合スコア284

JSON

JSON(JavaScript Object Notation)は軽量なデータ記述言語の1つである。構文はJavaScriptをベースとしていますが、JavaScriptに限定されたものではなく、様々なソフトウェアやプログラミング言語間におけるデータの受け渡しが行えるように設計されています。

非同期処理

非同期処理とは一部のコードを別々のスレッドで実行させる手法です。アプリケーションのパフォーマンスを向上させる目的でこの手法を用います。

Swift

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

クロージャ

クロージャは、プログラミング言語における関数オブジェクトの一種です。 引数以外の変数を実行時の環境ではなく、 自身が定義された環境において解決することを特徴とします。

0グッド

1クリップ

投稿2018/10/25 14:59

編集2018/10/30 14:05

OpenWetherMapのAPIをParsesしました。取得したデータを使って判定を行いたかったのですが、クロージャの中が非同期になっているようで、後続の処理が抜けてしまいます。

Swiftで複数の非同期処理の完了時に処理を行うこの記事を見るに、
DispatchGroupメソッドを使うと終了判定が取れるようだったので、使ってみたのですが、処理完了前に条件式を判定してしまいます。

Swift

1 override func viewDidLoad() { 2 super.viewDidLoad() 3 var jData:Root? 4 let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?q=Hachioji&appid=568cc88fc88a619182325a1338854669")! 5 let request = URLRequest(url: url) 6 let dispatchGroup = DispatchGroup() 7 8 task = URLSession.shared.dataTask(with: request) { (data, response, error) in 9 guard let data = data else{return} 10 do{ 11 dispatchGroup.enter() 12 jData = try JSONDecoder().decode(Root.self, from: data) 13 } catch let e { 14 print(e) 15 } 16 } 17 task.resume() 18 19 dispatchGroup.notify(queue: .main){ 20 print("<<<<<<<<<<<<<<<<<<<") 21 if let data = jData{ 22 print(data.weather[0].main) 23 } 24 print(">>>>>>>>>>>>>>>>>>>>>>>>>") 25 } 26 27 }

データが入る前に判定をされてしまっているので天気のデータがprintされずに上下のprint文の文字だけが出力されています

DEBUG

1<<<<<<<<<<<<<<<<<<< 2>>>>>>>>>>>>>>>>>>>>>>>>>

処理の完了はどのように検知すれば良いのでしょうか

※追記

クロージャの中でやればいいんじゃないかという意見をいただいたので追記します。
データを取得する部分だけ別クラスにしようと考えています。
雨かを判定して雨の時だけアプリの挙動を変えようと思っています。クロージャの中でreturnをしても元の関数を抜けることができないので非同期処理の終了を検知したいと考えました。

Swift

1class WetherClient: UIView { 2 private let openWetherMapAPI = "https://api.openweathermap.org/data/2.5/weather?q=Hachioji&appid=568cc88fc88a619182325a1338854669" 3 private var wether:Root! 4 5 func judgeWether() -> Bool{ 6 let url = URL(string: openWetherMapAPI)! 7 let request = URLRequest(url: url) 8 var task = URLSession.shared.dataTask(with: request){(data, response, error) in 9 guard let data = data else{return} 10 do{ 11 let jsonData = try JSONDecoder().decode(Root.self, from: data) 12 print(jsonData.weather[0].main) 13 self.wether = jsonData 14 } catch let e{ 15 print(e) 16 } 17 } 18 task.resume() 19 if let data = wether{ 20 if(data.weather[0].main == "Rain"){ 21 print(data.weather[0].main) 22 return true 23 } 24 } 25 print("jsonData:(wether.weather[0].main)") 26 return false 27 } 28} 29

※追記10/30

クロージャについて完璧には理解できていないと思いますがSwiftのクロージャについて整理してみた。という記事を参考にして、真似して実装を行なってみました。
ですが、wetherの値を確認するためにprintの部分にブレイクポイントを貼って値が入っているかを確認したのですが,しゅとくできておらずnullになってしまっていました

Swift

1class RainChecker: Any{ 2 private let openWetherMapAPI = "https://api.openweathermap.org/data/2.5/weather?q=Hachioji&appid=568cc88fc88a619182325a1338854669" 3 4 func judgeWether() -> Bool{ 5 6 var wether:Root? 7 getJsonData{(jsonData) in wether = jsonData} 8// if let data = wether{ 9// if(data.weather[0].main == "Rain"){ 10// print(data.weather[0].main) 11// return true 12// } 13// } 14// print("jsonData:(wether?.weather[0].main)") 15// return false 16 print(wether) 17 return true 18 } 19 func getJsonData(wetherData:@escaping (_ response: Root)->Void){ 20 let url = URL(string: openWetherMapAPI)! 21 let request = URLRequest(url: url) 22 23 24 var task = URLSession.shared.dataTask(with: request){(data, response, error) in 25 guard let data = data else{return} 26 do{ 27 let jsonData = try JSONDecoder().decode(Root.self, from: data) 28 wetherData(jsonData) 29 } catch let e{ 30 print(e) 31 } 32 33 } 34 task.resume() 35 } 36} 37

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

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

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

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

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

fuzzball

2018/10/26 00:10

dataTask(with:completionHandler:)のcompletionHandlerの中で処理を行うのではダメなのでしょうか?
takabosoft

2018/10/26 00:51

今回非同期処理が1つしかないので「Swiftで複数の非同期処理の完了時に処理を行う」という記事を参考にするのはちょっと違うでしょうね。
hodoru3sei

2018/10/26 10:08

関数の戻り値として値を返したいと思っていまして、クロージャ内でreturnをしても関数から抜けられずどうしたら良いのかわからなかったため非同期完了のタイミングをとるのが良いのかなと考えていました
fuzzball

2018/10/26 10:12

値を返したいと言っても、viewDidLoad()は戻り値無いですけどね。やろうとしていることの意図が分かるようなコードを書くようにしてもらえますか。
hodoru3sei

2018/10/26 10:54

コードを追記しました。本来は天気情報取得の部分だけを切り出してクラス化し、雨が降っているかの判定をしたいです。
guest

回答1

0

ベストアンサー

こうですね
enterとleaveが違います

swift

1class ViewController3: UIViewController { 2 3 struct Root: Codable { 4 var weather:[Weaser] = [] 5 6 struct Weaser: Codable { 7 var id:Int? 8 var main:String = "_" 9 var description:String? 10 var icon:String? 11 } 12 } 13 14 override func viewDidLoad() { 15 super.viewDidLoad() 16 17 var jData:Root? 18 let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?q=Hachioji&appid=568cc88fc88a619182325a1338854669")! 19 let request = URLRequest(url: url) 20 let dispatchGroup = DispatchGroup() 21 22 dispatchGroup.enter() 23 let task = URLSession.shared.dataTask(with: request) { (data, response, error) in 24 guard let data = data else{return} 25 do{ 26 jData = try JSONDecoder().decode(Root.self, from: data) 27 dispatchGroup.leave() 28 } catch let e { 29 print(e) 30 } 31 } 32 task.resume() 33 34 dispatchGroup.notify(queue: .main){ 35 print("<<<<<<<<<<<<<<<<<<<") 36 if let data = jData{ 37 print(data.weather[0].main) 38 } 39 print(">>>>>>>>>>>>>>>>>>>>>>>>>") 40 } 41 } 42} 43

didSetもいいですよ

swift

1 override func viewDidLoad() { 2 super.viewDidLoad() 3 4 var jData:Root? { 5 didSet { 6 if let jData = jData { 7 if jData.weather.count > 0 { 8 print(jData.weather[0].main) 9 } 10 } 11 } 12 } 13 14 let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?q=Hachioji&appid=568cc88fc88a619182325a1338854669")! 15 let request = URLRequest(url: url) 16 17 let task = URLSession.shared.dataTask(with: request) { (data, response, error) in 18 guard let data = data else{return} 19 do{ 20 jData = try JSONDecoder().decode(Root.self, from: data) 21 } catch let e { 22 print(e) 23 } 24 } 25 task.resume() 26 27 } 28 29

投稿2018/10/28 14:31

kosanai

総合スコア471

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

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

hodoru3sei

2018/10/29 13:16

ありがとうございます試してみます!
hodoru3sei

2018/10/29 13:37

dispathGroupのほうですが、関数にした際にどのようにreturnを返したら良いのかわかりませんでした。 dispatchGroup.notify(queue: .main){ if let data = self.wether{ if(data.weather[0].main == "Rain"){ print(data.weather[0].main) return true } } print("jsonData:(self.wether.weather[0].main)") return false } そのまま書いてみたのですが、Unexpected non-void return value in void functionというエラーが出てしまい通りませんでした。 didSetでも試してみたのですが、関数の戻り値として返そうとしたところエラーになってしまいました。 didSet{ if let wether = wether{ if wether.weather.count<0{ if wether.weather[0].main == "Rain"{ return true } } } } 出ていたエラーはUnexpected non-void return value in void functionというものでした。 私の理解力が低いせいでうまく活用することができていません、戻り値で返したい場合はreturnをどこにかけば良いのでしょうか?
kosanai

2018/10/30 02:53

非同期完了の処理はreturnではなくclosureを使います。 例えばUIView.animate(widhDuration:animations:completion:)のcompletionと同じ考え方です。 関数にするなら、notifyされた後に呼び出すclosureをreturnの代わりに設定しなければなりません。 func hoge(didReceiveWraser:((_ weaser:Weaser) -> ())) { // ... didReceiveWeaser(weaser) } swift+closure、非同期処理、スレッド処理、APIクライアント、URLSessionあたりで調べれば理解が進むかもしれませんが かえって混乱するかもしれませんね(試しにググってみましたが分かりやすいものがない) わかりそうになければあとでもう一回書いてみます
hodoru3sei

2018/10/30 13:10

すみません、調べてみたのですが難しくてまだ理解できていません。 書いていただいた下記の関数は func hoge(didReceiveWraser:((_ weaser:Weaser) -> ())) {   didReceiveWeaser(weaser) } この関数はどのタイミングで呼べば良いのでしょうか? APIと通信しているクロージャの中でデータを取得した時に呼べばいいのでしょうか?
hodoru3sei

2018/10/30 13:58

https://qiita.com/pei0804/items/9bd935ac6832cd07ffd2 この記事の最後の部分が私の実現したいことと一致していたので、自分のコードに落とし込んでみたのですが、データがうまく取得できず、nullになってしまいました、試したコードを質問に追記します
hodoru3sei

2018/11/01 12:00

ありがとうございます!自分のプロジェクトに反映できました‼️すごくわかりやすかったです 私はreturnで値を返す事ばかり考えていたのでそれにとらわれすぎて混乱してしまっていました。 イメージとしては非同期内から関数を呼び出すみたいな感じなんですね ありがとうございました、本当に助かりました!
kosanai

2018/11/02 01:17

ここは私も散々ハマりました(分かりやすい解説がない) 慣れると便利ですよ。
hodoru3sei

2018/11/02 14:11

まだまだわからない事だらけですが頑張って勉強してみようと思います。 丁寧に記事まで書いていただいてほんとありがたいです!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問