-
-
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 | |
} | |
} |
@nevstad Thank you for your snippet.
As of Swift 2.2 var
parameters are marked as deprecated. I've updated your snippet to fix the deprecation warning. There might be an easier way though.
extension NSURL {
var queryItems: [String: String]? {
var params = [String: String]()
return NSURLComponents(URL: self, resolvingAgainstBaseURL: false)?
.queryItems?
.reduce([:], combine: { (_, item) -> [String: String] in
params[item.name] = item.value
return params
})
}
}
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
}
}
Works with Swift 3. ;)
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
@nevstad, that's pretty perfect. thanks kindly!