[iOS_OSX] 웹 컨텐츠에 접근하기

URL Loading System

파운데이션 프레임워크 안에는 URL 기반으로 네트워크상에 위치한 리소스에 접근하는 도구들이 정의되어 있다. 네트워크를 통한 리소스 접근은 웹 브라우저를 생각해보면 되는데, 서버에 요청을 보내어 그 요청에 맞는 결과물을 전송받게 되는데, 이러한 일련의 과정을 제어하는 몇 가지 클래스를 묶어서 URL Loading System이라고 한다. 물론 파운데이션에는 이 URL Loading System 외에도 인증, 캐싱, 프로토콜을 제어할 수 있는 추가적인 클래스들이 정의되어 있어서 인터넷 혹은 네트워크와 관련된 기능들이 잘 추상화 되어 있다. 따라서 특별한 경우가 아니면 BSD소켓 프로그래밍까지는 잘 몰라도 네트워크 기능을 어느 정도 구현할 수 있다.

잠깐 코코아 터치의 네트워크 관련 프레임 워크들을 살펴보면, 가장 하층부에는 BSD Socket이 자리잡고 있다. 그 바로 위쪽에 CoreNetwork 프레임 워크가 있고, 또 CoreFoundation Networking 이 있다. 이러한 하부 구조 위의 최상단에는 Foundation 의 URL Loading System 이 있다.

어쨌거나 URL 로딩 시스템은 일반적인 http, https, ftp, file 등의 프로토콜에 필요한 기능을 거의 모두 구현해주고 있으므로 웹과 관련한 네트워킹은 비교적 손쉽게 처리할 수 있다.

NSURLRequest / NSURLResponse / NSURLConnection

URL 로딩 시스템을 이루는 세 줄기는 NSURLRequest, NSURLResponse, NSURLConnection 의 세 개의 클래스이다. 네트워크를 통해 요청을 보내고, 응답을 받고, 응답이 성공적인 경우 그에 따라 컨텐츠를 전송받게 된다. 이 과정의 각 단계에서 주요한 키 역할을 하는 것이 이 세 종류의 클래스이다.

NSURLRequest

서버에 보낼 요청을 표현하는 객체이다. 요청을 받을 곳의 URL을 담아두는데, POST 방식의 요청인 경우에는 bodyData에 필요한 인자값들을 담아둘 수 있다.

NSURLRequest *theRequest =
[NSURLRequest requestWithURL:[NSURL urlWithString:@"http://www.apple.com"]
                 cachePolicy:NSURLRequestUseProtocolCachePolicy
             timeoutInterval:60.0];

요청을 만들었으면 connection을 만든다. connection을 생성하면 자동으로1 연결을 시도한다. 서버로 연결되면 reponse를 받고 데이터를 내려받게 되는데, 이 때 각 상황에 맞는 동작을 처리하기 위해서 connection의 delegate가 필요하다. 델리게이트는 1) 응답을 받거나 2) 데이터를 다운로드하고 3) 다운로드가 완료되거나 4) 오류로 중지되었을 때의 상황을 처리하기 위해서 다음 4가지 메소드를 구현해야 한다.

NSURLConnection

연결 객체를 생성하고 실제로 연결하는 과정은 다음과 같다. recievedData는 연결을 통해 내려받는 데이터를 저장할 변수로 따로 선언해 두었다고 가정한다. 연결 객체가 성공적으로 만들어지면 데이터를 담을 변수의 객체를 만들고 이를 초기화한다.

 NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if(connection) {
    receivedData = [NSMutableData data]; [connection start];
}

Connection 델리게이트

서버에 요청을 보내면 서버는 실제 요청한 데이터를 내려보내기 전에 응답(NSURLResponse)을 먼저 보낸다. 응답에는 내려보내는 데이터의 MIME 타입과 같은 정보들이 담겨 있다. 응답을 보낸 직후 서버는 요청한 URL의 컨텐츠를 전송해준다. connection 객체가 응답을 받게 되면 델리게이트에게 -connection: didReceiveResponse: 메시지를 보내게 된다.

이후 데이터를 다운로드 받는 중간 중간에 계속해서 -connection: didReceiveData: 를 보내 전송 받은 데이터를 델리게이트에게 전달한다. 델리게이트는 미리 생성해놓은 NSMutableData 객체에 전달받는 데이터를 더해준다.

데이터 전송이 완료되면 connection 객체는 델리게이트에게 -connectionDidFinishLoading: 메시지를 보낸다. 만약 오류가 나서 전송이 실패하면 -connection: didFailWithError: 메시지를 보내는데, 여기서는 이미 받았던 데이터의 length를 0으로 만들어 중단된 데이터를 제거한다.

다음은 델리게이트 메소드의 예시이다.

-(void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response
{
    NSLog(@"Resoponse - MIME TYPE : %@", response.MIMEType);
}

-(void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data
{
    [receivedData appendData:data]
}

-(void)connection:(NSURLConnection*)connection didFailWithError:(NSError *)error
{
    NSLog(@"Fail to loading - %@", error.localizedReason);
    [receivedData setLength:0];
}

데이터를 전송받은 다음에는 이 데이터를 사용하면 된다. 예를 들어 theRequest의 URL이 특정한 이미지 파일이었다면 다음과 같이 이미지를 스크롤뷰에 넣어 표시할 수 있다. self 가 뷰 컨트롤러라고 가정할 때의 코드이다.

-(void)connectionDidFinishLoading:(NSURLConnection*)connection
{
    UIImage *image = [UIImage imageWithData:receivedData];
    UIImageView *imaageView = [[UIImageView alloc] initWithImage:image];
    UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:self.view.bound];
    scrollView.contentSize = image.size;

    [self.view addSubView:scrollView];
}

지난 번에 스크롤 뷰와 관련한 글에서도 살펴보았듯이 만약 스크롤뷰에서 확대/축소를 지원하려면 최대/최소 scale 값을 지정해주고, 뷰 컨트롤러가 UIScrollView의 델리게이트가 되어 어떤 뷰를 확대 축소할 것인지 알려주는 메소드를 구현해주어야 한다.


  1. 실제로는 자동으로 접속을 시도하지 않는 것 같다. start 메시지를 보내서 수동으로 연결을 시도하도록 해야 한다.

    -(void)connection: didReceiveResponse:
    -(void)connection: didReceiveData:
    -(void)connection: didFailWithError:
    -(void)connectionDidFinishLoading: