How to Build a Url with Query Parameters Containing Multiple Values for the Same Key in Swift

How can I build a URL with query parameters containing multiple values for the same key in Swift?

All you need is URLComponents (or NSURLComponents in Obj-C). The basic idea is to create a bunch of query items for your id's. Here's code you can paste into a playground:

import Foundation
import XCPlayground

let queryItems = [URLQueryItem(name: "id", value: "1"), URLQueryItem(name: "id", value: "2")]
var urlComps = URLComponents(string: "www.apple.com/help")!
urlComps.queryItems = queryItems
let result = urlComps.url!
print(result)

You should see an output of

www.apple.com/help?id=1&id=2

How can i build a URL with same key multiple times?

I managed to solve this issue:

I just created objects in my NSDictionaray like so:

    [self.myDictionary setObject:[NSSet setWithArray:self.myArray] forKey:@"myKeyNeeded];

The array has NSString objects in it and this seems to work perfectly.
I used array instead of NSMutableSet due needing to remove objects easily enough from the NSDictionary.

Alamofire Passing Parameter With Common Keys and Multiple Values?

You don't need a custom encoding for this format.

You can send parameters encoded like this:

category_name[]=rock&category_name[]=paper

By using URLEncoding (which you're already doing) and including the multiple values that should have the same key in an array:

let parameters: Parameters = ["category_name": ["rock", "paper"]]

It'll add the [] after category_name for you, so don't include it when you declare the parameters.

Best way to parse URL string to get values for keys?

edit (June 2018): this answer is better. Apple added NSURLComponents in iOS 7.

I would create a dictionary, get an array of the key/value pairs with

NSMutableDictionary *queryStringDictionary = [[NSMutableDictionary alloc] init];
NSArray *urlComponents = [urlString componentsSeparatedByString:@"&"];

Then populate the dictionary :

for (NSString *keyValuePair in urlComponents)
{
NSArray *pairComponents = [keyValuePair componentsSeparatedByString:@"="];
NSString *key = [[pairComponents firstObject] stringByRemovingPercentEncoding];
NSString *value = [[pairComponents lastObject] stringByRemovingPercentEncoding];

[queryStringDictionary setObject:value forKey:key];
}

You can then query with

[queryStringDictionary objectForKey:@"ad_eurl"];

This is untested, and you should probably do some more error tests.

Authoritative position of duplicate HTTP GET query keys

The situation seems to have changed since this question was asked and the accepted answer was written 12 years ago. I believe we now have an authoritative source: The WHATWG URL Standard describes the process of extracting and parsing a query string in detail in section 6.2 (https://url.spec.whatwg.org/#interface-urlsearchparams) and section 5.1 on x-www-form-urlencoded parsing (https://url.spec.whatwg.org/#urlencoded-parsing). The parsing output is "an initially empty list of name-value tuples where both name and value hold a string", where a list is defined as a finite ordered sequence, and the key-value pairs are added to this list in the order they appear in the URL. At first there is no mention of repeated keys, but some methods on the URLSearchParams class in section 6.2 (https://url.spec.whatwg.org/#interface-urlsearchparams) set clear expectations on ordering: "The getAll(name) method steps are to return the values of all name-value pairs whose name is name... in list order"; The sort() method specifies that "The relative order between name-value pairs with equal names must be preserved." (Emphasis mine). Examining the Github issue referenced in the commit where the sort method was added, we see that the original proposal was to sort on values where keys were identical, but this was changed: "The reason for the default sort not affecting the value order is that ordering of the values can be significant. We should not assume that it's ok to move the order of the values around." (https://github.com/whatwg/url/issues/26#issuecomment-271600764)

Decoding multiple types for the same key

Object Structure

Try and give the following a go:

// MARK: - Config
struct Config: Codable {
let ids: IDS
let names: Names
}

enum IDS: Codable {
case bool(Bool)
case idArray([ID])

init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let x = try? container.decode(Bool.self) {
self = .bool(x)
return
}
if let x = try? container.decode([ID].self) {
self = .idArray(x)
return
}
throw DecodingError.typeMismatch(IDS.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for IDS"))
}

func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .bool(let x):
try container.encode(x)
case .idArray(let x):
try container.encode(x)
}
}
}

// MARK: - ID
struct ID: Codable {
let id: String
}

enum Names: Codable {
case bool(Bool)
case nameArray([Name])

init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let x = try? container.decode(Bool.self) {
self = .bool(x)
return
}
if let x = try? container.decode([Name].self) {
self = .nameArray(x)
return
}
throw DecodingError.typeMismatch(Names.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Names"))
}

func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .bool(let x):
try container.encode(x)
case .nameArray(let x):
try container.encode(x)
}
}
}

// MARK: - Name
struct Name: Codable {
let name: String
}

How I Did It

I generated this using QuickType which can convert JSON into Swift objects. In order to achieve the following above, I entered the JSON as a list of Config options and then just removed the parts I didn't need from the given output...

[
{
"config": {
"ids": false,
"names": [
{
"name": "value1"
},
{
"name": "value2"
}
]
}
},
{
"config": {
"ids": [
{
"id": "id1"
},
{
"id": "id2"
}
],
"names": false
}
}
]


Related Topics



Leave a reply



Submit