|
7 | 7 |  |
8 | 8 |  |
9 | 9 |
|
10 | | -## 版本说明: |
11 | | -1.`release 0.0.6`:由于苹果近期给开发者邮件说明,要求尽快删除UIWebView等相关API,因此新增release 0.0.6版本,删除UIWebView的支持。 |
12 | | - |
13 | | -2.`release 0.1.0`:增加`WKJSBridge.js`,对JS侧接口做了完善,统一入口,提供参数传递,成功、失败回调。 |
14 | | - |
15 | | -3.`release 0.1.3`:将`WKJSBridge.js`拆分为`wk_bridge_ interface.js`和`wk_bridge_channel.js`,分别负责对h5暴漏接口和js-native通信使用。`wk_bridge_ interface.js`属于业务层,不包含在`pod`内,属于业务方自定义扩展使用,具体可以参考demo。 |
16 | | - |
17 | 10 |
|
18 | 11 | ## Link |
19 | 12 | * Blog : |
|
27 | 20 | [《写一个易于维护使用方便性能可靠的Hybrid框架(一)—— 思路构建》](https://juejin.im/post/5c07d95ee51d451d930b04c7) |
28 | 21 |
|
29 | 22 |
|
30 | | -## 介绍 |
31 | | - |
32 | | -**iOS 基于WKWebView的JS-Native交互框架,功能强大,轻松集成,一行代码即可,业务框架分离,易于拓展,致力于让h5调用native像调用一个接口那样简单。** |
33 | | -**关于通信方案说明:** |
34 | | -* WKWebView使用`addScritMessageHandler`构建通信,苹果提供的bridge,可以理解为亲儿子,好处自然不用多说。 |
35 | | - |
36 | | -**架构图** |
37 | | - |
38 | | - |
39 | | - |
40 | | - |
41 | | -**类图** |
42 | | - |
43 | | - |
44 | | - |
45 | 23 | ## 特性 |
46 | | -- 一行代码即可让WebView能力无限。 |
47 | | -- 支持h5扩展自定义插件。 |
48 | | -- 插件化JS-Native业务逻辑,业务完全分离,最大化解耦。 |
49 | | -- 支持在不影响原业务的情况下,集成超简单。(相当于在不影响原bridge的情况下新搭建了个bridge供h5使用) |
50 | | -- 支持业务模块异步回调。 |
51 | | -- 基于__attribute( )函数进行插件白名单注册,业务模块的注册只需要在自己内部注册即可,摆脱plist等传统注册方式。目前已知[阿里BeeHive](https://github.com/alibaba/BeeHive)/[美团Kylin组件](https://juejin.im/post/5c0a17d6e51d4570cf60d102)皆使用此方式进行注册。 |
52 | | -- 针对WKWebView进行了Cookie丢失处理。 |
53 | | -- 针对WKWebView白框架屏问题进行了处理。 |
54 | | -- 针对WKWebView所带来的一些Crash问题进行了容错处理。 |
55 | | - |
56 | | - |
57 | | -## 安装 |
58 | 24 |
|
59 | | -### 1.手动导入 |
| 25 | +- 一行代码集成。 |
| 26 | +- 支持业务分离,支持模块化开发。 |
| 27 | +- 异步调用。 |
| 28 | +- 支持业务模块白名单机制,提升安全性。 |
60 | 29 |
|
61 | | -下载WKJavaScriptBridge文件夹,将WKJavaScriptBridge文件夹拖入到你的工程中。 |
62 | 30 |
|
63 | | -### 2.CocoaPods |
64 | | -1. 在 Podfile 中添加 `pod 'WKJavaScriptBridge', '~> 0.1.3'`。 |
65 | | -2. 执行 `pod install` 或 `pod update`。 |
66 | | -3. native侧导入 `<WKWebViewEngine.h>`。 |
67 | | -4. js侧引入`wk_bridge_channel.js`,如果使用了类似于demo的`wk_bridge_ interface.js`,只需引入`wk_bridge_ interface.js`即可。 |
68 | | - |
69 | | - |
70 | | -## 用法 |
71 | | -`WKWebViewEngine`是框架的主体类,对外提供`bindBridgeWithWebView:withDelegate:`接口,用来拦截webView的代理函数。具体参照demo。 |
| 31 | +## 安装 |
72 | 32 |
|
73 | | -### 1.使用框架 |
| 33 | +### 方法一:Cocoapods |
| 34 | +- 1、: |
74 | 35 |
|
75 | 36 | ```objc |
76 | | -// WKWebView |
77 | | -WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init]; |
78 | | -WKPreferences *preferences = [WKPreferences new]; |
79 | | -preferences.javaScriptCanOpenWindowsAutomatically = YES; |
80 | | -preferences.minimumFontSize = 40.0; |
81 | | -configuration.preferences = preferences; |
82 | | -WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration]; |
83 | | -webView.navigationDelegate = self; |
84 | | -/***/ |
85 | | -[WKWebViewEngine bindBridgeWithWebView:webView withDelegate:self]; |
86 | | -/***/ |
87 | | -[self.view addSubview:webView]; |
88 | | -NSString *urlStr = [[NSBundle mainBundle] pathForResource:@"index.html" ofType:nil]; |
89 | | -NSURL *fileURL = [NSURL fileURLWithPath:urlStr]; |
90 | | -if (@available(iOS 9.0, *)) { |
91 | | -[webView loadFileURL:fileURL allowingReadAccessToURL:fileURL]; |
92 | | -} else { |
93 | | -// Fallback on earlier versions |
94 | | -} |
| 37 | +在Podfile文件里面添加:pod 'WKJavaScriptBridge' |
95 | 38 | ``` |
96 | | -
|
97 | | -
|
98 | | -### 2.自定义业务插件(原生侧) |
99 | | -
|
100 | | -2.1. 创建插件类,继承自`WKBasePlugin`。 |
101 | | -2.2. 插件类里面添加`@WKRegisterWebPlugin`宏,暂时用于插件是否需要提前初始化,加快第一次调用速度。也可以扩充一些其他功能。 |
102 | | -2.3. 插件里构建业务逻辑,通过`sendPluginResult:callbackId:`函数把结果回传给JS侧。 |
| 39 | +- 2、:对应文件添加头文件 |
103 | 40 |
|
104 | 41 | ```objc |
105 | | -#import "WKBasePlugin.h" |
106 | | -@interface WKFetchPlugin : WKBasePlugin |
107 | | -- (void)nativeFentch:(WKMsgCommand *)command; |
108 | | -@end |
109 | | -
|
| 42 | +#import "WKJavaScriptBridge.h" |
110 | 43 | ``` |
111 | 44 |
|
112 | | -```objc |
113 | | -@WKRegisterWebPlugin(WKFetchPlugin) |
114 | | - |
115 | | -@implementation WKFetchPlugin |
116 | | -- (void)nativeFentch:(WKMsgCommand *)command { |
117 | | - NSString *method = [command.arguments objectForKey:@"method"]; |
118 | | - NSString *url = [command.arguments objectForKey:@"url"]; |
119 | | - |
120 | | - NSLog(@"method : %@ ; url : %@", method, url); |
121 | | - |
122 | | - WKPluginResult *result = [WKPluginResult resultWithStatus:WKCommandStatus_ERROR messageAsDictionary:@{@"result": @"success!!"}]; |
123 | | - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; |
124 | | -} |
| 45 | +### 方法二:手动添加 |
| 46 | +- 1、下载WKJavaScriptBridge,将WKJavaScriptBridge文件夹拖入到你的工程中 |
125 | 47 |
|
126 | | -@end |
127 | | -``` |
128 | | -2.4.构建配置native端插件:用于给js调用,(iOS & android)两端需要相同的serviceId和actionId,才能启动相同的事件。 |
129 | | -```objc |
130 | | -@interface WKJSBridgeConfig : WKBasePlugin |
131 | | -- (void)fetchConfig:(WKMsgCommand *)command; |
132 | | -@end |
133 | | -``` |
| 48 | +- 2、引入头文件: |
134 | 49 |
|
135 | 50 | ```objc |
136 | | -@WKRegisterWebPlugin(WKJSBridgeConfig) |
137 | | - |
138 | | -@implementation WKJSBridgeConfig |
139 | | - |
140 | | -- (void)fetchConfig:(WKMsgCommand *)command { |
141 | | - NSDictionary *dict = @{ |
142 | | - @"fetch_id":@{//服务ID |
143 | | - @"service":@"WKFetchPlugin", |
144 | | - @"action":@{//具体事件 |
145 | | - @"action_get":@"nativeGet", |
146 | | - @"action_post":@"nativePost" |
147 | | - } |
148 | | - } |
149 | | - }; |
150 | | - WKPluginResult *result = [WKPluginResult resultWithStatus:WKCommandStatus_OK messageAsDictionary:dict]; |
151 | | - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; |
152 | | -} |
153 | | -@end |
154 | | -``` |
155 | | -
|
156 | | -这样一个插件就定义完毕了:插件的意思就是独立的,与其他功能模块无耦合的业务模块,用来处理一类JS-Native的交互。例如JS想要获取地图信息、wifi信息、文件处理等都可以定义为一个插件。插件的好处就是无耦合!!!拖入项目可以直接使用,删除后项目也不需要做任何修改,直接build!以后再有新的交互需求你只需要按照上面的步骤创建插件完成功能并把结果返回给JS就可以了!不需要动框架!! |
157 | | -
|
158 | | -
|
159 | | -### 3.自定义业务插件(JS侧) |
160 | | -
|
161 | | -3.1.以下为demo中自定义js插件代码,详情可参照demo: |
162 | | -```js |
163 | | -/** |
164 | | - * 调用 Natvie Post 网络请求 方法 |
165 | | - * @param actionArgs 数据 |
166 | | - * @param callback 调用回调 |
167 | | - */ |
168 | | -wk_bridge.prototype.nativePost = function (actionArgs, successCallback, failCallback) { |
169 | | - var fetchConfig = bridgeConfig.fetch_id; |
170 | | - var service = fetchConfig.service; |
171 | | - var action = fetchConfig.action.action_post; |
172 | | - window.WKJSBridge.callNative(service, action, actionArgs, successCallback, failCallback); |
173 | | -}; |
174 | | -
|
175 | | -/** |
176 | | - * 调用 Natvie Get 网络请求 方法 |
177 | | - * @param actionArgs 数据 |
178 | | - * @param callback 调用回调 |
179 | | - */ |
180 | | -wk_bridge.prototype.nativeGet = function (actionArgs, successCallback, failCallback) { |
181 | | - var fetchConfig = bridgeConfig.fetch_id; |
182 | | - var service = fetchConfig.service; |
183 | | - var action = fetchConfig.action.action_get; |
184 | | - window.WKJSBridge.callNative(service, action, actionArgs, successCallback, failCallback); |
185 | | -}; |
186 | | -``` |
187 | | - |
188 | | -3.2. JS调用Native |
189 | | -index.html中引用<script type="text/javascript" src="WKJSBridge.js" ></script> |
190 | | - |
191 | | -```js |
192 | | -// 模拟h5使用native来发送post请求 |
193 | | -function pokePost() { |
194 | | - //业务参数 |
195 | | - var command = { |
196 | | - 'method':'post', |
197 | | - 'url':'https:www.baidu.com' |
198 | | - } |
199 | | - window.wk_bridge.nativePost(command, function success(res) { |
200 | | - document.getElementById("returnValue").value = res.result; |
201 | | - }, function fail(res) { |
202 | | - document.getElementById("returnValue").value = res.result; |
203 | | - }); |
204 | | -} |
205 | | - |
206 | | -// 模拟h5使用native来发送get请求 |
207 | | -function pokeGet() { |
208 | | - //业务参数 |
209 | | - var command = { |
210 | | - 'method':'get', |
211 | | - 'url':'https:www.baidu.com' |
212 | | - } |
213 | | - window.wk_bridge.nativeGet(command, function success(res) { |
214 | | - document.getElementById("returnValue").value = res.result; |
215 | | - }, function fail(res) { |
216 | | - document.getElementById("returnValue").value = res.result; |
217 | | - }); |
218 | | -} |
| 51 | +#import "WKJavaScriptBridge.h" |
219 | 52 | ``` |
220 | 53 |
|
221 | | -`command`为想要传递的参数。 |
222 | | -`nativePost`为第一步自定义的js插件。 |
223 | | -`pokeGet`为第一步自定义的另一个插件, |
224 | 54 |
|
225 | | -3.3. 详细使用参照Demo。 |
| 55 | +## 用法 |
| 56 | +- 1、: |
226 | 57 |
|
227 | | -### 4.一些针对WKWebView带来的Crash问题处理 |
228 | | -4.1 由于WKWebView在请求过程中用户可能退出界面销毁对象,当请求回调时由于接收处理对象不存在,造成Bad Access crash,所以可将WKProcessPool设为单例 |
229 | 58 | ```objc |
230 | | -static WKProcessPool *_sharedWKProcessPoolInstance = nil; |
231 | | -static dispatch_once_t onceToken; |
232 | | -dispatch_once(&onceToken, ^{ |
233 | | - _sharedWKProcessPoolInstance = [[WKProcessPool alloc] init]; |
234 | | -}); |
235 | | -self.processPool = _sharedWKProcessPoolInstance; |
236 | | - |
237 | | -WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init]; |
238 | | -configuration.processPool = self.processPool; |
| 59 | +//在创建WKWebView的地方调用此方法 |
| 60 | +self.bridge = [WKJavaScriptBridge bindBridgeWithWebView:yourwebView]; |
239 | 61 | ``` |
| 62 | +- 2、: |
240 | 63 |
|
241 | | -4.2 解决window.alert() 时 completionHandler 没有被调用导致崩溃问题 |
242 | | -
|
243 | | -```objc |
244 | | -@property (nonatomic, assign, getter=loadFinished) BOOL isLoadFinished; |
245 | | -``` |
246 | | -```objc |
247 | | -- (void)viewDidLoad { |
248 | | - [super viewDidLoad]; |
249 | | - self.isLoadFinished = NO; |
250 | | -} |
251 | | -``` |
252 | | -```objc |
253 | | -- (void)viewDidAppear:(BOOL)animated { |
254 | | - [super viewDidAppear:animated]; |
255 | | - self.isLoadFinished = YES; |
256 | | -} |
257 | | -``` |
258 | 64 | ```objc |
259 | | -- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler { |
260 | | - |
261 | | - if (!self.isLoadFinished) { |
262 | | - completionHandler(); |
263 | | - return; |
264 | | - } |
265 | | - UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"" message:message preferredStyle:UIAlertControllerStyleAlert]; |
266 | | - [alertController addAction:[UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) { completionHandler(); }]]; |
267 | | - if (self) |
268 | | - [self presentViewController:alertController animated:YES completion:^{}]; |
269 | | - else |
270 | | - completionHandler(); |
271 | | -} |
| 65 | +//开启白名单,默认关闭。若开启,插件需要进行注册`@WKRegisterWhiteList(你的模块类名)`,参照Demo。 |
| 66 | +[self.bridge openWhiteList:YES]; |
272 | 67 | ``` |
273 | 68 |
|
274 | | -## 后续功能延伸: |
275 | | - |
276 | | -1. JS侧插件化。(complete) |
277 | | -2. 基于此引入离线包。 |
278 | | -3. 引入flutter。 |
279 | | -4. 构建小程序。 |
280 | | -5. other ... |
281 | | - |
282 | 69 | ## License |
283 | 70 |
|
284 | 71 | WKJavaScriptBridge is available under the Apache License 2.0. See the [LICENSE](https://github.com/GitWangKai/WKJavaScriptBridge/blob/master/LICENSE) file for more info. |
|
0 commit comments