69

I wonder if it's possible to directly send an array (not wrapped in a dictionary) in a POST request. Apparently the parameters parameter should get a map of: [String: AnyObject]? But I want to be able to send the following example json:

[
    "06786984572365",
    "06644857247565",
    "06649998782227"
]

9 Answers 9

149

You can just encode the JSON with NSJSONSerialization and then build the NSURLRequest yourself. For example, in Swift 3:

var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")

let values = ["06786984572365", "06644857247565", "06649998782227"]

request.httpBody = try! JSONSerialization.data(withJSONObject: values)

AF.request(request)                               // Or `Alamofire.request(request)` in prior versions of Alamofire
    .responseJSON { response in
        switch response.result {
        case .failure(let error):
            print(error)
            
            if let data = response.data, let responseString = String(data: data, encoding: .utf8) {
                print(responseString)
            }
        case .success(let responseObject):
            print(responseObject)
        }
}

For Swift 2, see previous revision of this answer.

Sign up to request clarification or add additional context in comments.

4 Comments

@Rob I am getting the error like Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around character 0." UserInfo={NSDebugDescription=Invalid value around character 0.} Any idea?
@NikunjJadav The response is not JSON. You might want to look at response.data (see revised answer above) to see what precisely the web service returned. Often there's a narrative description regarding the nature of the problem.
@Rob great answer thank you so much, but in my case what if the method is .get instead of .post?
With GET request, there's no body and parameters are supplied in x-www-form-urlencoded key-value pairs. The most logical solution is to supply a key-value pair where the value is an array: AF.request("https://robertmryan.com/privatetest/form/results.php", method: .get, parameters: ["stooges": ["moe", "larry", "curly"]]). You would retrieve the array of names using the key, e.g. $_GET['stooges'] or $_REQUEST['stooges'].
68

For swift 3 and Alamofire 4 I use the following ParametersEncoding and Array extension:

import Foundation
import Alamofire

private let arrayParametersKey = "arrayParametersKey"

/// Extenstion that allows an array be sent as a request parameters
extension Array {
    /// Convert the receiver array to a `Parameters` object. 
    func asParameters() -> Parameters {
        return [arrayParametersKey: self]
    }
}


/// Convert the parameters into a json array, and it is added as the request body. 
/// The array must be sent as parameters using its `asParameters` method.
public struct ArrayEncoding: ParameterEncoding {

    /// The options for writing the parameters as JSON data.
    public let options: JSONSerialization.WritingOptions


    /// Creates a new instance of the encoding using the given options
    ///
    /// - parameter options: The options used to encode the json. Default is `[]`
    ///
    /// - returns: The new instance
    public init(options: JSONSerialization.WritingOptions = []) {
        self.options = options
    }

    public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
        var urlRequest = try urlRequest.asURLRequest()

        guard let parameters = parameters,
            let array = parameters[arrayParametersKey] else {
                return urlRequest
        }

        do {
            let data = try JSONSerialization.data(withJSONObject: array, options: options)

            if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
                urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
            }

            urlRequest.httpBody = data

        } catch {
            throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error))
        }

        return urlRequest
    }
}

Basically, it converts the array to a Dictionary in order to be accepted as Parameters argument, and then it takes back the array from the dictionary, convert it to JSON Data and adds it as the request body.

Once you have it, you can create request this way:

let values = ["06786984572365", "06644857247565", "06649998782227"]
Alamofire.request(url,
                  method: .post,
                  parameters: values.asParameters(),
                  encoding: ArrayEncoding())

Comments

3

Here is an example of encoding an Array of type Thing to JSON, using a router, and Ogra to do the JSON encoding:

import Foundation
import Alamofire
import Orga

class Thing {
    ...
}

enum Router: URLRequestConvertible {
    static let baseURLString = "http://www.example.com"

    case UploadThings([Thing])

    private var method: Alamofire.Method {
        switch self {
        case .UploadThings:
            return .POST
        }
    }

    private var path: String {
        switch self {
        case .UploadThings:
            return "upload/things"
        }
    }

    var URLRequest: NSMutableURLRequest {
        let r = NSMutableURLRequest(URL: NSURL(string: Router.baseURLString)!.URLByAppendingPathComponent(path))
        r.HTTPMethod = method.rawValue

        switch self {
        case .UploadThings(let things):
            let custom: (URLRequestConvertible, [String:AnyObject]?) -> (NSMutableURLRequest, NSError?) = {
                (convertible, parameters) in
                var mutableRequest = convertible.URLRequest.copy() as! NSMutableURLRequest
                do {
                    let jsonObject = things.encode().JSONObject()
                    let data = try NSJSONSerialization.dataWithJSONObject(jsonObject, options: NSJSONWritingOptions.PrettyPrinted)
                    mutableRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
                    mutableRequest.HTTPBody = data
                    return (mutableRequest, nil)
                } catch let error as NSError {
                    return (mutableRequest, error)
                }
            }
            return ParameterEncoding.Custom(custom).encode(r, parameters: nil).0
        default:
            return r
        }
    }
}

