Parse NSURL propriété de requête

J'ai une URL comme myApp://action/1?parameter=2&secondparameter=3

Avec la requête de propriété, je reçois une partie suivante de mon URL

parameter=2&secondparameter=3

Est-il un moyen facile de mettre cela dans une NSDictionary ou Array?

Thx beaucoup

77
demandé sur RaffAl 2010-10-22 18:48:49

16 réponses

Quelque Chose comme ça:

NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
for (NSString *param in [url componentsSeparatedByString:@"&"]) {
  NSArray *elts = [param componentsSeparatedByString:@"="];
  if([elts count] < 2) continue;
  [params setObject:[elts lastObject] forKey:[elts firstObject]];
}

Note : Ceci est un exemple de code. Tous les cas d'erreur ne sont pas gérés.

51
répondu Benoît 2015-10-06 14:25:47

Vous pouvez utiliser queryItems dans URLComponents.

Lorsque vous obtenez la valeur de cette propriété, La classe NSURLComponents analyse la chaîne de requête et renvoie un tableau D'objets NSURLQueryItem, chacun représentant une seule paire clé-valeur, dans l'ordre dans lequel ils apparaissent dans la chaîne de requête d'origine.

Swift

let url = "http://example.com?param1=value1&param2=param2"
let queryItems = URLComponents(string: url)?.queryItems
let param1 = queryItems?.filter({$0.name == "param1"}).first
print(param1?.value)

Objectif C

Utiliser NSURLComponents.

NSURLComponents *urlComponents = [NSURLComponents componentsWithURL:url 
                                            resolvingAgainstBaseURL:NO];
NSArray *queryItems = urlComponents.queryItems;
NSString *param1 = [self valueForKey:@"param1" 
                  fromQueryItems:queryItems];
NSLog(@"%@", param1);
…

- (NSString *)valueForKey:(NSString *)key
           fromQueryItems:(NSArray *)queryItems
{
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name=%@", key];
    NSURLQueryItem *queryItem = [[queryItems 
                                  filteredArrayUsingPredicate:predicate]
                                 firstObject];
    return queryItem.value;
}
133
répondu Onato 2018-07-15 02:43:39

J'avais des raisons d'écrire des extensions pour ce comportement qui pourraient être utiles. D'abord l'en-tête:

#import <Foundation/Foundation.h>

@interface NSString (XQueryComponents)
- (NSString *)stringByDecodingURLFormat;
- (NSString *)stringByEncodingURLFormat;
- (NSMutableDictionary *)dictionaryFromQueryComponents;
@end

@interface NSURL (XQueryComponents)
- (NSMutableDictionary *)queryComponents;
@end

@interface NSDictionary (XQueryComponents)
- (NSString *)stringFromQueryComponents;
@end

Ces méthodes étendent NSString, NSURL et NSDictionary, pour vous permettre de convertir des chaînes de composants de requête et des objets de dictionnaire contenant les résultats.

Maintenant le connexe .code m:

#import "XQueryComponents.h"

