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

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

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

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

Xcode

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

Swift

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

iPhone

iPhoneとは、アップル社が開発・販売しているスマートフォンです。 同社のデジタルオーディオプレーヤーiPodの機能、電話機能、インターネットやメールなどのWeb通信機能の3つをドッキングした機器です。

Q&A

解決済

2回答

4370閲覧

非同期通信で、ある処理のあとに別の非同期通信を行いたい。

退会済みユーザー

退会済みユーザー

総合スコア0

iOS

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

Xcode

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

Swift

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

iPhone

iPhoneとは、アップル社が開発・販売しているスマートフォンです。 同社のデジタルオーディオプレーヤーiPodの機能、電話機能、インターネットやメールなどのWeb通信機能の3つをドッキングした機器です。

0グッド

1クリップ

投稿2017/04/12 10:05

編集2017/04/14 02:51

###前提・実現したいこと
アプリを開発中にログイン機能を実装しています。(サーバーは実装済み)
その際に、ログインした後しかメッセージの情報を取得できないのですが
Alamofireを利用して「ユーザー情報登録要求(この処理の最後にログイン処理を実行)」「ログイン処理」「メッセージ数取得」の順番で実行すると
「メッセージ数取得」のレスポンスが先に返ってきてしまい"ログインされていない"エラーになってしまいます。
アプリ起動直後の画面でメッセージ数を表示したいので、ログイン処理の最後にメッセージ数取得の処理を行うと、
画面の更新ができないと思うのですが、どのように実装すればうまくいくでしょうか。

###該当のソースコード

Swift

