수색…


비고

NSURLSession 클래스 및 관련 클래스는 콘텐츠를 다운로드하기위한 API를 제공합니다. 이 API는 인증 지원을위한 다양한 대리자 메소드를 제공하며 앱이 실행되지 않을 때 백그라운드 다운로드를 수행 할 수 있도록하거나 iOS에서는 앱이 일시 중지 된 상태에서 앱을 다운로드하는 기능을 제공합니다.

높은 수준에서 NSURLSession 은 세션 및 작업 개념을 기반으로합니다. 작업은 단일 URL에 대한 단일 요청 (또는 단일 URL에 대한 단일 업로드)을 나타냅니다. 세션은 관련 요청 그룹입니다.

운영 체제는 하나의 기존 세션, 즉 기본적으로 NSURLConnection처럼 작동하는 공유 세션을 제공합니다. 또한 필요에 따라 앱에서 자체 세션을 만들 수 있습니다.

다른 앱은 여러 가지 방식으로 세션을 사용합니다. 많은 앱이 출시 될 때 하나의 세션을 생성하고 계속 재사용합니다. 다른 응용 프로그램은 관련 작업 그룹을 취소 할 수 있으므로 (예 : 탭을 닫을 때 모든 대기중인 요청을 취소하는 웹 브라우저) 관련 요청의 각 그룹을 보관할 하나의 세션을 만들 수 있습니다.

NSURLSession을 사용할 때의 첫 번째 단계는 세션 구성 객체를 만드는 것입니다. 재사용 가능한 (대개) 재사용 가능한 객체에는 최대 동시성, 각 요청과 함께 보낼 추가 헤더, 셀룰러 라디오 (iOS에만 해당)를 통해 전송할 수 있도록 허용할지 여부, 시간 초과 등 여러 가지 요구 사항에 맞게 조정할 수있는 다양한 세션 설정이 포함되어 있습니다. 자격 증명 저장소, 최소 TLS 버전 및 프록시 설정까지 포함 할 수 있습니다.

결과 세션의 작동 방식에 따라 세 가지 유형의 세션 구성이 있습니다.

  • 기본 구성 은 NSURLConnection과 비슷한 방식으로 작동하는 세션을 만듭니다.
  • 백그라운드 구성 은 요청이 프로세스 외부에서 발생하는 세션을 생성하므로 앱이 더 이상 실행되지 않아도 다운로드가 계속 진행될 수 있습니다.
  • 임시 구성 은 디스크에 아무것도 캐시하지 않고 디스크에 쿠키를 저장하지 않는 세션을 생성하므로 시크릿 브라우저 창과 같은 것들을 백업하는 데 적합합니다.

백그라운드 구성을 만들 때 나중에 (응용 프로그램이 종료되거나 OS에 의해 일시 중지되거나 종료 된 경우) 배경 세션을 나중에 다시 연결할 수있는 세션 식별자를 제공해야합니다. 앱에서 동일한 식별자가 활성화 된 두 세션 이상의 인스턴스가 없어야합니다. 따라서 규칙에 따라 이러한 구성을 재사용 할 수 없습니다. 다른 모든 세션 구성을 재사용하여 원하는만큼의 세션을 생성 할 수 있습니다. 따라서 비슷한 설정으로 여러 세션을 만들어야하는 경우 새 세션을 만들 때마다 구성을 한 번 만들고 다시 사용할 수 있습니다.

세션을 만든 후에는 해당 세션에서 작업을 만들 수 있습니다. 세 가지 유형의 작업이 있습니다.

  • 데이터 작업은 데이터를 NSData 객체로 반환합니다. 이들은 일반적으로 사용하기에 적합하지만 백그라운드 세션에서는 지원되지 않습니다.
  • 다운로드 작업은 데이터를 디스크의 파일로 반환합니다. 이들은 더 큰 요청이나 백그라운드 세션에서 사용하기에 적합합니다.
  • 업로드 작업 은 NSData 객체 또는 디스크의 파일에서 데이터를 업로드합니다 . POST 본문을 제공하는 데이터 객체 또는 파일을 제공합니다. 작업에 제공 한 본문 데이터 / 파일은 NSURLRequest 개체에 제공된 본문 데이터 / 파일을 덮어 씁니다 (해당되는 경우).

이러한 각 유형을 사용하면 블록 기반 콜백을 사용하거나 세션에서 대리인을 제공하고 대리인 메서드를 구현하는 등 여러 가지 방식으로 응답 데이터를 얻을 수 있습니다.

또한 NSURLSession을 사용하면 인증 처리, 사용자 지정 TLS 인증서 처리 (클라이언트 인증서와 서버 유효성 검사 모두) 수행, 캐싱 동작 변경 등에 대한 대리 메서드를 제공 할 수 있습니다.

