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

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

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

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

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

Q&A

解決済

1回答

731閲覧

class funcを利用してAPI取得用のメソッドを共通化したい

Ka_ya_

総合スコア31

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

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

0グッド

0クリップ

投稿2020/05/13 09:57

編集2020/05/13 11:47

質問失礼します。

アプリ内の複数箇所で同じAPIを使用する為、API検索用の値を引数にして
取得した値をエンティティクラスとして返すclass funcをUtilクラスに作成したいです。

下記の様にコードを記載して挙動を確認したのですが、
APIから情報を取得するクロージャーの中身がいつ発動されるか分からず、
値が必要な画面遷移前までに値を取得出来ずにいます。
(現状では画面遷移前のprintの後にクロージャー内のprintが表示される為、順序が逆になってしまいます)

これはどの様にコードを修正すれば解消する事ができるのでしょうか。
どなたかご教授いただけますと嬉しいです。
よろしくお願い致します。

コントローラークラス

Swift

1//写真を撮影した後 2 func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { 3 //インスタンスを用意しておく 4 var dataList = DataList() 5 6 if let pickedImage = info[.editedImage] as? UIImage { 7 8 //イメージをbase64にエンコードする 9 let imageData = pickedImage.pngData()! as NSData 10 let imageString = imageData.base64EncodedString(options: .lineLength64Characters) 11 12 //imageStringを引数として渡して、DataList型として値を受け取りたい 13 dataList = Util.returnData(image:imageString) 14 15 } 16 print(dataList.data1)←値が空欄になってしまう 17 18 //画面遷移する 19 picker.dismiss(animated: true, completion: nil) 20 } 21}

エンティティクラス

Swift

