Last active
March 19, 2020 07:12
-
-
Save jackreichert/81a7ce9d0cefd5d1780f to your computer and use it in GitHub Desktop.
This is a swift extension for NSURL so you can parse the query string and get back a dictionary of the variables.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} | |
} |
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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).
Used like this (it returned a
[String : String]
array):