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

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

ただいまの
回答率

90.50%

  • Swift

    7243questions

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

  • Xcode

    4098questions

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

  • API

    1524questions

    APIはApplication Programming Interfaceの略です。APIはプログラムにリクエストされるサービスがどのように動作するかを、デベロッパーが定めたものです。

APIからのレスポンス取得がタイムアウト後になってしまう:Alamofire4 Xcode8 swift3

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 629

tukusa

score 32

使用環境
Xcode8.2.1
swift3.0
Alamofire4

  //APIからデータを取得
  func getData() -> Bool {
    var isComplete = false
    var isError = true
    //APIからデータ取得
    Alamofire.request(ApiConst.Url,
                      parameters: [ ApiConst.Key: ApiConst.Val]
      ).responseJSON {response in
        print("データ取得")
        if response.result.isSuccess {
          isError = false
        } else {
          isError = true
        }
        isComplete = true
    }

    let calendar = Calendar(identifier: .gregorian)
    let startTime = Date()
    var isTimeout = false
    let runLoop = RunLoop.current
    //レスポンスがあるか、タイムリミットが尽きるまで待機する(0.1秒刻み)
    while !isComplete && !isTimeout &&
      runLoop.run(mode: RunLoopMode.defaultRunLoopMode, before: NSDate(timeIntervalSinceNow: 0.1) as Date) {
        //開始時間から現在までの時間のスパンがタイムリミットに達したらタイムアウトフラグを立てる
        let nowTime = Date()
        let span = calendar.dateComponents([.second], from: startTime, to: nowTime).second
        if span! > ApiConst.LimitTime {
          isTimeout = true
          print("タイムアウト")
        }
    }
    print("データ返却")
    if isComplete && !isError {
      print("成功")
      return true
    } else {
      print("失敗")
      return false
    }
  }

上記はプロトコル・デリゲートを使用せず、ちと強引に取得をreturn前に終わらせている例です。

--

  @IBAction func getAPIAction(_ sender: Any) {
    _ = self.getData()
  }


上記の使用方法では、予定通りの動作をしてくれます。

-- Print Log --
データ取得
データ返却
成功

--

  @IBAction func getAPIAction(_ sender: Any) {
    let alert = UIAlertController(title: "", message: "取得する",
                                  preferredStyle: UIAlertControllerStyle.alert)
    let yesAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default,
                                  handler: {(_) -> Void in
                                    _ = self.getData()
                                  })
    alert.addAction(yesAction)
    self.present(alert, animated: true, completion: nil)
  }


しかし、上記の使用方法では、レスポンスの取得が全ての動作後になってしまいます。
-- Print Log --
タイムアウト
データ返却
失敗
データ取得

Dispatch等も調べて試してみたでのすが、どうにもうまくいかず・・・。
どなた様か、コレの解消方法にお気づきになられました方、ご教授お願いします。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 1

checkベストアンサー

+1

非同期通信しているんでしょうから、データ受信後の処理は、

isComplete = true

のところに書かないと。

下記のコードは、1.request-2.requested-3.receivedの順に実行されます。

print("1.request")
Alamofire.request(...).responseJSON {response in
    print("3.received")
}
print("2.requested")

ということでしょうか?

 再チャレンジ

呼び出し元が、受信後の処理をクロージャで渡します。
(Swift2 + 古いAlamofireでのコードなので注意)

//呼び出し元
self.getData { response in
    print("受信後の処理")
}
func getData(completionHandler: Response<AnyObject, NSError> -> Void) -> Bool {
    :
    Alamofire.request(...)
        //渡されたcompletionHandlerをそのまま渡して実行する
        .responseJSON(completionHandler: completionHandler)
    :
    return true
}

ということでしょうか?

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/04/03 17:56

    回答ありがとうございます。
    失礼しました。説明不足でした。

    1つ目のコードは、実際はDataSourceとして結果を返す別クラスでして、
    if response.result.isSuccess {
    isError = false
       実際はココで取得データを保持し、
    } else {
    isError = true
    }

    その後、
    isComplete = true を受け、
    while !isComplete && !isTimeout && runLoop.run
    にて回し続けているループを解除し、リターンで取得したデータを返すという強引な方法を使っています。


    fuzzball様の言われましたように、print("3.received")の位置でプロトコルか何かで値を返せば問題なく動くのですが、、、、
    なんとか1メソッドで値を返せないかなと思い、チャレンジしてしまいました。

    キャンセル

  • 2017/04/03 18:27

    追記してみました。

    キャンセル

  • 2017/04/03 19:01 編集

    このようなソースに再度ご回答ありがとうございます。

    データソース
    func getData(completionHandler: ((Any) -> Void)?) {
    //APIからデータ取得
    Alamofire.request(ApiConst.Url,
    parameters: [ ApiConst.Key: ApiConst.Val]
    ).responseJSON {response in
    if let completion = completionHandler {
    completion(response)
    }
    }
    }

    コントローラー
    let alert = UIAlertController(title: "", message: "取得する",
    preferredStyle: UIAlertControllerStyle.alert)
    let yesAction = UIAlertAction(title: UIString.alertYesAction, style: UIAlertActionStyle.default,
    handler: {(_) -> Void in
    self.getData(completionHandler: { response in
    print(response)
    })
    })
    alert.addAction(yesAction)
    self.present(alert, animated: true, completion: nil)

    実際、上記のコードで動かしてみました。
    こちらは予定通りの動作をしてくれました。

    やはりプロトコルインターフェイスやハンドラー(クロージャ)などで結果を返す(動作させる)のが正道なのですよね。

    C#なんかの let res = await〜の様に、リポジトリで受け取り方を気にせずにデータソースから値だけを取得できれば、、と思ったのですが、
    1リターンで取得結果を返すのは、諦めるべきなのでしょうか。。

    改めて、ご回答ありがとうございました。

    キャンセル

  • 2017/04/03 19:16

    イマイチ質問を理解出来ていないようで申し訳ないです。
    もしかして、同期通信をしたいということでしょうか?

    キャンセル

  • 2017/04/03 19:23

    です。
    実質、同期通信
    レスポンスがあるか、タイムアウトまで待機する様な動作をさせたかったのです。

    回答一文目に「非同期通信しているんでしょうから、」を見落としていました。。。。
    余分にお手数かけてしまいました。
    申し訳ありません。

    キャンセル

  • 2017/04/03 19:30

    「swift semaphore 同期通信」あたりでググってみて下さい。

    キャンセル

  • 2017/04/03 19:49

    ありがとうございます。
    セマフォというものを初めて知りました!

    記事、いくつか読ませて頂きました。

    残念ながらalamofireとは相性が悪いみたいですが(自分と同じループ作戦やってる方が上位に居て、悲しみを覚えました…。)、
    どうにかこちらのほうを使って同期通信を作ってみようと思います。

    ベストアンサーを贈らせていただきます
    ありがとうございました

    キャンセル

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

  • ただいまの回答率 90.50%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

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

  • Swift

    7243questions

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

  • Xcode

    4098questions

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

  • API

    1524questions

    APIはApplication Programming Interfaceの略です。APIはプログラムにリクエストされるサービスがどのように動作するかを、デベロッパーが定めたものです。