본문 바로가기

iPhone

UIWebView 와 Application (App) 간의 통신


UIWebView 에 특정 페이지를 Request 하고나면, Application 의 메소드를 실행하고 싶지만 말 그대로 웹뷰이기 때문에 불가능한 경우가 많이 있습니다.

예를 들어, 어플 상에서 UITableView 같은 컨트롤에 리스트를 출력하고, 선택하면 UIWebView 로 상세 페이지를 보여준다고 가정합니다. 이런 경우, 페이지간 이동을 Navigation으로 해서 Back 을 통해 다시 리스트로 돌아가는 방법이 있을 뿐, UIWebView 내에서 <input type=submit ...> 같은 버튼을 통해서 Back 하거나 새로운 UIViewController 로 연결할 수가 없습니다.

이런 경우에 해결책은 다름 아닌, UIWebViewDelegate 에 있습니다.

구현 시나리오는 이렇습니다.

1. Web 과 App 이 통신할 수 있도록 하나의 Protocol 을 정의.
2. Web 상에서 버튼 등 Request가 일어나게 하는 컨트롤을 이용해 Protocol 을 써줍니다. 
3. App 상에서 webView: shouldStartLoadWithRequest: navigationType: 메서드를 구현하여 정의된 Protocol 을 찾아 구현 될 메서드를 실행.

저는 Javascript를 이용해 구현 해 봤습니다. 

1. Protocol 을 정의
"toApp:relationButton:param1"

저는 Protocol 을 이렇게 정의 했습니다. 원하시는 형태로 마음껏 구성하시되 3번 항에서 파싱만 완벽히 맞춰 주시면 되겠죠.
위 프로토콜을 정의하면서 파싱을 고려해야 하는데요, 저는 : 로 문자열을 나눈 후, 첫번째와 두번째 텍스트 각각이 toApp, relationButton 일 경우 세번째 텍스트인 param1 를 App 상에서 받아서 처리하기 위해서 Protocol 을 위와 같이 정의 한 것입니다.

2. Web 상에서 버튼 등 Request가 일어나게 하는 컨트롤을 이용해 Protocol을 써준다.
Web 상에서는 버튼등의 컨트롤을 사용해 Request가 일어나게 할 수 있죠. 이를 이용해 Javascript로 버튼이 onClick 시에 스크립트를 실행하도록 할 것이고, 스크립트에서는 Protocol을 써줄것입니다.

2-1. 스크립트에 Protocol을 써 주는 메서드를 정의
<script type="text/javascript">
     function eventtoApp(param1) {
          document.location = "toApp:relationButton:" + param1;
     }
</script>

2-2. HTML Body 내에서 버튼의 onClick 이벤트에 스크립트 실행 연결
<input type=submit onClick="javascript:eventtoApp('P0001001')";/>

3. App 상에서 webView: shouldStartLoadWithRequest: navigationType: 메서드를 구현하여 정의된 Protocol 을 찾아 구현 될 메서드를 실행.
마지막으로, App상에서 UIWebViewDelegate내 메서드인 webView: shouldStartLoadWithRequest: navigationType: 메서드를 구현 해 줍니다.
이 메서드는 UIWebView 컨트롤에서 Request가 일어날 때마다 들어오게 되는데요. 메서드의 반환형식은 BOOL 타입입니다. YES를 반환할 경우 UIWebView 에서 컨텐츠를 로드하게 됩니다.
그래서, 1번항에서 미리 정의 했던 Protocol 을 파싱하는 로직을 구현하되, 매치되지 않는 경우에는 return YES; 를 해 줘야 합니다. 정확히 매치되는 경우에만 어플에서 처리를 할 수 있도록 하면 되는 것이죠. 바로, 이 부분이 앞서 Protocol 에서 App 상에서 필요한 것은 param1 뿐이지만, toApp:relationButton 와 같은 전제를 붙인 이유입니다. 정확히 toApp:relationButton 가 매치되는 경우에만 파싱을 해서 param1을 가져오기 위함인 것이죠.

파싱은 간단하게 구현 하실 수 있습니다.

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request 

 navigationType:(UIWebViewNavigationType)navigationType {

NSString *requestString = [[request URL] absoluteString];

NSArray *components = [documentLocation componentsSeparatedByString:@":"];

if([components count] <= 1) {

return YES;

}


if ([(NSString *)[components objectAtIndex:0] isEqualToString:@"toApp"]) {

// 1번째 문자열이 toApp 경우

if([(NSString *)[components objectAtIndex:1] isEqualToString:@"relationButton"]) {

// 2번째 문자열이 relationButton 경우

NSLog(@"%@", [components objectAtIndex:2]); // param1

return NO;

}

}

}


Request가 일어난 뒤 받아온 문자열을 : 로 구분하여 나눈 뒤, 첫번째 텍스트와 두번째 텍스트가 각각 toApp, relationButton 인 경우에만 3번째 param1 을 받아와서 사용합니다. 그 외의 경우에는 return YES; 를 통해 UIWebView 컨트롤에서 컨텐트를 로드하도록 나두고요. 

여기 예문에서는 NSLog로 어플에서 잘 받아지는가만 출력 해 봤습니다. param1을 어플 내 화면에 출력하거나, 다른 메서드를 실행하기 위한 파라미터로 사용하거나 하는 등.. 결과적으로는, Web에서 App을 호출하는 형태를 구현하실 수 있습니다. :)