8

I have to send array of dictionaries via POST request. For example:

materials: [[String, String]] = [ [ "material_id": 1, "qty": 10 ], [ "material_id": 2, "qty": 5 ] ] 

Alamofire.request sends the next post data:

materials => array( [0] => array("material_id" => 1), [1] => array("qty" => 10), [2] => array("material_id" => 2), [3] => array("qty" => 5), ) 

I want receive that representation:

materials => array( [0] => array( "material_id" => 1, "qty" => 10 ), [1] => array( "material_id" => 2, "qty" => 5 ), ) 
5
  • what exactly you wants to do? Commented Jan 6, 2015 at 8:41
  • I have added what I want to do Commented Jan 6, 2015 at 8:56
  • embed that with url as array. Commented Jan 6, 2015 at 9:00
  • stackoverflow.com/questions/718429/… try this... Commented Jan 6, 2015 at 9:02
  • Sorry, but I not understand yet how to use it and integrate with Alamofire framework.. Commented Jan 6, 2015 at 10:36

3 Answers 3

9

The problem was in append method. I have coded on PHP 5 years and forgoted that in Swift the indexes not automatically assigned like in PHP. So, my first bugged code was:

func getParameters() -> [[String: AnyObject]] { var result = [[String: AnyObject]]() for mmap in mmaps { let material: [String: AnyObject] = [ "material_id": mmap.material.id, "quantity": mmap.qty ] result.append(material) } return result } 

The answer is hard assign the keys as you need:

func getParameters() -> [String: [String: AnyObject]] { var result = [String: [String: AnyObject]]() let mmaps = self.mmaps.allObjects as [Mmap] for i in 0..<mmaps.count { let mmap = mmaps[i] let material: [String: AnyObject] = [ "material_id": mmap.material.id, "quantity": mmap.qty ] result["\(i)"] = material } return result } 
Sign up to request clarification or add additional context in comments.

Comments

2

A couple of thoughts:

  1. It would be easiest if you sent the response as a dictionary with one key, and it will correctly encode the array within the dictionary:

    let materials = [ "materials": [ [ "material_id": 1, "qty": 10 ], [ "material_id": 2, "qty": 5 ] ] ] 

    You could then just supply that as the parameters of request(), and Alamofire will properly encode that for you.

  2. If you wanted to send an array of dictionaries, an alternative would be to change the web service to accept JSON. You could then encode the JSON yourself (using JSONSerialization or JSONEncoder), set the body of the request, and then send that request.

  3. If you want to send application/x-www-form-urlencoded request with the array of dictionaries, you'd have to encode that yourself. In Swift 3 and later, that might look like:

    func encodeParameters(_ object: Any, prefix: String? = nil) -> String { if let dictionary = object as? [String: Any] { return dictionary.map { key, value -> String in self.encodeParameters(value, prefix: prefix != nil ? "\(prefix!)[\(key)]" : key) }.joined(separator: "&") } else if let array = object as? [Any] { return array.enumerated().map { (index, value) -> String in return self.encodeParameters(value, prefix: prefix != nil ? "\(prefix!)[\(index)]" : "\(index)") }.joined(separator: "&") } else { let escapedValue = "\(object)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed)! return prefix != nil ? "\(prefix!)=\(escapedValue)" : "\(escapedValue)" } } 

    Where

    extension CharacterSet { /// Returns the character set for characters allowed in the individual parameters within a query URL component. /// /// The query component of a URL is the component immediately following a question mark (?). /// For example, in the URL `http://www.example.com/index.php?key1=value1#jumpLink`, the query /// component is `key1=value1`. The individual parameters of that query would be the key `key1` /// and its associated value `value1`. /// /// According to RFC 3986, the set of unreserved characters includes /// /// `ALPHA / DIGIT / "-" / "." / "_" / "~"` /// /// In section 3.4 of the RFC, it further recommends adding `/` and `?` to the list of unescaped characters /// for the sake of compatibility with some erroneous implementations, so this routine also allows those /// to pass unescaped. static var urlQueryValueAllowed: CharacterSet = { let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4 let subDelimitersToEncode = "!$&'()*+,;=" var allowed = CharacterSet.urlQueryAllowed allowed.remove(charactersIn: generalDelimitersToEncode + subDelimitersToEncode) return allowed }() } 

    Obviously, use whatever response method is appropriate for the nature of your server's response (e.g. response vs. responseJSON vs. ...).

    Anyway, the above generates a request body that looks like:

    materials[0][material_id]=1&materials[0][qty]=10&materials[1][material_id]=2&materials[1][qty]=5 

    And this appears to be parsed by servers as you requested in your question.

It's worth noting that this final point illustrates the preparation of an application/x-www-form-urlencoded request with nested dictionary/array structure, as contemplated here. This works on my server run by a major ISP, but I must confess that I haven't seen this convention documented in formal RFCs, so I'd be wary of doing it. I'd personally be inclined to implement this as JSON interface.

For prior versions of Swift, see previous revision of this answer.

2 Comments

Hi Rob, can you please modify it as current version of swift, I tried it but query string is created with optional(" values ").
@Nitesh - Yeah, Swift has handled how it handles implicitly unwrapped optionals. See revised answer.
0

You can make your array as JSON string and post in to server, then parse JSON in server end and from that you can get your desired data, lke this:

NSError *error; NSData *jsonData = [NSJSONSerialization dataWithJSONObject: yourArry options:NSJSONWritingPrettyPrinted error:&error]; NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; 

Hope this helps.. :)

1 Comment

I know that, but I dont want to use JSON. And the server did not accept JSON

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.