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

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

ただいまの
回答率

90.34%

  • Swift

    7665questions

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

  • Xcode

    4332questions

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

  • iOS

    4176questions

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

  • iPhone

    1019questions

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

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

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,346
退会済みユーザー

退会済みユーザー

前提・実現したいこと

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

該当のソースコード

import UIKit

class TopPage: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        self.checkUserData()
    }

    private func checkUserData() {
        if let data = Variable.getUserId() {
            userID = data
            KeyManager.publicKey = KeyManager.findKeyString(keyTag: publicKeyData)
            UserAPI.userLogin()
            UserAPI.checkMessage(result: { count in
                print(count) //ここでエラー
                /*
                ログイン後にメッセージ数取得を行わないといけない、
                かつ画面の更新も行いたい。
                */
            })
        } else {
            userID = NSUUID().uuidString
            Variable.saveUserId()
            _ = KeyManager.createKeyPairs()
            KeyManager.publicKey = KeyManager.findKeyString(keyTag: publicKeyData)
            UserAPI.userRegister()
            UserAPI.checkMessage(result: { count in
                print(count) //ここでエラー
                /*
                ログイン後にメッセージ数取得を行わないといけない、
                かつ画面の更新も行いたい。
                */
            })
        }
    }
import UIKit
import SVProgressHUD
import SwiftyJSON

class UserAPI: NSObject {

    static let server = "テストサーバー"
    static var header = ["ヘッダーの内容"]

    //ユーザー登録
    class func userRegister() {
        SVProgressHUD.show()
        let param = ["user_id": userID, "public_key": KeyManager.publicKey!]
        NetworkManager.shared.ApiManager().request(authRegister, method: .post, parameters: param).responseJSON { response in
            guard let object = response.result.value else { print(".Error"); return }
            SVProgressHUD.dismiss()
            let json = JSON(object)
            self.userLogin()
        }
    }

    class func userLogin() {
        SVProgressHUD.show()
        let param = ["user_id": userID, "public_key": KeyManager.publicKey!]
        NetworkManager.shared.ApiManager().request(authLogin, method: .post, parameters: param, headers: header).responseJSON { response in
            guard let object = response.result.value else { print(".Error"); return }
            SVProgressHUD.dismiss()
            let json = JSON(object)
        }
    }

    //メッセージ数確認
    class func checkMessage(result: @escaping (Int) -> ()) {
        SVProgressHUD.show()
        NetworkManager.shared.ApiManager().request(checkMessage, method: .get, headers: header).responseJSON { response in
            guard let object = response.result.value else { print(".Error"); return }
            SVProgressHUD.dismiss()
            let json = JSON(object)
            let count = json["result"]["message_count"].int!
            result(count)
        }
    }

}

試したこと

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

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

【swift3】【Xcode8.2.1】

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

check解決した方法

+1

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

import UIKit
import Alamofire

class ViewController: UIViewController {

    let semaphore = DispatchSemaphore(value: 0)

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        DispatchQueue.global(qos: .default).async {
            self.test()
            self.test2()
        }
    }

    func test() {
        Alamofire.request("https://qiita.com/api/v2/items", method: .get).responseString { response in
            guard let object = response.result.value else { return }
            print(object)
            self.semaphore.signal()
        }
        print("waiting...")
        semaphore.wait()
        print("finish")
    }

    func test2() {
        print("test2")
    }
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/04/14 13:04

    Alamofireを使わない擬似コードでですが、こちらでも確認できました。(メインスレッドから呼ぶとデッドロックしたので、正しい擬似コードだと思います)

    なんとなく複雑怪奇なコードになってきたように思うのは気のせいでしょうかw
    素直にコールバックを使った書き方にした方が良いかな、と個人的には思います。

    キャンセル

  • 2017/04/14 13:14 編集

    非同期処理をセマフォで同期処理にし、それを非同期処理で実行するという感じですかね?

    でもメインのクラスはきれいに収まりますね。
    もう少し考えてどちらにするか決めたいと思います。
    (個人的にメインがきれいなので悪くないかなとも思ってたりしますが…)

    キャンセル

  • 2017/04/14 14:04

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

    キャンセル

+1

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

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

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


サンプルコード(Swift2)

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
    if (新規登録) {
        self.userRegister()
    }
    self.userLogin()
    self.checkMessage()
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/04/13 10:26

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

    キャンセル

  • 2017/04/13 10:35

    いちいち書かんと分からんもんかね。

    キャンセル

  • 2017/04/13 10:38 編集

    >コールバック(入れ子のように)にしてしまえば実装は出来そうですが
    >それだとコードが見づらいものになってしまうかと思いまして。

    この記載みえないのかね?
    そんでもっていちいち喧嘩腰な対応はどうなのかと思います。

    キャンセル

  • 2017/04/13 10:54

    それなら最初のコメントで突っ込めよw

    キャンセル

  • 2017/04/13 10:57

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

    キャンセル

  • 2017/04/13 13:13 編集

    反省して回答を修正しました。(コード無しでアレですが)

    キャンセル

  • 2017/04/13 17:53 編集

    回答ありがとうございます。
    また別のサンプルでテストしてみました。
    質問内に追記してみたのですが、こういうことでしょうか

    結局自分で入れ子にしてるので、おそらく意図していらっしゃるものと違うと思いますが。。。

    キャンセル

  • 2017/04/14 10:26

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

    キャンセル

  • 2017/04/14 10:44

    ありがとうございます。
    セマフォなどはまだ勉強不足なので、調べつつやっていきたいと思います。

    ちなみにこの同期関数化だとAlamofireは使えなくなりますか?

    キャンセル

  • 2017/04/14 10:54

    http://grandbig.github.io/blog/2016/01/18/semaphore/

    Alamofire含めこちらのページに載っているかもしれないです。
    参考にしてみます。

    キャンセル

  • 2017/04/14 10:55 編集

    そのまま使えます。
    現状のコードにセマフォを追加して、非同期処理が終了するまで関数を抜けないようにするだけなので。
    もちろん、全面的にコードを書き換えてもいいですが。

    【追記】サイト見てみましたが詳しく書かれているようで良いと思います。デッドロックで記事が終わっているのが気になりますがw

    キャンセル

  • 2017/04/14 11:12

    デッドロックなのは私も気になりましたw

    https://teratail.com/questions/34385
    こちら以前回答されているものも参考になりそうです。

    キャンセル

  • 2017/04/14 11:23

    案の定デッドロックしましたw

    キャンセル

  • 2017/04/14 11:54

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

    キャンセル

同じタグがついた質問を見る

  • Swift

    7665questions

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

  • Xcode

    4332questions

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

  • iOS

    4176questions

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

  • iPhone

    1019questions

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