Convert dictionary to query string in swift?

Viewed 17116

I have a dictionary as [String:Any].Now i want to convert this dictionary keys & value as key=value&key=value.I have created below extension to work but it didn't work for me.

extension Dictionary {

    var queryString: String? {
        var output: String = ""
        for (key,value) in self {
            output +=  "\(key)=\(value)\(&)"
        }
        return output
    }
}
12 Answers

Another Swift-esque approach:

let params = [
    "id": 2,
    "name": "Test"
]

let urlParams = params.flatMap({ (key, value) -> String in
    return "\(key)=\(value)"
}).joined(separator: "&")

Same as @KKRocks with update for swift 4.1.2

func queryItems(dictionary: [String:Any]) -> String {
    var components = URLComponents()
    print(components.url!)
    components.queryItems = dictionary.map {
        URLQueryItem(name: $0, value: String(describing: $1))
    }
   return (components.url?.absoluteString)!
}

Compact version of @luckyShubhra's answer

Swift 5.0

extension Dictionary {
    var queryString: String {
        var output: String = ""
        forEach({ output += "\($0.key)=\($0.value)&" })
        output = String(output.dropLast())
        return output
    }
}

Usage

let populatedDictionary = ["key1": "value1", "key2": "value2"]
let urlQuery = populatedDictionary.queryString
print(urlQuery)
import Foundation

    extension URL {
        var queryItemsDictionary: [String: String] {
            var queryItemsDictionary = [String: String]()

            // we replace the "+" to space and then encode space to "%20" otherwise after creating URLComponents object
            // it's not possible to distinguish the real percent from the space in the original URL
            let plusEncodedString = self.absoluteString.replacingOccurrences(of: "+", with: "%20")

            if let queryItems = URLComponents(string: plusEncodedString)?.queryItems {
                queryItems.forEach { queryItemsDictionary[$0.name] = $0.value }
            }
            return queryItemsDictionary
        }
    }

That extension will allow you to parse URL where you have both encoded + sign and space with plus, for example:

https://stackoverflow.com/?q=First+question&email=mail%2B10@mail.com

That extension will parse "q" as "First question" and "email" as "mail+10@mail.com"

protocol ParametersConvertible {
    func asParameters() -> [String:Any] 
}

protocol QueryStringConvertible {
    func asQuery() -> String
}

extension QueryStringConvertible where Self: ParametersConvertible {
    func asQuery() -> String {
        var queries: [URLQueryItem] = []
        for (key, value) in self.asParameters() {
            queries.append(.init(name: key, value: "\(value)"))
        }

        guard var components = URLComponents(string: "") else {
            return ""
        }

        components.queryItems = queries
        return components.percentEncodedQuery ?? ""
    }
}

Add this function to your controller

func getQueryString(params : [String : Any])-> String{

        let urlParams = params.compactMap({ (key, value) -> String in
            return "\(key)=\(value)"
        }).joined(separator: "&")
        var urlString = "?" + urlParams
        if let url = urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed){
            urlString = url
        }
          return urlString
    }

Example

self.getQueryString(params: ["name" : "deep ios developer" , "age" :22])

For anyone that wants the "short, short" version.

Condensed using map, you don't need the forEach or for loops. Also protocol constrained for type enforcement on the dictionary.

extension Dictionary where Key : StringProtocol, Value : StringProtocol {
    var queryString: String {
        self.map { "\($0)=\($1)" }.joined(separator: "&")
    }
}

I wrote this helper fn, which I thought was clean:

private static func queryStringParamsToString(_ dictionary: [String: Any]) -> String {
    return dictionary
        .map({(key, value) in "\(key)=\(value)"})
        .joined(separator: "&")
        .addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)!

}
Related