AFNetworkingのAFHTTPSessionManagerを使っていてBodyに素のXMLを入れてPOSTしたかったのだが、そのやり方がなかなか見当たらずソースコードを読んで解決したのでメモしておく。

ざっくり言うとAFHTTPSessionManagerでのPOSTはこんな感じに行う。

let manager = AFHTTPSessionManager()
manager.POST(url, parameters: params, success: { (task, response) -> Void in
    // success
}) { (task, error) -> Void in
    // failure
}

POSTするパラメータがあれば、上のメソッドのparametersのところにdictionaryとかで渡せばいいはずだが(確か)、今回やりたいのはBodyにXMLのテキストを入れるということだった。

まず気軽にparametersにXML文字列を直接渡してみようなどとやってみたら失敗した。

let manager = AFHTTPSessionManager()
let xmlstr = "<?xml ..."
manager.POST(url, parameters: xmlstr, success: { (task, response) -> Void in
    // success
}) { (task, error) -> Void in
    // failure
}

これでできたリクエストを見てみるとBodyにはこんなのが入っていた。

(null)=%3C%3Fxml%20...

どうやら名前が空で値がURLエンコード済文字列のパラメータをBodyとして設定してしまっているらしい。

ソースコードを読むとそれがデフォルトの動作だということがわかった。

// AFURLRequestSerialization.m
switch (self.queryStringSerializationStyle) {
    case AFHTTPRequestQueryStringDefaultStyle:
        query = AFQueryStringFromParametersWithEncoding(parameters, self.stringEncoding);
        break;
}

じゃあデフォルトじゃない動作は、というと上のコードの近くにあった。

// AFURLRequestSerialization.m
if (self.queryStringSerialization) {
    NSError *serializationError;
    query = self.queryStringSerialization(request, parameters, &serializationError);

    if (serializationError) {
        if (error) {
            *error = serializationError;
        }

        return nil;
    }
}

self.queryStringSerializationはBlockで、このブロックを設定してparameterのシリアライズをカスタマイズできるよ、ということのようだった。

つまり文字列をそのままBodyに入れたい場合は、

let manager = AFHTTPSessionManager()
let xmlstr = "<?xml ..."

manager.requestSerializer.setQueryStringSerializationWithBlock { (request, params, error) -> String! in
    return params as! String
}

manager.POST(url, parameters: xmlstr, success: { (task, response) -> Void in
    // success
}) { (task, error) -> Void in
    // failure
}

manager.requestSerializer.setQueryStringSerializationWithBlockでparameterをそのまま文字列と返してあげればよかった。

これで解決できた。良かった。