1import UIKit 2 3class TopPage: UIViewController { 4 5 override func viewDidLoad() { 6 super.viewDidLoad() 7 // Do any additional setup after loading the view, typically from a nib. 8 self.checkUserData() 9 } 10 11 private func checkUserData() { 12 if let data = Variable.getUserId() { 13 userID = data 14 KeyManager.publicKey = KeyManager.findKeyString(keyTag: publicKeyData) 15 UserAPI.userLogin() 16 UserAPI.checkMessage(result: { count in 17 print(count) //ここでエラー 18 /* 19 ログイン後にメッセージ数取得を行わないといけない、 20 かつ画面の更新も行いたい。 21 */ 22 }) 23 } else { 24 userID = NSUUID().uuidString 25 Variable.saveUserId() 26 _ = KeyManager.createKeyPairs() 27 KeyManager.publicKey = KeyManager.findKeyString(keyTag: publicKeyData) 28 UserAPI.userRegister() 29 UserAPI.checkMessage(result: { count in 30 print(count) //ここでエラー 31 /* 32 ログイン後にメッセージ数取得を行わないといけない、 33 かつ画面の更新も行いたい。 34 */ 35 }) 36 } 37 }

Swift

1import UIKit 2import SVProgressHUD 3import SwiftyJSON 4 5class UserAPI: NSObject { 6 7 static let server = "テストサーバー" 8 static var header = ["ヘッダーの内容"] 9 10 //ユーザー登録 11 class func userRegister() { 12 SVProgressHUD.show() 13 let param = ["user_id": userID, "public_key": KeyManager.publicKey!] 14 NetworkManager.shared.ApiManager().request(authRegister, method: .post, parameters: param).responseJSON { response in 15 guard let object = response.result.value else { print(".Error"); return } 16 SVProgressHUD.dismiss() 17 let json = JSON(object) 18 self.userLogin() 19 } 20 } 21 22 class func userLogin() { 23 SVProgressHUD.show() 24 let param = ["user_id": userID, "public_key": KeyManager.publicKey!] 25 NetworkManager.shared.ApiManager().request(authLogin, method: .post, parameters: param, headers: header).responseJSON { response in 26 guard let object = response.result.value else { print(".Error"); return } 27 SVProgressHUD.dismiss() 28 let json = JSON(object) 29 } 30 } 31 32 //メッセージ数確認 33 class func checkMessage(result: @escaping (Int) -> ()) { 34 SVProgressHUD.show() 35 NetworkManager.shared.ApiManager().request(checkMessage, method: .get, headers: header).responseJSON { response in 36 guard let object = response.result.value else { print(".Error"); return } 37 SVProgressHUD.dismiss() 38 let json = JSON(object) 39 let count = json["result"]["message_count"].int! 40 result(count) 41 } 42 } 43 44}

###試したこと
「ユーザー情報登録要求(この処理の最後にログイン処理を実行)」「ログイン処理」も
コールバック(入れ子のように)にしてしまえば実装は出来そうですが
それだとコードが見づらいものになってしまうかと思いまして。
他にスタイリッシュに記述する方法を教えていただけませんか?

###補足情報(言語/FW/ツール等のバージョンなど)
【swift3】【Xcode8.2.1】

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

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

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

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

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

guest

回答2

0

ベストアンサー

一応この方法でtest1()の非同期通信後にtest2()を実行する形にできました。

Swift

1import UIKit 2import Alamofire 3 4class ViewController: UIViewController { 5 6 let semaphore = DispatchSemaphore(value: 0) 7 8 override func viewDidLoad() { 9 super.viewDidLoad() 10 // Do any additional setup after loading the view, typically from a nib. 11 DispatchQueue.global(qos: .default).async { 12 self.test() 13 self.test2() 14 } 15 } 16 17 func test() { 18 Alamofire.request("https://qiita.com/api/v2/items", method: .get).responseString { response in 19 guard let object = response.result.value else { return } 20 print(object) 21 self.semaphore.signal() 22 } 23 print("waiting...") 24 semaphore.wait() 25 print("finish") 26 } 27 28 func test2() { 29 print("test2") 30 } 31}

投稿2017/04/14 02:53

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

fuzzball

2017/04/14 04:04

Alamofireを使わない擬似コードでですが、こちらでも確認できました。(メインスレッドから呼ぶとデッドロックしたので、正しい擬似コードだと思います) なんとなく複雑怪奇なコードになってきたように思うのは気のせいでしょうかw 素直にコールバックを使った書き方にした方が良いかな、と個人的には思います。
退会済みユーザー

退会済みユーザー

2017/04/14 04:15 編集

非同期処理をセマフォで同期処理にし、それを非同期処理で実行するという感じですかね? でもメインのクラスはきれいに収まりますね。 もう少し考えてどちらにするか決めたいと思います。 (個人的にメインがきれいなので悪くないかなとも思ってたりしますが…)
fuzzball

2017/04/14 05:04

エラー処理のこともお忘れなく。
guest

0

  1. userRegister()、userLogin()、checkMessage()を同期関数にする。(userRegister()内のuserLogin()は削除しておくこと)

  2. GCDを使って3つの処理を裏で実行する。

同期関数化にはセマフォなどを使って下さい。


サンプルコード(Swift2)

swift

1dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { 2 if (新規登録) { 3 self.userRegister() 4 } 5 self.userLogin() 6 self.checkMessage() 7}

投稿2017/04/12 23:51

編集2017/04/14 01:25
fuzzball

総合スコア16731

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

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

退会済みユーザー

退会済みユーザー

2017/04/13 01:26

アカウントなしの場合は「userRegister」「userLogin」「checkMessage」 アカウントありの場合は「userLogin」「checkMessage」の流れになりますが、 回答いただいた方法だと前述の場合に対応できなくなりませんか?
fuzzball

2017/04/13 01:35

いちいち書かんと分からんもんかね。
退会済みユーザー

退会済みユーザー

2017/04/13 01:39 編集

>コールバック(入れ子のように)にしてしまえば実装は出来そうですが >それだとコードが見づらいものになってしまうかと思いまして。 この記載みえないのかね? そんでもっていちいち喧嘩腰な対応はどうなのかと思います。
fuzzball

2017/04/13 01:54

それなら最初のコメントで突っ込めよw
退会済みユーザー

退会済みユーザー

2017/04/13 01:57

そうですね、失礼しました。
fuzzball

2017/04/13 04:15 編集

反省して回答を修正しました。(コード無しでアレですが)
退会済みユーザー

退会済みユーザー

2017/04/13 09:00 編集

回答ありがとうございます。 また別のサンプルでテストしてみました。 質問内に追記してみたのですが、こういうことでしょうか 結局自分で入れ子にしてるので、おそらく意図していらっしゃるものと違うと思いますが。。。
fuzzball

2017/04/14 01:26

昨日の時点では「ふんわり」していたものを形にしてみました。
退会済みユーザー

退会済みユーザー

2017/04/14 01:44

ありがとうございます。 セマフォなどはまだ勉強不足なので、調べつつやっていきたいと思います。 ちなみにこの同期関数化だとAlamofireは使えなくなりますか?
fuzzball

2017/04/14 01:58 編集

そのまま使えます。 現状のコードにセマフォを追加して、非同期処理が終了するまで関数を抜けないようにするだけなので。 もちろん、全面的にコードを書き換えてもいいですが。 【追記】サイト見てみましたが詳しく書かれているようで良いと思います。デッドロックで記事が終わっているのが気になりますがw
退会済みユーザー

退会済みユーザー

2017/04/14 02:12

デッドロックなのは私も気になりましたw https://teratail.com/questions/34385 こちら以前回答されているものも参考になりそうです。
退会済みユーザー

退会済みユーザー

2017/04/14 02:23

案の定デッドロックしましたw
退会済みユーザー

退会済みユーザー

2017/04/14 02:54

テストで作成してみました、このような形でよいのでしょうか。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問