@implementation NSString (XQueryComponents)
- (NSString *)stringByDecodingURLFormat
{
    NSString *result = [self stringByReplacingOccurrencesOfString:@"+" withString:@" "];
    result = [result stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    return result;
}

- (NSString *)stringByEncodingURLFormat
{
    NSString *result = [self stringByReplacingOccurrencesOfString:@" " withString:@"+"];
    result = [result stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    return result;
}

- (NSMutableDictionary *)dictionaryFromQueryComponents
{
    NSMutableDictionary *queryComponents = [NSMutableDictionary dictionary];
    for(NSString *keyValuePairString in [self componentsSeparatedByString:@"&"])
    {
        NSArray *keyValuePairArray = [keyValuePairString componentsSeparatedByString:@"="];
        if ([keyValuePairArray count] < 2) continue; // Verify that there is at least one key, and at least one value.  Ignore extra = signs
        NSString *key = [[keyValuePairArray objectAtIndex:0] stringByDecodingURLFormat];
        NSString *value = [[keyValuePairArray objectAtIndex:1] stringByDecodingURLFormat];
        NSMutableArray *results = [queryComponents objectForKey:key]; // URL spec says that multiple values are allowed per key
        if(!results) // First object
        {
            results = [NSMutableArray arrayWithCapacity:1];
            [queryComponents setObject:results forKey:key];
        }
        [results addObject:value];
    }
    return queryComponents;
}
@end

@implementation NSURL (XQueryComponents)
- (NSMutableDictionary *)queryComponents
{
    return [[self query] dictionaryFromQueryComponents];
}
@end

@implementation NSDictionary (XQueryComponents)
- (NSString *)stringFromQueryComponents
{
    NSString *result = nil;
    for(__strong NSString *key in [self allKeys])
    {
        key = [key stringByEncodingURLFormat];
        NSArray *allValues = [self objectForKey:key];
        if([allValues isKindOfClass:[NSArray class]])
            for(__strong NSString *value in allValues)
            {
                value = [[value description] stringByEncodingURLFormat];
                if(!result)
                    result = [NSString stringWithFormat:@"%@=%@",key,value];
                else 
                    result = [result stringByAppendingFormat:@"&%@=%@",key,value];
            }
        else {
            NSString *value = [[allValues description] stringByEncodingURLFormat];
            if(!result)
                result = [NSString stringWithFormat:@"%@=%@",key,value];
            else 
                result = [result stringByAppendingFormat:@"&%@=%@",key,value];
        }
    }
    return result;
}
@end
54
répondu BadPirate 2013-09-26 23:53:19

Essayez ceci ;)!

NSString *query = @"parameter=2&secondparameter=3"; // replace this with [url query];
NSArray *components = [query componentsSeparatedByString:@"&"];
NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init];
for (NSString *component in components) {
    NSArray *subcomponents = [component componentsSeparatedByString:@"="];
    [parameters setObject:[[subcomponents objectAtIndex:1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]
                   forKey:[[subcomponents objectAtIndex:0] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
}
13
répondu elslooo 2014-07-09 19:14:10

Tous les messages précédents ne font pas l'encodage d'url correctement. Je suggère les méthodes suivantes:

+(NSString*)concatenateQuery:(NSDictionary*)parameters {
    if([parameters count]==0) return nil;
    NSMutableString* query = [NSMutableString string];
    for(NSString* parameter in [parameters allKeys])
        [query appendFormat:@"&%@=%@",[parameter stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLQueryAllowedCharacterSet],[[parameters objectForKey:parameter] stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLQueryAllowedCharacterSet]];
    return [[query substringFromIndex:1] copy];
}
+(NSDictionary*)splitQuery:(NSString*)query {
    if([query length]==0) return nil;
    NSMutableDictionary* parameters = [NSMutableDictionary dictionary];
    for(NSString* parameter in [query componentsSeparatedByString:@"&"]) {
        NSRange range = [parameter rangeOfString:@"="];
        if(range.location!=NSNotFound)
            [parameters setObject:[[parameter substringFromIndex:range.location+range.length] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] forKey:[[parameter substringToIndex:range.location] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
        else [parameters setObject:[[NSString alloc] init] forKey:[parameter stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
    }
    return [parameters copy];
}
9
répondu Kristian Kraljic 2015-06-12 21:36:45

Voici L'extension dans swift:

extension NSURL{
        func queryParams() -> [String:AnyObject] {
            var info : [String:AnyObject] = [String:AnyObject]()
            if let queryString = self.query{
                for parameter in queryString.componentsSeparatedByString("&"){
                    let parts = parameter.componentsSeparatedByString("=")
                    if parts.count > 1{
                        let key = (parts[0] as String).stringByReplacingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
                        let value = (parts[1] as String).stringByReplacingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
                        if key != nil && value != nil{
                            info[key!] = value
                        }
                    }
                }
            }
            return info
        }
    }
7
répondu mukaissi 2014-10-13 18:34:03

Selon la réponse déjà très propre de Onato j'ai écrit une extension pour NSURL dans Swift où vous pouvez obtenir un paramètre de requête comme ceci:

Par exemple, L'URL contient la paire param = some_value

let queryItem = url.queryItemForKey("param")
let value = queryItem.value // would get String "someValue"

L'extension ressemble à:

extension NSURL {

  var allQueryItems: [NSURLQueryItem] {
      get {
          let components = NSURLComponents(URL: self, resolvingAgainstBaseURL: false)!
          let allQueryItems = components.queryItems!
          return allQueryItems as [NSURLQueryItem]
      }
  }

  func queryItemForKey(key: String) -> NSURLQueryItem? {

      let predicate = NSPredicate(format: "name=%@", key)!
      return (allQueryItems as NSArray).filteredArrayUsingPredicate(predicate).first as? NSURLQueryItem

  }
}
7
répondu Hendrik 2017-05-23 11:55:10

Pour ceux qui utilisent boulons Cadre Vous pouvez utiliser:

NSDictionary *parameters = [BFURL URLWithURL:yourURL].inputQueryParameters;

N'oubliez pas d'importer:

#import <Bolts/BFURL.h>

Si vous avez Facebook SDK dans votre projet, vous avez également Boulons. Facebook utilise ce cadre comme une dépendance.

5
répondu RaffAl 2015-02-11 13:20:11

La façon préférée de traiter les URL est maintenant NSURLComponents. En particulier, l'queryItems propriété qui retourne un NSArray de paramètres.

Si vous voulez les paramètres dans un NSDictionary, Voici une méthode:

+(NSDictionary<NSString *, NSString *>*)queryParamsFromURL:(NSURL*)url
{
    NSURLComponents* urlComponents = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO];

    NSMutableDictionary<NSString *, NSString *>* queryParams = [NSMutableDictionary<NSString *, NSString *> new];
    for (NSURLQueryItem* queryItem in [urlComponents queryItems])
    {
        if (queryItem.value == nil)
        {
            continue;
        }
        [queryParams setObject:queryItem.value forKey:queryItem.name];
    }
    return queryParams;
}

Mise en garde: les URL peuvent avoir des paramètres répétés, mais le dictionnaire ne contiendra que la dernière valeur de tout paramètre dupliqué. Si cela n'est pas souhaitable, utilisez directement le tableau queryItems.

3
répondu William Denniss 2015-09-14 06:39:33

Swift 2.1

Oneliner:

"p1=v1&p2=v2".componentsSeparatedByString("&").map {
    $0.componentsSeparatedByString("=") 
}.reduce([:]) {
    (var dict: [String:String], p) in
    dict[p[0]] = p[1]
    return dict
}

// ["p1": "v1", "p2": "v2"]

Utilisé comme extension sur NSURL:

extension NSURL {

    /**
     * URL query string as dictionary. Empty dictionary if query string is nil.
     */
    public var queryValues : [String:String] {
        get {
            if let q = self.query {
                return q.componentsSeparatedByString("&").map {
                    $0.componentsSeparatedByString("=") 
                }.reduce([:]) {
                    (var dict: [String:String], p) in
                    dict[p[0]] = p[1]
                    return dict
                }
            } else {
                return [:]
            }
        }
    }

}

Exemple:

let url = NSURL(string: "http://example.com?p1=v1&p2=v2")!
let queryDict = url.queryValues

// ["p1": "v1", "p2": "v2"]

Veuillez noter que si vous utilisez OS X 10.10 ou iOS 8 (ou version ultérieure), il est probablement préférable d'utiliser NSURLComponents et la propriété queryItems et de créer directement le dictionnaire à partir du NSURLQueryItems.

Voici une solution d'extension NSURLComponents basée sur NSURL:

extension NSURL {

    /// URL query string as a dictionary. Empty dictionary if query string is nil.
    public var queryValues : [String:String] {
        get {
            guard let components = NSURLComponents(URL: self, resolvingAgainstBaseURL: false) else {
                return [:]
            }

            guard let queryItems = components.queryItems else {
                return [:]
            }

            var result:[String:String] = [:]
            for q in queryItems {
                result[q.name] = q.value
            }
            return result
        }
    }

}

une note de bas de page à L'extension NSURL est qu'il est réellement possible dans Swift de donner la propriété le même nom que la propriété String existante - query. Je ne savais pas jusqu'à ce que je l'ai essayé, mais le polymorphisme dans Swift vous permet de ne différer que sur le type de retour. Donc, si la propriété NSURL étendue est public var query: [String:String], cela fonctionne. Je ne l'ai pas utilisé dans l'exemple car je trouve ça un peu fou, mais ça marche ...

3
répondu Erik Tjernlund 2015-11-08 09:19:43

J'ai publié une classe simple faisant le travail sous MIT:

Https://github.com/anegmawad/URLQueryToCocoa

Avec elle, vous pouvez avoir des tableaux et des objets dans la requête, qui sont collectés et collés ensemble

Par Exemple

users[0][firstName]=Amin&users[0][lastName]=Negm&name=Devs&users[1][lastName]=Kienle&users[1][firstName]=Christian

Deviendra:

@{
   name : @"Devs",
   users :
   @[
      @{
         firstName = @"Amin",
         lastName = @"Negm"
      },
      @{
         firstName = @"Christian",
         lastName = @"Kienle"
      }
   ]
 }

Vous pouvez le considérer comme une contrepartie de requête URL de NSJSONSerializer.

2
répondu Amin Negm-Awad 2014-07-15 14:03:51

Il semble que vous l'utilisiez pour traiter les données entrantes d'une autre application iOS. Si oui, c'est ce que j'utilise dans le même but.

Appel Initial (par exemple dans une application externe):

UIApplication *application = [UIApplication sharedApplication];
NSURL *url = [NSURL URLWithString:@"myApp://action/1?parameter=2&secondparameter=3"];
if ([application canOpenURL:url]) {
    [application openURL:url];
    NSLog(@"myApp is installed");
} else {
    NSLog(@"myApp is not installed");
}

Méthode pour extraire les données QueryString de NSURL et enregistrer en tant que NSDictionary:

-(NSDictionary *) getNSDictionaryFromQueryString:(NSURL *)url {
   NSMutableDictionary *result = [[NSMutableDictionary alloc] init];
   NSRange needle = [url.absoluteString rangeOfString:@"?" options:NSCaseInsensitiveSearch];
   NSString *data = nil;

   if(needle.location != NSNotFound) {
       NSUInteger start = needle.location + 1;
       NSUInteger end = [url.absoluteString length] - start;
       data = [url.absoluteString substringWithRange:NSMakeRange(start, end)];
   }

   for (NSString *param in [data componentsSeparatedByString:@"&"]) {
       NSArray *keyvalue = [param componentsSeparatedByString:@"="];
       if([keyvalue count] == 2){
           [result setObject:[keyvalue objectAtIndex:1] forKey:[keyvalue objectAtIndex:0]];
       }
   }

  return result;
}

Utilisation:

NSDictionary *result = [self getNSDictionaryFromQueryString:url];
1
répondu Gillsoft AB 2015-08-18 18:49:05

Cette classe est une bonne solution pour l'analyse d'url.

.h fichier

@interface URLParser : NSObject {
    NSArray *variables;
}

@property (nonatomic, retain) NSArray *variables;

- (id)initWithURLString:(NSString *)url;
- (NSString *)valueForVariable:(NSString *)varName;

@end

.m fichier

#import "URLParser.h"

@implementation URLParser
@synthesize variables;

- (id) initWithURLString:(NSString *)url{
    self = [super init];
    if (self != nil) {
        NSString *string = url;
        NSScanner *scanner = [NSScanner scannerWithString:string];
        [scanner setCharactersToBeSkipped:[NSCharacterSet characterSetWithCharactersInString:@"&?"]];
        NSString *tempString;
        NSMutableArray *vars = [NSMutableArray new];
        [scanner scanUpToString:@"?" intoString:nil];       //ignore the beginning of the string and skip to the vars
        while ([scanner scanUpToString:@"&" intoString:&tempString]) {
            [vars addObject:[tempString copy]];
        }
        self.variables = vars;
    }
    return self;
}

- (NSString *)valueForVariable:(NSString *)varName {
    for (NSString *var in self.variables) {
        if ([var length] > [varName length]+1 && [[var substringWithRange:NSMakeRange(0, [varName length]+1)] isEqualToString:[varName stringByAppendingString:@"="]]) {
            NSString *varValue = [var substringFromIndex:[varName length]+1];
            return varValue;
        }
    }
    return nil;
}

@end
0
répondu ymutlu 2014-07-10 11:38:59

Hendrik a écrit un bel exemple d'extension dans cette question, mais j'ai dû le réécrire pour ne pas utiliser de méthodes de bibliothèque objective-C. L'utilisation de NSArray dans swift n'est pas la bonne approche.

C'est le résultat, tout rapide et un peu plus sûr. L'exemple d'utilisation sera moins de lignes de code avec Swift 1.2.

public extension NSURL {
    /*
    Set an array with all the query items
    */
    var allQueryItems: [NSURLQueryItem] {
        get {
            let components = NSURLComponents(URL: self, resolvingAgainstBaseURL: false)!
            if let allQueryItems = components.queryItems {
                return allQueryItems as [NSURLQueryItem]
            } else {
                return []
            }
        }
    }

    /**
    Get a query item form the URL query

    :param: key The parameter to fetch from the URL query

    :returns: `NSURLQueryItem` the query item
    */
    public func queryItemForKey(key: String) -> NSURLQueryItem? {
        let filteredArray = filter(allQueryItems) { $0.name == key }

        if filteredArray.count > 0 {
            return filteredArray.first
        } else {
            return nil
        }
    }
}

Utilisation:

let queryItem = url.queryItemForKey("myItem")

Ou, utilisation plus détaillée:

if let url = NSURL(string: "http://www.domain.com/?myItem=something") {
    if let queryItem = url.queryItemForKey("myItem") {
        if let value = queryItem.value {
            println("The value of 'myItem' is: \(value)")
        }
    }
}
0
répondu Paul Peelen 2017-05-23 11:47:28

Essayez ceci:

-(NSDictionary *)getUrlParameters:(NSString *)url{
    NSArray *justParamsArr = [url componentsSeparatedByString:@"?"];
    url = [justParamsArr lastObject];
    NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
    for (NSString *param in [url componentsSeparatedByString:@"&"]) {
        NSArray *elts = [param componentsSeparatedByString:@"="];
        if([elts count] < 2) continue;
        [params setObject:[elts lastObject] forKey:[elts firstObject]];
    }
    return params;
}
0
répondu reza_khalafi 2017-06-15 12:39:41

Solution la plus robuste si vous utilisez une URL pour transmettre des données de l'application web au téléphone et que vous souhaitez transmettre des tableaux, des nombres, des chaînes, ...

JSON encoder votre objet en PHP

header("Location: myAppAction://".urlencode(json_encode($YOUROBJECT)));

Et JSON décodent le résultat dans iOS

NSData *data = [[[request URL] host] dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *packed = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
-5
répondu William Entriken 2012-07-20 02:12:57