阅读 126

WKWebView-WKScriptMessageHandler实际应用(二)

项目中代码

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
    if ([message.name isEqualToString:@"TCXJSbridge"]) {
        //解析数据,执行相应的代码
        NSString *actionName = body[@"name"]; 
        if (@"map.getLocation" isEqualToString:actionName]){
            //经纬度业务
        }else if (@"user.getInfomation" isEqualToString:actionName]){
            //用户信息业务
        }else{
            //其他...
        }
     }
}复制代码

问题分析

随着项目越来越大,WebView与HTML的交互越来越多,就会暴露新的问题。

这样写代码会存在什么问题呢?

  • VC代码庞大;

  • 代码中太多的if...else了,可维护性差;

  • 团队开发过程中,同时修改一个文件容易产生冲突;

  • 可复用性低。其他VC使用时,只能copy,大大提高了后期的维护成本。

解决方案:

代码放到工具类或者viewModel中

虽然VC的代码少了,但是viewModel中还是存在大量的if...else代码,而且viewModel中会有UIKit的存在,这样写有点不优雅。

通过面向协议开发
  • 实现结耦、团队多人同时开发。

  • 单一职责,每个JS交互都对应一个类,负责自己的业务逻辑。

代码优化

分析共性

  • 初始化方法

  • 接收参数

  • 处理业务逻辑

  • 回调数据给HTML页面

优化后的代码

思路:利用runtime和面向协议编程思想

1、jS交互的名称和实际类名关联起来

2、定一个协议,声明协议方法,判断是否能处理这个JS交互、处理逻辑的方法、创建类的方法

3、通过JS传过来的数据找到类名、利用runtime生成处理JS的model

4、然后再执行处理的协议方法。

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
    if ([@"TCXJSbridge" isEqualToString:message.name]) {
        //native 和 js 交互
        NSDictionary *body = message.body;
        if (!body) {
            return;
        }
        if ([body isKindOfClass:[NSDictionary class]]) {
            NSString *actionName = body[@"name"];
            @weakify(self);
            TWebActionCallback callback = ^(TWebActionCallbackData * _Nonnull data) {
                @strongify(self)
                if(data.jsCallback){
                    [self.webView evaluateJavaScript:data.jsCallback completionHandler:^(id _Nullable object, NSError * _Nullable error) {}];
                }
            };
            //JS传递参数封装成对象
            TWebAction *webAction = [TWebAction webActionFromActionName:actionName runtimeParam:body[@"data"] callback:callback];
            webAction.callbackFunc = body[@"callback"];

            //创建具体处理业务的target
            id<TWebActionProtocol> target = [[TWebActionManager sharedManager] createTargetWithWebAction:webAction];

            if (!target) {
                TWebActionCallbackData *callModel = [[TWebActionCallbackData alloc] init];
                callModel.status = 404;
                callModel.jsCallbackFunc = webAction.callbackFunc;
                callModel.actionName = webAction.actionName;
                callModel.errorMsg = @"请升级至最新版本";
                webAction.webActionCallback(callModel);
                return;
            }
            //处理实际业务
            if([target respondsToSelector:@selector(handleWebAction:withHandler:)]){
                [target handleWebAction:webAction withHandler:self];
            }
        }
    }
}复制代码

定义协议

@protocol TWebActionProtocol <NSObject>

/**
 是否能处理这个操作
 @param actionName 操作名称
 @return YES/NO
 */

+ (BOOL)canHandleWebAction:(NSString*)actionName;

/**
 处理这个操作
 @param handler handler
 */
- (void)handleWebAction:(TWebAction*)webAction withHandler:(id)handler;

/**
 创建具体的实例
 @return id
 */
+ (id)createTargetWithWebAction:(TWebAction*)webAction;复制代码

TWebActionManager核心代码,在实现了TWebActionProtocol协议的数组里,根据名称查找,并创建对象

#pragma mark - 创建具体的webAction实现类

/**

 创建对象

 @param webAction 操作类

 @return 实现协议的对象

 */

-(id<TWebActionProtocol>)createTargetWithWebAction:(TWebAction*)webAction{
    if (!webAction) {
        return nil;
    }
    Class class = [self findClassForWebAction:webAction];
    if (!class) {
        return nil;
    }
    return [self createTargetForWebAction:webAction withClass:class];
}

/**

 根据webAction查找对应的类名

 @param webAction 操作类

 @return class

 */

- (Class)findClassForWebAction:(TWebAction *)webAction {

    NSString *actionName = webAction.actionName;

    if (!actionName || ![actionName isKindOfClass:[NSString class]] || actionName.length == 0) {
        return nil;
    }
    Class cacheClass = [self.classMapCache objectForKey:webAction.actionName];

    if (cacheClass) {
        //已经缓存的类型,可以直接跳转
        return cacheClass;

    } else {

#pragma clang diagnostic push

#pragma clang diagnostic ignored "-Wobjc-method-access"

        //未缓存的,先判断该种类型的跳转能被哪个类响应,并缓存下来,再执行跳转

        for (Class<TWebActionProtocol> webActionClass in self.webActionClasses) {

            BOOL canHandle = [webActionClass performSelector:@selector(canHandleWebAction:) withObject:webAction.actionName];

            if (canHandle) {

                [self.classMapCache setObject:webActionClass forKey:webAction.actionName];

                return webActionClass;
            }
        }
#pragma clang diagnostic pop
    }
    return nil;
}

/**
 根据webAction创建具体的类
 @param webAction 操作类
 @param webActionTargetClass 目标类的class
 @return 具体的类
 */
- (id)createTargetForWebAction:(TWebAction *)webAction withClass:(Class)webActionTargetClass {
    id target;
    //创建具体的类
    if ([webActionTargetClass respondsToSelector:@selector(createTargetWithWebAction:)]) {
        target = [webActionTargetClass performSelector:@selector(createTargetWithWebAction:) withObject:webAction];
    }else{
        return nil;
    }
    //设置参数
    if (target){
        [webAction setValuesForObject:target];
    }
    //设置callback
    if (webAction.webActionCallback && [target isKindOfClass:[NSObject class]]) {
        ((NSObject *)target).webActionCallback = webAction.webActionCallback;
        webAction.webActionCallback = nil;
    }    
    return target;
}复制代码

举个例子

以获取经纬度为例

1、新建一个类,继承TCXWebActionModel实现TWebActionProtocol协议。继承的目的是一些通用的处理实现可以放在基类。例如初始化等。

@interface TCXWebGetLocation : TCXWebActionModel<TWebActionProtocol>
@end复制代码

2、实现协议方法。

3、handleWebAction:withHandler:方法实现具体业务逻辑。并回调到VC,VC执行JS代码。

@implementation THKWebGetLocation
+ (BOOL)canHandleWebAction:(NSString *)actionName {
    if ([@"map.getLocation" isEqualToString:actionName]) {
        return YES;
    }
    return NO;
}

- (void)handleWebAction:(TWebAction*)webAction withHandler:(id)handler {
    TWebActionCallbackData *model = [self successCallbackData];
    double lg = 116.397128;
    double lat = 39.916527;
    NSMutableDictionary * mutableDict = [NSMutableDictionary dictionaryWithCapacity:2];
    [mutableDict setValue:@(lat) forKey:@"latitude"]
    [mutableDict setValue:@(lg) forKey:@"longitude"];
    model.data = mutableDict;
    self.webActionCallback(model);
}
@end


作者:龙在掘金62077
链接:https://juejin.cn/post/7015211645290938399


文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