Comments

3

Swift 2.0
This code below post object array.This code is tested on swift 2.0

func POST(RequestURL: String,postData:[AnyObject]?,successHandler: (String) -> (),failureHandler: (String) -> ()) -> () {

        print("POST : \(RequestURL)")

        let request = NSMutableURLRequest(URL: NSURL(string:RequestURL)!)
        request.HTTPMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")

        var error: NSError?
        do {
             request.HTTPBody  = try NSJSONSerialization.dataWithJSONObject(postData!, options:[])


        } catch {
            print("JSON serialization failed:  \(error)")
        }

        Alamofire.request(request)
            .responseString{ response in
                switch response.result {
                case .Success:
                    print(response.response?.statusCode)
                    print(response.description)
                    if response.response?.statusCode == 200 {
                        successHandler(response.result.value!)
                    }else{
                        failureHandler("\(response.description)")
                    }

                case .Failure(let error):
                    failureHandler("\(error)")
                }
        }

    }

2 Comments

i am getting error while calling a method. POST(baseUrl+WS_LOGIN, postData: jsonObject as a, successHandler: <#T##(String) -> ()#>, failureHandler: <#T##(String) -> ()#>) what should i use in handler
Argument type 'NSMutableURLRequest' does not conform to expected type 'URLRequestConvertible'
0

@manueGE 's answer is right. I have a similar approach according to alamofire github's instruction: `

struct JSONDocumentArrayEncoding: ParameterEncoding {
    private let array: [Any]
    init(array:[Any]) {
        self.array = array
    }
    func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
        var urlRequest = urlRequest.urlRequest

        let data = try JSONSerialization.data(withJSONObject: array, options: [])

        if urlRequest!.value(forHTTPHeaderField: "Content-Type") == nil {
            urlRequest!.setValue("application/json", forHTTPHeaderField: "Content-Type")
        }

        urlRequest!.httpBody = data

        return urlRequest!
    }
}

` Then call this by customize a request instead of using the default one with parameter. Basically discard the parameter, since it is a dictionary.

let headers = getHeaders()
    var urlRequest = URLRequest(url: URL(string: (ServerURL + Api))!)
    urlRequest.httpMethod = "post"
    urlRequest.allHTTPHeaderFields = headers
    let jsonArrayencoding = JSONDocumentArrayEncoding(array: documents)

    let jsonAryEncodedRequest = try? jsonArrayencoding.encode(urlRequest, with: nil)

    request = customAlamofireManager.request(jsonAryEncodedRequest!)
    request?.validate{request, response, data in
        return .success
        }
        .responseJSON { /*[unowned self] */(response) -> Void in
            ...
    }

Also, the way to handle error in data is very helpful.

Comments

0
let url = try Router.baseURL.asURL()

// Make Request
var urlRequest = URLRequest(url: url.appendingPathComponent(path))
urlRequest.httpMethod = "post"

// let dictArray: [[String: Any]] = []
urlRequest = try! JSONEncoding.default.encode(urlRequest, withJSONObject: dictArray)

Something I do in my project to upload a JSON array

Comments

0
  func placeOrderApi(getUserId:String,getDateId:String,getTimeID:String,getAddressId:String,getCoupon:String)
            {
                let data = try! JSONSerialization.data(withJSONObject: self.arrOfServices, options: [])
                let jsonBatch : String = String(data: data, encoding: .utf8)!
                
                //try! JSONSerialization.data(withJSONObject: values)
               let params = [
                   "user_id":getUserId,
                   "time_id":getTimeID,
                   "date_id":getDateId,
                   "address_id":getAddressId,
                   "services":jsonBatch,
                   "payment_mode":paymentVia,
                   "coupon":getCoupon
                ] as [String : Any]
               
               print(params)
               self.objHudShow()
                
                Alamofire.request(BaseViewController.API_URL + "place_order", method: .post, parameters: params, encoding: JSONEncoding.default)
                    .responseJSON { response in
                        debugPrint(response)
               
                    
                    switch response.result {
                        
                    case .success (let data):
                        print(data)
                                
                    self.objHudHide()
                    if response.result.value != nil
                    {
                       
                        let json : JSON = JSON(response.result.value!)
                       
                         if json["status"] == true
                         {
                           
                         }
                          else
                         {
                            self.view.makeToast(NSLocalizedString(json["msg"].string ?? "", comment: ""), duration: 3.0, position: .bottom)
                          }
                     
                             
                    }
                               
                        break
                                
                    case .failure:
                               self.objHudHide()
                               
                               print("Error in upload:)")
                                break
                            }
                        }
                    }

Comments

-1

There are 2 approach to send send JSON content as parameter.

  1. You can send json as string and your web service will parse it on server.

     d["completionDetail"] = "[{"YearOfCompletion":"14/03/2017","Completed":true}]"
    
  2. You can pass each value within your json (YearOfCompletion and Completed) in form of sequential array. And your web service will insert that data in same sequence. Syntax for this will look a like

    d["YearOfCompletion[0]"] = "1998"  
    d["YearOfCompletion[1]"] = "1997"  
    d["YearOfCompletion[2]"] = "1996"  
    
    d["Completed[0]"] = "true"  
    d["Completed[1]"] = "false"  
    d["Completed[2]"] = "true"  
    

I have been using following web service call function with dictionary, to trigger Alamofire request Swift3.0.

func wsDataRequest(url:String, parameters:Dictionary<String, Any>) {
    debugPrint("Request:", url, parameters as NSDictionary, separator: "\n")

    //check for internete collection, if not availabale, don;t move forword
    if Rechability.connectedToNetwork() == false {SVProgressHUD.showError(withStatus: NSLocalizedString("No Network available! Please check your connection and try again later.", comment: "")); return}

    //
    self.request = Alamofire.request(url, method: .post, parameters: parameters)
    if let request = self.request as? DataRequest {
        request.responseString { response in
            var serializedData : Any? = nil
            var message = NSLocalizedString("Success!", comment: "")//MUST BE CHANGED TO RELEVANT RESPONSES

            //check content availability and produce serializable response
            if response.result.isSuccess == true {
                do {
                    serializedData = try JSONSerialization.jsonObject(with: response.data!, options: JSONSerialization.ReadingOptions.allowFragments)
                    //print(serializedData as! NSDictionary)
                    //debugPrint(message, "Response Dictionary:", serializedData ?? "Data could not be serialized", separator: "\n")
                }catch{
                    message = NSLocalizedString("Webservice Response error!", comment: "")
                    var string = String.init(data: response.data!, encoding: .utf8) as String!

                    //TO check when html coms as prefix of JSON, this is hack mush be fixed on web end. 
                    do {
                        if let index = string?.characters.index(of: "{") {
                            if let s = string?.substring(from: index) {
                                if let data = s.data(using: String.Encoding.utf8) {
                                    serializedData = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments)
                                    debugPrint(message, "Courtesy SUME:", serializedData ?? "Data could not be serialized", separator: "\n")
                                }
                            }
                        }
                    }catch{debugPrint(message, error.localizedDescription, "Respone String:", string ?? "No respone value.", separator: "\n")}

                    //let index: Int = text.distance(from: text.startIndex, to: range.lowerBound)
                    debugPrint(message, error.localizedDescription, "Respone String:", string ?? "No respone value.", separator: "\n")
                }

                //call finised response in all cases
                self.delegate?.finished(succes: response.result.isSuccess, and: serializedData, message: message)
            }else{
                if self.retryCounter < 1 {//this happens really frequntly so in that case this fn being called again as a retry
                    self.wsDataRequest(url: url, parameters: parameters)
                }else{
                    message = response.error?.localizedDescription ?? (NSLocalizedString("No network", comment: "")+"!")
                    SVProgressHUD.showError(withStatus: message);//this will show errror and hide Hud
                    debugPrint(message)

                    //call finised response in all cases
                    self.delay(2.0, closure: {self.delegate?.finished(succes: response.result.isSuccess, and: serializedData, message:message)})
                }
                self.retryCounter += 1
            }
        }
    }
}

Comments

-11

I think based on Alamofire documentation you can write the code as following:

let values = ["06786984572365", "06644857247565", "06649998782227"]

Alamofire.request(.POST, url, parameters: values, encoding:.JSON)
    .authenticate(user: userid, password: password)
    .responseJSON { (request, response, responseObject, error) in
        // do whatever you want here

        if responseObject == nil {
            println(error)
        } else {
            println(responseObject)
        }
}

2 Comments

No, the parameters is a [String: AnyObject]?. You cannot pass it an array. It must be a dictionary. Hence the question.
This answer would be true, if you pass a dictionary object rather than an array. encoding parameter will handle the rest.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.