1class DataList { 2 3 var data1 = "" 4 var data2 = "" 5 var data3 = "" 6 7//値を設定する時だけ引数を渡してインスタンスを生成したい 8 init(data1:String, data2:String, data3:String){ 9 10 self. data1 = data1 11 self. data2 = data2 12 self. data3 = data3 13 } 14 15//インスタンスの準備用 16 init(){ 17 } 18}

Utilクラス

Swift

1class Util{ 2 3 class func returnData(image:String) -> DataList { 4 //インスタンスを用意しておく 5 var dataList = DataList() 6 7 //JSONデータを取得する 8 session.dataTask(with: request) { (data, response, error) in 9 if error == nil, let data = data, let response = response { 10 11 let jsonData = try! JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as? [String: [String:Any]] 12 13 if let data = jsonData?["hits"] { 14 15 let data1 = data["data1"] as! String 16 let data2 = data["data2"] as! String 17 let data3 = data["data3"] as! String 18 //取得した値を格納する 19 dataList = DataList(data1,data2,data3) 20 21 print(dataList.data1)←ここでは値を正常に取得出来る 22 } 23 } else { 24 // API通信失敗 25 print(error as Any) 26 } 27 }.resume() 28 29print(dataList.data1)←ここでは値が空になってしまう 30 31 return dataList 32 } 33}

*コントローラークラスのAPIメソッド呼び出し箇所を修正*

Swift

1//写真を撮影した後 2 func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { 3 4 if let pickedImage = info[.editedImage] as? UIImage { 5 6 //イメージをbase64にエンコードする 7 let imageData = pickedImage.pngData()! as NSData 8 let imageString = imageData.base64EncodedString(options: .lineLength64Characters) 9 10 var dataList = Util.returnData(image:imageString, completion: { 11 result in 12 print(result.data1)←値が表示されない 13 //商品リストへ遷移する 14 picker.dismiss(animated: true, completion: nil)←画面遷移しない 15 }) 16}

*UtilクラスのAPI呼び出し箇所の修正*

Swift

1class Util{ 2 3 class func returnData(image:String), completion: @escaping (DataList) -> Void) { 4 //インスタンスを用意しておく 5 var dataList = DataList() 6 7 //JSONデータを取得する 8 session.dataTask(with: request) { (data, response, error) in 9 if error == nil, let data = data, let response = response { 10 11 let jsonData = try! JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as? [String: [String:Any]] 12 13 if let data = jsonData?["hits"] { 14 15 let data1 = data["data1"] as! String 16 let data2 = data["data2"] as! String 17 let data3 = data["data3"] as! String 18 //取得した値を格納する 19 dataList = DataList(data1,data2,data3) 20 } 21 } else { 22 // API通信失敗 23 print(error as Any) 24 } 25 }.resume() 26 } 27}

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

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

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

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

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

hoshi-takanori

2020/05/13 10:16

非同期なので値がいつ取得できるかは分かりません。値が取得できるまでは値が必要な画面に遷移しないようにするか、値がまだ取得できてなかったら「取得中です」と表示するなどの対応が必要になります。
Ka_ya_

2020/05/13 11:52

hoshi-takanoriさん、ご回答ありがとうございます。 非同期の事をキチンと理解出来ていなかったので、 この機会にご回答いただいた内容を元にしっかり勉強しようと思います^^
guest

回答1

0

ベストアンサー

クロージャの中の処理は非同期処理になっています。

クロージャ外部のprintはすぐに表示されますが、クロージャ内部の処理はデータを受信、もしくは受信失敗したときまで処理されませんから、そのタイミングでprintされます。

では、どのように解決すればいいかというと、一般的には処理完了時に実行するクロージャを別途渡して、そこで処理を行わせることになります。

つまり、return で値を返すのではなく、独自の処理を行うクロージャを引数として渡してあげて、それをデータ受診後に処理することで値を受け取る処理を行う必要があります。

Alamofireを使った例ですが、大まかな流れは同じです。
過去の回答が下記のリンクにありますので、一度ご覧になってご検討ください。

投稿2020/05/13 10:16

TsukubaDepot

総合スコア5086

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

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

Ka_ya_

2020/05/13 11:50

TsukubaDepotさん、ご回答ありがとうございます。 クロージャーの処理がおかしかったのですね…!! 分かりやすく説明していただきありがとうございます^^ 記載していただいたリンクを参考に修正したコードを質問内容に追記したのですが、 resultを受け取るクロージャーの中身が起動しないようで、 画面遷移をする事が出来ませんでした。 せっかく丁寧に記載のある回答を教えていただいたのに申し訳ないのですが、 よろしければどこがおかしいのか教えていただけますととても助かります。 たびたびお手数をおかけしますが、よろしくお願い致します。
TsukubaDepot

2020/05/13 14:23

class func returnData(image:String), completion: @escaping (DataList) -> Void) { ここで指定した completion を実行する部分が無いみたいです(ちなみに、Stringのあとの閉じるカッコが不要です)。 dataList = DataList(data1,data2,data3) completion(dataList) という具合に引数で指定したクロージャを実行する部分が必要になるのでご確認ください。 ところで、追記されたコードなどをXcodeに貼り付けても、カッコの位置などがおかしくエラーになる場合があります。 こちらとしても問題点を正確にご指摘したいので、コードはコピーアンドペーストして貼り付けるなど、実際に動くような形で転載していただけると助かります。
TsukubaDepot

2020/05/13 14:29

ちなみに、引数として与えるクロージャ内部で pickerView のように UI部品を操作するメソッドを呼び出していますが、それらのメソッドはメインスレッドで呼び出すことが推奨されているため、 dataList = DataList(data1,data2,data3) DispatchQueue.main.async { completion(dataList) } のように、クロージャの呼び出しをメインスレッドで行うようにする必要があります。
Ka_ya_

2020/05/13 14:53

TsukubaDepotさん、親切にご回答いただきありがとうございます。 せっかくお時間を割いて教えて下さっているのに、 コードに不備がありお手数をおかけして本当に申し訳ありません。 以後記載方法に気をつけようと思います。 お陰様で、コードを修正したところ無事動くようになりました^^ 勉強不足でメインスレッドなどを意識した事がなかったのですが、 クロージャがマルチスレッドというものになるのですね…!! 新しい知識が知れて、とても勉強になりました。 いつも色々と丁寧に教えて下さり、本当にありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問