iOS WebView Javascript send value to native objective-c

iOS因為每次送審必須花上幾天的審核時間,所以很多需要經常變動的頁面常會使用web的方式來進行設計,但iOS因為WebView沒開放Nitro,所以速度上比起原生的差了不少,然而部分還是可以透過些小技巧來加速,畢竟如Google+ ,Facebook等為了常變更timeline也都會採用此方式。

原生的SDK就有提供Native(Objective-C)直接來呼叫Javascript的function或變數,也可以一並取得function、變數的值,方法很簡單,就下面這一行:
[webView stringByEvaluatingJavaScriptFromString:@"YOUR_FUNCTION_NAME"]

這方式相當的好用,甚至可以直接將Javascript需要另外執行的function等東西直接用這方式塞入html中,可以方變得改變webview所呈現的內容,但如果光靠這種方式還是不太夠的,畢竟這是單向的由Native(Objective-C)來監看webview的狀態,使用個timer在背景掃描也會造成系統的負載,畢竟在Mobile上能省電就省電,一個太耗效能的App沒多久就會被使用者發現,然後就被刪除,所以必須有方法來由Javascript用Event的概念直接將值傳回Native(Objective-C),原理很簡單,就是使用URL scheme的方式來傳,詳細的問題點下面會再說明,先直接來看作法:


首先Html Javascript這邊的準備如下:
sendToNative('哈哈哈哈哈哈');

function sendToNative(msg){
document.location = 'YOUR_SCHEME://myText='+encodeURIComponent(msg);
};

乍看之下不就是把當前的URL改掉,按照正常的WebView不就會直接的轉跳到其他頁面去,但是在webView的Delegate是可以避免掉這問題,方法如下

//init webview
UIWebView *webView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
NSString *path = [[NSBundle mainBundle] pathForResource:@"YOUR_FILE_NAME" ofType:@"html" inDirectory:@"YOUR_FOLDER_NAME"];
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:path]]];
[webView setDelegate:self];
[self.view addSubview:webView];;


//webView delegate
-(BOOL) webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
//取得Scheme
NSString *scheme = [[[request URL] scheme] lowercaseString];

//如果scheme與自己定義的相同
if([scheme isEqualToString:@"YOUR_SCHEME"]) {
NSString *url = [NSString stringWithFormat:@"%@", [request URL]];
NSDictionary *myDic = [self urlToDictionaryWithURL:url scheme:scheme];

return NO;
}
return YES;
}

//將所有參數轉為Dictionary
-(NSMutableDictionary *) urlToDictionaryWithURL:(NSString *)url scheme:(NSString *)scheme{
NSArray *tempArray = [[url substringFromIndex:scheme.length+3] componentsSeparatedByString:@"&"] ;
NSMutableDictionary *myDic = [NSMutableDictionary new];

for (int i = 0; i < [tempArray count]; i++) { NSString *myString = [[tempArray objectAtIndex:i] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; NSArray *temp = [myString componentsSeparatedByString:@"="]; [myDic setValue:[[temp objectAtIndex:1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] forKey:[temp objectAtIndex:0]]; } return myDic; }

webView的Delegate 當return為YES時會允許webview的url作他該做的動作,NO時則不會進行,但還是可以順利取得當時的url來運用,上面範例中的"urlToDictionaryWithURL"為將URL參數轉為Dictionary方便使用,URL參數格式如下,也就是GET的格式,使用"&"來做區隔。
YOUR_SCHEME://a=11111&b=22222&c=33333


但使用URL來傳值也是會有些限制,在字數、部分符號上都會造成問題,根據"LinkedIn for iPad: The Native/Web Messaging Bridge and WebSockets"這篇文章所提還是會建議使用URL方式來呼叫Native(Objective-C),再用"stringByEvaluatingJavaScriptFromString"來取得Javascript參數,可以避免URL的限制,文章中也提到使用WebSocket來進行傳送,是效率最高,但相同也會有些其他問題,最好的方式應該還是混合使用。



相關連結
LinkedIn for iPad: The Native/Web Messaging Bridge and WebSockets
iOS Developer Library
iOS Developer Library - WebView

沒有留言:

張貼留言