간단한 GET 요청

    // define url
    let url = NSURL(string: "https://urlToGet.com")

    //create a task to get data from a url
    let task = NSURLSession.sharedSession().dataTaskWithURL(url!)
    {

       /*inside this block, we have access to NSData *data, NSURLResponse *response, and NSError *error returned by the dataTaskWithURL() function*/
      (data, response, error) in

      if error == nil 
      {
          // Data from the request can be manipulated here
      }
      else
      {
          // An error occurred 
      }
    }

    //make the request
    task.resume()

목표 -C 데이터 세션 및 데이터 만들기

NSURL *url = [NSURL URLWithString:@"http://www.example.com/"];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];

// Configure the session here.

NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
    
[[session dataTaskWithURL:url
        completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
    // The response object contains the metadata (HTTP headers, status code)

    // The data object contains the response body

    // The error object contains any client-side errors (e.g. connection
    // failures) and, in some cases, may report server-side errors.
    // In general, however, you should detect server-side errors by
    // checking the HTTP status code in the response object.
}] resume];

백그라운드 구성 설정

백그라운드 세션을 만들려면

 // Swift:
 let mySessionID = "com.example.bgSession"
 let bgSessionConfig = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier(mySessionID)
 
 let session = NSURLSession(configuration: bgSessionConfig)
 
 // add tasks here


 // Objective-C:
 NSString *mySessionID = @"com.example.bgSession";
 NSURLSessionConfiguration *configuration =
     [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier: mySessionID];
 NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
                                                       delegate:self]

또한 iOS에서는 백그라운드 앱 다시 시작 처리에 대한 지원을 설정해야합니다. application:handleEventsForBackgroundURLSession:completionHandler: 프로그램의 application:handleEventsForBackgroundURLSession:completionHandler: method (Objective-C) 또는 application(_:handleEventsForBackgroundURLSession:completionHandler:) 메서드가 호출되면 세션에서 작업을 처리하기 위해 백그라운드에서 응용 프로그램이 다시 시작되었음을 의미합니다.

이 방법에서는 제공된 식별자로 새 세션을 만들고 포 그라운드에서 평상시처럼 이벤트를 처리 할 수있는 대리인을 구성해야합니다. 또한 제공된 완성 처리기를 세션을 키로 사용하여 사전에 저장해야합니다.

대리자의 URLSessionDidFinishEventsForBackgroundURLSession: (Obj-C) / URLSessionDidFinishEventsForBackgroundURLSession (Swift) 메서드가 처리되어 더 이상 처리 할 이벤트가 없다고 알리면 앱에서 해당 세션의 완료 핸들러를 검색하고 사전에서 세션을 제거한 다음 완료 처리기를 호출하여 세션과 관련하여 더 이상 처리되지 않은 처리가 더 이상 없음을 운영 체제에 알립니다. 해당 대리자 호출을받을 때 어떤 이유로 든 계속 작업하는 경우 완료 될 때까지 기다리십시오. 해당 메서드를 호출하면 즉시 백그라운드 세션이 무효화됩니다.

그런 다음 응용 프로그램이 응용 프로그램을 수신 한 경우 application:application:didFinishLaunchingWithOptions: call (백그라운드 이벤트 처리 중 사용자가 응용 프로그램을 포 그라운드했다는 것을 나타내는 경우), 동일한 식별자로 백그라운드 세션을 만드는 것이 안전합니다. 식별자가 더 이상 존재하지 않습니다.

세부 사항에 대해 궁금해 할 때, 높은 수준에서 배경 세션을 만들 때 다음 두 가지 작업을 수행합니다.

  • 다운로드를 처리하기 위해 외부 데몬 (nsurlsessiond)에 세션 만들기
  • NSXPC를 통해 외부 데몬과 대화하는 앱 내 세션 만들기

일반적으로 앱이 한 번 실행될 때 동일한 세션 ID로 두 세션을 생성하는 것은 위험합니다. 둘 다 백그라운드 데몬에서 같은 세션과 통신하려고하기 때문입니다. 이것이 공식 문서가 동일한 식별자로 다중 세션을 생성하지 않는다는 이유입니다. 그러나 첫 번째 세션이 handleEventsForBackgroundURLSession 호출의 일부로 생성 된 임시 세션 인 경우, 현재 비활성화 된 인앱 세션과 백그라운드 데몬의 세션 사이의 연결이 더 이상 존재하지 않습니다.

Objective-C에서 NSURLSession을 사용하는 인수를 사용하여 POST 요청 보내기

