-
-
Save jackreichert/81a7ce9d0cefd5d1780f to your computer and use it in GitHub Desktop.
extension NSURL { | |
func getKeyVals() -> Dictionary<String, String>? { | |
var results = [String:String]() | |
var keyValues = self.query?.componentsSeparatedByString("&") | |
if keyValues?.count > 0 { | |
for pair in keyValues! { | |
let kv = pair.componentsSeparatedByString("=") | |
if kv.count > 1 { | |
results.updateValue(kv[1], forKey: kv[0]) | |
} | |
} | |
} | |
return results | |
} | |
} |
I tried the above solution by @mihai8804858 with Swift 3 / Xcode 8 but I couldn't get it to work. The "+" in the .reduce
line of the URL extension produced a compile error: "ambiguous reference to member +". Mihai, I would still love to try out your solution - any chance you can clarify what is going on in thereduce line?
I was able to get @nevstad's NSURL extension working with Swift 3 so wanted to share that. It worked perfectly for my purpose (parsing a universal deep link URL).
extension NSURL {
var queryItems: [String: String]? {
var params = [String: String]()
return NSURLComponents(url: self as URL, resolvingAgainstBaseURL: false)?
.queryItems?
.reduce([:], { (_, item) -> [String: String] in
params[item.name] = item.value
return params
})
}
}
Used like this (it returned a [String : String]
array):
let url = NSURL(string: myURLString)
if let queryString = url?.queryItems {
print(queryString)
for (index, item) in queryString.enumerated() {
print(item.key)
print(item.value)
}
}
extension URL {
var queryItems: [String: String]? {
return URLComponents(url: self, resolvingAgainstBaseURL: false)?
.queryItems?
.flatMap { $0.dictionaryRepresentation }
.reduce([:], +)
}
}
extension URLQueryItem {
var dictionaryRepresentation: [String: String]? {
if let value = value {
return [name: value]
}
return nil
}
}
func +<Key, Value> (lhs: [Key: Value], rhs: [Key: Value]) -> [Key: Value] {
var result = lhs
rhs.forEach{ result[$0] = $1 }
return result
}
This works great for Swift 3
Cleaned up Swift 3 version without operator overloading:
extension URL {
public var queryItems: [String: String] {
var params = [String: String]()
return URLComponents(url: self, resolvingAgainstBaseURL: false)?
.queryItems?
.reduce([:], { (_, item) -> [String: String] in
params[item.name] = item.value
return params
}) ?? [:]
}
}
I don't like that much using reduce
for side effects, this also works and is more readable IMO:
var params = [String: String]()
queryItems?.forEach { item in
params[item.name] = item.value
}
return params
I also would put, for consistency, this in an extension of URLComponents
(instead of directly in URL
), which contains accessors for all the other parts of the URL.
@i-schuetz your solution can lose information. a perfectly cromulent query might look like ?name=Alice&name=Bob&name=Ted&name=Carol
. If you ran your fragment on that queryItem list params["name"]
would only return Carol
Here's an extension to URL that works with Swift 4.2
extension URL {
private func splitQuery(_ query: String) -> [String: [String]] {
return query.components(separatedBy: "&").map { $0.components(separatedBy: "=") }.reduce(into: [String: [String]]()) { result, element in
guard !element.isEmpty,
let key = element[0].removingPercentEncoding,
let value = element.count >= 2 ? element[1].removingPercentEncoding : "" else { return }
var values = result[key, default: [String]()]
values.append(value)
result[key] = values
}
}
var fragmentItems: [String: [String]] {
guard let fragment = self.fragment else {
return [:]
}
return splitQuery(fragment)
}
var queryItems: [String: [String]] {
guard let query = self.query else {
return [:]
}
return splitQuery(query)
}
}
// queryItems has the same behavior as fragmentItems
let test = URL(string: "http://example.com#name=Alice%20Barker&name=Bob&job=developer")!
test.fragmentItems // ["name": ["Alice Barker", "Bob"], "job": ["developer"]]
test.fragmentItems["name"] // Optional(["Alice Barker", "Bob"])
let test2 = URL(string: "http://example.com#name")!
test2.fragmentItems // ["name": [""]]
test2.fragmentItems["name"] // Optional([""])
let test3 = URL(string: "http://example.com#")!
test3.fragmentItems // ["": [""]]
test3.fragmentItems["name"] // nil
let test4 = URL(string: "http://example.com")!
test4.fragmentItems // [:]
test4.fragmentItems["name"] // nil
Works with Swift 3. ;)