출처
: http://blog.saltfactory.net/86
HTML5의 인기 때문에 요즘 하이브리드 앱의 인기는 계속적으로 상승하는 분위기이다.
HTML5에서 추가된 속성으로 마치 네이티브와 같은 개발을 웹 개발 방법으로 할수 있게 되었다.
또한 실제 네이티브 코드로 작성하면 복잡하고 어려운 부분도 javascript나 css3로 편리하게
구현할 수도 있다.
하지만 내장 브라우저(webkit)에서만 모든 프로세스를 처리하기에는 브라우저 자체도
하나의 app이기 때문에 메모리와 cpu 사용의 한계가 있다. HTML5로 만들어서 사용하는 웹 앱이
과연 성능이 네이티브 앱과 같은 성능을 내어 주는지 궁금하기도 해서 여러 자문을 구하는데
몇몇 기능을 제외하고 순수 javascript로만 구현하면 네이티브보다 성능이 좋게 나오지 않는다고
한다는 이야기를 들었다.
javascript 인터프리터로 모든것을 처리하기에는 한계가 있기 때문에 핵심부분 데이터 처리나
device api를 호출하기 위해서 javascript에서 objective-c 의 메소드를 호출해야하는 경우가
생긴다.
iOS는 이러한 webkit의 한계를 극복하기 위해서 objective-c의 메소드를 호출하는 방법을
제공하고 있다. 물론 objective-c에서 javascript를 호출하는 방법도 제공한다.
iOS는 이러한 기능을 만들어 놓았기 때문에 개발자가 보다 더 나은 방법으로
네이티비브 코드의 한계와 웹 코드의 한계를 이해하고 더 나은 방법으로
하이브리드 앱을 개발할 수 있다.
1. Objective-C에서 Javascript를 호출하는 방법
Objective-C에서 Javascript를 호출하는 방법은 문자열을 evaluation 시키는 방법이다.
AppViewController를 새로 생성하고 Outlet으로 UIWebView인 appWebView를 @property와
@synthesize를 만든다. 그리고 UIBarButtonItem을 누르면 onCallJavascriptButton: 이
호출될 수 있게 선언한다.
그리고 구현클래스에서 IBAction와 연결된 onCallJavascriptButton: 메소드를 구현하는데
이때 stringByEvaluationJavascriptFromString: 메소드를 이용해서 Javascript의 메소드를
문자열로 입력한다.
실제 Javascript는 app.html 파일 안에 <script>callJavascriptFromObjectiveC()</script>에
구현이 된다. 즉, stringByEvaluationJavascriptFromString: 메소드를 이용해 Javascript의 이름을
이용하여 Javascript 메소드를 호출하는 것이다.
AppViewController.h
#import <UIKit/UIKit.h>
@interface AppViewController : UIViewController<UIWebViewDelegate>
@property (retain, nonatomic) IBOutlet UIWebView *appWebView;
- (IBAction)onCallJavascriptButton:(id)sender;
@end
#import "AppViewController.h"
@interface AppViewController ()
@end
AppViewController.m
@implementation AppViewController
@synthesize appWebView;
- (IBAction)onCallJavascriptButton:(id)sender {
[appWebView stringByEvaluatingJavaScriptFromString:@"callJavascriptFromObjectiveC();"];
}
- (void)viewDidLoad
{
[super viewDidLoad];
appWebView.delegate = self;
NSString *path = [[NSBundle mainBundle] pathForResource:@"app" ofType:@"html" inDirectory:@"www"];
NSURL *url = [NSURL fileURLWithPath:path];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[appWebView loadRequest:request];
}
app.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<meta name = "viewport" content = "width = device-width"/>
<title>Hybrid App</title>
<script type="text/javascript">
function callJavascriptFromObjectiveC() {
alert('called javascript function by objective-c');
}
</script>
</head>
<body>
<div id="container">
Hello Hybrid App
</div>
</body>
</html>
2. Javascript에서 Objective-C를 호출하는 방법
Javascript에서 Objective-C를 호출하는 방법은 커스텀 Scheme를 이용하는 방법이다.
안드로이드에서도 URI로 리소스에 접근할수 있는데 이렇게 외부에서 URI scheme를 이용해서
특정 메소드에서 접근하는 방법을 사용하면 웹에서 javascript나 hyper text로 어플리케이션의
특정 메소드를 호출할 수 있는 브릿지를 구현할 수 있다. app.html에 button을 하나 만들고
버턴이 눌러지면 javascript에 구현한 callObjectiveCFromJavascript() 를 호출한다.
이 메소드 안에는 window.location를 이용해서 커스텀 scheme (ex. jscall://callObjectiveCFromJavascript)로 이동하는 코드를 넣어둔다.
그러면 webView의 delegate 메소드의 webView: souldStartLoadWithRequest:navigationType
메소드를 호출하는데 이 때 특정 메소드가 실행되게 구현하면 된다.
예제 코드에서는 objective-c 메소드 속에 단순하게 Log를 출력하는 것만 넣어 두었다.
app.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<meta name = "viewport" content = "width = device-width"/>
<title>Hybrid App</title>
<script type="text/javascript">
function callJavascriptFromObjectiveC() {
alert('called javascript function by objective-c');
}
function callObjectiveCFromJavascript(){
window.location="jscall://callObjectiveCFromJavascript";
}
</script>
</head>
<body>
<div id="container">
<h3>Hello Hybrid App </h3>
<button onclick="callObjectiveCFromJavascript();">Call Objective-C</button>
</div>
</body>
</html>
AppViewController.m
- (void)callObjectiveCFromJavascript {
NSLog(@"called objective-c from javascript");
}
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType
{
if ([[[request URL] absoluteString] hasPrefix:@"jscall:"]) {
NSString *requestString = [[request URL] absoluteString];
NSArray *components = [requestString componentsSeparatedByString:@"://"];
NSString *functionName = [components objectAtIndex:1];
[self performSelector:NSSelectorFromString(functionName)];
return NO;
}
return YES;
}
JavaScript to Native
안드로이드에서 Bridge를 사용해서 Web에서 JavaScript 호출하면 Native를 호출 할수가 있었다.
안드로이드 예제
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.addJavascriptInterface(new AndroidBridge(),"MyScript");
public class AndroidBridge {
public void webViewClose() {
Log.d(TAG, "웹뷰 닫기~")
}
}
iOS7 이후 부터는 안드로이드 처리하는게 가능하다.
(그 전에는 shouldStartLoadWithRequest
에서 scheme 파싱해서 처리 했었다.)
자세한 내용은 다음 WWDC를 참조 (WWDC 615 - Integrating JavaScript into Native Apps)
사용방법은 다음과 같다.
- 호출할 프로토콜을 JSExport상속받아 생성
자바스크립트 처리할 클래스 만들고 프로토콜 처리
@objc protocol ScriptProtocol : JSExport { func webViewClose() } @objc class MyScript: NSObject { var webView: UIWebView init(webView: UIWebView) { self.webView = webView } } extension MyScript: ScriptProtocol { func webViewClose() { print("웹뷰 닫기~") } }
WebView 로딩후 자바스크립트로 클래스와 함수 등록
웹뷰 에서 delegate 설정하고
webViewDidFinishLoad
에서 JavaScript를 실행시켜주면 된다.webView.delegate = self ... extension ViewController: UIWebViewDelegate { func webViewDidFinishLoad(_ webView: UIWebView) { let key = "documentView.webView.mainFrame.javaScriptContext" if let context = webView.value(forKeyPath: key) as? JSContext { context.setObject(MyScript(self.webView), forKeyedSubscript: "MyScript" as (NSCopying & NSObjectProtocol)!) context.evaluateScript("function webViewClose() {webViewClose();}") } } }
웹(JavaScript)에서는 다음과 같이 설정 되어 있으면 된다.
function webClose() {
window.MyScript.webViewClose();
}
<button onclick="webClose();">종료</button>
'IT_Programming > Objective-C · Swift · iOS' 카테고리의 다른 글
[펌] 이펙티브 오브젝티브씨 | Effective Objective-c (0) | 2014.09.10 |
---|---|
[iOS] App 이름 지역화하기. (0) | 2014.03.26 |
[iOS] 다국어 스트링 지원하기 (0) | 2014.03.26 |
[iOS] 기기별 아이콘 이미지 처리 (0) | 2014.03.26 |
[iOS] 팝업 윈도우를 UIWebView에서 사용하기 (0) | 2010.08.30 |