[objective-c] ObjectiveC의 NSDictionary 개체에서 URL 쿼리 매개 변수 만들기

표준 Cocoa 라이브러리 (NSURL, NSMutableURL, NSMutableURLRequest 등)에있는 모든 URL 처리 개체를 사용하여 프로그래밍 방식으로 GET 요청을 작성하는 쉬운 방법을 간과해야한다는 것을 알고 있습니다.

현재 수동으로 “?”를 추가하고 있습니다. “&”로 결합 된 이름 값 쌍이 뒤 따르지만 모든 이름 및 값 쌍은 수동으로 인코딩해야 NSMutableURLRequest가 URL에 연결하려고 할 때 완전히 실패하지 않습니다.

이것은 내가 미리 구운 API를 사용할 수 있어야하는 것처럼 느껴집니다 …. NSURL에 쿼리 매개 변수의 NSDictionary를 추가 할 수있는 상자가 있습니까? 이에 접근해야하는 다른 방법이 있습니까?



답변

iOS8 및 OS X 10.10에 도입 된 것은 NSURLQueryItem쿼리 작성에 사용할 수있는입니다. NSURLQueryItem 의 문서에서 :

NSURLQueryItem 개체는 URL의 쿼리 부분에있는 항목에 대한 단일 이름 / 값 쌍을 나타냅니다. NSURLComponents 개체의 queryItems 속성과 함께 쿼리 항목을 사용합니다.

하나를 만들려면 지정된 이니셜 라이저 queryItemWithName:value:를 사용한 다음에 추가 NSURLComponents하여 NSURL. 예를 들면 :

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

물음표와 앰퍼샌드는 자동으로 처리됩니다. NSURL매개 변수 사전에서 생성하는 것은 다음과 같이 간단합니다.

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;

및 을 사용하여 URL을 작성하는 방법에 대한 블로그 게시물 도 작성했습니다 .NSURLComponentsNSURLQueryItems


답변

이를 위해 카테고리를 생성 NSDictionary할 수 있습니다. Cocoa 라이브러리에는 제가 찾을 수있는 표준 방법이 없습니다. 내가 사용하는 코드는 다음과 같습니다.

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

@interface NSDictionary (UrlEncoding)

-(NSString*) urlEncodedString;

@end

이 구현으로 :

// 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

코드가 매우 간단하다고 생각하지만 http://blog.ablepear.com/2008/12/urlencoding-category-for-nsdictionary.html 에서 좀 더 자세히 논의합니다 .


답변

Chris의 답변을 사용하고 싶었지만 ARC ( Automatic Reference Counting) 용으로 작성되지 않았 으므로 업데이트했습니다. 다른 사람이 이와 동일한 문제가있는 경우를 대비하여 내 솔루션을 붙여 넣을 것이라고 생각했습니다. 참고 :self 적절한 경우 인스턴스 또는 클래스 이름으로 바꾸십시오 .

+(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;
}


답변

다른 답변은 값이 문자열이면 훌륭하게 작동하지만 값이 사전이나 배열이면이 코드가 처리합니다.

쿼리 문자열을 통해 배열 / 사전을 전달하는 표준 방법은 없지만 PHP는이 출력을 잘 처리합니다.

-(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:@"&"];
}

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

foo = bar & translations [one] = uno & translations [two] = dos & translations [three] = tres

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

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


답변

리팩토링하고 AlBeebe의 ARC 답변으로 변환했습니다.

- (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);
}


답변

이미 AFNetworking을 사용하고 있다면 (나와 마찬가지로) 클래스 AFHTTPRequestSerializer를 사용하여 필요한 NSURLRequest.

[[AFHTTPRequestSerializer serializer] requestWithMethod:@"GET" URLString:@"YOUR_URL" parameters:@{PARAMS} error:nil];

작업에 대한 URL 만 필요한 경우 NSURLRequest.URL.


답변

다음은 Swift (iOS8 +) 의 간단한 예입니다 .

private let kSNStockInfoFetchRequestPath: String = "http://dev.markitondemand.com/Api/v2/Quote/json"

private func SNStockInfoFetchRequestURL(symbol:String) -> NSURL? {
  if let components = NSURLComponents(string:kSNStockInfoFetchRequestPath) {
    components.queryItems = [NSURLQueryItem(name:"symbol", value:symbol)]
    return components.URL
  }
  return nil
}