4

I need to pass simple dictionary:

var dictionary: [AnyHashable: Any] = [
    "a": 1,
    "b": "title",
    "c": ["a", "b"],
    "d": ["a": 1, "b": ["a", "b"]]
]

to URL:

let url = URL(string: "http://example.com")!

How can I do this?

And from the other way, how to access parameters from URL?

The output in imessage:

enter image description here

Bartłomiej Semańczyk
  • 59,234
  • 49
  • 233
  • 358

2 Answers2

15

Based on your parameter requirements I don't know if this is what you are looking for but...

If you would like to append and examine URL parameters you can use NSURLComponents

In your case you could get away with something like this:

var dictionary: [AnyHashable: Any] = [
    "a": 1,
    "b": "title",
    "c": ["a", "b"],
    "d": ["a": 1, "b": ["a", "b"]]
]

let url = URL(string: "http://example.com")!

var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false)

let queryItems = dictionary.map{
    return URLQueryItem(name: "\($0)", value: "\($1)")
}

urlComponents?.queryItems = queryItems

print(urlComponents?.url) //gives me Optional(http://example.com?b=title&a=1&d=%5B%22b%22:%20%5B%22a%22,%20%22b%22%5D,%20%22a%22:%201%5D&c=%5B%22a%22,%20%22b%22%5D)

The interesting parts are the URLComponents itself, which "explodes" the URL you give it into parts that you can then examine.

Then, to add URL query parameters you use the URLQueryItem class and give an array of those URLQueryItem items to your URLComponents instance. In this quick example I just map from your dictionary to an array of URLQueryItems but you can probably think of something a bit more solid :)

URLComponents can of course also be used the other way around...I mean, if you have an URL with parameters you can create an instance of URLComponents and then query the queryItems.

Hope that helps you.

pbodsk
  • 6,787
  • 3
  • 21
  • 51
  • Is there any library in cocoapods for this?;) – Bartłomiej Semańczyk Mar 22 '17 at 07:43
  • @BartłomiejSemańczyk not that I am aware of :) So you get the chance to create your own function or - if you're up for it - an extension to `URL` for instance...how cool is that ;) – pbodsk Mar 22 '17 at 07:49
  • I will check it. Why did you say I need sth bit more solid? Will your solution work for a more conplex dictionaries? – Bartłomiej Semańczyk Mar 22 '17 at 08:30
  • @BartłomiejSemańczyk I'm thinking that just trying to convert whatever is in your dictionary into a `String` by doing `"\($1)"` could go wrong in some odd cases, for instance if the dictionary was more complex maybe. I mean, I'm taking the easy road here and just relying on the built in "convert to String" method...maybe that produces weird results sometimes. But...I dunno, this is purely trying to foresee potential problems, and hey, maybe it just works :) Good luck at least :) – pbodsk Mar 22 '17 at 08:58
  • and what about to convert a whole dictionary to base64 encoding? and such data convert as one parameter... – Bartłomiej Semańczyk Mar 22 '17 at 09:00
  • Would you add that as a URL parameter? I mean....like so: `http://yoururl.com?verylongvalue=somethingridiculouslylonghere`. Isn't there an upper limit as to how long a URL can be? – pbodsk Mar 22 '17 at 09:04
  • So there is no way to pass a huge dictionary because of a limit? – Bartłomiej Semańczyk Mar 22 '17 at 09:44
  • It depends :) Some browsers have an upper limit to the number of characters you can pass in the URL string (http://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers). I'm assuming you'd like to do a GET request since you are using URL parameters. If that is the case, then it seems doable to pass a body to a GET request (http://stackoverflow.com/questions/978061/http-get-with-request-body). Or...maybe you can do a POST request instead where you are certain that you can pass a large amount of data in the body. – pbodsk Mar 22 '17 at 09:49
  • what if value is any of following characters: `()\/% "`? – Bartłomiej Semańczyk Mar 22 '17 at 13:13
  • seems to work :) You could try pasting the code into a playground and change one of the values. If I change the value of "B" to `"b": "\\(title%)"` I get this URL in return `Optional(http://example.com?b=%5C(title%25))` – pbodsk Mar 22 '17 at 13:30
  • it doesnt work for dictionary like this: `["a": [1, 2, 3], "b": ["c": 34, "d": "Malwina jest OK:-)"]]`. Why? on console the output is: `https://example.com?b=%5B%22d%22:%20%22Malwina%20jest%20OK:-)%22,%20%22c%22:%2034%5D&a=%5B1,%202,%203%5D` but when I send that link to imessage it is broken... why? – Bartłomiej Semańczyk Mar 22 '17 at 13:41
  • iMessage do not convert a whole string as one, but split into two and only the first one is converted as a link... You can see the image in the question – Bartłomiej Semańczyk Mar 22 '17 at 13:47
  • OK then maybe you could URL encode the strings in your dictionary before you try to create URLQueryItems from them (http://stackoverflow.com/a/24552028/4063602). Don't know if that helps you. – pbodsk Mar 22 '17 at 14:03
4

If you simply want to create a parameter string from the dictionary of values, and you want it to be simple, you can do something like this:

var str = ""
for key in dictionary.keys {
    if str.isEmpty {
        str = "\(key)=\(dictionary[key]!)"
    } else {
        str += "&\(key)=\(dictionary[key]!)"
    }

However, the above does not encode special characters nor does it do any other special handling. So, a better option would be to add an extension to Dictionary along the following lines:

extension Dictionary {
    var queryParameters: String {
        var parts: [String] = []
        for (key, value) in self {
            let part = String(format: "%@=%@",
                              String(describing: key).addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!,
                              String(describing: value).addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!)
            parts.append(part as String)
        }
        return parts.joined(separator: "&")
    }

}

    }

Then, you can simply say dictionary.queryParameters to get a string which has the dictionary content converted into a parameter string.

Fahim
  • 3,466
  • 1
  • 12
  • 18