90 votes

Création de paramètres de requête d'URL à partir d'objets NSDictionary en ObjectiveC

Avec tous les objets de gestion d'URL qui se trouvent dans les bibliothèques Cocoa standard (NSURL, NSMutableURL, NSMutableURLRequest, etc.), je sais que je dois négliger un moyen facile de composer une requête GET par programmation.

Actuellement, j'ajoute manuellement les " ?" suivis des paires nom-valeur jointes par "&", mais toutes mes paires nom-valeur doivent être codées manuellement pour que NSMutableURLRequest n'échoue pas complètement lorsqu'il tente de se connecter à l'URL.

J'ai l'impression que je devrais être en mesure d'utiliser une API préétablie pour cela.... existe-t-il quelque chose de prêt à l'emploi pour ajouter un NSDictionary de paramètres de requête à un NSURL ? Y a-t-il une autre façon d'aborder la question ?

133voto

Joe Masilotti Points 920

Introduit dans iOS8 et OS X 10.10 est NSURLQueryItem qui peuvent être utilisés pour construire des requêtes. D'après les documents sur NSURLQueryItem :

Un objet NSURLQueryItem représente une paire nom/valeur unique pour un élément de la partie requête d'une URL. Vous utilisez les éléments de requête avec la propriété queryItems d'un objet NSURLComponents.

Pour en créer un, utilisez l'initialisateur désigné queryItemWithName:value: et les ajouter ensuite à NSURLComponents pour générer un NSURL . Par exemple :

NSURLComponents *components = [NSURLComponents componentsWithString:@"http://stackoverflow.com"];
NSURLQueryItem *search = [NSURLQueryItem queryItemWithName:@"q" value:@"ios"];
NSURLQueryItem *count = [NSURLQueryItem queryItemWithName:@"count" value:@"10"];
components.queryItems = @[ search, count ];
NSURL *url = components.URL; // http://stackoverflow.com?q=ios&count=10

Remarquez que le point d'interrogation et l'esperluette sont automatiquement traités. La création d'un NSURL à partir d'un dictionnaire de paramètres est aussi simple que :

NSDictionary *queryDictionary = @{ @"q": @"ios", @"count": @"10" };
NSMutableArray *queryItems = [NSMutableArray array];
for (NSString *key in queryDictionary) {
    [queryItems addObject:[NSURLQueryItem queryItemWithName:key value:queryDictionary[key]]];
}
components.queryItems = queryItems;

J'ai également écrit un article de blog sur comment construire des URLs avec NSURLComponents y NSURLQueryItems .

47voto

Don McCaughey Points 4433

Vous pouvez créer une catégorie pour NSDictionary pour faire cela - il n'y a pas de méthode standard dans la bibliothèque Cocoa que j'ai pu trouver non plus. Le code que j'utilise ressemble à ceci :

// file "NSDictionary+UrlEncoding.h"
#import <cocoa/cocoa.h>

@interface NSDictionary (UrlEncoding)

-(NSString*) urlEncodedString;

@end

avec cette mise en œuvre :

// file "NSDictionary+UrlEncoding.m"
#import "NSDictionary+UrlEncoding.h"

// helper function: get the string form of any object
static NSString *toString(id object) {
  return [NSString stringWithFormat: @"%@", object];
}

