UIWebView和H5的JS交互那些事儿

iOS与H5交互是JS之间的相互调用,这里我们使用的UIWebView和JavaScriptCore来交互,H5调用客户端的JS有两种形式,一种是H5去取客户端的相关数据,然后H5自己进行业务处理;另一种是H5通过回调的形式,调用客户端的方法,让客户端进行业务处理。

1.H5调用客户端方法,获取客户端数据
这里有个业务场景,假如说进入一个页面之后需要用户的user信息,这个时候H5就调用js方法直接去取数据,为了保持H5端代码的可读性,我们的H5业务是在window下面花了一个TEST来进行业务处理(类似调用的时候是window.TEST进行处理),所以我们绑定的时候和平时是不一样的需要用到JSExport这个类
H5直接在window下进行方法处理的时候

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
    __weak typeof(self) weakSelf = self;
    JSContext *context= [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    context[@"getShareUrl"] = ^(NSString *articleId, NSString *title, NSString *imageUrl, NSString *shareUrl, NSString *url){
      //获取H5给的参数信息,进行方法处理。
        });
    };
    return YES;
}

上面的代码就是在webview的delegate方法里绑定了一个'getShareUrl'的JS方法,然后回调给客户端5个参数,我们通过绑定JSContext上下文和webview绑定之后就可以交互了。

H5在window下面挂了TEST这样二级window的时候
为了能够相应这样的JS调用,继承JSExport,创建一个代理相应类

@protocol ReceiveJSObjectProtocol 
//获取客户端user信息
-(NSString *)userInfo;
//传给客户端一个内容
-(void)title:(NSString *)content;
//调用客户端登录方法
-(void)login;

@end

创建一个对象,实现我们刚才穿件的代理方法

//.h文件
@interface SpeakingReadOverJsExport : NSObject

@property (nonatomic,weak) UIWebView *webView;

@property (nonatomic,weak) id delegate;

@end

//.m文件
@implementation SpeakingReadOverJsExport

-(void)newShare:(NSString *)title :(NSString *)description :(NSString *)url :(NSString *)imageUrl :(NSString *)channel :(NSString*)funcname{
    if ([self.delegate respondsToSelector:@selector(newShare::::::)]) {//类似于这样的js调用真是坑爹,我只想说,H5的,你们能规范点儿吗,坑啊!
        [self.delegate newShare:title :description :url :imageUrl :channel :funcname];
    }
}

-(void)tips:(NSString *)content{
    if ([self.delegate respondsToSelector:@selector(tips:)]) {
        [self.delegate tips:content];
    }
}

-(void)title:(NSString *)content{
    if ([self.delegate respondsToSelector:@selector(title:)]) {
        [self.delegate title:content];
    }
}

-(void)login{
    if ([self.delegate respondsToSelector:@selector(login)]) {
        [self.delegate login]; //调用登录方法
    }
}

-(NSString *)userInfo
{
    if ([self.delegate respondsToSelector:@selector(userInfo)]) {
        return [self.delegate userInfo]; //这个就可以把客户端的user信息回传给H5了
    }
    else
    {
        return @"";
    }
}

这一切执行的前提是客户端能够相应到,H5在调用客户端JS的时候一定要等webview加载完成,JSContext上下文和webview绑定之后,我们的H5在和客户端交互的时候,没有等客户端加载完成就去调用客户端JS方法,结果没有响应到,在当初项目比较紧的情况下想了一个办法,在我们绑定完成之后,客户端调用H5的JS方法告诉他们,是不是很楼比啊。

- (void)webViewDidFinishLoad:(UIWebView *)webView{
    if (!webView.isLoading) {//这个是为了解决我们H5给的页面里多次load的问题
        JSContext *context= [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
        __weak typeof(self) weakSelf = self;
        SpeakingReadOverJsExport *jsExport = [SpeakingReadOverJsExport new];
        jsExport.delegate = _jsManger;
        _jsManger.webView = webView;
        context[@"TEST"] = jsExport;
        [_jsManger responseHsCallBack:@"init" dict:@{}];//通知H5绑定成功,可以交互了
    }
    DebugLog(@"webview finish");
}

最近仔细想了想其实没有哪家公司的H5是这么玩的,H5的window和controller一样类似,也是有自己的生命周期的,所以就在window. onload 方法里调用客户端的方法就不存在JSContext上下文没有绑定好的问题了,有一点差异的是,绑定的时间节点变成了在webViewDidStartLoad代理方法里执行

- (void)webViewDidStartLoad:(UIWebView *)webView{
    DebugLog(@"webview start");
    JSContext *context= [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    __weak typeof(self) weakSelf = self;
    SpeakingReadOverJsExport *jsExport = [SpeakingReadOverJsExport new];
    jsExport.delegate = _jsManger;
    _jsManger.webView = webView;
    context[@"TEST"] = jsExport;
}

这种有二级window的调用有一个很坑爹的问题就是,取客户端信息的时候,

客户端调用H5的JS方法

        JSContext *context= [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
        context.exceptionHandler = ^(JSContext *context, JSValue *exception) {
            [JSContext currentContext].exception = exception;
            DebugLog(@"exception:%@",exception);
        };
        NSDictionary *dict = @{
                               @"status":@(status),
                               };
//调用H5的名为h5Back 的JS方法 ,里面是两个参数,参数一定要加单引号
        NSString *text = [NSString stringWithFormat:@"h5Back('%@','%@')",@"",dict];
        [context evaluateScript:text];

好了,啰里啰嗦就是这么多,欢迎大家讨论补充!

你可能感兴趣的:(UIWebView和H5的JS交互那些事儿)