POST 요청 본문을 인코딩하는 두 가지 일반적인 방법으로 URL 인코딩 (application / x-www-form-urlencoded) 및 양식 데이터 (multipart / form-data)가 있습니다. 대부분의 코드는 비슷하지만 본문 데이터를 만드는 방식이 다릅니다.

URL 인코딩을 사용하여 요청 보내기

소규모 애플리케이션 용 서버가 있거나 전체 백엔드 엔지니어가있는 팀에서 일하고 있다면 iOS 애플리케이션으로 한 번에 해당 서버와 대화하고 싶을 것입니다.

다음 코드에서 대상 서버 스크립트가 사용자의 경우에 따라 변경되는 작업을 수행하는 데 사용할 인수 문자열을 작성합니다. 예를 들어 문자열을 보내려는 경우가 있습니다.

name = 브렌든 & 패스워드 = abcde

사용자가 응용 프로그램에 등록 할 때 서버에 연결하여 서버가이 정보를 데이터베이스에 저장할 수 있도록합니다.

시작하자. 다음 코드를 사용하여 NSURLSession POST 요청을 생성하려고합니다.

// Create the configuration, which is necessary so we can cancel cacheing amongst other things.
NSURLSessionConfiguration * defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
// Disables cacheing
defaultConfigObject.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
NSURLSession * defaultSession = [NSURLSession sessionWithConfiguration:defaultConfigObject delegate:self delegateQueue:[NSOperationQueue mainQueue]];

NSString * scriptURL = [NSString stringWithFormat:@"https://server.io/api/script.php"];
//Converts the URL string to a URL usable by NSURLSession
NSMutableURLRequest * urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:scriptURL]];
NSString * postDataString = [NSString stringWithFormat:@"name=%@&password=%@", [self nameString], [self URLEncode:passwordString]];
[urlRequest setHTTPMethod:@"POST"];
[urlRequest setHTTPBody:[postDataString dataUsingEncoding:NSUTF8StringEncoding]];

NSURLSessionDataTask * dataTask = [defaultSession dataTaskWithRequest:urlRequest];
// Fire the data task.
[dataTask resume];

위의 코드는 방금 서버에 대한 POST 요청을 만들고 실행했습니다. 상황에 따라 스크립트 URL 및 POST 데이터 문자열이 변경된다는 점을 기억하십시오. 이 글을 읽는다면, 그 변수들을 어떻게 채울 지 알게 될 것입니다.

또한 URL 인코딩을 수행하는 작은 메소드를 추가해야합니다.

- (NSString *)URLEncode:(NSString *)originalString encoding:(NSStringEncoding)encoding
{
    return (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(
        kCFAllocatorDefault,
        (__bridge CFStringRef)originalString,
        NULL,
        CFSTR(":/?#[]@!$&'()*+,;="),
        CFStringConvertNSStringEncodingToEncoding(encoding));
}

따라서 서버가이 데이터 처리를 마칠 때 iOS 앱으로 돌아갑니다. 그래서 우리는이 수익을 처리해야하지만 어떻게해야합니까?

이벤트 주도 프로그래밍을 사용하고 NSURLSession의 대리자 메서드를 사용합니다. 즉, 서버가 응답을 보내면 이러한 메소드가 트리거링을 시작합니다. 다음 5 가지 방법은 ENTIRE 요청이있을 때마다 요청할 때마다 트리거됩니다.

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response
 completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler;

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data;

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error;

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler;

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler;

아래에서 위의 방법을 컨텍스트에서 사용하는 것을 볼 수 있습니다. 각자의 목적은 애플 덕분에 꽤 자명하다. 그러나 나는 어쨌든 그들의 용도에 대해 논평했다.

// Response handling delegates
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response
 completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler{
    // Handler allows us to receive and parse responses from the server
    completionHandler(NSURLSessionResponseAllow);
}

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data{

    // Parse the JSON that came in into an NSDictionary
    NSError * err = nil;
    NSDictionary * jsonDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&err];

    if (!err){ // if no error occurred, parse the array of objects as normal
        // Parse the JSON dictionary 'jsonDict' here
    }else{ // an error occurred so we need to let the user know
        // Handle your error here
    }
}

// Error handling delegate
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
    if(error == nil){
        // Download from API was successful
        NSLog(@"Data Network Request Did Complete Successfully.");
    }else{
        // Describes and logs the error preventing us from receiving a response
        NSLog(@"Error: %@", [error userInfo]);

        // Handle network error, letting the user know what happened.
    }
}

