Skip to content

Instantly share code, notes, and snippets.

@jackreichert
Last active March 19, 2020 07:12
Show Gist options
  • Save jackreichert/81a7ce9d0cefd5d1780f to your computer and use it in GitHub Desktop.
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.
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
}
}
@scalessec
Copy link

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
            }) ?? [:]
    }  
}

@ivnsch
Copy link

ivnsch commented Aug 21, 2017

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.

@tncowart
Copy link

tncowart commented Jan 18, 2019

@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