-
-
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 | |
} | |
} |
@koenput Beware, that if the fragment is empty, then keyValue
is an array of a single empty string, and gets through as [String]?
, gets splitted with pair.componentsSeparatedByString("=")
resulting in a brutal crash when indexed blindly as having two elements keyValue[0]
and keyValue[1]
. Should probably add an extra nil
check.
Works beautifully this way (maybe not too pretty though):
// Returns a dict of a decoded query string, if present in the URL fragment.
extension NSURL {
var fragments: [String: String] {
var results = [String: String]()
if let pairs = self.fragment?.componentsSeparatedByString("&") where pairs.count > 0 {
for pair: String in pairs {
if let keyValue = pair.componentsSeparatedByString("=") as [String]? {
if(keyValue.count > 1) {
results.updateValue(keyValue[1], forKey: keyValue[0])
}
}
}
}
return results
}
}
This is really what NSURLComponents
is for. If you still feel the need to have it in [String: String]
you could do something like:
extension NSURL {
var queryItems: [String: String]? {
return NSURLComponents(URL: self, resolvingAgainstBaseURL: false)?
.queryItems?
.reduce([:], combine: { (var params: [String: String], item) -> [String: String] in
params[item.name] = item.value
return params
})
}
}
@nevstad, that's pretty perfect. thanks kindly!
@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
I use it to parse the
fragment
of aNSURL
.