41155


以下のような関数を作成して返り値を確認したところ、[]が返ってきました。  
しかし関数を実行すると、関数内のprintの部分ではちゃんと結果が表示されます。

クロージャを用いれば解決するのは調べたところわかるのですが、具体的にこのコードをどう変更すれば良いのかがわかりません。

どなたかわかる方はいらっしゃるでしょうか？

[code]

    import SwiftyJSON
    import Alamofire
    
    public func post_request(postString:String, url:String) -> Array<JSON> {
        var data:Array<JSON> = []
    
        var request = URLRequest(url: URL(string: url)!)
        request.httpMethod = "POST"
        request.httpBody = postString.data(using: .utf8)
    
        Alamofire.request(request as URLRequestConvertible)
            .responseJSON { response in
    
                print(response.request as Any)  // original URL request
                print(response.response as Any) // HTTP URL response
                print(response.data as Any)     // server data
                print(response.result.value as Any)   // result of response serialization
    
                let json = try! JSON(data: response.data!)
                data = json.arrayValue
                print("*************")
                print(data)
                print("*************")
        }
    
        return data
    }
    
[/code]


CC BY-SA 3.0
2018-01-23T09:13:18.763
41152
2018-01-23T11:54:33.090
26153
question
swift alamofire クロージャ
Alamofireでのリクエスト結果を返り値にしたい


類似の質問は、ここ日本語版StackOverflowにもいくつか上がっているはずですが、非同期のメソッドを呼び出す場合、「メソッドの戻り値で結果を返す」と言う方法論を見直す必要があります。

(dokubeko さんもコメントで書かれていますが、あなたのコードの中で、`{response in
...}`の部分は、完了ハンドラとして`responseJSON`メソッドに渡されますが、その **完了ハンドラが実行されるのは通信が完了した後**
です。`return data`は通信が完了する前に実行されるので、`return data`で戻される値は空になります。あちこちに`print(1)`,
`print(2)`...などを置いてみて実行順序を確かめてみられるといいでしょう。)

このような場合によく使われる方法で確実なのは、自分で定義したメソッドも **完了ハンドラパターン**
にしてしまうことです。慣れるまでは少々分かりにくく見えるかもしれませんが、パターンに当てはめるだけなので、慣れてしまえば非常に単純です。

まずは、ご自分のメソッドから戻り値型を削除して引数を1個付け足します。

付け足す最後の引数は受け取りたい結果を引数とするクロージャー型(あなたのコードの場合なら、`(Array<JSON>)->Void`型)にしておきます。

また、元の非同期処理の完了ハンドラの中で、自前の完了ハンドラを呼ぶ(以下のコードで`completion(data)`の部分)ようにします。

[code]

    //`Array<JSON>`を戻り値にするのではなく、`Array<JSON>`をパラメータとして受け取るクロージャー型の引数(自前の完了ハンドラ)を追加する
    public func post_request(postString:String, url:String, completion: (Array<JSON>)->Void) {
        var data:Array<JSON> = []
    
        var request = URLRequest(url: URL(string: url)!)
        request.httpMethod = "POST"
        request.httpBody = postString.data(using: .utf8)
    
        Alamofire.request(request as URLRequestConvertible)
            .responseJSON { response in
    
                print(response.request as Any)  // original URL request
                print(response.response as Any) // HTTP URL response
                print(response.data as Any)     // server data
                print(response.result.value as Any)   // result of response serialization
    
                let json = try! JSON(data: response.data!)
                data = json.arrayValue
                print("*************")
                print(data)
                print("*************")
    
                //元の非同期処理の完了ハンドラの中で自前の完了ハンドラを呼び出す
                completion(data)
        }
    }
    
[/code]

(Swiftのコードではほとんど見ないアンダーライン区切りの識別子を使っている点、配列型の変数に`data`と名付けている点、「私のアプリをクラッシュさせて」演算子(`try!`や後置の`!`)を多用している点等々…は書き直したいところですが、とりあえずそのままにしてあります。)

呼び出す場合には、結果を戻り値として受け取るのではなく、結果を受け取るようなクロージャを引数として渡すことになります。

[code]

    let postString = "..."
    let urlString = "https://..."
    post_request(postString: postString, url: urlString, completion: {(result: Array<JSON>) -> Void in
        print(result)
    })
    
[/code]

上記のコードは末尾クロージャーの省略形を含め、できるだけ省略形で書くと、こんな風にも書けます。

[code]

    let postString = "..."
    let urlString = "https://..."
    post_request(postString: postString, url: urlString) {result in
        print(result)
    }
    
[/code]

元の`responseJSON`メソッドと非常に似通った使い方になっているのがお分かりでしょうか。

最初に書いたように、非同期処理のメソッドをうまく使うためには、発想を少々変えてもらう必要があります。私はSwiftJSONもAlamofireも使ったことがないので、細かい部分で修正が必要かもしれませんが、考え方は上記の形で動かせるようになるはずです。お試しください。


CC BY-SA 3.0
2018-01-23T11:54:33.090
41155
2018-01-23T11:54:33.090
13972
41152
answer