Foundation’s URLQueryItem
is just a stringly-typed key-value pair. You create one with a name and value:
public init(name: String, value: String?)
Since Swift supports literal initialization, you’d think you could use a dictionary to set up a [URLQueryItem]
array, right? Well, yes and no.
You can’t just conform Array where Element == URLQueryItem
to ExpressibleByDictionaryLiteral
. Array extensions with constraints cannot have inheritance clauses. There are several ways around this limitation.
First (and best), you can just map an initializer across a dictionary literal:
let result = ["key1": "value1", "key2": "value2", "key3": "value3"] .map({ URLQueryItem(name: $0.key, value: $0.value) })
Second, you could make URLQueryItem
itself dictionary initializable, but that starts to get ugly:
extension URLQueryItem: ExpressibleByDictionaryLiteral { public typealias Key = String public typealias Value = String public init(dictionaryLiteral elements: (String, String)...) { guard elements.count == 1 else { fatalError("URLQueryItem requires single key-value pair") } self.init(name: elements.last!.0, value: elements.last!.1) } } let uq: URLQueryItem = ["key": "value"] // okay let uqs: [URLQueryItem] = [["key1": "value1"], ["key2": "value2"], ["key3": "value3"]] // bleh
Third, you could use some kind of intermediate type to produce a URL query item array using Swift shortcuts. For example, you can set up a struct that builds the query item array and then pull from there:
struct URLQueryItems: ExpressibleByDictionaryLiteral { public typealias Key = String public typealias Value = String let items: [URLQueryItem] public init(dictionaryLiteral elements: (String, String)...) { items = elements.map({ URLQueryItem(name: $0.0, value: $0.1) }) } } let uqis: URLQueryItems = ["key1": "value1", "key2": "value2", "key3": "value3"] uqis.items
But again, it’s really not an improvement on using a mapped dictionary.
In the best of all worlds, which doesn’t exist, you’d be able to do something like this, but I don’t think there’s a way to accomplish this in modern Swift. Solution 2 is about as close as you get.
myRequest.queryItems = ["key1": "value1", "key2": "value2", "key3": "value3"]
Is there something I’m overlooking? If so, let me know. Drop a comment, mail, or tweet. Thanks.
Update:
You could build on option 3 by creating an init on Dictionary from the intermediate type… pic.twitter.com/mBAMLKPUX2
— Airspeed Velocity (@AirspeedSwift) June 9, 2017
and
My best shot: https://t.co/FUni8pn5I7
— Brent Royal-Gordon (@brentdax) June 9, 2017
Comments are closed.