Il y a 1 mois -

Temps de lecture 2 minutes

Pépite #25 – Les URL dans tous leurs états

Depuis iOS8, iOS propose une nouvelle classe pour construire ses URL à partir de plusieurs paramètres : URLComponents. Seulement celle-ci possède… quelques subtilités.

You know nothing, urlQuery!

Imaginez : vous avez une API pour requêter des informations concernant un numéro de téléphone. Celui-ci est au format international. (+33xxxxxxxxx pour la France). Avec URLComponents, vous pourriez construire votre URL comme suit :

var builder = URLComponents(string: "https://smsservice.com")!

builder.path = "/status"
builder.queryItems = [URLQueryItem(name: "number", value: "+33601020304")]

let requestURL = builder.url

 

Rien de compliqué. Vous envoyez alors votre requête au serveur qui vous répond… erreur 400 ! Notre requête est malformée. Mais pourquoi ?

Parlez-vous RFC ?

Le problème est simple, mais subtil. D’après la documentation de URLComponents, queryItems est encodé selon la norme RFC 3986, dans laquelle “+” est un caractère valide (et donc non encodé) pour une query.
hello+world correspond donc à… hello+world 👍.

 

A l’inverse, la W3C recommendations for URI addressing définit le “+” comme un équivalent à espace, hello+world correspond donc à… hello world 😲. Et devinez quoi ? Les serveurs suivent la W3C recommendations !

// requestURL vaut "https://smsservice.com/status?number=+33601020304"
// alors que le serveur s'attend à https://smsservice.com/status?number=%2B33601020304
print(requestURL!)

Il va donc être donc nécessaire, pour envoyer un « + » à notre serveur, de l’encoder afin qu’il ne soit pas interprété comme un espace 😒.

À tout problème sa solution

Pour ce faire, le plus simple est de définir une méthode qui va encoder les paramètres selon la norme W3C :

extension URLQueryItem {
    func percentEncoded() -> URLQueryItem {
        /// addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) encode les paramètres selon la norme RFC 3986
        /// Par dessus, nous allons venir encoder le + par son équivalent HTTP

        var newQueryItem = self
        newQueryItem.value = value?
            .addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)?
            .replacingOccurrences(of: "+", with: "%2B")

        return newQueryItem
    }
}

extension Array where Element == URLQueryItem {
    func percentEncoded() -> Array<Element> {
        return map { $0.percentEncoded() }
    }
}

 

Il ne nous reste plus qu’à l’appeler au moment de définir nos paramètres :

let encodedQueryItems = [URLQueryItem(name: "number", value: "+33601020304")].percentEncoded()

builder.queryItems = encodedQueryItems

print(builder.url)

 

Le résultat sera quelque peu surprenant : « https://smsservice.com/status?number=%252B33601020304 » au lieu de « https://smsservice.com/status?number=%2B33601020304 » !

La réponse vient de queryItems : les paramètres y sont encodés automatiquement, mais nous les avions nous-même déjà encodés auparavant. Les caractères spéciaux se retrouvent donc encodés deux fois…

Heureusement, il existe une solution à cela ! Caché au fin fond de la documentation Apple se trouve percentEncodedQueryItems, la version raw (non encodée) de queryItems.

builder.percentEncodedQueryItems = encodedQueryItems

print(builder.url)

Et voilà ! Après de durs labeurs, votre URL vaut enfin « https://smsservice.com/status?number=%2B33601020304« . Hourra ! 🎉

Publié par Jean-Christophe Pastant

Jean-Christophe est consultant iOS et partage régulièrement ses bonnes pratiques avec la communauté iOS.

Commentaire

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Nous recrutons

Être un Sapient, c'est faire partie d'un groupe de passionnés ; C'est l'opportunité de travailler et de partager avec des pairs parmi les plus talentueux.