Разбор NSURL собственность запросом
у меня есть URL, как myApp://action/1?parameter=2&secondparameter=3
с запросом свойства я получаю следующую часть моего URL
parameter=2&secondparameter=3
есть ли способ легко положить в NSDictionary
или Array
?
Спасибо большое
16 ответов:
что-то вроде этого:
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]]; }
Примечание : Это пример кода. Все случаи ошибок не управляются.
можно использовать
queryItems
наURLComponents
.при получении значения этого свойства класс NSURLComponents анализирует строку запроса и возвращает массив объектов NSURLQueryItem, каждый из которых представляет одну пару ключ-значение в том порядке, в котором они отображаются в исходной строке запроса.
Свифт
let url = "http://example.com?param1=value1¶m2=param2" let queryItems = URLComponents(string: url)?.queryItems let param1 = queryItems?.filter({.name == "param1"}).first print(param1?.value)
С
использовать
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; }
у меня была причина написать некоторые расширения для этого поведения, которые могут пригодиться. Сначала заголовок:
#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
эти методы расширяют NSString, NSURL и NSDictionary, чтобы вы могли конвертировать в и из строк компонентов запроса и объектов словаря, содержащих результаты.
теперь связаны .м код:
#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
попробуйте это ;)!
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]]; }
все предыдущие сообщения не делают кодировку url должным образом. Я бы предложил следующие методы:
+(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]; }
вот расширение в 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 } }
согласно уже очень чистый ответ Onato Я написал расширение для NSURL в Swift, где вы можете получить запрос param следующим образом:
например, URL содержит пару param=some_value
let queryItem = url.queryItemForKey("param") let value = queryItem.value // would get String "someValue"
расширением выглядит так:
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 } }
для тех, кто использует Основы Болты вы можете использовать:
NSDictionary *parameters = [BFURL URLWithURL:yourURL].inputQueryParameters;
запомнить импорт:
#import <Bolts/BFURL.h>
Если у вас есть Facebook SDK в вашем проекте, у вас также есть болты. Facebook использует эту структуру в качестве зависимостей.
предпочтительный способ борьбы с URL-адресами теперь
NSURLComponents
. В частности,queryItems
свойство, которое возвращаетNSArray
параметрам.если вы хотите params в
NSDictionary
, вот такой метод:+(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; }
предостережение: URL-адреса могут иметь повторяющиеся параметры, но словарь будет содержать только последнее значение любого дублированного параметра. Если это нежелательно, используйте
queryItems
массив напрямую.
Swift 2.1
Oneliner:
"p1=v1&p2=v2".componentsSeparatedByString("&").map { .componentsSeparatedByString("=") }.reduce([:]) { (var dict: [String:String], p) in dict[p[0]] = p[1] return dict } // ["p1": "v1", "p2": "v2"]
используется в качестве расширения на 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 { .componentsSeparatedByString("=") }.reduce([:]) { (var dict: [String:String], p) in dict[p[0]] = p[1] return dict } } else { return [:] } } } }
пример:
let url = NSURL(string: "http://example.com?p1=v1&p2=v2")! let queryDict = url.queryValues // ["p1": "v1", "p2": "v2"]
обратите внимание, если вы используете OS X 10.10 или iOS 8 (или более позднюю версию), вероятно, лучше использовать
NSURLComponents
иqueryItems
свойство и создать словарь изNSURLQueryItems
напрямую.здесь
NSURLComponents
на основе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 } } }
сноску к Расширение NSURL заключается в том, что в Swift фактически можно дать свойству то же имя, что и существующее строковое свойство-
query
. Я не знал, пока не попробовал, но полиморфизм в Swift позволяет вам отличаться только по типу возврата. Поэтому, если расширенное свойство NSURLpublic var query: [String:String]
это работает. Я не использовал это в примере, поскольку нахожу его немного сумасшедшим, но он работает ...
я опубликовал простой класс, выполняющий эту работу в MIT:
https://github.com/anegmawad/URLQueryToCocoa
С его помощью вы можете иметь массивы и объекты в запросе, которые собираются и склеиваются
users[0][firstName]=Amin&users[0][lastName]=Negm&name=Devs&users[1][lastName]=Kienle&users[1][firstName]=Christian
будет:
@{ name : @"Devs", users : @[ @{ firstName = @"Amin", lastName = @"Negm" }, @{ firstName = @"Christian", lastName = @"Kienle" } ] }
вы можете думать об этом как URL-запрос аналог
NSJSONSerializer
.
похоже, что вы используете его для обработки поступающих данных из другого приложения iOS. Если да, то это то, что я использую для той же цели.
начальный вызов (например, во внешнем приложении):
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"); }
метод извлечения данных строки запроса из NSURL и сохранения как 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; }
использование:
NSDictionary *result = [self getNSDictionaryFromQueryString:url];
этот класс является хорошим решением для синтаксического анализа url.
.файл H
@interface URLParser : NSObject { NSArray *variables; } @property (nonatomic, retain) NSArray *variables; - (id)initWithURLString:(NSString *)url; - (NSString *)valueForVariable:(NSString *)varName; @end
.файл м
#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
Хендрик написал хороший пример для расширения в этом вопросе, однако мне пришлось переписать его, чтобы не использовать методы библиотеки objective-C. Используя
NSArray
в swift это не правильный подход.это результат, все быстро и немного более безопасно. Пример использования будет меньше строк кода с 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) { .name == key } if filteredArray.count > 0 { return filteredArray.first } else { return nil } } }
использование:
let queryItem = url.queryItemForKey("myItem")
или, более детальное использование:
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)") } } }
попробуйте это:
-(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; }
наиболее надежное решение, если вы используете URL для передачи данных из веб-приложения на телефон, и вы хотите передать массивы, числа, строки,...
JSON кодирует ваш объект в PHP
header("Location: myAppAction://".urlencode(json_encode($YOUROBJECT)));
и JSON декодируют результат в iOS
NSData *data = [[[request URL] host] dataUsingEncoding:NSUTF8StringEncoding]; NSDictionary *packed = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];