// helper function: get the url encoded string form of any object
static NSString *urlEncode(id object) {
  NSString *string = toString(object);
  return [string stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
}

@implementation NSDictionary (UrlEncoding)

-(NSString*) urlEncodedString {
  NSMutableArray *parts = [NSMutableArray array];
  for (id key in self) {
    id value = [self objectForKey: key];
    NSString *part = [NSString stringWithFormat: @"%@=%@", urlEncode(key), urlEncode(value)];
    [parts addObject: part];
  }
  return [parts componentsJoinedByString: @"&"];
}

@end

Je pense que le code est assez simple, mais j'en parle plus en détail à l'adresse suivante http://blog.ablepear.com/2008/12/urlencoding-category-for-nsdictionary.html .

30voto

Anonymous Dev Points 151

Je voulais utiliser la réponse de Chris, mais elle n'a pas été écrite pour Comptage automatique des références (ARC) donc je l'ai mis à jour. J'ai pensé que je devais coller ma solution au cas où quelqu'un d'autre aurait ce même problème. Note : remplacer self avec le nom de l'instance ou de la classe, le cas échéant.

+(NSString*)urlEscapeString:(NSString *)unencodedString 
{
    CFStringRef originalStringRef = (__bridge_retained CFStringRef)unencodedString;
    NSString *s = (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,originalStringRef, NULL, (CFStringRef)@"!*'\"();:@&=+$,/?%#[]% ", kCFStringEncodingUTF8);
    CFRelease(originalStringRef);
    return s;
}

+(NSString*)addQueryStringToUrlString:(NSString *)urlString withDictionary:(NSDictionary *)dictionary
{
    NSMutableString *urlWithQuerystring = [[NSMutableString alloc] initWithString:urlString];

    for (id key in dictionary) {
        NSString *keyString = [key description];
        NSString *valueString = [[dictionary objectForKey:key] description];

        if ([urlWithQuerystring rangeOfString:@"?"].location == NSNotFound) {
            [urlWithQuerystring appendFormat:@"?%@=%@", [self urlEscapeString:keyString], [self urlEscapeString:valueString]];
        } else {
            [urlWithQuerystring appendFormat:@"&%@=%@", [self urlEscapeString:keyString], [self urlEscapeString:valueString]];
        }
    }
    return urlWithQuerystring;
}

22voto

AlBeebe Points 3273

Les autres réponses fonctionnent très bien si les valeurs sont des chaînes de caractères, mais si les valeurs sont des dictionnaires ou des tableaux, ce code s'en chargera.

Il est important de noter qu'il n'y a pas de moyen standard de passer un tableau/dictionnaire via la chaîne de requête, mais PHP gère cette sortie sans problème.

-(NSString *)serializeParams:(NSDictionary *)params {
    /*

     Convert an NSDictionary to a query string

     */

    NSMutableArray* pairs = [NSMutableArray array];
    for (NSString* key in [params keyEnumerator]) {
        id value = [params objectForKey:key];
        if ([value isKindOfClass:[NSDictionary class]]) {
            for (NSString *subKey in value) {
                NSString* escaped_value = (NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,
                                                                                              (CFStringRef)[value objectForKey:subKey],
                                                                                              NULL,
                                                                                              (CFStringRef)@"!*'();:@&=+$,/?%#[]",
                                                                                              kCFStringEncodingUTF8);
                [pairs addObject:[NSString stringWithFormat:@"%@[%@]=%@", key, subKey, escaped_value]];
            }
        } else if ([value isKindOfClass:[NSArray class]]) {
            for (NSString *subValue in value) {
                NSString* escaped_value = (NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,
                                                                                              (CFStringRef)subValue,
                                                                                              NULL,
                                                                                              (CFStringRef)@"!*'();:@&=+$,/?%#[]",
                                                                                              kCFStringEncodingUTF8);
                [pairs addObject:[NSString stringWithFormat:@"%@[]=%@", key, escaped_value]];
            }
        } else {
            NSString* escaped_value = (NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,
                                                                                          (CFStringRef)[params objectForKey:key],
                                                                                          NULL,
                                                                                          (CFStringRef)@"!*'();:@&=+$,/?%#[]",
                                                                                          kCFStringEncodingUTF8);
            [pairs addObject:[NSString stringWithFormat:@"%@=%@", key, escaped_value]];
            [escaped_value release];
        }
    }
    return [pairs componentsJoinedByString:@"&"];
}

Exemples

[foo] => bar
[translations] => 
        {
            [one] => uno
            [two] => dos
            [three] => tres
        }

foo=bar&traductions[un]=uno&traductions[deux]=dos&traductions[trois]=tres

[foo] => bar
[translations] => 
        {
            uno
            dos
            tres
        }

foo=bar&translations[]=uno&translations[]=dos&translations[]=tres

8voto

Rene Dohan Points 766

J'ai refactorisé et converti en réponse ARC par AlBeebe

- (NSString *)serializeParams:(NSDictionary *)params {
    NSMutableArray *pairs = NSMutableArray.array;
    for (NSString *key in params.keyEnumerator) {
        id value = params[key];
        if ([value isKindOfClass:[NSDictionary class]])
            for (NSString *subKey in value)
                [pairs addObject:[NSString stringWithFormat:@"%@[%@]=%@", key, subKey, [self escapeValueForURLParameter:[value objectForKey:subKey]]]];

        else if ([value isKindOfClass:[NSArray class]])
            for (NSString *subValue in value)
            [pairs addObject:[NSString stringWithFormat:@"%@[]=%@", key, [self escapeValueForURLParameter:subValue]]];

        else
            [pairs addObject:[NSString stringWithFormat:@"%@=%@", key, [self escapeValueForURLParameter:value]]];

}
return [pairs componentsJoinedByString:@"&"];

}

- (NSString *)escapeValueForURLParameter:(NSString *)valueToEscape {
     return (__bridge_transfer NSString *) CFURLCreateStringByAddingPercentEscapes(NULL, (__bridge CFStringRef) valueToEscape,
             NULL, (CFStringRef) @"!*'();:@&=+$,/?%#[]", kCFStringEncodingUTF8);
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X