// When the session receives a challenge (because of iOS 9 App Transport Security blocking non-valid SSL certificates) we use the following methods to tell NSURLSession "Chill out, I can trust me".
// The following is not necessary unless your server is using HTTP, not HTTPS

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler{
    if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]){
        if([challenge.protectionSpace.host isEqualToString:@"DomainNameOfServer.io"]){
            NSURLCredential * credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
            completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
        }
    }
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler{
    if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]){
        if([challenge.protectionSpace.host isEqualToString:@"DomainNameOfServer.io"]){
            NSURLCredential * credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
            completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
        }
    }
}

그래서 그게 다야! 이것이 iOS 9에서 API 요청을 보내고 받고 구문 분석하는 데 필요한 모든 코드입니다. 좋아요 ... 그것은 많은 종류의 코드였습니다. 그러나 위와 같이 올바르게 구현하면 오류를 방지 할 수 있습니다! 항상 위에 제시된 오류를 처리하도록하십시오.

양식 인코딩을 사용하여 요청 보내기

URL 인코딩은 임의의 데이터를 인코딩하는 데 광범위하게 호환되는 방법입니다. 그러나 모든 비 ASCII 바이트가 3 문자 코드로 바뀌기 때문에 이진 데이터 (예 : 사진)를 업로드하는 것은 상대적으로 비효율적입니다. 또한 첨부 파일을 지원하지 않으므로 별도의 필드로 파일 이름과 파일 데이터를 전달해야합니다.

효율적이고 실제로 서버 측의 파일처럼 보이는 방식으로 사진을 업로드한다고 가정합니다. 한 가지 방법은 대신 양식 인코딩을 사용하는 것입니다. 이렇게하려면 다음과 같이 NSURLSession을 만드는 코드를 편집하십시오.

UIImage * imgToSend;

// 2nd parameter of UIImageJPEGRepresentation represents compression quality. 0 being most compressed, 1 being the least
// Using 0.4 likely stops us hitting the servers upload limit and costs us less server space
NSData * imageData = UIImageJPEGRepresentation(imgToSend, 0.4f);

// Alternatively, if the photo is on disk, you can retrieve it with
// [NSData dataWithContentsOfURL:...]

// Set up the body of the POST request.

// This boundary serves as a separator between one form field and the next.
// It must not appear anywhere within the actual data that you intend to
// upload.
NSString * boundary = @"---------------------------14737809831466499882746641449";

// Body of the POST method
NSMutableData * body = [NSMutableData data];

// The body must start with the boundary preceded by two hyphens, followed
// by a carriage return and newline pair.
//
// Notice that we prepend two additional hyphens to the boundary when
// we actually use it as part of the body data.
//
[body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];

// This is followed by a series of headers for the first field and then
// TWO CR-LF pairs.
[body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"tag_name\"\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];

// Next is the actual data for that field (called "tag_name") followed by
// a CR-LF pair, a boundary, and another CR-LF pair.
[body appendData:[strippedCompanyName dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];

// Encode the filename and image data as the "userfile" CGI parameter.
// This is similar to the previous field, except that it is being sent
// as an actual file attachment rather than a blob of data, which means
// it has both a filename and the actual file contents.
//
// IMPORTANT: The filename MUST be plain ASCII (and if encoded like this,
//            must not include quotation marks in the filename).
//
NSString * picFileName = [NSString stringWithFormat:@"photoName"];
NSString * appendDataString = [NSString stringWithFormat:@"Content-Disposition: form-data; name=\"userfile\"; filename=\"%@.jpg\"\r\n", picFileName];
[body appendData:[appendDataString dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[@"Content-Type: application/octet-stream\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[NSData dataWithData:imageData]];

// Close the request body with one last boundary with two
// additional hyphens prepended **and** two additional hyphens appended.
[body appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];

// Create the session
// We can use the delegate to track upload progress and disable cacheing
NSURLSessionConfiguration * defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
defaultConfigObject.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
NSURLSession * defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate: self delegateQueue: [NSOperationQueue mainQueue]];

// Data uploading task.
NSURL * url = [NSURL URLWithString:@"https://server.io/api/script.php"];
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url];
NSString * contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",boundary];
[request addValue:contentType forHTTPHeaderField:@"Content-Type"];
request.HTTPMethod = @"POST";
request.HTTPBody = body;
NSURLSessionDataTask * uploadTask = [defaultSession dataTaskWithRequest:request];
[uploadTask resume];

이것은 이전과 마찬가지로 NSURLSession 요청을 생성하고 시작하며, 결과적으로 델리게이트 메서드는 똑같은 방식으로 동작합니다. 이미지가 전송되는 스크립트 (변수 url )가 이미지를 예상하고 올바르게 구문 분석 할 수 있는지 확인하십시오.



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow