关于iOS中原生和h5交互的知识总结(一) UIWebView

前言

第一次写简书,本人是从事iOS开发工作的,由于工作中经常涉及一些原生和h5交互的知识,再加上领导的建议,特来总结一下开发过程中所涉及的知识和坑。

目录

关于iOS中原生和h5交互的知识总结(一)UIWebView
关于iOS中原生和h5交互的知识总结(二)WKWebView

基于UIWebView的实现,请注意以下几点

1.模型注入
2.注入时机*

样例

负责与js交互的工具类
JSHandler.h

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

@protocol JSHandlerProtocol <JSExport>

/*
    多参数的方法
    由于涉及到多参数的问题,从第二个参数开始,外部参数名都要使用大写开头
    因为JS调用OC方法时,是将OC方法拼接连成字符串,如果无法区分就会造成无法识别
    比如对于下面的OC方法,JS调用时
    javascript.sayHelloToWithGreeting(参数1,参数2) //正确写法
    javascript.sayHelloTowithGreeting(参数1,参数2) //错误写法(就是注意大小写啦)
*/

//- (void)sayHelloTo:(NSString *)name WithGreeting:(NSString *)greeting;

#pragma mark - methods for js

/**
 js端要传参,调用native端的加密方法
 */
- (NSString *)aesEncryptString:(NSString *)text;

/** 
 js端要传参,调用native端的解密方法
 */
- (NSString *)aesDecryptString:(NSString *)text;


/**
 js端不传参,调用native端获取token
 */
- (NSString *)getToken;


/**
 js端调用客户端,显示登录界面,无返回值
 */
- (void)showLoginScene:(NSString *)message;

@end

@interface JSHandler : NSObject<JSHandlerProtocol>

@end

JSHandler.m

#import "JSHandler.h"
#import "LocalSaveManager.h"
#import <JavaScriptCore/JavaScriptCore.h>

@implementation JSHandler:NSObject

#pragma mark - 实现代理方法
- (NSString *)aesEncryptString:(NSString *)text
{
    
}

- (NSString *)aesDecryptString:(NSString *)text
{
    
}

- (NSString *)getToken
{
   
}

- (void)showLoginScene:(NSString *)message
{
  
}

使用JSHandler
HomeViewController.m

#import <JavaScriptCore/JavaScriptCore.h>
#import "JSHandler.h"
#import "NSObject+JSContextTracker.h"

static NSString *const JSContextObject = @"jsHandler";
@interface HomeViewController ()
@property (weak, nonatomic) IBOutlet UIWebView *webView;
@property (nonatomic, strong) JSContext *jsContext;
@property (nonatomic, strong) JSHandler *jsHandler;

@end

@implementation HomeViewController

/*
可能大多数人都会这样使用,这里为了引出“注入时机”的问题,先演示个常规写法,在viewDidLoad里注入jsHandler对象。
并不是说在viewDidLoad时注入jsHandler不对,而是这样会有注入时机的问题。比如在js加载时我们就去native端获取token,
而不是去点击某个按钮才去获取token。这时JSContext还没有创建完毕,但是我们缺向JSContext中注入jsHandler对象,
所以当在js端使用jsHandler对象时会报找不到jsHandler这个对象的错误!
*/

/*
 有的同学会想如果在webView的这两个代理方法中注入jsHandler对象是否是正确的时机呢
- (void)webViewDidStartLoad:(UIWebView *)webView;
- (void)webViewDidFinishLoad:(UIWebView *)webView;
答案是否定的,webViewDidStartLoad时JSContext还没有创建,webViewDidFinishLoad看似是页面已经加载完的回调,
但这时JSContext真的有创建完毕吗,肯能有些同学发现在webViewDidFinishLoad时,当你切换html页面的时候,
有时候能找到jsHandler对象,有时不能找到,晕!JSContext对象创建完成,注入jsHandler对象真的是一个很微妙的时机,
并且webView那少的可怜的几个代理方法真的不能解决我们的问题,肿么办,往下看!
*/
- (void)viewDidLoad {
    [super viewDidLoad];
    self.jsHandler = [JSHandler new];
    self.jsContext = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    self.jsContext[JSContextObject] = self.jsHandler;
    self.jsContext.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {
        context.exception = exceptionValue;
        NSLog(@"异常信息:%@", exceptionValue);
    };
}

还记得我们引入了#import "NSObject+JSContextTracker.h"这个头文件吗,这是个关键的类目啊,现在给出这个类目的实现
NSObject+JSContextTracker.h


#import <Foundation/Foundation.h>

static NSString *const JSContextTrackerNotifycation = @"JSContextTrackerNotifycation";
@interface NSObject (JSContextTracker)

@end

NSObject+JSContextTracker.m

#import "NSObject+JSContextTracker.h"
#import <JavaScriptCore/JavaScriptCore.h>
@implementation NSObject (JSContextTracker)
/*
这个类目在创建JSContext对象时会发出一个通知,这个类目不需要我们主动去调用,在JSContext对象创建时会自动调用,
至于比较偏底层的原理,网上也有介绍,感觉都是东一块西一块,要不就是英文翻译过来的,看了之后也不是特别理解。
由于我本人理解的也是有些模糊,在此就不解释原理的,怕误导大家,如果之后我弄清楚了,会及时更新的。
感兴趣的朋友也可以网上自行去找找资料,如果弄清楚了可以留言,帮助我和大家解惑,谢谢啦
*/
- (void)webView:(id)unused didCreateJavaScriptContext:(JSContext *)context forFrame:(id)alsoUnused {
    if (!context)
        return;
    [[NSNotificationCenter defaultCenter] postNotificationName:JSContextTrackerNotifycation object:context];
}

@end

现在给出HomeViewController.m最佳注入jsHandler对象的时机

#import <JavaScriptCore/JavaScriptCore.h>
#import "JSHandler.h"
#import "NSObject+JSContextTracker.h"

static NSString *const JSContextObject = @"jsHandler";
@interface HomeViewController ()
@property (weak, nonatomic) IBOutlet UIWebView *webView;
@property (nonatomic, strong) JSContext *jsContext;
@property (nonatomic, strong) JSHandler *jsHandler;

@end

@implementation HomeViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.jsHandler = [JSHandler new];
    NSString *url = @"";
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestReloadRevalidatingCacheData timeoutInterval:60];
    [self.webView loadRequest:request];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(createJSContext:) name:JSContextTrackerNotifycation object:nil];
}

/*
  现在createJSContext中就是注入jsHandler对象的最佳时机
*/
-(void)createJSContext:(NSNotification*)notification
{
    //注意以下代码如果不在主线程调用会发生闪退。
    dispatch_async( dispatch_get_main_queue(), ^{
    
        self.jsContext = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
        self.jsContext[JSContextObject] = self.jsHandler;
        
        self.jsContext.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {
            context.exception = exceptionValue;
            NSLog(@"异常信息:%@", exceptionValue);
        };
    });
}

最后给出js端的代码,是如何调用原生的,把他作为工具类,专门处理和native交互
native-tool.js

function isAndroid() {
    var u = navigator.userAgent;
    var isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1;
    return isAndroid;
}

function isIOS() {
    var u = navigator.userAgent;
    var isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
    return isIOS;
}

//得到native端token
function native_getToken() {
    var token;
    if (isIOS()) {
        token = jsHandler.getToken();
    } else if (isAndroid()) {
        token = contact.getToken();
    }

    return token;
}

//显示登录界面
function native_showLoginScene(message) {

    if (isIOS()) {
        jsHandler.showLoginScene(message);
    } else if (isAndroid()) {
        contact.showLoginScene(message);
    }
}

//调用native加密
function native_encrypt(str) {
    var res;
    if (isIOS())
    {
        res = jsHandler.aesEncryptString(str);
    } 
    else if (isAndroid())
    {
        res = contact.encrypt(str);
    }
    return res;
}

UIWebView js和原生交互结束语

这里只介绍了“模型注入”的方式,并且填了“注入时机“这个坑,因为UIWebView暴露给我们的方法太少了,而且iOS11中,UIWebView已经不推荐使用了,所以接下来我们着重介绍iOS的负责web显示的新宠儿WKWebView

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容

  • 这篇文章发于2017/03/08 “第七岛”原创公众号 现在在“简书”补充发布 我将与shasha(我的小老板)一...
    Sun渣婷阅读 339评论 0 0
  • 惊闻我友焕莲离世,甚是惋惜。呜呼!为何人生无常,天妒英才? 刚过五十,上要孝亲,下要育儿,她如此抽身离去,怎能忍心...
    喵喵喵喵宝阅读 592评论 0 2
  • 我一闺蜜小A,是个浓眉大眼乐观开朗的女孩,富有灵性。眼角眉稍,笑意流转,仿佛哪怕全世界就只剩下她一个,也能自嗨起来...
    小竹筏儿阅读 500评论 1 2
  • 认识“马云",身边的朋友都说我是奔着他响亮的名字去的,这让我很无语。第一,认识他的时候我并不知道他姓啥叫谁;第二,...
    十月城池阅读 513评论 4 7
  • 热水打在身上,脸上,顺着头发滑落,滴滴答答的水流从发尖摔落地板砖上,溅开了无数的水花,迫不及待落下,无声胜有声...
    简晓爱阅读 208评论 0 0