iOS日常开发

iOS:JavaScriptCore实现Native和JS交互

2017-12-05  本文已影响42人  风与鸾

前言

自从iOS7以后苹果提供了JavaScriptCore用于实现Native和JS交互

实践

目前项目有些分享页面用H5实现的,后端返回一个URL,前端这边用webView展现这个界面。


1341512444174_.pic.jpg

需求

H5这边需要前端这边提供一个token,同时由H5这边返回需要分享的信息(title,iconImage,需要分享的URL),点击邀请好友调用Native端代码弹出分享窗口。

1351512444422_.pic.jpg

开始之前如果没有JavaScriptCore基础的,需要先去了解JSContext,JSExport,JSValue相关知识了解了这些你就能大概知道Native是如何与JS交互的。

通过获取的webView URL加载webViewController

//如果需要先清理缓存的可以这么做
    NSHTTPCookie *cookie;
    NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    for (cookie in [storage cookies]){
        [storage deleteCookie:cookie];
    }
    //清除UIWebView的缓存
    [[NSURLCache sharedURLCache] removeAllCachedResponses];
    NSURLCache * cache = [NSURLCache sharedURLCache];
    [cache removeAllCachedResponses];
    [cache setDiskCapacity:0];
    [cache setMemoryCapacity:0];

   NSMutableURLRequest *requset = [NSMutableURLRequest      requestWithURL:[NSURL URLWithString:urlString]];
    [self.webView loadRequest:requset];

在webView的delegate里面

#pragma mark webViewDelegate
- (void)webViewDidFinishLoad:(UIWebView *)webView {
    self.title = [self.webView stringByEvaluatingJavaScriptFromString:@"document.title"];
    
    self.jsContext = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    /**注入JS需要的OC对象*/
    JSNativeDelegate *jsDelegate = [[JSNativeDelegate alloc] initWith:self];
    self.jsContext[@"Toyun"] = jsDelegate;
    self.jsContext.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {
        context.exception = exceptionValue;
        NSLog(@"JKRWebViewController异常信息:%@", exceptionValue);
    };
}

JSNativeDelegate.h 的代码

#import <Foundation/Foundation.h>
#import <JavaScriptCore/JavaScriptCore.h>

@protocol JSObjcDelegate<JSExport>
- (void)shareAction;
- (void)getToken;
- (void)setShareInfo:(NSString *)shareInfo;
@end

@protocol JSNativeManagerDelegate
- (void)shareAction;
- (void)getToken;
- (void)setShareInfo:(NSString *)shareInfo;
@end

@interface JSNativeDelegate : NSObject
@property (nonatomic, weak) id<JSNativeManagerDelegate>delegate;
- (instancetype)initWith:(id<JSNativeManagerDelegate>)delegate;

@end

JSNativeDelegate.m实现

#import "JSNativeDelegate.h"

@interface JSNativeDelegate()<JSObjcDelegate>
@end

@implementation JSNativeDelegate

- (instancetype)initWith:(id<JSNativeManagerDelegate>)delegate
{
    self = [super init];
    if(self)
    {
        self.delegate = delegate;
    }
    return self;
}
- (void)shareAction
{
    if (self.delegate) {
        [self.delegate shareAction];
    }
}
- (void)getToken
{
    if (self.delegate) {
        [self.delegate getToken];
    }
}
- (void)setShareInfo:(NSString *)shareInfo
{
    if (self.delegate) {
        [self.delegate setShareInfo:shareInfo];
    }
}
@end

其中处理JS和OC数据部分转换的对象JSValue很重要,也是个核心。
比如你H5这边写了一个函数setShareInfoCallback,可以通过JSContext获取这个函数并前端这边调用如下:

/**获取JS函数*/
    JSValue *shareCallback = self.jsContext[@"setShareInfoCallback"];
    [shareCallback callWithArguments:@[shareInfo]];

JSExport我的理解是Native与JS的代理,JS这边调用Native的代码通过这个类实现的,不过方法名需要H5和前端这边约定一样即可。
比如:

@protocol JSObjcDelegate<JSExport>
//H5这边邀请好友按钮click事件
- (void)shareAction;
//H5加载时通过前端获取token
- (void)getToken;
//H5这边返回分享信息
- (void)setShareInfo:(NSString *)shareInfo;
@end

这里值得注意的是在ViewController里面,如果直接把self作为JS直接引用对象,会引起内存泄漏,我这里的处理方法是引用一个第三方对象来处理,如上文的JSNativeDelegate
如下文这样写会引起内存泄漏:

/**注入JS需要的OC对象*/
    self.jsContext[@"Toyun"] = self;

我的处理方式:

/**注入JS需要的OC对象*/
    JSNativeDelegate *jsDelegate = [[JSNativeDelegate alloc] initWith:self];
    self.jsContext[@"Toyun"] = jsDelegate;

然后在ViewController处理JSNativeDelegate代理方法即可实现与JS与Native的交互。

Native端部分代码

- (void)setShareInfo:(NSString *)shareInfo {
    _shareInfo = shareInfo;
    /**获取JS函数*/
    JSValue *shareCallback = self.jsContext[@"setShareInfoCallback"];
    [shareCallback callWithArguments:@[shareInfo]];
    NSData *jsonData = [shareInfo dataUsingEncoding:NSUTF8StringEncoding];
    NSError *err;
    NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&err];
    if(err) {
        NSLog(@"json解析失败:%@",err);
        self.model = nil;
    }
    JKRShareModel *model = [JKRShareModel mj_objectWithKeyValues:dic];
    self.model = model;
}

- (void)setModel:(JKRShareModel *)model {
    _model = model;
    @weakify(self);
    dispatch_async(dispatch_get_main_queue(), ^{
        @strongify(self);
        if (_model) {
            UIBarButtonItem *rightBarBtn = [[UIBarButtonItem alloc] initWithCustomView:self.shareBtn];
            self.navigationItem.rightBarButtonItem = rightBarBtn;
        } else {
            self.navigationItem.rightBarButtonItem = nil;
        }
    });
}

- (void)shareAction {
    __weak typeof(self) weakSelf = self;
    if ([weakSelf.model.type isEqualToString:@"normal"]) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [JKRShareManager jkr_shareURLWithTitle:weakSelf.model.title
                                              icon:weakSelf.model.imgUrl
                                           subHead:weakSelf.model.desc
                                               url:weakSelf.model.link
                             currentViewController:nil];
        });
    }
}
上一篇 下一篇

猜你喜欢

热点阅读