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
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.
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¶m2=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;
}
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
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]];
}
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];
}
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
}
}
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
}
}
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.
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
.
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 ...
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
.
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];
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
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)")
}
}
}
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;
}
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];