diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f912e32..0000000 --- a/.travis.yml +++ /dev/null @@ -1,6 +0,0 @@ -language: objective-c -xcode_project: dsbridge.xcodeproj -xcode_scheme: dsbridge -osx_image: xcode9.1 -script: - - xcodebuild clean build -project dsbridge.xcodeproj -scheme dsbridge diff --git a/Build/Products/Debug-iphoneos/libdsbridge.a b/Build/Products/Debug-iphoneos/libdsbridge.a deleted file mode 100644 index f6878bc..0000000 Binary files a/Build/Products/Debug-iphoneos/libdsbridge.a and /dev/null differ diff --git a/Build/Products/Debug-iphonesimulator/libdsbridge.a b/Build/Products/Debug-iphonesimulator/libdsbridge.a index cc017e2..abb298a 100644 Binary files a/Build/Products/Debug-iphonesimulator/libdsbridge.a and b/Build/Products/Debug-iphonesimulator/libdsbridge.a differ diff --git a/Build/Products/Release-iphoneos/libdsbridge.a b/Build/Products/Release-iphoneos/libdsbridge.a deleted file mode 100644 index 7c4f7b1..0000000 Binary files a/Build/Products/Release-iphoneos/libdsbridge.a and /dev/null differ diff --git a/File b/File deleted file mode 100644 index e69de29..0000000 diff --git a/README-chs.md b/README-chs.md new file mode 100644 index 0000000..7a2a9ec --- /dev/null +++ b/README-chs.md @@ -0,0 +1,188 @@ +# DSBridge + +> DSBridge是目前地球上最好的IOS/Android javascript bridge. 通过它可以在web和native之间调用彼此方法。DSBridge是目前唯一一个支持同步调用的跨平台Js bridge. + +DSBridge-IOS github:https://github.com/wendux/DSBridge-IOS + +## 使用 + +1. Native 实现API 代理类 + + ```objective-c + //JsApiTest.m + @implementation JsApiTest + //for synchronous invocation + - (NSString *) testSyn:(NSDictionary *) args + { + return [(NSString *)[args valueForKey:@"msg"] stringByAppendingString:@"[ syn call]"]; + } + //for asynchronous invocation + - (NSString *) testAsyn:(NSDictionary *) args :(void (^)(NSString * _Nullable result))handler + { + handler([(NSString *)[args valueForKey:@"msg"] stringByAppendingString:@"[ asyn call]"]); + } + @end + ``` + +2. 注册api代理类至DWebview + + ```objective-c + DWebview * webview=[[DWebview alloc] initWithFrame:bounds]; + jsApi=[[JsApiTest alloc] init]; + webview.JavascriptInterfaceObject=jsApi; + ``` + +3. 在Javascript中调用Native API + + ```javascript + //Call Native API + var bridge = getJsBridge(); + //Call synchronously + var str=bridge.call("testSyn", {msg: "testSyn"}); + //Call asynchronously + bridge.call("testAsyn", {msg: "testAsyn"}, function (v) { + alert(v); + }) + + //Test will be called by oc, must be global function! + function test(arg1,arg2){ + return arg1+arg2; + } + ``` + +4. 最后,Native中调用Javascript API + + ```objective-c + [_webview callHandler:@"test" + arguments:[[NSArray alloc] initWithObjects:@1,@"hello", nil] + completionHandler:^(NSString * value){ + NSLog(@"%@",value); + }]; + ``` + + + +## Javascript API 介绍 + +### getJsBridge + +获取javascript bridge 对象;此方法为sdk内置,可在任何地方调用。 + +### bridge.call(method,[args,callback]) + +功能:调用Native api + +method: api函数名 + +args:参数,类型:json, 可选参数 + +callback(String returnValue): 处理调用结果的回调,**仅异步调用时需要**. + + + +## 注意 + +### Native API 方法签名 + +**为了在ios和android平台下兼容,对IOS端Native API接口约定如下:** + +1. 所有API返回值类型为NSString, 不存在时返回nil即可。 +2. 参数以JSON传递; DSBridge会将js参数自动转化为NSDictionary + +注:JsApiTest.m中实现的方法可以不在JsApiTest.h中声明 + +### 调用Javascript + +DWebView提供了两个api用于调用js + +```objective-c +//调用js api(函数) +-(void)callHandler:(NSString *)methodName arguments:(NSArray * _Nullable)args + completionHandler:(void (^)(NSString * _Nullable))completionHandler; +//执行任意js代码 +- (void)evaluateJavaScript:(NSString *)javaScriptString + completionHandler:(void (^ _Nullable)(NSString * _Nullable))completionHandler; +``` + +callHandler中,methodName 为js函数名,args为参数数组,可以接受数字、字符串等。 + +两个函数中completionHandler为完成回调,用于获取js执行的结果。 + +**调用时机** + +DWebview只有在javascript context初始化成功后才能正确执行js代码,而javascript context初始化完成的时机一般都比整个页面加载完毕要早,随然DSBridge能捕获到javascript context初始化完成的时机,但是一些js api可能声明在页面尾部,甚至单独的js文件中,如果在javascript context刚初始化完成就调用js api, 此时js api 可能还没有加载,所以会失败,为此专门提供了一个api设置一个回调,它会在页面加载结束后调用,为了和didpagefinished区分,我们取名如下: + +```objective-c +- (void)setJavascriptContextInitedListener:(void(^_Nullable)(void))callback; +``` + + 若是端上主动调用js,请在此回调中进行 。示例如下: + +```objective-c +__block DWebview * _webview=webview; +[webview setJavascriptContextInitedListener:^(){ + [_webview callHandler:@"test" + arguments:[[NSArray alloc] initWithObjects:@1,@"hello", nil] + completionHandler:^(NSString * value){ + NSLog(@"%@",value); + }]; +}]; +``` + +完整的示例请查看demo工程。 + + +### 关于DWebview + +SDK中有三个webview: + +DWKwebview:继承自WKWebView,内部已经实现js prompt、alert、confirm函数对应的对话框。 + +DUIwebview:继承自UIWebView + +DWebview:自定义view, 内部在ios8.0以下会使用DUIwebview, 大于等于8.0会使用DWKwebview。 + +所有的webview除了都实现了上述api之外,提供了一个加载网页的便捷函数: + +```objective-c +- (void)loadUrl: (NSString *) url; +``` + + **您可以根据具体业务使用任意一个**,不过一般情况下优先选用DWebview,它在新设备上更省资源,效率更高。 + +DWebview还提供了一些其它api和属性,具体请查看其头文件,需要特殊说明的是,有一个api: + +```objective-c +- (id _Nullable) getXWebview; +``` + +它可以返回DWebview内部使用的真实webview, 值会是DUIwebview和DWKwebview的实例之一,您可以通过isKindOfClass来判断,吃函数主要用于扩展DWebview,下面可以看一下loadRequest的大概实现: + +```objective-c +- (void)loadRequest:(NSURLRequest *)request +{ + id webview=[self getXWebview]; + if([webview isKindOfClass:[DUIwebview class]]){ + [(DUIwebview *)webview loadRequest:request]; + }else{ + [(DWKwebview *)webview loadRequest:request]; + } +} +``` + +### Alert dialog + +DWebview已经实现 alert、prompt、comfirm对话框,您可以不做处理,也可以自定义。值得一提的是js 在调用alert函数正常情况下只要用户没有关闭alert对话框,js代码是会阻塞的,但是考虑到alert 对话框只有一个确定按钮,也就是说无论用户关闭还是确定都不会影响js代码流程,所以**DWebview中在弹出alert对话框时会先给js返回**,这样一来js就可以继续执行,而提示框等用户关闭时在关闭即可。如果你就是想要阻塞的alert,可以自定义。而DWebview的prompt、comfirm实现完全符合ecma标准,都是阻塞的。 + +请不要手动设置DUIwebview的delegate属性,因为DUIwebview在内部已经设置了该属性,如果您需要自己处理页面加载过程,请设置WebEventDelegate属性。 + +### 相关资料 + +DSBridge-Android:https://github.com/wendux/DSBridge-Android + +与WebViewJavascriptBridge的对比 [DSBridge VS WebViewJavascriptBridge]( http://www.jianshu.com/p/d967b0d85b97)。 + +### 拉票 + +如果你觉得不错,麻烦star一下以便让更多人知道😄。 + diff --git a/README.md b/README.md index 644cb4b..52fbec8 100644 --- a/README.md +++ b/README.md @@ -1,532 +1,143 @@ -English| [中文简体](https://github.com/wendux/DSBridge-IOS/blob/master/readme-chs.md) -# DSBridge for IOS - -![dsBridge](https://github.com/wendux/DSBridge-IOS/raw/master/img/dsbridge.png) - - -[![cocoapods](https://img.shields.io/cocoapods/v/dsBridge.svg?style=flat)](https://github.com/wendux/DSBridge-IOS) ![](https://img.shields.io/badge/language-object--c-yellow.svg) [![](https://travis-ci.org/wendux/DSBridge-IOS.svg?branch=master)](https://travis-ci.org/wendux/DSBridge-IOS) [![MIT Licence](https://img.shields.io/packagist/l/doctrine/orm.svg)](https://opensource.org/licenses/mit-license.php) ![support](https://img.shields.io/badge/support-IOS%208%2B-green.svg) ![platform](https://img.shields.io/badge/platform-ios|osx-ff69b4.svg) [![GitHub last commit](https://img.shields.io/github/last-commit/wendux/DSBridge-IOS.svg?color=blue)](https://github.com/wendux/DSBridge-IOS/) - - -> Modern cross-platform JavaScript bridge, through which you can invoke each other's functions synchronously or asynchronously between JavaScript and native applications. - - -DSBridge-Android:https://github.com/wendux/DSBridge-Android - - -### Notice - -DSBridge v3.0 is a milestone version. Compared with v2.0, we have made a lot of changes. Note that v3.0 is **incompatible** with v2.0, but v2.0 will continue to maintain. If you are a new user, use >=v3.0. - -[DSBridge v3.0.0 change list](https://github.com/wendux/DSBridge-IOS/issues/25) - - - -## Features - -1. The three ends of Android, IOS and Javascript are easy to use, light and powerful, safe and strong - -2. Both synchronous and asynchronous calls are supported - -3. Support **API Object**, which centrally implements APIs in a Java Class or a Javascript object - -4. Support API namespace - -5. Support debug mode - -6. Support the test of whether API exists - -7. Support **Progress Callback**: one call, multiple returns - -8. Support event listener for Javascript to close the page - -9. Support Modal and Modeless popup box for javascript - -10. Support the X5 webcore of Tencent for Android - - ​ - -## Installation - -```shell -pod "dsBridge" -``` - - - -## Examples - -See the `dsbridgedemo/` package. run the `app` project and to see it in action. - -To use a dsBridge in your own project: +# DSBridge +> DSBridge is currently the best Javascript bridge in the world , by which we can call functions synchronously and asynchronously between web and Native . Moreover, both android and ios are supported ! +DSBridge-IOS:https://github.com/wendux/DSBridge-IOS +DSBridge-Android:https://github.com/wendux/DSBridge-Android +中文文档请移步:http://www.jianshu.com/p/633d9fde946f +与WebViewJavascriptBridge的对比请移步 [DSBridge VS WebViewJavascriptBridge]( http://www.jianshu.com/p/d967b0d85b97)。 ## Usage -1. Implement APIs in a class +1. Implement API delegate class in Object-C ```objective-c - #import "dsbridge.h" - ... + //JsApiTest.m @implementation JsApiTest //for synchronous invocation - - (NSString *) testSyn:(NSString *) msg + - (NSString *) testSyn:(NSDictionary *) args { - return [msg stringByAppendingString:@"[ syn call]"]; + return [(NSString *)[args valueForKey:@"msg"] stringByAppendingString:@"[ syn call]"]; } //for asynchronous invocation - - (void) testAsyn:(NSString *) msg :(JSCallback)completionHandler + - (NSString *) testAsyn:(NSDictionary *) args :(void (^)(NSString * _Nullable result))handler { - completionHandler([msg stringByAppendingString:@" [ asyn call]"],YES); + handler([(NSString *)[args valueForKey:@"msg"] stringByAppendingString:@"[ asyn call]"]); } - @end + @end ``` -2. add API object to DWKWebView +2. Setup API delegate class to DWebView. ```objective-c - DWKWebView * dwebview=[[DWKWebView alloc] initWithFrame:bounds]; - // register api object without namespace - [dwebview addJavascriptObject:[[JsApiTest alloc] init] namespace:nil]; + DWebview * webview=[[DWebview alloc] initWithFrame:bounds]; + jsApi=[[JsApiTest alloc] init]; + webview.JavascriptInterfaceObject=jsApi; ``` -3. Call Native (Java/Object-c/swift) API in Javascript, and register javascript API 。 - - - Init dsBridge - - ```javascript - //cdn - // - //npm - //npm install dsbridge@3.1.4 - var dsBridge=require("dsbridge") - ``` - - - Call Native API and register a javascript API for Native invocation - - ```javascript +3. Call Object-C API in Javascript, and declare a global javascript function for the following Object-c invocation. - //Call synchronously - var str=dsBridge.call("testSyn","testSyn"); - - //Call asynchronously - dsBridge.call("testAsyn","testAsyn", function (v) { - alert(v); - }) + ```javascript + //Call Object-C API + var bridge = getJsBridge(); + //Call synchronously + var str=bridge.call("testSyn", {msg: "testSyn"}); + //Call asynchronously + bridge.call("testAsyn", {msg: "testAsyn"}, function (v) { + alert(v); + }) - //Register javascript API for Native - dsBridge.register('addValue',function(l,r){ - return l+r; - }) - ``` + //Test will be called by Object-c, must be global function + function test(arg1,arg2){ + return arg1+arg2; + } + ``` -4. Call Javascript API in Object-C +4. Call Javascript function in Object-C . ```objective-c - [dwebview callHandler:@"addValue" arguments:@[@3,@4] completionHandler:^(NSNumber* value){ - NSLog(@"%@",value); - }]; + [_webview callHandler:@"test" + arguments:[[NSArray alloc] initWithObjects:@1,@"hello", nil] + completionHandler:^(NSString * value){ + NSLog(@"%@",value); + }]; ``` +## Javascript API introduction +### **getJsBridge** +Get the bridge object。 Although you can call it anywhere in the page, we also advise you to call it after dom ready. -## Object-C API signature - -In order to be compatible with IOS , we make the following convention on Object-C API signature: - -1. For synchronous API. - - **`(id) handler:(id) msg`** - - - The argument type can be any class type, but the type of return value can't be **void**. - -2. For asynchronous API. - - **` (void) handler:(id) arg :(JSCallback)completionHandler)`** - - `JSCallback` is a block type: - - ```objective-c - typedef void (^JSCallback)(NSString * _Nullable result,BOOL complete); - ``` - - > Attention: API name can't start with "init", because it is reserved in OC class. - -## Using in Swift - -In Swift, you should declare APIs as follows: - -```swift -//MUST USE "_" to ignore the first argument name explicitly。 -@objc func testSyn( _ arg:String) -> String { - return String(format:"%@[Swift sync call:%@]", arg, "test") -} - -@objc func testAsyn( _ arg:String, handler: (String, Bool)->Void) { - handler(String(format:"%@[Swift async call:%@]", arg, "test"), true) -} -``` - -Two points you should keep in mind: - -- Must add "@objc" to Swift API. -- Must use "_" to ignore the first argument name explicitly - -The complete example is [here](https://github.com/wendux/DSBridge-IOS/blob/master/dsbridgedemo/JsApiTestSwift.swift) . - -## Namespace - -Namespaces can help you better manage your APIs, which is very useful in hybrid applications, because these applications have a large number of APIs. DSBridge (>= v3.0.0) allows you to classify API with namespace. And the namespace can be multilevel, between different levels with '.' division. - -## Debug mode - -In debug mode, some errors will be prompted by a popup dialog , and the exception caused by the native APIs will not be captured to expose problems. We recommend that the debug mode be opened at the development stage. You can open debug mode : - -```objective-c -// open debug mode -[dwebview setDebugMode:true]; -``` - - - -## Progress Callback - -Normally, when a API is called to end, it returns a result, which corresponds one by one. But sometimes a call need to repeatedly return multipule times, Suppose that on the Native side, there is a API to download the file, in the process of downloading, it will send the progress information to Javascript many times, then Javascript will display the progress information on the H5 page. Oh...You will find it is difficult to achieve this function. Fortunately, DSBridge supports **Progress Callback**. You can be very simple and convenient to implement a call that needs to be returned many times. Here's an example of a countdown: - -In Object-c - -```objective-c -- ( void )callProgress:(NSDictionary *) args :(JSCallback)completionHandler -{ - value=10; - hanlder=completionHandler; - timer = [NSTimer scheduledTimerWithTimeInterval:1.0 - target:self - selector:@selector(onTimer:) - userInfo:nil - repeats:YES]; -} --(void)onTimer:t{ - if(value!=-1){ - hanlder([NSNumber numberWithInt:value--],NO); - }else{ - hanlder(@"",YES); - [timer invalidate]; - } -} -``` - -In Javascript - -```javascript -dsBridge.call("callProgress", function (value) { - document.getElementById("progress").innerText = value -}) -``` - -For the complete sample code, please refer to the demo project. - - - -## Javascript popup box - -For Javascript popup box functions (alert/confirm/prompt), DSBridge has implemented them all by default. the default dialog label text language is Chinese, you can custom the text by calling `customJavascriptDialogLabelTitles`. If you still want to implement them by yourself , set the `DSUIDelegate` property which is a proxy of `WKUIDelegate`. - -Note That the default dialog box implemented by DSBridge is modal. This will block the UI thread. If you need modeless, please refer to `disableJavascriptDialogBlock`. - - - -# WKUIDelegate - -In `DWKWebView ` , please use ` DSUIDelegate` instead of `UIDelegate` , because `UIDelegate` has already been set inner `DWKWebView ` , `DSUIDelegate` is a proxy of `UIDelegate` . - - - -## API Reference - -### Object-C API - -In Object-c, the object that implements the javascript interfaces is called **Object-c API object**. - -##### `addJavascriptObject:(id) object namespace:(NSString *) namespace` - -Add the Object-c API object with supplied namespace into DWKWebView. The javascript can then call OC APIs with `bridge.call("namespace.api",...)`. - -If the namespace is empty, the Object-c API object have no namespace. The javascript can call OC APIs with `bridge.call("api",...)`. - -Example: +### bridge.call(method,[args,callback]) -**In Object-c** +Call Object-C api synchronously and asynchronously。 -```objective-c -@implementation JsEchoApi -- (id) syn:(id) arg -{ - return arg; -} -- (void) asyn: (id) arg :(JSCallback)completionHandler -{ - completionHandler(arg,YES); -} -@end -// register api object with namespace "echo" -[dwebview addJavascriptObject:[[JsEchoApi alloc] init] namespace:@"echo"]; -``` - -**In Javascript** - -```javascript -// call echo.syn -var ret=dsBridge.call("echo.syn",{msg:" I am echoSyn call", tag:1}) -alert(JSON.stringify(ret)) -// call echo.asyn -dsBridge.call("echo.asyn",{msg:" I am echoAsyn call",tag:2},function (ret) { - alert(JSON.stringify(ret)); -}) -``` - - - -##### `removeJavascriptObject:(NSString *) namespace` - -Remove the Object-c API object with supplied namespace. - - - -##### `callHandler:(NSString *) methodName arguments:(NSArray *) args` - -##### `callHandler:(NSString *) methodName completionHandler:(void (^)(id value))completionHandler` - -##### `callHandler:(NSString *) methodName arguments:(NSArray *) args completionHandler:(void (^ )(id value))completionHandler` - -Call the javascript API. If a `completionHandler` is given, the javascript handler can respond. the `methodName` can contain the namespace. **The completionHandler will be called in main thread**. - -Example: - -```objective-c -[dwebview callHandler:@"append" arguments:@[@"I",@"love",@"you"] - completionHandler:^(NSString * _Nullable value) { - NSLog(@"call succeed, append string is: %@",value); -}]; -// call with namespace 'syn', More details to see the Demo project -[dwebview callHandler:@"syn.getInfo" completionHandler:^(NSDictionary * _Nullable value) { - NSLog(@"Namespace syn.getInfo: %@",value); -}]; -``` - - - -##### `disableJavascriptDialogBlock:(bool) disable` +method: Object-c method name -BE CAREFUL to use. if you call any of the javascript popup box functions (`alert`,` confirm`, and `prompt`), the app will hang, and the javascript execution flow will be blocked. if you don't want to block the javascript execution flow, call this method, the popup box functions will return immediately( `confirm` return `true`, and the `prompt` return empty string). +args: arguments with json object -Example: +callback(String returnValue):callback to handle the result. **only asynchronous invocation required**. -```objective-c -[dwebview disableJavascriptDialogBlock: true] -``` - -if you want to enable the block, just calling this method with the argument value `false` . +## Notice +### Object-c API signature +In order to be compatible with IOS and Android, we make the following convention on native api signature: -##### `setJavascriptCloseWindowListener:(void(^_Nullable)(void))callback` +1. The tye of return value must be NSString; if not need, just return nil. +2. The arguments passed by NSDictionary, if the API doesn't need argument, you still need declare the argument. -DWKWebView calls `callback` when Javascript calls `window.close`, you can provide a block to add your hanlder . +### Call javascript code -Example: +There are two methods provided by DWebView to call javascript ```objective-c -[dwebview setJavascriptCloseWindowListener:^{ - NSLog(@"window.close called"); -}]; +//call javascript functions +-(void)callHandler:(NSString *)methodName arguments:(NSArray * _Nullable)args + completionHandler:(void (^)(NSString * _Nullable))completionHandler; +//execute any javascript code +- (void)evaluateJavaScript:(NSString *)javaScriptString + completionHandler:(void (^ _Nullable)(NSString * _Nullable))completionHandler; ``` -##### `hasJavascriptMethod:(NSString*) handlerName methodExistCallback:(void(^)(bool exist))callback` - -Test whether the handler exist in javascript. + **Opportunity of Native calling javascript functions** -Example: +JavaScript code can only execute when the javascript context initialization is successful . There is a Api: ```objective-c -// test if javascript method exists. -[dwebview hasJavascriptMethod:@"addValue" methodExistCallback:^(bool exist) { - NSLog(@"method 'addValue' exist : %d",exist); -}]; +- (void)setJavascriptContextInitedListener:(void(^_Nullable)(void))callback; ``` - - -##### `setDebugMode:(bool) debug` - -Set debug mode. if in debug mode, some errors will be prompted by a popup dialog , and the exception caused by the native APIs will not be captured to expose problems. We recommend that the debug mode be opened at the development stage. - - - -##### `customJavascriptDialogLabelTitles:(NSDictionary*) dic` - -custom the label text of javascript dialog that includes alert/confirm/prompt, the default text language is Chinese. - -Example: +example: ```objective-c -[dwebview customJavascriptDialogLabelTitles:@{ - @"alertTitle":@"Notification", - @"alertBtn":@"OK", - @"confirmTitle":@"", - @"confirmCancelBtn":@"CANCEL", - @"confirmOkBtn":@"OK", - @"promptCancelBtn":@"CANCEL", - @"promptOkBtn":@"OK" +__block DWebview * _webview=webview; +[webview setJavascriptContextInitedListener:^(){ + [_webview callHandler:@"test" + arguments:[[NSArray alloc] initWithObjects:@1,@"hello", nil] + completionHandler:^(NSString * value){ + NSLog(@"%@",value); + }]; }]; ``` -### Javascript API - -##### dsBridge - -"dsBridge" is accessed after dsBridge Initialization . - - - -##### `dsBridge.call(method,[arg,callback])` - -Call Java api synchronously and asynchronously。 - -`method`: Java API name, can contain the namespace。 - -`arg`: argument, Only one allowed, if you expect multiple parameters, you can pass them with a json object. - -`callback(String returnValue)`: callback to handle the result. **only asynchronous invocation required**. - - - -##### `dsBridge.register(methodName|namespace,function|synApiObject)` - -##### `dsBridge.registerAsyn(methodName|namespace,function|asyApiObject)` - -Register javascript synchronous and asynchronous API for Native invocation. There are two types of invocation - -1. Just register a method. For example: - - In Javascript - - ```javascript - dsBridge.register('addValue',function(l,r){ - return l+r; - }) - dsBridge.registerAsyn('append',function(arg1,arg2,arg3,responseCallback){ - responseCallback(arg1+" "+arg2+" "+arg3); - }) - ``` - - In Object-c - - ```objective-c - // call javascript method - [dwebview callHandler:@"addValue" arguments:@[@3,@4] completionHandler:^(NSNumber * value){ - NSLog(@"%@",value); - }]; - - [dwebview callHandler:@"append" arguments:@[@"I",@"love",@"you"] completionHandler:^(NSString * _Nullable value) { - NSLog(@"call succeed, append string is: %@",value); - }]; - ``` - - ​ - -2. Register a Javascript API object with supplied namespace. For example: - - **In Javascript** - - ```java - //namespace test for synchronous - dsBridge.register("test",{ - tag:"test", - test1:function(){ - return this.tag+"1" - }, - test2:function(){ - return this.tag+"2" - } - }) - - //namespace test1 for asynchronous calls - dsBridge.registerAsyn("test1",{ - tag:"test1", - test1:function(responseCallback){ - return responseCallback(this.tag+"1") - }, - test2:function(responseCallback){ - return responseCallback(this.tag+"2") - } - }) - ``` - - > Because JavaScript does not support function overloading, it is not possible to define asynchronous function and sync function of the same name。 - > - - **In Object-c** - - ```objective-c - [dwebview callHandler:@"test.test1" completionHandler:^(NSString * _Nullable value) { - NSLog(@"Namespace test.test1: %@",value); - }]; - - [dwebview callHandler:@"test1.test1" completionHandler:^(NSString * _Nullable value) { - NSLog(@"Namespace test1.test1: %@",value); - }]; - ``` - - - - -##### `dsBridge.hasNativeMethod(handlerName,[type])` - -Test whether the handler exist in Java, the `handlerName` can contain the namespace. - -`type`: optional`["all"|"syn"|"asyn" ]`, default is "all". - -```javascript -dsBridge.hasNativeMethod('testAsyn') -//test namespace method -dsBridge.hasNativeMethod('test.testAsyn') -// test if exist a asynchronous function that named "testSyn" -dsBridge.hasNativeMethod('testSyn','asyn') //false -``` - - - -##### `dsBridge.disableJavascriptDialogBlock(disable)` - -Calling `dsBridge.disableJavascriptDialogBlock(...)` has the same effect as calling ` disableJavascriptDialogBlock` in Java. - -Example: - -```javascript -//disable -dsBridge.disableJavascriptDialogBlock() -//enable -dsBridge.disableJavascriptDialogBlock(false) -``` - - - -## Work with fly.js +### More about DWebview -As we all know, In browser, AJax request are restricted by same-origin policy, so the request cannot be initiated across the domain. However, [Fly.js](https://github.com/wendux/fly) supports forwarding the http request to Native through any Javascript bridge, And fly.js has already provide the dsBridge adapter.Because the Native side has no the same-origin policy restriction, fly.js can request any resource from any domain. +There are three webviews available, DWKwebview、DUIwebview and DWebview, all of them provide the same interface, you can user any one you want. It is worth mentioning that the DWebview is just a proxy of DWKwebview and DUIwebview, while the ios system vesion >=8.0 , DWKwebview will be used, otherwise, DUIwebview will be. -Another typical scene is in the hybrid App, [Fly.js](https://github.com/wendux/fly) will forward all requests to Native, then, the unified request management, cookie management, certificate verification, request filtering and so on are carried out on Native. +### warnnig -More details please refer to https://github.com/wendux/fly. + If you're using DUIwebview, don't set the delegate prop. because the delegate prop has been setted inner , please set WebEventDelegate instead ! -## Finally +### Alert dialog -If you like DSBridge, please star to let more people know it , Thank you ! +In order to prevent unnecessary obstruction, the alert dialog was implemented asynchronously , that is to say, if you call alert in javascript , it will be returned directly no matter whether the user has to deal with. becase the code flow is not subject to the user operation no matter whether user click ok button or close the alert dialog. if you don't need this feature, you can custom the alert dialog by override "onJsAlert" callback in WebChromeClient class. +### Finally +If you like DSBridge, please star to let more people know it , Thank you 😄. \ No newline at end of file diff --git a/dsBridge.podspec b/dsBridge.podspec deleted file mode 100644 index 27d5839..0000000 --- a/dsBridge.podspec +++ /dev/null @@ -1,140 +0,0 @@ -# -# Be sure to run `pod spec lint dsBridge.podspec' to ensure this is a -# valid spec and to remove all comments including this before submitting the spec. -# -# To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html -# To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ -# - -Pod::Spec.new do |s| - - # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - # - # These will help people to find your library, and whilst it - # can feel like a chore to fill in it's definitely to your advantage. The - # summary should be tweet-length, and the description more in depth. - # - - s.name = "dsBridge" - s.version = "3.0.6" - s.summary = "An ios bridge for calling functions synchronously and asynchronously between JavaScript and Object-C in WKWebView/UIWebView" - - # This description is used to generate tags and improve search results. - # * Think: What does it do? Why did you write it? What is the focus? - # * Try to keep it short, snappy and to the point. - # * Write the description between the DESC delimiters below. - # * Finally, don't worry about the indent, CocoaPods strips it! - s.description = <<-DESC - An javascript bridge for calling functions synchronously and asynchronously - DESC - - s.homepage = "https://github.com/wendux/DSBridge-IOS.git" - # s.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif" - - - # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - # - # Licensing your code is important. See http://choosealicense.com for more info. - # CocoaPods will detect a license file if there is a named LICENSE* - # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. - # - - s.license = "MIT" - # s.license = { :type => "MIT", :file => "FILE_LICENSE" } - - - # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - # - # Specify the authors of the library, with email addresses. Email addresses - # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also - # accepts just a name if you'd rather not provide an email address. - # - # Specify a social_media_url where others can refer to, for example a twitter - # profile URL. - # - - s.author = { "lazydu" => "824783146@qq.com" } - # Or just: s.author = "lazydu" - # s.authors = { "lazydu" => "824783146@qq.com" } - # s.social_media_url = "http://twitter.com/wen.du" - - # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - # - # If this Pod runs only on iOS or OS X, then specify the platform and - # the deployment target. You can optionally include the target after the platform. - # - - s.platform = :ios, "8.0" - # s.platform = :ios, "5.0" - - # When using multiple platforms - s.ios.deployment_target = "8.0" - # s.osx.deployment_target = "10.7" - # s.watchos.deployment_target = "2.0" - # s.tvos.deployment_target = "9.0" - - - # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - # - # Specify the location from where the source should be retrieved. - # Supports git, hg, bzr, svn and HTTP. - # - - s.source = { :git => "https://github.com/wendux/DSBridge-IOS.git", :tag => "#{s.version}" } - - - # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - # - # CocoaPods is smart about how it includes source code. For source files - # giving a folder will include any swift, h, m, mm, c & cpp files. - # For header files it will include any header in the folder. - # Not including the public_header_files will make all headers public. - # - - s.source_files = "dsbridge/*" - #s.exclude_files = "Classes/Exclude" - - s.public_header_files = "dsbridge/*.h" - - - # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - # - # A list of resources included with the Pod. These are copied into the - # target bundle with a build phase script. Anything else will be cleaned. - # You can preserve files from being cleaned, please don't preserve - # non-essential files like tests, examples and documentation. - # - - # s.resource = "icon.png" - # s.resources = "Resources/*.png" - - # s.preserve_paths = "FilesToSave", "MoreFilesToSave" - - - # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - # - # Link your library with frameworks, or libraries. Libraries do not include - # the lib prefix of their name. - # - - s.frameworks = "UIKit" - # s.frameworks = "SomeFramework", "AnotherFramework" - - # s.library = "iconv" - # s.libraries = "iconv", "xml2" - - - # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # - # - # If your library depends on compiler flags you can set them in the xcconfig hash - # where they will only apply to your library. If you depend on other Podspecs - # you can include multiple dependencies to ensure it works. - - # s.requires_arc = true - - # s.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } - # s.dependency "JSONKit", "~> 1.4" - -end - - diff --git a/dsbridge.xcodeproj/project.pbxproj b/dsbridge.xcodeproj/project.pbxproj index b9da3f3..62df9d0 100644 --- a/dsbridge.xcodeproj/project.pbxproj +++ b/dsbridge.xcodeproj/project.pbxproj @@ -7,11 +7,9 @@ objects = { /* Begin PBXBuildFile section */ - 8142EA1E214212E700BEE5C0 /* JsApiTestSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8142EA1D214212E700BEE5C0 /* JsApiTestSwift.swift */; }; - 815612EB2020304600C4F5CD /* InternalApis.m in Sources */ = {isa = PBXBuildFile; fileRef = 815612EA2020304600C4F5CD /* InternalApis.m */; }; - 815612EE202041B000C4F5CD /* DSCallInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 815612ED202041B000C4F5CD /* DSCallInfo.m */; }; - 815612F12022E42D00C4F5CD /* JsEchoApi.m in Sources */ = {isa = PBXBuildFile; fileRef = 815612F02022E42D00C4F5CD /* JsEchoApi.m */; }; 815FCC931E1CE48F0073CA79 /* JSBUtil.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 818F0BBE1E18C59200679925 /* JSBUtil.h */; }; + 815FCC941E1CE4E70073CA79 /* JSBWebEventDelegate.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 818F0BCE1E1A3EEE00679925 /* JSBWebEventDelegate.h */; }; + 818F0B931E18C08100679925 /* dsbridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 818F0B921E18C08100679925 /* dsbridge.m */; }; 818F0B941E18C08100679925 /* dsbridge.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 818F0B911E18C08100679925 /* dsbridge.h */; }; 818F0BA21E18C0AA00679925 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 818F0BA11E18C0AA00679925 /* main.m */; }; 818F0BA51E18C0AA00679925 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 818F0BA41E18C0AA00679925 /* AppDelegate.m */; }; @@ -19,15 +17,16 @@ 818F0BAB1E18C0AA00679925 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 818F0BA91E18C0AA00679925 /* Main.storyboard */; }; 818F0BAD1E18C0AA00679925 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 818F0BAC1E18C0AA00679925 /* Assets.xcassets */; }; 818F0BB01E18C0AA00679925 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 818F0BAE1E18C0AA00679925 /* LaunchScreen.storyboard */; }; - 818F0BB71E18C3B800679925 /* DWKWebView.m in Sources */ = {isa = PBXBuildFile; fileRef = 818F0BB51E18C3B800679925 /* DWKWebView.m */; }; + 818F0BB71E18C3B800679925 /* DWKwebview.m in Sources */ = {isa = PBXBuildFile; fileRef = 818F0BB51E18C3B800679925 /* DWKwebview.m */; }; + 818F0BBA1E18C45E00679925 /* DUIwebview.m in Sources */ = {isa = PBXBuildFile; fileRef = 818F0BB91E18C45E00679925 /* DUIwebview.m */; }; + 818F0BBD1E18C58000679925 /* DWebview.m in Sources */ = {isa = PBXBuildFile; fileRef = 818F0BBC1E18C58000679925 /* DWebview.m */; }; 818F0BC01E18C59200679925 /* JSBUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 818F0BBF1E18C59200679925 /* JSBUtil.m */; }; - 818F0BC41E18D44A00679925 /* DWKWebView.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 818F0BB61E18C3B800679925 /* DWKWebView.h */; }; + 818F0BC21E18D44A00679925 /* DUIwebview.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 818F0BB81E18C45E00679925 /* DUIwebview.h */; }; + 818F0BC31E18D44A00679925 /* DWebview.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 818F0BBB1E18C58000679925 /* DWebview.h */; }; + 818F0BC41E18D44A00679925 /* DWKwebview.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 818F0BB61E18C3B800679925 /* DWKwebview.h */; }; 818F0BC91E18DD3600679925 /* JsApiTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 818F0BC81E18DD3600679925 /* JsApiTest.m */; }; 818F0BCD1E18E6D200679925 /* libdsbridge.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 818F0B8E1E18C08100679925 /* libdsbridge.a */; }; 818F0BD01E1A820B00679925 /* test.html in Resources */ = {isa = PBXBuildFile; fileRef = 818F0BCF1E1A820B00679925 /* test.html */; }; - 81CFC8602029456C00CF9D83 /* readme-chs.md in Sources */ = {isa = PBXBuildFile; fileRef = 81CFC85F2029456C00CF9D83 /* readme-chs.md */; }; - 81CFC868202C42AA00CF9D83 /* img in Resources */ = {isa = PBXBuildFile; fileRef = 81CFC867202C42AA00CF9D83 /* img */; }; - 81EBBFFF1EF24DDE00D1E4C1 /* README.md in Sources */ = {isa = PBXBuildFile; fileRef = 81EBBFFD1EF24DDE00D1E4C1 /* README.md */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -47,8 +46,11 @@ dstPath = "include/$(PRODUCT_NAME)"; dstSubfolderSpec = 16; files = ( + 815FCC941E1CE4E70073CA79 /* JSBWebEventDelegate.h in CopyFiles */, 815FCC931E1CE48F0073CA79 /* JSBUtil.h in CopyFiles */, - 818F0BC41E18D44A00679925 /* DWKWebView.h in CopyFiles */, + 818F0BC21E18D44A00679925 /* DUIwebview.h in CopyFiles */, + 818F0BC31E18D44A00679925 /* DWebview.h in CopyFiles */, + 818F0BC41E18D44A00679925 /* DWKwebview.h in CopyFiles */, 818F0B941E18C08100679925 /* dsbridge.h in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; @@ -56,17 +58,9 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 812F1D3B216F1F9D00B5BD0C /* publish.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = publish.sh; sourceTree = ""; }; - 8142EA1C214212E600BEE5C0 /* dsbridgedemo-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "dsbridgedemo-Bridging-Header.h"; sourceTree = ""; }; - 8142EA1D214212E700BEE5C0 /* JsApiTestSwift.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JsApiTestSwift.swift; sourceTree = ""; }; - 815612E92020304600C4F5CD /* InternalApis.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InternalApis.h; sourceTree = ""; }; - 815612EA2020304600C4F5CD /* InternalApis.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InternalApis.m; sourceTree = ""; }; - 815612EC202041B000C4F5CD /* DSCallInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DSCallInfo.h; sourceTree = ""; }; - 815612ED202041B000C4F5CD /* DSCallInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DSCallInfo.m; sourceTree = ""; }; - 815612EF2022E42D00C4F5CD /* JsEchoApi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JsEchoApi.h; sourceTree = ""; }; - 815612F02022E42D00C4F5CD /* JsEchoApi.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JsEchoApi.m; sourceTree = ""; }; 818F0B8E1E18C08100679925 /* libdsbridge.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdsbridge.a; sourceTree = BUILT_PRODUCTS_DIR; }; 818F0B911E18C08100679925 /* dsbridge.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = dsbridge.h; sourceTree = ""; }; + 818F0B921E18C08100679925 /* dsbridge.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = dsbridge.m; sourceTree = ""; }; 818F0B9E1E18C0AA00679925 /* dsbridgedemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = dsbridgedemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 818F0BA11E18C0AA00679925 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 818F0BA31E18C0AA00679925 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; @@ -77,18 +71,18 @@ 818F0BAC1E18C0AA00679925 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 818F0BAF1E18C0AA00679925 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 818F0BB11E18C0AA00679925 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 818F0BB51E18C3B800679925 /* DWKWebView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DWKWebView.m; sourceTree = ""; }; - 818F0BB61E18C3B800679925 /* DWKWebView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DWKWebView.h; sourceTree = ""; }; + 818F0BB51E18C3B800679925 /* DWKwebview.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DWKwebview.m; sourceTree = ""; }; + 818F0BB61E18C3B800679925 /* DWKwebview.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DWKwebview.h; sourceTree = ""; }; + 818F0BB81E18C45E00679925 /* DUIwebview.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DUIwebview.h; sourceTree = ""; }; + 818F0BB91E18C45E00679925 /* DUIwebview.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DUIwebview.m; sourceTree = ""; }; + 818F0BBB1E18C58000679925 /* DWebview.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DWebview.h; sourceTree = ""; }; + 818F0BBC1E18C58000679925 /* DWebview.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DWebview.m; sourceTree = ""; }; 818F0BBE1E18C59200679925 /* JSBUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSBUtil.h; sourceTree = ""; }; 818F0BBF1E18C59200679925 /* JSBUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSBUtil.m; sourceTree = ""; }; 818F0BC81E18DD3600679925 /* JsApiTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JsApiTest.m; sourceTree = ""; }; 818F0BCB1E18DE7100679925 /* JsApiTest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JsApiTest.h; sourceTree = ""; }; + 818F0BCE1E1A3EEE00679925 /* JSBWebEventDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSBWebEventDelegate.h; sourceTree = ""; }; 818F0BCF1E1A820B00679925 /* test.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = test.html; sourceTree = ""; }; - 81CFC85F2029456C00CF9D83 /* readme-chs.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = "readme-chs.md"; sourceTree = ""; }; - 81CFC861202953CB00CF9D83 /* .travis.yml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = .travis.yml; sourceTree = ""; }; - 81CFC867202C42AA00CF9D83 /* img */ = {isa = PBXFileReference; lastKnownFileType = folder; path = img; sourceTree = ""; }; - 81EBBFF71EF246BD00D1E4C1 /* dsBridge.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dsBridge.podspec; sourceTree = ""; }; - 81EBBFFD1EF24DDE00D1E4C1 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -113,16 +107,9 @@ 818F0B851E18C08100679925 = { isa = PBXGroup; children = ( - 812F1D3B216F1F9D00B5BD0C /* publish.sh */, - 81CFC867202C42AA00CF9D83 /* img */, - 81CFC861202953CB00CF9D83 /* .travis.yml */, - 81CFC85F2029456C00CF9D83 /* readme-chs.md */, - 81EBBFFD1EF24DDE00D1E4C1 /* README.md */, - 81EBBFF71EF246BD00D1E4C1 /* dsBridge.podspec */, 818F0B901E18C08100679925 /* dsbridge */, 818F0B9F1E18C0AA00679925 /* dsbridgedemo */, 818F0B8F1E18C08100679925 /* Products */, - 81EBBFFA1EF24A7300D1E4C1 /* Frameworks */, ); sourceTree = ""; }; @@ -138,15 +125,17 @@ 818F0B901E18C08100679925 /* dsbridge */ = { isa = PBXGroup; children = ( - 818F0BB61E18C3B800679925 /* DWKWebView.h */, - 818F0BB51E18C3B800679925 /* DWKWebView.m */, + 818F0BB81E18C45E00679925 /* DUIwebview.h */, + 818F0BB91E18C45E00679925 /* DUIwebview.m */, + 818F0BBB1E18C58000679925 /* DWebview.h */, + 818F0BBC1E18C58000679925 /* DWebview.m */, + 818F0BB61E18C3B800679925 /* DWKwebview.h */, + 818F0BB51E18C3B800679925 /* DWKwebview.m */, 818F0B911E18C08100679925 /* dsbridge.h */, + 818F0B921E18C08100679925 /* dsbridge.m */, 818F0BBE1E18C59200679925 /* JSBUtil.h */, 818F0BBF1E18C59200679925 /* JSBUtil.m */, - 815612E92020304600C4F5CD /* InternalApis.h */, - 815612EA2020304600C4F5CD /* InternalApis.m */, - 815612EC202041B000C4F5CD /* DSCallInfo.h */, - 815612ED202041B000C4F5CD /* DSCallInfo.m */, + 818F0BCE1E1A3EEE00679925 /* JSBWebEventDelegate.h */, ); path = dsbridge; sourceTree = ""; @@ -166,10 +155,6 @@ 818F0BA61E18C0AA00679925 /* ViewController.h */, 818F0BA71E18C0AA00679925 /* ViewController.m */, 818F0BCF1E1A820B00679925 /* test.html */, - 815612EF2022E42D00C4F5CD /* JsEchoApi.h */, - 815612F02022E42D00C4F5CD /* JsEchoApi.m */, - 8142EA1D214212E700BEE5C0 /* JsApiTestSwift.swift */, - 8142EA1C214212E600BEE5C0 /* dsbridgedemo-Bridging-Header.h */, ); path = dsbridgedemo; sourceTree = ""; @@ -182,13 +167,6 @@ name = "Supporting Files"; sourceTree = ""; }; - 81EBBFFA1EF24A7300D1E4C1 /* Frameworks */ = { - isa = PBXGroup; - children = ( - ); - name = Frameworks; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -243,7 +221,6 @@ 818F0B9D1E18C0AA00679925 = { CreatedOnToolsVersion = 8.2.1; DevelopmentTeam = XTLLEQZMD7; - LastSwiftMigration = 0940; ProvisioningStyle = Automatic; }; }; @@ -272,7 +249,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 81CFC868202C42AA00CF9D83 /* img in Resources */, 818F0BD01E1A820B00679925 /* test.html in Resources */, 818F0BB01E18C0AA00679925 /* LaunchScreen.storyboard in Resources */, 818F0BAD1E18C0AA00679925 /* Assets.xcassets in Resources */, @@ -287,12 +263,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 81EBBFFF1EF24DDE00D1E4C1 /* README.md in Sources */, - 81CFC8602029456C00CF9D83 /* readme-chs.md in Sources */, 818F0BC01E18C59200679925 /* JSBUtil.m in Sources */, - 818F0BB71E18C3B800679925 /* DWKWebView.m in Sources */, - 815612EE202041B000C4F5CD /* DSCallInfo.m in Sources */, - 815612EB2020304600C4F5CD /* InternalApis.m in Sources */, + 818F0BBD1E18C58000679925 /* DWebview.m in Sources */, + 818F0BB71E18C3B800679925 /* DWKwebview.m in Sources */, + 818F0BBA1E18C45E00679925 /* DUIwebview.m in Sources */, + 818F0B931E18C08100679925 /* dsbridge.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -303,9 +278,7 @@ 818F0BA81E18C0AA00679925 /* ViewController.m in Sources */, 818F0BC91E18DD3600679925 /* JsApiTest.m in Sources */, 818F0BA51E18C0AA00679925 /* AppDelegate.m in Sources */, - 815612F12022E42D00C4F5CD /* JsEchoApi.m in Sources */, 818F0BA21E18C0AA00679925 /* main.m in Sources */, - 8142EA1E214212E700BEE5C0 /* JsApiTestSwift.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -456,17 +429,12 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - DEFINES_MODULE = YES; DEVELOPMENT_TEAM = XTLLEQZMD7; INFOPLIST_FILE = dsbridgedemo/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = wendu.dsbridgedemo.xx; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "dsbridgedemo/dsbridgedemo-Bridging-Header.h"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -474,16 +442,12 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - DEFINES_MODULE = YES; DEVELOPMENT_TEAM = XTLLEQZMD7; INFOPLIST_FILE = dsbridgedemo/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = wendu.dsbridgedemo.xx; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "dsbridgedemo/dsbridgedemo-Bridging-Header.h"; - SWIFT_VERSION = 3.0; }; name = Release; }; diff --git a/dsbridge.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/dsbridge.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d9810..0000000 --- a/dsbridge.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/dsbridge.xcodeproj/project.xcworkspace/xcuserdata/xuejinghao.xcuserdatad/UserInterfaceState.xcuserstate b/dsbridge.xcodeproj/project.xcworkspace/xcuserdata/xuejinghao.xcuserdatad/UserInterfaceState.xcuserstate deleted file mode 100644 index c8e97c8..0000000 Binary files a/dsbridge.xcodeproj/project.xcworkspace/xcuserdata/xuejinghao.xcuserdatad/UserInterfaceState.xcuserstate and /dev/null differ diff --git a/dsbridge.xcodeproj/xcuserdata/du.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/dsbridge.xcodeproj/xcuserdata/du.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index c965ffe..689311c 100644 --- a/dsbridge.xcodeproj/xcuserdata/du.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/dsbridge.xcodeproj/xcuserdata/du.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -179,6 +179,102 @@ landmarkType = "7"> + + + + + + + + + + + + + + + + + + + + + + + + @@ -233,13 +329,29 @@ shouldBeEnabled = "No" ignoreCount = "0" continueAfterRunningActions = "No" - filePath = "dsbridgedemo/JsApiTest.m" - timestampString = "560930195.2322249" + filePath = "dsbridge/JSBUtil.m" + timestampString = "511857257.557767" + startingColumnNumber = "9223372036854775807" + endingColumnNumber = "9223372036854775807" + startingLineNumber = "49" + endingLineNumber = "49" + landmarkName = "+call::JavascriptInterfaceObject:jscontext:" + landmarkType = "7"> + + + + diff --git a/dsbridge.xcodeproj/xcuserdata/xuejinghao.xcuserdatad/xcschemes/xcschememanagement.plist b/dsbridge.xcodeproj/xcuserdata/xuejinghao.xcuserdatad/xcschemes/xcschememanagement.plist deleted file mode 100644 index 92d4003..0000000 --- a/dsbridge.xcodeproj/xcuserdata/xuejinghao.xcuserdatad/xcschemes/xcschememanagement.plist +++ /dev/null @@ -1,19 +0,0 @@ - - - - - SchemeUserState - - dsbridge.xcscheme - - orderHint - 0 - - dsbridgedemo.xcscheme - - orderHint - 1 - - - - diff --git a/dsbridge/DSCallInfo.h b/dsbridge/DSCallInfo.h deleted file mode 100644 index f847d96..0000000 --- a/dsbridge/DSCallInfo.h +++ /dev/null @@ -1,8 +0,0 @@ - -#import - -@interface DSCallInfo : NSObject -@property (nullable, nonatomic) NSString* method; -@property (nullable, nonatomic) NSNumber* id; -@property (nullable,nonatomic) NSArray * args; -@end diff --git a/dsbridge/DSCallInfo.m b/dsbridge/DSCallInfo.m deleted file mode 100644 index d569b62..0000000 --- a/dsbridge/DSCallInfo.m +++ /dev/null @@ -1,13 +0,0 @@ -// -// DSCallInfo.m -// dsbridge -// -// Created by du on 2018/1/30. -// Copyright © 2018年 杜文. All rights reserved. -// - -#import "DSCallInfo.h" - -@implementation DSCallInfo - -@end diff --git a/dsbridge/DUIwebview.h b/dsbridge/DUIwebview.h new file mode 100644 index 0000000..76f44d1 --- /dev/null +++ b/dsbridge/DUIwebview.h @@ -0,0 +1,22 @@ +// +// DSUIwebview.h +// dspider +// +// Created by 杜文 on 16/12/30. +// Copyright © 2016年 杜文. All rights reserved. +// + +#import +#import "JSBWebEventDelegate.h" +#import +@interface DUIwebview: UIWebView{ + void(^javascriptContextInitedListener)(); +} +- (void)loadUrl: (NSString * _Nonnull) url; +- (void)setJavascriptContextInitedListener:(void(^_Nullable)(void))callback; +- (void)evaluateJavaScript:(NSString * _Nonnull)javaScriptString completionHandler:(void (^ _Nullable)(NSString * _Nullable))completionHandler; +-(void) callHandler:(NSString * _Nonnull) methodName arguments:(NSArray * _Nullable) args completionHandler:(void (^ _Nullable)(NSString * _Nullable ))completionHandler; + +@property (nullable, nonatomic, weak) id WebEventDelegate; +@property (nullable, nonatomic, weak) id JavascriptInterfaceObject ; +@end diff --git a/dsbridge/DUIwebview.m b/dsbridge/DUIwebview.m new file mode 100644 index 0000000..f69abd1 --- /dev/null +++ b/dsbridge/DUIwebview.m @@ -0,0 +1,163 @@ +// +// DSUIwebview.m +// dspider +// +// Created by 杜文 on 16/12/30. +// Copyright © 2016年 杜文. All rights reserved. +// + +#import "DUIwebview.h" +#import "JSBUtil.h" +#import + +static const char kTSJavaScriptContext[] = "ts_javaScriptContext"; +static NSHashTable* g_webViews = nil; + +@interface UIWebView (TS_JavaScriptCore_private) +- (void) ts_didCreateJavaScriptContext:(JSContext *)ts_javaScriptContext; +@end + +@protocol TSWebFrame +- (id) parentFrame; +@end + +@implementation NSObject (TS_JavaScriptContext) + +- (void) webView: (id) unused didCreateJavaScriptContext: (JSContext*) ctx forFrame: (id) frame +{ + NSParameterAssert( [frame respondsToSelector: @selector( parentFrame )] ); + // only interested in root-level frames + if ( [frame respondsToSelector: @selector( parentFrame) ] && [frame parentFrame] != nil ) + return; + + void (^notifyDidCreateJavaScriptContext)() = ^{ + + for ( UIWebView* webView in g_webViews ) + { + NSString* cookie = [NSString stringWithFormat: @"ts_jscWebView_%lud", (unsigned long)webView.hash ]; + + [webView stringByEvaluatingJavaScriptFromString: [NSString stringWithFormat: @"var %@ = '%@'", cookie, cookie ] ]; + + if ( [ctx[cookie].toString isEqualToString: cookie] ) + { + + [webView willChangeValueForKey: @"ts_javaScriptContext"]; + objc_setAssociatedObject( webView , kTSJavaScriptContext, ctx, OBJC_ASSOCIATION_RETAIN); + [webView didChangeValueForKey: @"ts_javaScriptContext"]; + if ( [webView.delegate respondsToSelector: NSSelectorFromString(@"webView:didCreateJavaScriptContext:")] ) + { + SuppressPerformSelectorLeakWarning( + [webView performSelector:NSSelectorFromString(@"webView:didCreateJavaScriptContext:") withObject:webView withObject:ctx ] ;//webView: webView didCreateJavaScriptContext: ctx]; + ); + } + return; + } + } + }; + + if ( [NSThread isMainThread] ) + { + notifyDidCreateJavaScriptContext(); + } + else + { + dispatch_async( dispatch_get_main_queue(), notifyDidCreateJavaScriptContext ); + } +} + +@end + + +@implementation DUIwebview +@synthesize WebEventDelegate,JavascriptInterfaceObject; + ++ (id) allocWithZone:(struct _NSZone *)zone +{ + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + g_webViews = [NSHashTable weakObjectsHashTable]; + }); + NSAssert( [NSThread isMainThread], @"uh oh - why aren't we on the main thread?"); + id webView = [super allocWithZone: zone]; + [g_webViews addObject: webView]; + return webView; +} + +-(instancetype)initWithFrame:(CGRect)frame +{ + if(self=[super initWithFrame:frame]){ + self.scalesPageToFit=YES; + self.delegate=self; + } + return self; +} + +-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType +{ + return YES; +} + +- (void)webView:(UIWebView *)webView didCreateJavaScriptContext:(JSContext *)ctx +{ + __block JSContext * ctx_=ctx; + ctx[@"_dsbridge"]=^(NSString * method,NSString * args){ + return [JSBUtil call:method :args JavascriptInterfaceObject:JavascriptInterfaceObject jscontext:ctx_]; + }; + [ctx evaluateScript:INIT_SCRIPT]; +} + +- (void)setJavascriptContextInitedListener:(void (^)(void))callback +{ + javascriptContextInitedListener=callback; +} + +- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^)(NSString *))completionHandler +{ + NSString *js_result = [self stringByEvaluatingJavaScriptFromString:javaScriptString]; + if(completionHandler) completionHandler(js_result); +} + +-(void)callHandler:(NSString *)methodName arguments:(NSArray *)args completionHandler:(void (^)(NSString *))completionHandler +{ + if(!args){ + args=[[NSArray alloc] init]; + } + NSString *script=[NSString stringWithFormat:@"%@.apply(window,%@)",methodName,[JSBUtil objToJsonString:args]]; + [self evaluateJavaScript:script completionHandler:^(NSString * value){ + if(completionHandler) completionHandler(value); + }]; +} + +- (void)loadUrl: (NSString *)url +{ + NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]]; + [self loadRequest:request];//加载 +} + +//UIWebviewDelegate +-(void)webViewDidFinishLoad:(UIWebView *)webView +{ + if(javascriptContextInitedListener){ + javascriptContextInitedListener(); + } + if([WebEventDelegate respondsToSelector:NSSelectorFromString(@"onpageFinished:")]){ + [WebEventDelegate onpageFinished: [webView.request.URL absoluteString]]; + } +} + +-(void)webViewDidStartLoad:(UIWebView *)webView +{ + if([WebEventDelegate respondsToSelector:NSSelectorFromString(@"onPageStart:")]){ + [WebEventDelegate onPageStart:[webView.request.URL absoluteString]]; + } +} + +-(void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error +{ + if(error.code!=NSURLErrorCancelled){ + if([WebEventDelegate respondsToSelector: NSSelectorFromString(@"onpageError::")]){ + [WebEventDelegate onpageError:[webView.request.URL absoluteString] :[error localizedDescription]]; + } + } +} +@end diff --git a/dsbridge/DWKWebView.h b/dsbridge/DWKWebView.h deleted file mode 100644 index 365f526..0000000 --- a/dsbridge/DWKWebView.h +++ /dev/null @@ -1,54 +0,0 @@ -// -// DSWKwebview.h -// dspider -// -// Created by 杜文 on 16/12/28. -// Copyright © 2016年 杜文. All rights reserved. -// - -#import - -typedef void (^JSCallback)(NSString * _Nullable result,BOOL complete); - -@interface DWKWebView : WKWebView - -@property (nullable, nonatomic, weak) id DSUIDelegate; - -- (void)loadUrl: (NSString * _Nonnull) url; - -// Call javascript handler --(void) callHandler:(NSString * _Nonnull) methodName arguments:(NSArray * _Nullable) args; --(void) callHandler:(NSString * _Nonnull) methodName completionHandler:(void (^ _Nullable)(id _Nullable value))completionHandler; --(void) callHandler:(NSString * _Nonnull) methodName arguments:(NSArray * _Nullable) args completionHandler:(void (^ _Nullable)(id _Nullable value))completionHandler; - -// set a listener for javascript closing the current page. -- (void)setJavascriptCloseWindowListener:(void(^_Nullable)(void))callback; - -/** - * Add a Javascript Object to dsBridge with namespace. - * @param object - * which implemented the javascript interfaces - * @param namespace - * if empty, the object have no namespace. - **/ -- (void)addJavascriptObject:(id _Nullable ) object namespace:(NSString * _Nullable) namespace; - -// Remove the Javascript Object with the supplied namespace -- (void)removeJavascriptObject:(NSString * _Nullable) namespace; - -// Test whether the handler exist in javascript -- (void) hasJavascriptMethod:(NSString * _Nonnull) handlerName methodExistCallback:(void(^ _Nullable)(bool exist))callback; - -// Set debug mode. if in debug mode, some errors will be prompted by a dialog -// and the exception caused by the native handlers will not be captured. -- (void) setDebugMode:(bool) debug; - -- (void) disableJavascriptDialogBlock:(bool) disable; - -// custom the label text of javascript dialog that includes alert/confirm/prompt -- (void) customJavascriptDialogLabelTitles:(NSDictionary*_Nullable) dic; - -// private method, the developer shoudn't call this method -- (id _Nullable ) onMessage:(NSDictionary *_Nonnull) msg type:(int) type; - -@end diff --git a/dsbridge/DWKWebView.m b/dsbridge/DWKWebView.m deleted file mode 100644 index 1bfbbff..0000000 --- a/dsbridge/DWKWebView.m +++ /dev/null @@ -1,488 +0,0 @@ -#import "DWKWebView.h" -#import "JSBUtil.h" -#import "DSCallInfo.h" -#import "InternalApis.h" -#import - -@implementation DWKWebView -{ - void (^alertHandler)(void); - void (^confirmHandler)(BOOL); - void (^promptHandler)(NSString *); - void(^javascriptCloseWindowListener)(void); - int dialogType; - int callId; - bool jsDialogBlock; - NSMutableDictionary *javaScriptNamespaceInterfaces; - NSMutableDictionary *handerMap; - NSMutableArray * callInfoList; - NSDictionary *dialogTextDic; - UITextField *txtName; - UInt64 lastCallTime ; - NSString *jsCache; - bool isPending; - bool isDebug; -} - - --(instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration -{ - txtName=nil; - dialogType=0; - callId=0; - alertHandler=nil; - confirmHandler=nil; - promptHandler=nil; - jsDialogBlock=true; - callInfoList=[NSMutableArray array]; - javaScriptNamespaceInterfaces=[NSMutableDictionary dictionary]; - handerMap=[NSMutableDictionary dictionary]; - lastCallTime = 0; - jsCache=@""; - isPending=false; - isDebug=false; - dialogTextDic=@{}; - - WKUserScript *script = [[WKUserScript alloc] initWithSource:@"window._dswk=true;" - injectionTime:WKUserScriptInjectionTimeAtDocumentStart - forMainFrameOnly:YES]; - [configuration.userContentController addUserScript:script]; - self = [super initWithFrame:frame configuration: configuration]; - if (self) { - super.UIDelegate=self; - } - // add internal Javascript Object - InternalApis * interalApis= [[InternalApis alloc] init]; - interalApis.webview=self; - [self addJavascriptObject:interalApis namespace:@"_dsb"]; - return self; -} - -- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt - defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame -completionHandler:(void (^)(NSString * _Nullable result))completionHandler -{ - NSString * prefix=@"_dsbridge="; - if ([prompt hasPrefix:prefix]) - { - NSString *method= [prompt substringFromIndex:[prefix length]]; - NSString *result=nil; - if(isDebug){ - result =[self call:method :defaultText ]; - }else{ - @try { - result =[self call:method :defaultText ]; - }@catch(NSException *exception){ - NSLog(@"%@", exception); - } - } - completionHandler(result); - - }else { - if(!jsDialogBlock){ - completionHandler(nil); - } - if(self.DSUIDelegate && [self.DSUIDelegate respondsToSelector: - @selector(webView:runJavaScriptTextInputPanelWithPrompt - :defaultText:initiatedByFrame - :completionHandler:)]) - { - return [self.DSUIDelegate webView:webView runJavaScriptTextInputPanelWithPrompt:prompt - defaultText:defaultText - initiatedByFrame:frame - completionHandler:completionHandler]; - }else{ - dialogType=3; - if(jsDialogBlock){ - promptHandler=completionHandler; - } - UIAlertView *alert = [[UIAlertView alloc] - initWithTitle:prompt - message:@"" - delegate:self - cancelButtonTitle:dialogTextDic[@"promptCancelBtn"]?dialogTextDic[@"promptCancelBtn"]:@"取消" - otherButtonTitles:dialogTextDic[@"promptOkBtn"]?dialogTextDic[@"promptOkBtn"]:@"确定", - nil]; - [alert setAlertViewStyle:UIAlertViewStylePlainTextInput]; - txtName = [alert textFieldAtIndex:0]; - txtName.text=defaultText; - [alert show]; - } - } -} - -- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message -initiatedByFrame:(WKFrameInfo *)frame -completionHandler:(void (^)(void))completionHandler -{ - if(!jsDialogBlock){ - completionHandler(); - } - if( self.DSUIDelegate && [self.DSUIDelegate respondsToSelector: - @selector(webView:runJavaScriptAlertPanelWithMessage - :initiatedByFrame:completionHandler:)]) - { - return [self.DSUIDelegate webView:webView runJavaScriptAlertPanelWithMessage:message - initiatedByFrame:frame - completionHandler:completionHandler]; - }else{ - dialogType=1; - if(jsDialogBlock){ - alertHandler=completionHandler; - } - UIAlertView *alertView = - [[UIAlertView alloc] initWithTitle:dialogTextDic[@"alertTitle"]?dialogTextDic[@"alertTitle"]:@"提示" - message:message - delegate:self - cancelButtonTitle:dialogTextDic[@"alertBtn"]?dialogTextDic[@"alertBtn"]:@"确定" - otherButtonTitles:nil,nil]; - [alertView show]; - } -} - --(void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message -initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler -{ - if(!jsDialogBlock){ - completionHandler(YES); - } - if( self.DSUIDelegate&& [self.DSUIDelegate respondsToSelector: - @selector(webView:runJavaScriptConfirmPanelWithMessage:initiatedByFrame:completionHandler:)]) - { - return[self.DSUIDelegate webView:webView runJavaScriptConfirmPanelWithMessage:message - initiatedByFrame:frame - completionHandler:completionHandler]; - }else{ - dialogType=2; - if(jsDialogBlock){ - confirmHandler=completionHandler; - } - UIAlertView *alertView = - [[UIAlertView alloc] initWithTitle:dialogTextDic[@"confirmTitle"]?dialogTextDic[@"confirmTitle"]:@"提示" - message:message - delegate:self - cancelButtonTitle:dialogTextDic[@"confirmCancelBtn"]?dialogTextDic[@"confirmCancelBtn"]:@"取消" - otherButtonTitles:dialogTextDic[@"confirmOkBtn"]?dialogTextDic[@"confirmOkBtn"]:@"确定", nil]; - [alertView show]; - } -} - -- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures{ - if( self.DSUIDelegate && [self.DSUIDelegate respondsToSelector: - @selector(webView:createWebViewWithConfiguration:forNavigationAction:windowFeatures:)]){ - return [self.DSUIDelegate webView:webView createWebViewWithConfiguration:configuration forNavigationAction:navigationAction windowFeatures:windowFeatures]; - } - return nil; -} - -- (void)webViewDidClose:(WKWebView *)webView{ - if( self.DSUIDelegate && [self.DSUIDelegate respondsToSelector: - @selector(webViewDidClose:)]){ - [self.DSUIDelegate webViewDidClose:webView]; - } -} - -- (BOOL)webView:(WKWebView *)webView shouldPreviewElement:(WKPreviewElementInfo *)elementInfo{ - if( self.DSUIDelegate - && [self.DSUIDelegate respondsToSelector: - @selector(webView:shouldPreviewElement:)]){ - return [self.DSUIDelegate webView:webView shouldPreviewElement:elementInfo]; - } - return NO; -} - -- (UIViewController *)webView:(WKWebView *)webView previewingViewControllerForElement:(WKPreviewElementInfo *)elementInfo defaultActions:(NSArray> *)previewActions{ - if( self.DSUIDelegate && - [self.DSUIDelegate respondsToSelector:@selector(webView:previewingViewControllerForElement:defaultActions:)]){ - return [self.DSUIDelegate - webView:webView - previewingViewControllerForElement:elementInfo - defaultActions:previewActions - ]; - } - return nil; -} - - -- (void)webView:(WKWebView *)webView commitPreviewingViewController:(UIViewController *)previewingViewController{ - if( self.DSUIDelegate - && [self.DSUIDelegate respondsToSelector:@selector(webView:commitPreviewingViewController:)]){ - return [self.DSUIDelegate webView:webView commitPreviewingViewController:previewingViewController]; - } -} - -- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex -{ - if(dialogType==1 && alertHandler){ - alertHandler(); - alertHandler=nil; - }else if(dialogType==2 && confirmHandler){ - confirmHandler(buttonIndex==1?YES:NO); - confirmHandler=nil; - }else if(dialogType==3 && promptHandler && txtName) { - if(buttonIndex==1){ - promptHandler([txtName text]); - }else{ - promptHandler(@""); - } - promptHandler=nil; - txtName=nil; - } -} - -- (void) evalJavascript:(int) delay{ - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_MSEC)), dispatch_get_main_queue(), ^{ - @synchronized(self){ - if([jsCache length]!=0){ - [self evaluateJavaScript :jsCache completionHandler:nil]; - isPending=false; - jsCache=@""; - lastCallTime=[[NSDate date] timeIntervalSince1970]*1000; - } - } - }); -} - --(NSString *)call:(NSString*) method :(NSString*) argStr -{ - NSArray *nameStr=[JSBUtil parseNamespace:[method stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]]; - - id JavascriptInterfaceObject=javaScriptNamespaceInterfaces[nameStr[0]]; - NSString *error=[NSString stringWithFormat:@"Error! \n Method %@ is not invoked, since there is not a implementation for it",method]; - NSMutableDictionary*result =[NSMutableDictionary dictionaryWithDictionary:@{@"code":@-1,@"data":@""}]; - if(!JavascriptInterfaceObject){ - NSLog(@"Js bridge called, but can't find a corresponded JavascriptObject , please check your code!"); - }else{ - method=nameStr[1]; - NSString *methodOne = [JSBUtil methodByNameArg:1 selName:method class:[JavascriptInterfaceObject class]]; - NSString *methodTwo = [JSBUtil methodByNameArg:2 selName:method class:[JavascriptInterfaceObject class]]; - SEL sel=NSSelectorFromString(methodOne); - SEL selasyn=NSSelectorFromString(methodTwo); - NSDictionary * args=[JSBUtil jsonStringToObject:argStr]; - id arg=args[@"data"]; - if(arg==[NSNull null]){ - arg=nil; - } - NSString * cb; - do{ - if(args && (cb= args[@"_dscbstub"])){ - if([JavascriptInterfaceObject respondsToSelector:selasyn]){ - __weak typeof(self) weakSelf = self; - void (^completionHandler)(id,BOOL) = ^(id value,BOOL complete){ - NSString *del=@""; - result[@"code"]=@0; - if(value!=nil){ - result[@"data"]=value; - } - value=[JSBUtil objToJsonString:result]; - value=[value stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]; - - if(complete){ - del=[@"delete window." stringByAppendingString:cb]; - } - NSString*js=[NSString stringWithFormat:@"try {%@(JSON.parse(decodeURIComponent(\"%@\")).data);%@; } catch(e){};",cb,(value == nil) ? @"" : value,del]; - __strong typeof(self) strongSelf = weakSelf; - @synchronized(self) - { - UInt64 t=[[NSDate date] timeIntervalSince1970]*1000; - jsCache=[jsCache stringByAppendingString:js]; - if(t-lastCallTime<50){ - if(!isPending){ - [strongSelf evalJavascript:50]; - isPending=true; - } - }else{ - [strongSelf evalJavascript:0]; - } - } - - }; - - void(*action)(id,SEL,id,id) = (void(*)(id,SEL,id,id))objc_msgSend; - action(JavascriptInterfaceObject,selasyn,arg,completionHandler); - break; - } - }else if([JavascriptInterfaceObject respondsToSelector:sel]){ - id ret; - id(*action)(id,SEL,id) = (id(*)(id,SEL,id))objc_msgSend; - ret=action(JavascriptInterfaceObject,sel,arg); - [result setValue:@0 forKey:@"code"]; - if(ret!=nil){ - [result setValue:ret forKey:@"data"]; - } - break; - } - NSString*js=[error stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]; - if(isDebug){ - js=[NSString stringWithFormat:@"window.alert(decodeURIComponent(\"%@\"));",js]; - [self evaluateJavaScript :js completionHandler:nil]; - } - NSLog(@"%@",error); - }while (0); - } - return [JSBUtil objToJsonString:result]; -} - -- (void)setJavascriptCloseWindowListener:(void (^)(void))callback -{ - javascriptCloseWindowListener=callback; -} - -- (void)setDebugMode:(bool)debug{ - isDebug=debug; -} - -- (void)loadUrl: (NSString *)url -{ - NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]]; - [self loadRequest:request]; -} - - -- (void)callHandler:(NSString *)methodName arguments:(NSArray *)args{ - [self callHandler:methodName arguments:args completionHandler:nil]; -} - -- (void)callHandler:(NSString *)methodName completionHandler:(void (^)(id _Nullable))completionHandler{ - [self callHandler:methodName arguments:nil completionHandler:completionHandler]; -} - --(void)callHandler:(NSString *)methodName arguments:(NSArray *)args completionHandler:(void (^)(id _Nullable value))completionHandler -{ - DSCallInfo *callInfo=[[DSCallInfo alloc] init]; - callInfo.id=[NSNumber numberWithInt: callId++]; - callInfo.args=args==nil?@[]:args; - callInfo.method=methodName; - if(completionHandler){ - [handerMap setObject:completionHandler forKey:callInfo.id]; - } - if(callInfoList!=nil){ - [callInfoList addObject:callInfo]; - }else{ - [self dispatchJavascriptCall:callInfo]; - } -} - -- (void)dispatchStartupQueue{ - if(callInfoList==nil) return; - for (DSCallInfo * callInfo in callInfoList) { - [self dispatchJavascriptCall:callInfo]; - } - callInfoList=nil; -} - -- (void) dispatchJavascriptCall:(DSCallInfo*) info{ - NSString * json=[JSBUtil objToJsonString:@{@"method":info.method,@"callbackId":info.id, - @"data":[JSBUtil objToJsonString: info.args]}]; - [self evaluateJavaScript:[NSString stringWithFormat:@"window._handleMessageFromNative(%@)",json] - completionHandler:nil]; -} - -- (void) addJavascriptObject:(id)object namespace:(NSString *)namespace{ - if(namespace==nil){ - namespace=@""; - } - if(object!=NULL){ - [javaScriptNamespaceInterfaces setObject:object forKey:namespace]; - } -} - -- (void) removeJavascriptObject:(NSString *)namespace { - if(namespace==nil){ - namespace=@""; - } - [javaScriptNamespaceInterfaces removeObjectForKey:namespace]; -} - -- (void)customJavascriptDialogLabelTitles:(NSDictionary *)dic{ - if(dic){ - dialogTextDic=dic; - } -} - -- (id)onMessage:(NSDictionary *)msg type:(int)type{ - id ret=nil; - switch (type) { - case DSB_API_HASNATIVEMETHOD: - ret= [self hasNativeMethod:msg]?@1:@0; - break; - case DSB_API_CLOSEPAGE: - [self closePage:msg]; - break; - case DSB_API_RETURNVALUE: - ret=[self returnValue:msg]; - break; - case DSB_API_DSINIT: - ret=[self dsinit:msg]; - break; - case DSB_API_DISABLESAFETYALERTBOX: - [self disableJavascriptDialogBlock:[msg[@"disable"] boolValue]]; - break; - default: - break; - } - return ret; -} - -- (bool) hasNativeMethod:(NSDictionary *) args -{ - NSArray *nameStr=[JSBUtil parseNamespace:[args[@"name"]stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]]; - NSString * type= [args[@"type"] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - id JavascriptInterfaceObject= [javaScriptNamespaceInterfaces objectForKey:nameStr[0]]; - if(JavascriptInterfaceObject){ - bool syn=[JSBUtil methodByNameArg:1 selName:nameStr[1] class:[JavascriptInterfaceObject class]]!=nil; - bool asyn=[JSBUtil methodByNameArg:2 selName:nameStr[1] class:[JavascriptInterfaceObject class]]!=nil; - if(([@"all" isEqualToString:type]&&(syn||asyn)) - ||([@"asyn" isEqualToString:type]&&asyn) - ||([@"syn" isEqualToString:type]&&syn) - ){ - return true; - } - } - return false; -} - -- (id) closePage:(NSDictionary *) args{ - if(javascriptCloseWindowListener){ - javascriptCloseWindowListener(); - } - return nil; -} - -- (id) returnValue:(NSDictionary *) args{ - void (^ completionHandler)(NSString * _Nullable)= handerMap[args[@"id"]]; - if(completionHandler){ - if(isDebug){ - completionHandler(args[@"data"]); - }else{ - @try{ - completionHandler(args[@"data"]); - }@catch (NSException *e){ - NSLog(@"%@",e); - } - } - if([args[@"complete"] boolValue]){ - [handerMap removeObjectForKey:args[@"id"]]; - } - } - return nil; -} - -- (id) dsinit:(NSDictionary *) args{ - [self dispatchStartupQueue]; - return nil; -} - -- (void) disableJavascriptDialogBlock:(bool) disable{ - jsDialogBlock=!disable; -} - -- (void)hasJavascriptMethod:(NSString *)handlerName methodExistCallback:(void (^)(bool exist))callback{ - [self callHandler:@"_hasJavascriptMethod" arguments:@[handlerName] completionHandler:^(NSNumber* _Nullable value) { - callback([value boolValue]); - }]; -} - -@end - - diff --git a/dsbridge/DWKwebview.h b/dsbridge/DWKwebview.h new file mode 100644 index 0000000..6cde14b --- /dev/null +++ b/dsbridge/DWKwebview.h @@ -0,0 +1,23 @@ +// +// DSWKwebview.h +// dspider +// +// Created by 杜文 on 16/12/28. +// Copyright © 2016年 杜文. All rights reserved. +// + +#import + +@interface DWKwebview : WKWebView +{ + bool confirmDone; + BOOL confirmResult; + void(^javascriptContextInitedListener)(void); + +} +@property (nullable, nonatomic, weak) id DSUIDelegate; +@property (nullable, nonatomic, weak) id JavascriptInterfaceObject; +- (void)loadUrl: (NSString * _Nonnull) url; +-(void) callHandler:(NSString * _Nonnull) methodName arguments:(NSArray * _Nullable) args completionHandler:(void (^ _Nullable)(NSString * _Nullable))completionHandler; +- (void)setJavascriptContextInitedListener:(void(^_Nullable)(void))callback; +@end diff --git a/dsbridge/DWKwebview.m b/dsbridge/DWKwebview.m new file mode 100644 index 0000000..ad8820d --- /dev/null +++ b/dsbridge/DWKwebview.m @@ -0,0 +1,158 @@ +// +// DSWKwebview.m +// dspider +// +// Created by 杜文 on 16/12/28. +// Copyright © 2016年 杜文. All rights reserved. +// + +#import "DWKwebview.h" +#import "JSBUtil.h" + +@implementation DWKwebview + +/* + // Only override drawRect: if you perform custom drawing. + // An empty implementation adversely affects performance during animation. + - (void)drawRect:(CGRect)rect { + // Drawing code + } + */ + +-(instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration +{ + //NSString * js=@"function setupWebViewJavascriptBridge(b){var a={call:function(d,c){return prompt('_dspiercall='+d,c)}};b(a)};"; + + NSString * js=[@"_dswk='_dsbridge=';" stringByAppendingString: INIT_SCRIPT]; + WKUserScript *script = [[WKUserScript alloc] initWithSource:js + injectionTime:WKUserScriptInjectionTimeAtDocumentStart + forMainFrameOnly:YES]; + [configuration.userContentController addUserScript:script]; + WKUserScript *scriptDomReady = [[WKUserScript alloc] initWithSource:@";prompt('_dsinited');" + injectionTime:WKUserScriptInjectionTimeAtDocumentEnd + forMainFrameOnly:YES]; + [configuration.userContentController addUserScript:scriptDomReady]; + self = [super initWithFrame:frame configuration: configuration]; + if (self) { + super.UIDelegate=self; + } + return self; +} +- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt + defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame +completionHandler:(void (^)(NSString * _Nullable result))completionHandler +{ + NSString * prefix=@"_dsbridge="; + if ([prompt hasPrefix:prefix]) + { + NSString *method= [prompt substringFromIndex:[prefix length]]; + NSString *result =[JSBUtil call:method :defaultText JavascriptInterfaceObject:_JavascriptInterfaceObject jscontext:webView]; + completionHandler(result); + }else if([prompt hasPrefix:@"_dsinited"]){ + completionHandler(@""); + if(javascriptContextInitedListener) javascriptContextInitedListener(); + + }else { + if(self.DSUIDelegate && [self.DSUIDelegate respondsToSelector: + @selector(webView:runJavaScriptTextInputPanelWithPrompt + :defaultText:initiatedByFrame + :completionHandler:)]) + { + return [self.DSUIDelegate webView:webView runJavaScriptTextInputPanelWithPrompt:prompt + defaultText:defaultText + initiatedByFrame:frame + completionHandler:completionHandler]; + }else{ + UIAlertView *alert = [[UIAlertView alloc] initWithTitle:prompt message:@"" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil]; + [alert setAlertViewStyle:UIAlertViewStylePlainTextInput]; + UITextField *txtName = [alert textFieldAtIndex:0]; + txtName.text=defaultText; + [alert show]; + while (!confirmDone){ + [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; + } + confirmDone=false; + completionHandler([txtName text]); + } + } +} + +- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message +initiatedByFrame:(WKFrameInfo *)frame +completionHandler:(void (^)(void))completionHandler +{ + if( self.DSUIDelegate && [self.DSUIDelegate respondsToSelector: + @selector(webView:runJavaScriptAlertPanelWithMessage + :initiatedByFrame:completionHandler:)]) + { + return [self.DSUIDelegate webView:webView runJavaScriptAlertPanelWithMessage:message + initiatedByFrame:frame + completionHandler:completionHandler]; + }else{ + UIAlertView *alertView = + [[UIAlertView alloc] initWithTitle:@"提示" + message:message + delegate:nil + cancelButtonTitle:@"确定" + otherButtonTitles:nil,nil]; + completionHandler(); + [alertView show]; + } +} + +-(void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message +initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler +{ + if( self.DSUIDelegate && [self.DSUIDelegate respondsToSelector: + @selector(webView:runJavaScriptConfirmPanelWithMessage:initiatedByFrame:completionHandler:)]) + { + return[self.DSUIDelegate webView:webView runJavaScriptConfirmPanelWithMessage:message + initiatedByFrame:frame + completionHandler:completionHandler]; + }else{ + UIAlertView *alertView = + [[UIAlertView alloc] initWithTitle:@"提示" + message:message + delegate:self + cancelButtonTitle:@"取消" + otherButtonTitles:@"确定", nil]; + [alertView show]; + while (!confirmDone){ + [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; + } + confirmDone=false; + completionHandler(confirmResult); + } +} + +- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex +{ + confirmDone=true; + confirmResult=buttonIndex==1?YES:NO; +} + +- (void)setJavascriptContextInitedListener:(void (^)(void))callback +{ + javascriptContextInitedListener=callback; +} + +- (void)loadUrl: (NSString *)url +{ + NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]]; + [self loadRequest:request];//加载 +} + +-(void)callHandler:(NSString *)methodName arguments:(NSArray *)args completionHandler:(void (^)(NSString * _Nullable))completionHandler +{ + if(!args){ + args=[[NSArray alloc] init]; + } + NSString *script=[NSString stringWithFormat:@"%@.apply(null,%@)",methodName,[JSBUtil objToJsonString:args]]; + [self evaluateJavaScript:script completionHandler:^(id value,NSError * error){ + if(completionHandler) completionHandler(value); + }]; +} + +@end + + diff --git a/dsbridge/DWebview.h b/dsbridge/DWebview.h new file mode 100644 index 0000000..f64bbf4 --- /dev/null +++ b/dsbridge/DWebview.h @@ -0,0 +1,39 @@ +// +// SynJsBridgeWebview.h +// dspider +// +// Created by 杜文 on 16/12/30. +// Copyright © 2016年 杜文. All rights reserved. +// + +#import +#import "DWKwebview.h" +#import "DUIwebview.h" +NS_ASSUME_NONNULL_BEGIN +@interface DWebview : UIView +@property(nonatomic, readonly, getter=canGoBack) BOOL canGoBack; +@property(nonatomic, readonly, getter=canGoForward) BOOL canGoForward; +@property(nonatomic, readonly, strong)NSURLRequest * _Nonnull request; +@property(nonatomic, readonly, getter=isLoading) BOOL loading; +@property (nullable, nonatomic, weak,setter=setJavascriptInterfaceObject:) id JavascriptInterfaceObject ; + +- (id _Nullable) getXWebview; + +- (void)loadUrl: (NSString *) url; +- (void)loadRequest:(NSURLRequest * )request; +- (void)loadHTMLString:(NSString *)string baseURL:(NSURL *)baseURL; +- (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)textEncodingName baseURL:(NSURL *)baseURL; + +- (void)stopLoading; +- (void)reload; + +- (void)setJavascriptContextInitedListener:(void (^)(void))callback; +- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(NSString *result))completionHandler; +-(void)callHandler:(NSString *)methodName arguments:(NSArray * _Nullable)args completionHandler:(void (^)(NSString * _Nullable))completionHandler; + +- (void) clearCache; + + + +@end +NS_ASSUME_NONNULL_END diff --git a/dsbridge/DWebview.m b/dsbridge/DWebview.m new file mode 100644 index 0000000..d697682 --- /dev/null +++ b/dsbridge/DWebview.m @@ -0,0 +1,220 @@ +// +// SynJsBridgeWebview.m +// dspider +// +// Created by 杜文 on 16/12/30. +// Copyright © 2016年 杜文. All rights reserved. +// + +#import "DWebview.h" + +@interface DWebview () +@property (weak) id webview; +@end + +@implementation DWebview +{ + void(^javascriptContextInitedListener)(void); + //NSString * ua; + +} + +@synthesize webview; +- (instancetype)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self) { + id wv; + if ([[UIDevice currentDevice].systemVersion floatValue] >=8.0) { + wv=[[DWKwebview alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)]; + }else{ + wv=[[DUIwebview alloc] initWithFrame:frame]; + } + [self addSubview:wv]; + webview=wv; + } + return self; +} + +- (id _Nullable) getXWebview +{ + return webview; +} + +- (void)loadRequest:(NSURLRequest *)request +{ + if([webview isKindOfClass:[DUIwebview class]]){ + [(DUIwebview *)webview loadRequest:request]; + }else{ + [(DWKwebview *)webview loadRequest:request]; + } +} + + + +- (void)loadHTMLString:(NSString *)string baseURL:(NSURL *)baseURL +{ + if([webview isKindOfClass:[DUIwebview class]]){ + [(DUIwebview *)webview loadHTMLString:string baseURL:baseURL]; + }else{ + [(DWKwebview *)webview loadHTMLString:string baseURL:baseURL]; + } +} + +- (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)textEncodingName baseURL:(NSURL *)baseURL +{ + if([webview isKindOfClass:[DUIwebview class]]){ + [(DUIwebview *)webview loadData:data MIMEType:MIMEType textEncodingName:textEncodingName baseURL:baseURL]; + }else{ + [(DWKwebview *)webview loadData:data MIMEType:MIMEType characterEncodingName:textEncodingName baseURL:baseURL]; + } +} + +-(BOOL)canGoBack +{ + if([webview isKindOfClass:[DUIwebview class]]){ + return ((DUIwebview *)webview).canGoBack; + }else{ + return ((DWKwebview *)webview).canGoBack; + } +} + +-(BOOL)canGoForward +{ + if([webview isKindOfClass:[DUIwebview class]]){ + return ((DUIwebview *)webview).canGoForward; + }else{ + return ((DWKwebview *)webview).canGoForward; + } +} + +-(BOOL)isLoading +{ + if([webview isKindOfClass:[DUIwebview class]]){ + return ((DUIwebview *)webview).isLoading; + }else{ + return ((DWKwebview *)webview).isLoading; + } +} + +-(void)reload +{ + if([webview isKindOfClass:[DUIwebview class]]){ + [(DUIwebview *)webview reload]; + }else{ + [(DWKwebview *)webview reload]; + } +} + +- (void)stopLoading +{ + if([webview isKindOfClass:[DUIwebview class]]){ + [(DUIwebview *)webview stopLoading]; + }else{ + [(DWKwebview *)webview stopLoading]; + } +} + +-(void)setJavascriptInterfaceObject:(id)jsib +{ + if([webview isKindOfClass:[DUIwebview class]]){ + ((DUIwebview *)webview).JavascriptInterfaceObject=jsib; + }else{ + ((DWKwebview *)webview).JavascriptInterfaceObject=jsib; + } +} + +- (void)loadUrl: (NSString *)url +{ + NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]]; + [self loadRequest:request];//加载 +} + +- (void)setJavascriptContextInitedListener:(void (^)(void))callback +{ + if([webview isKindOfClass:[DUIwebview class]]){ + [(DUIwebview *)webview setJavascriptContextInitedListener:callback]; + }else{ + [(DWKwebview *)webview setJavascriptContextInitedListener:callback]; + } + +} + +- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^)(NSString *))completionHandler +{ + if([webview isKindOfClass:[DUIwebview class]]){ + [(DUIwebview *)webview evaluateJavaScript:javaScriptString completionHandler:^(NSString * result){ + if(completionHandler) completionHandler(result); + }]; + }else{ + [(DWKwebview *)webview evaluateJavaScript:javaScriptString completionHandler:^(NSString * result, NSError * error){ + if(error){ + NSLog(@"WKwebview exec js error: %@",error); + } + if(!result) result=@""; + if(completionHandler ) completionHandler(error?nil:result); + }]; + } +} + +-(void)callHandler:(NSString *)methodName arguments:(NSArray *)args completionHandler:(void (^)(NSString * _Nullable))completionHandler +{ + if([webview isKindOfClass:[DUIwebview class]]){ + [(DUIwebview *)webview callHandler:methodName arguments:args completionHandler:completionHandler]; + }else{ + [(DWKwebview *)webview callHandler:methodName arguments:args completionHandler:completionHandler]; + } +} + + +- (void)clearCache +{ + if ([[UIDevice currentDevice].systemVersion floatValue] >= 9.0) { + NSSet *websiteDataTypes= [NSSet setWithArray:@[ + WKWebsiteDataTypeDiskCache, + //WKWebsiteDataTypeOfflineWebApplication + WKWebsiteDataTypeMemoryCache, + //WKWebsiteDataTypeLocal + WKWebsiteDataTypeCookies, + //WKWebsiteDataTypeSessionStorage, + //WKWebsiteDataTypeIndexedDBDatabases, + //WKWebsiteDataTypeWebSQLDatabases + ]]; + + // All kinds of data + //NSSet *websiteDataTypes = [WKWebsiteDataStore allWebsiteDataTypes]; + NSDate *dateFrom = [NSDate dateWithTimeIntervalSince1970:0]; + [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:websiteDataTypes modifiedSince:dateFrom completionHandler:^{ + + }]; + + } else { + //先删除cookie + NSHTTPCookie *cookie; + NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; + for (cookie in [storage cookies]) + { + [storage deleteCookie:cookie]; + } + + NSString *libraryDir = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0]; + NSString *bundleId = [[[NSBundle mainBundle] infoDictionary] + objectForKey:@"CFBundleIdentifier"]; + NSString *webkitFolderInLib = [NSString stringWithFormat:@"%@/WebKit",libraryDir]; + NSString *webKitFolderInCaches = [NSString + stringWithFormat:@"%@/Caches/%@/WebKit",libraryDir,bundleId]; + NSString *webKitFolderInCachesfs = [NSString + stringWithFormat:@"%@/Caches/%@/fsCachedData",libraryDir,bundleId]; + NSError *error; + /* iOS8.0 WebView Cache的存放路径 */ + [[NSFileManager defaultManager] removeItemAtPath:webKitFolderInCaches error:&error]; + [[NSFileManager defaultManager] removeItemAtPath:webkitFolderInLib error:nil]; + /* iOS7.0 WebView Cache的存放路径 */ + [[NSFileManager defaultManager] removeItemAtPath:webKitFolderInCachesfs error:&error]; + NSString *cookiesFolderPath = [libraryDir stringByAppendingString:@"/Cookies"]; + [[NSFileManager defaultManager] removeItemAtPath:cookiesFolderPath error:&error]; + } + +} + +@end diff --git a/dsbridge/InternalApis.h b/dsbridge/InternalApis.h deleted file mode 100644 index 688d55c..0000000 --- a/dsbridge/InternalApis.h +++ /dev/null @@ -1,8 +0,0 @@ -#import -#import "DWKWebView.h" - -@interface InternalApis : NSObject -@property (nullable, nonatomic, weak) DWKWebView* webview; -@end - - diff --git a/dsbridge/InternalApis.m b/dsbridge/InternalApis.m deleted file mode 100644 index 528b3c3..0000000 --- a/dsbridge/InternalApis.m +++ /dev/null @@ -1,25 +0,0 @@ -#import "InternalApis.h" -#import "JSBUtil.h" - -@implementation InternalApis -- (id) hasNativeMethod:(id) args -{ - return [self.webview onMessage:args type: DSB_API_HASNATIVEMETHOD]; -} - -- (id) closePage:(id) args{ - return [self.webview onMessage:args type:DSB_API_CLOSEPAGE]; -} - -- (id) returnValue:(NSDictionary *) args{ - return [self.webview onMessage:args type:DSB_API_RETURNVALUE]; -} - -- (id) dsinit:(id) args{ - return [self.webview onMessage:args type:DSB_API_DSINIT]; -} - -- (id) disableJavascriptDialogBlock:(id) args{ - return [self.webview onMessage:args type:DSB_API_DISABLESAFETYALERTBOX]; -} -@end diff --git a/dsbridge/JSBUtil.h b/dsbridge/JSBUtil.h index c2a6b6f..7946788 100644 --- a/dsbridge/JSBUtil.h +++ b/dsbridge/JSBUtil.h @@ -1,17 +1,27 @@ +// +// Util.h +// dspider +// +// Created by 杜文 on 16/12/27. +// Copyright © 2016年 杜文. All rights reserved. +// + #import +#import "DWebview.h" + +static NSString * _Nonnull INIT_SCRIPT=@"function getJsBridge(){return{call:function(e,c,a){var b='';if(typeof c=='function'){a=c;c={}}if(typeof a=='function'){window.dscb=window.dscb||0;var d='dscb'+window.dscb++;window[d]=a;c._dscbstub=d}c=JSON.stringify(c||{});if(window._dswk){b=prompt(window._dswk+e,c)}else{if(typeof _dsbridge=='function'){b=_dsbridge(e,c)}else{b=_dsbridge.call(e,c)}}return b}}};"; -enum{ - DSB_API_HASNATIVEMETHOD, - DSB_API_CLOSEPAGE, - DSB_API_RETURNVALUE, - DSB_API_DSINIT, - DSB_API_DISABLESAFETYALERTBOX -}; +#define SuppressPerformSelectorLeakWarning(Stuff) \ +{ \ +_Pragma("clang diagnostic push") \ +_Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"") \ +Stuff; \ +_Pragma("clang diagnostic pop") \ +} @interface JSBUtil : NSObject + (NSString * _Nullable)objToJsonString:(id _Nonnull)dict; + (id _Nullable)jsonStringToObject:(NSString * _Nonnull)jsonString; -+(NSString *_Nullable)methodByNameArg:(NSInteger)argNum - selName:( NSString * _Nullable)selName class:(Class _Nonnull )class; -+ (NSArray *_Nonnull)parseNamespace: (NSString *_Nonnull) method; ++ (NSString * _Nullable)call:(NSString* _Nonnull) method :(NSString* _Nonnull) args JavascriptInterfaceObject:(id _Nonnull) JavascriptInterfaceObject jscontext:(id _Nonnull) jscontext; + @end diff --git a/dsbridge/JSBUtil.m b/dsbridge/JSBUtil.m index 21f8f65..358c8d7 100644 --- a/dsbridge/JSBUtil.m +++ b/dsbridge/JSBUtil.m @@ -5,20 +5,14 @@ // #import "JSBUtil.h" +#import "DWebview.h" #import -#import "DWKWebView.h" - @implementation JSBUtil + (NSString *)objToJsonString:(id)dict { NSString *jsonString = nil; NSError *error; - - if (![NSJSONSerialization isValidJSONObject:dict]) { - return @"{}"; - } - NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dict options:0 error:&error]; if (! jsonData) { return @"{}"; @@ -28,58 +22,128 @@ + (NSString *)objToJsonString:(id)dict return jsonString; } +UInt64 g_ds_last_call_time = 0; +NSString *g_ds_js_cache=@""; +bool g_ds_have_pending=false; + ++(NSString *)call:(NSString*) method :(NSString*) args JavascriptInterfaceObject:(id) JavascriptInterfaceObject jscontext:(id) jscontext +{ + NSString *methodOne = [JSBUtil methodByNameArg:1 selName:method class:[JavascriptInterfaceObject class]]; + NSString *methodTwo = [JSBUtil methodByNameArg:2 selName:method class:[JavascriptInterfaceObject class]]; + SEL sel=NSSelectorFromString(methodOne); + SEL selasyn=NSSelectorFromString(methodTwo); + NSString *error=[NSString stringWithFormat:@"Error! \n Method %@ is not invoked, since there is not a implementation for it",method]; + NSString *result=@""; + if(!JavascriptInterfaceObject){ + NSLog(@"Js bridge method called, but there is not a JavascriptInterfaceObject, please set JavascriptInterfaceObject first!"); + }else{ + NSDictionary * json=[JSBUtil jsonStringToObject:args]; + NSString * cb; + do{ + if(json && (cb= [json valueForKey:@"_dscbstub"])){ + if([JavascriptInterfaceObject respondsToSelector:selasyn]){ + void (^completionHandler)(NSString *) = ^(NSString * value){ + value=[value stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]; + NSString*js=[NSString stringWithFormat:@"try {%@(decodeURIComponent(\"%@\"));delete window.%@; } catch(e){};",cb,(value == nil) ? @"" : value,cb]; + if([jscontext isKindOfClass:JSContext.class]){ + [jscontext evaluateScript:js ]; + }else if([jscontext isKindOfClass:WKWebView.class]){ + @synchronized(jscontext) + { + UInt64 t=[[NSDate date] timeIntervalSince1970]*1000; + g_ds_js_cache=[g_ds_js_cache stringByAppendingString:js]; + if(t-g_ds_last_call_time<50){ + if(!g_ds_have_pending){ + [self evalJavascript:(WKWebView *)jscontext :50]; + g_ds_have_pending=true; + } + }else{ + [self evalJavascript:(WKWebView *)jscontext :0]; + } + } + } + }; + SuppressPerformSelectorLeakWarning( + [JavascriptInterfaceObject performSelector:selasyn withObject:json withObject:completionHandler]; + + ); + //when performSelector is performing a selector that return value type is void, + //the return value of performSelector always seem to be the first argument of the selector in real device(simulator is nil). + //So,you should declare the return type of all api as NSString explicitly. + if(result==(id)json){ + result=@""; + } + + break; + } + }else if([JavascriptInterfaceObject respondsToSelector:sel]){ + SuppressPerformSelectorLeakWarning( + result=[JavascriptInterfaceObject performSelector:sel withObject:json]; + ); + break; + } + NSString*js=[error stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]; + js=[NSString stringWithFormat:@"window.alert(decodeURIComponent(\"%@\"));",js]; + if([jscontext isKindOfClass:JSContext.class]){ + [jscontext evaluateScript:js ]; + }else if([jscontext isKindOfClass:WKWebView.class]){ + [(WKWebView *)jscontext evaluateJavaScript :js completionHandler:nil]; + } + NSLog(@"%@",error); + }while (0); + } + if(result == nil||![result isKindOfClass:[NSString class]]){ + result=@""; + } + return result; +} + //get this class all method -+ (NSArray *)allMethodFromClass:(Class)class { - NSMutableArray *methods = [NSMutableArray array]; - while (class) { - unsigned int count = 0; - Method *method = class_copyMethodList(class, &count); - for (unsigned int i = 0; i < count; i++) { - SEL name1 = method_getName(method[i]); - const char *selName= sel_getName(name1); - NSString *strName = [NSString stringWithCString:selName encoding:NSUTF8StringEncoding]; - [methods addObject:strName]; - } - free(method); - - Class cls = class_getSuperclass(class); - class = [NSStringFromClass(cls) isEqualToString:NSStringFromClass([NSObject class])] ? nil : cls; ++(NSArray *)allMethodFromClass:(Class)class +{ + NSMutableArray *arr = [NSMutableArray array]; + u_int count; + Method *methods = class_copyMethodList(class, &count); + for (int i =0; i 0) { - NSString *methodName = [method substringWithRange:NSMakeRange(0, range.location)]; - if ([methodName isEqualToString:selName] && tmpArr.count == (argNum + 1)) { - result = method; - return result; - } - } + NSArray *arr = [JSBUtil allMethodFromClass:class]; + for (int i=0; i + +CF_ASSUME_NONNULL_BEGIN +@protocol JSBWebEventDelegateProtocol +@optional +- (void) onPageStart:(NSString *)url; +- (void) onpageFinished:(NSString *)url; +- (void) onpageError:(NSString *)url :(NSString *) msg; +@end +CF_ASSUME_NONNULL_END diff --git a/dsbridge/dsbridge.h b/dsbridge/dsbridge.h index 7974c40..d41208c 100644 --- a/dsbridge/dsbridge.h +++ b/dsbridge/dsbridge.h @@ -1,4 +1,15 @@ +// +// jsbridge.h +// jsbridge +// +// Created by 杜文 on 17/1/1. +// Copyright © 2017年 杜文. All rights reserved. +// + #import #import "JSBUtil.h" -#import "DWKWebView.h" +#import "DWebview.h" + +@interface jsbridge : NSObject +@end diff --git a/dsbridge/dsbridge.m b/dsbridge/dsbridge.m new file mode 100644 index 0000000..51b20ae --- /dev/null +++ b/dsbridge/dsbridge.m @@ -0,0 +1,13 @@ +// +// jsbridge.m +// jsbridge +// +// Created by 杜文 on 17/1/1. +// Copyright © 2017年 杜文. All rights reserved. +// + +#import "dsbridge.h" + +@implementation jsbridge + +@end diff --git a/dsbridgedemo/Assets.xcassets/AppIcon.appiconset/Contents.json b/dsbridgedemo/Assets.xcassets/AppIcon.appiconset/Contents.json index 19882d5..b8236c6 100644 --- a/dsbridgedemo/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/dsbridgedemo/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -39,11 +39,6 @@ "idiom" : "iphone", "size" : "60x60", "scale" : "3x" - }, - { - "idiom" : "ios-marketing", - "size" : "1024x1024", - "scale" : "1x" } ], "info" : { diff --git a/dsbridgedemo/JsApiTest.h b/dsbridgedemo/JsApiTest.h index 498829a..39440fc 100644 --- a/dsbridgedemo/JsApiTest.h +++ b/dsbridgedemo/JsApiTest.h @@ -7,7 +7,6 @@ // #import -#import "dsbridge.h" @interface JsApiTest : NSObject diff --git a/dsbridgedemo/JsApiTest.m b/dsbridgedemo/JsApiTest.m index d1da4e6..dd7f094 100644 --- a/dsbridgedemo/JsApiTest.m +++ b/dsbridgedemo/JsApiTest.m @@ -1,5 +1,6 @@ // // JsApiTest.m +// dspider // // Created by 杜文 on 16/12/30. // Copyright © 2016年 杜文. All rights reserved. @@ -7,23 +8,16 @@ #import "JsApiTest.h" -@interface JsApiTest(){ - NSTimer * timer ; - void(^hanlder)(id value,BOOL isComplete); - int value; -} -@end - @implementation JsApiTest -- (NSString *) testSyn: (NSString *) msg +- (NSString *) testSyn:(NSDictionary *) args { - return [msg stringByAppendingString:@"[ syn call]"]; + return [(NSString *)[args valueForKey:@"msg"] stringByAppendingString:@"[ syn call]"]; } -- (void) testAsyn:(NSString *) msg :(JSCallback) completionHandler +- (void) testAsyn:(NSDictionary *) args :(void (^)(NSString * _Nullable result))completionHandler { - completionHandler([msg stringByAppendingString:@" [ asyn call]"],YES); + completionHandler([(NSString *)[args valueForKey:@"msg"] stringByAppendingString:@"[ asyn call]"]); } - (NSString *)testNoArgSyn:(NSDictionary *) args @@ -31,39 +25,10 @@ - (NSString *)testNoArgSyn:(NSDictionary *) args return @"testNoArgSyn called [ syn call]"; } -- ( void )testNoArgAsyn:(NSDictionary *) args :(JSCallback)completionHandler -{ - completionHandler(@"testNoArgAsyn called [ asyn call]",YES); -} - -- ( void )callProgress:(NSDictionary *) args :(JSCallback)completionHandler +- ( void )testNoArgAsyn:(NSDictionary *) args :(void (^)(NSString * _Nullable result))completionHandler { - value=10; - hanlder=completionHandler; - timer = [NSTimer scheduledTimerWithTimeInterval:1.0 - target:self - selector:@selector(onTimer:) - userInfo:nil - repeats:YES]; -} - --(void)onTimer:t{ - if(value!=-1){ - hanlder([NSNumber numberWithInt:value--],NO); - }else{ - hanlder(0,YES); - [timer invalidate]; - } -} - -/** - * Note: This method is for Fly.js - * In browser, Ajax requests are sent by browser, but Fly can - * redirect requests to native, more about Fly see https://github.com/wendux/fly - * @param requestInfo passed by fly.js, more detail reference https://wendux.github.io/dist/#/doc/flyio-en/native - */ --(void)onAjaxRequest:(NSDictionary *) requestInfo :(JSCallback)completionHandler{ - + completionHandler(@"testNoArgAsyn called [ asyn call]"); + //return nil; } @end diff --git a/dsbridgedemo/JsApiTestSwift.swift b/dsbridgedemo/JsApiTestSwift.swift deleted file mode 100644 index 4d84f73..0000000 --- a/dsbridgedemo/JsApiTestSwift.swift +++ /dev/null @@ -1,23 +0,0 @@ -// -// JsApiTestSwift.swift -// dsbridgedemo -// -// Created by du on 2018/9/7. -// Copyright © 2018年 杜文. All rights reserved. -// - -import Foundation -typealias JSCallback = (String, Bool)->Void - -class JsApiTestSwift: NSObject { - - //MUST use "_" to ignore the first argument name explicitly。 - @objc func testSyn( _ arg:String) -> String { - return String(format:"%@[Swift sync call:%@]", arg, "test") - } - - @objc func testAsyn( _ arg:String, handler: JSCallback) { - handler(String(format:"%@[Swift async call:%@]", arg, "test"), true) - } - -} diff --git a/dsbridgedemo/JsEchoApi.h b/dsbridgedemo/JsEchoApi.h deleted file mode 100644 index 6e92407..0000000 --- a/dsbridgedemo/JsEchoApi.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// JsEchoApi.h -// dsbridge -// -// Created by du on 2018/2/1. -// Copyright © 2018年 杜文. All rights reserved. -// - -#import - -@interface JsEchoApi : NSObject - -@end diff --git a/dsbridgedemo/JsEchoApi.m b/dsbridgedemo/JsEchoApi.m deleted file mode 100644 index 005e52b..0000000 --- a/dsbridgedemo/JsEchoApi.m +++ /dev/null @@ -1,24 +0,0 @@ -// -// JsEchoApi.m -// dsbridge -// -// Created by du on 2018/2/1. -// Copyright © 2018 wendu. All rights reserved. -// - -#import "JsEchoApi.h" -#import "dsbridge.h" - -@implementation JsEchoApi - -- (id) syn:(id) arg -{ - return arg; -} - -- (void) asyn: (id) arg :(JSCallback)completionHandler -{ - completionHandler(arg,YES); -} - -@end diff --git a/dsbridgedemo/ViewController.h b/dsbridgedemo/ViewController.h index a4526f3..8f6c272 100644 --- a/dsbridgedemo/ViewController.h +++ b/dsbridgedemo/ViewController.h @@ -7,10 +7,9 @@ // #import -#import "dsbridge.h" #import "JsApiTest.h" -@interface ViewController : UIViewController +@interface ViewController : UIViewController { JsApiTest *jsApi; } diff --git a/dsbridgedemo/ViewController.m b/dsbridgedemo/ViewController.m index 4b8f12b..2a2e723 100644 --- a/dsbridgedemo/ViewController.m +++ b/dsbridgedemo/ViewController.m @@ -2,14 +2,13 @@ // ViewController.m // jsbridgedemo // -// Created by wendu on 17/1/1. -// Copyright © 2017 wendu. All rights reserved. +// Created by 杜文 on 17/1/1. +// Copyright © 2017年 杜文. All rights reserved. // #import "ViewController.h" -#import -#import "JsEchoApi.h" -#import "dsbridgedemo-Swift.h" +#import "dsbridge.h" + @interface ViewController () @end @@ -17,26 +16,15 @@ @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; CGRect bounds=self.view.bounds; - DWKWebView * dwebview=[[DWKWebView alloc] initWithFrame:CGRectMake(0, 25, bounds.size.width, bounds.size.height-25)]; - [self.view addSubview:dwebview]; - - // register api object without namespace - [dwebview addJavascriptObject:[[JsApiTest alloc] init] namespace:nil]; - - // register api object without namespace - [dwebview addJavascriptObject:[[ JsApiTestSwift alloc] init] namespace:@"swift"]; - - // register api object with namespace "echo" - [dwebview addJavascriptObject:[[JsEchoApi alloc] init] namespace:@"echo"]; - - // open debug mode, Release mode should disable this. - [dwebview setDebugMode:true]; - - [dwebview customJavascriptDialogLabelTitles:@{@"alertTitle":@"Notification",@"alertBtn":@"OK"}]; - - dwebview.navigationDelegate=self; - - // load test.html + //bounds.origin.y=20; + //bounds.size.height-=20; + DWebview * webview=[[DWebview alloc] initWithFrame:CGRectMake(0, 0, bounds.size.width, bounds.size.height-25)]; + webview.backgroundColor=UIColor.redColor; + jsApi=[[JsApiTest alloc] init]; + webview.JavascriptInterfaceObject=jsApi; + [self.view addSubview:webview]; + + //加载test.html NSString *path = [[NSBundle mainBundle] bundlePath]; NSURL *baseURL = [NSURL fileURLWithPath:path]; NSString * htmlPath = [[NSBundle mainBundle] pathForResource:@"test" @@ -44,67 +32,21 @@ - (void)viewDidLoad { NSString * htmlContent = [NSString stringWithContentsOfFile:htmlPath encoding:NSUTF8StringEncoding error:nil]; - [dwebview loadHTMLString:htmlContent baseURL:baseURL]; - - // call javascript method - [dwebview callHandler:@"addValue" arguments:@[@3,@4] completionHandler:^(NSNumber * value){ - NSLog(@"%@",value); - }]; - - [dwebview callHandler:@"append" arguments:@[@"I",@"love",@"you"] completionHandler:^(NSString * _Nullable value) { - NSLog(@"call succeed, append string is: %@",value); - }]; - - // this invocation will be return 5 times - [dwebview callHandler:@"startTimer" completionHandler:^(NSNumber * _Nullable value) { - NSLog(@"Timer: %@",value); - }]; - - // namespace syn test - [dwebview callHandler:@"syn.addValue" arguments:@[@5,@6] completionHandler:^(NSDictionary * _Nullable value) { - NSLog(@"Namespace syn.addValue(5,6): %@",value); - }]; - - [dwebview callHandler:@"syn.getInfo" completionHandler:^(NSDictionary * _Nullable value) { - NSLog(@"Namespace syn.getInfo: %@",value); - }]; - - // namespace asyn test - [dwebview callHandler:@"asyn.addValue" arguments:@[@5,@6] completionHandler:^(NSDictionary * _Nullable value) { - NSLog(@"Namespace asyn.addValue(5,6): %@",value); - }]; - - [dwebview callHandler:@"asyn.getInfo" completionHandler:^(NSDictionary * _Nullable value) { - NSLog(@"Namespace asyn.getInfo: %@",value); - }]; - - // test if javascript method exists. - [dwebview hasJavascriptMethod:@"addValue" methodExistCallback:^(bool exist) { - NSLog(@"method 'addValue' exist : %d",exist); + [webview loadHTMLString:htmlContent baseURL:baseURL]; + __weak DWebview * _webview=webview; + [webview setJavascriptContextInitedListener:^(){ + [_webview callHandler:@"test" + arguments:[[NSArray alloc] initWithObjects:@1,@"hello", nil] + completionHandler:^(NSString * value){ + NSLog(@"%@",value); + }]; }]; - - [dwebview hasJavascriptMethod:@"XX" methodExistCallback:^(bool exist) { - NSLog(@"method 'XX' exist : %d",exist); - }]; - - [dwebview hasJavascriptMethod:@"asyn.addValue" methodExistCallback:^(bool exist) { - NSLog(@"method 'asyn.addValue' exist : %d",exist); - }]; - - [dwebview hasJavascriptMethod:@"asyn.XX" methodExistCallback:^(bool exist) { - NSLog(@"method 'asyn.XX' exist : %d",exist); - }]; - - // set javascript close listener - [dwebview setJavascriptCloseWindowListener:^{ - NSLog(@"window.close called"); - } ]; - } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. } diff --git a/dsbridgedemo/dsbridgedemo-Bridging-Header.h b/dsbridgedemo/dsbridgedemo-Bridging-Header.h deleted file mode 100644 index 1b2cb5d..0000000 --- a/dsbridgedemo/dsbridgedemo-Bridging-Header.h +++ /dev/null @@ -1,4 +0,0 @@ -// -// Use this file to import your target's public headers that you would like to expose to Swift. -// - diff --git a/dsbridgedemo/test.html b/dsbridgedemo/test.html index 732ba64..50192e1 100644 --- a/dsbridgedemo/test.html +++ b/dsbridgedemo/test.html @@ -1,167 +1,85 @@ - - - DSBridge Test - - - - - - - - -
Synchronous call
-
Asynchronous call
-
Syn call (swift)
-
Async call(swift)
-
Sync call without argument
-
Async call without argument
-
echo.syn
-
echo.asyn
-
Stress test,2K times consecutive asynchronous API calls
-
Never call because without @JavascriptInterface - annotation
( This test is - just for Android ,should be ignored in IOS ) -
-
call progress
-
hasNativeMethod("xx")
-
hasNativeMethod("testSyn")
- - - + + diff --git a/img/dsbridge.png b/img/dsbridge.png deleted file mode 100644 index 7371403..0000000 Binary files a/img/dsbridge.png and /dev/null differ diff --git a/img/logo.png b/img/logo.png deleted file mode 100644 index 273f2f2..0000000 Binary files a/img/logo.png and /dev/null differ diff --git a/publish.sh b/publish.sh deleted file mode 100644 index 8fa8e16..0000000 --- a/publish.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -pod trunk push --allow-warnings diff --git a/readme-chs.md b/readme-chs.md deleted file mode 100644 index c5fa783..0000000 --- a/readme-chs.md +++ /dev/null @@ -1,515 +0,0 @@ -# DSBridge for IOS - -![dsBridge](https://github.com/wendux/DSBridge-IOS/raw/master/img/dsbridge.png) - - -[![cocoapods](https://img.shields.io/cocoapods/v/dsBridge.svg?style=flat)](https://github.com/wendux/DSBridge-IOS) ![](https://img.shields.io/badge/language-object--c-yellow.svg) [![](https://travis-ci.org/wendux/DSBridge-IOS.svg?branch=master)](https://travis-ci.org/wendux/DSBridge-IOS) [![MIT Licence](https://img.shields.io/packagist/l/doctrine/orm.svg)](https://opensource.org/licenses/mit-license.php) ![support](https://img.shields.io/badge/support-IOS%208%2B-green.svg) ![platform](https://img.shields.io/badge/platform-ios|osx-ff69b4.svg) [![GitHub last commit](https://img.shields.io/github/last-commit/wendux/DSBridge-IOS.svg?color=blue)](https://github.com/wendux/DSBridge-IOS/) -> 三端易用的现代跨平台 Javascript bridge, 通过它,你可以在Javascript和原生之间同步或异步的调用彼此的函数. - -### 注意 - -DSBridge v3.0 是一个里程碑版本,和v2.0相比,有许多变化,需要注意的是v3.0**不兼容**之前版本,但是我们也会继续维护v2.0分支,所以,如果你是v2.0的使用者,请放心继续使用v2.0,如果你是新用户,请使用>=v3.0. - -[DSBridge v3.0.0 更新列表](https://github.com/wendux/DSBridge-IOS/issues/25) - -[DSBridge-Android](https://github.com/wendux/DSBridge-Android) - -## 特性 - -1. Android、IOS、Javascript 三端易用,轻量且强大、安全且健壮。 -2. 同时支持同步调用和异步调用 -3. 支持以类的方式集中统一管理API -4. 支持API命名空间 -5. 支持调试模式 -6. 支持API存在性检测 -7. 支持进度回调:一次调用,多次返回 -8. 支持Javascript关闭页面事件回调 -9. 支持Javascript 模态/非模态对话框 -10. Android端支持腾讯X5内核 - -## 安装 - -```shell -pod "dsBridge" -``` - -## 示例 - -请参考工程目录下的 `dsbridgedemo/` 文件夹. 运行并查看示例交互. - -如果要在你自己的项目中使用 dsBridge : - -## 使用 - -1. 新建一个类,实现API - - ```objective-c - #import "dsbridge.h" - ... - @implementation JsApiTest - //同步API - - (NSString *) testSyn:(NSString *) msg - { - return [msg stringByAppendingString:@"[ syn call]"]; - } - //异步API - - (void) testAsyn:(NSString *) msg :(JSCallback)completionHandler - { - completionHandler([msg stringByAppendingString:@" [ asyn call]"],YES); - } - @end - ``` - 可以看到,DSBridge正式通过API类的方式集中、统一地管理API。 - -2. 添加API类实例到 DWKWebView - - ```objective-c - DWKWebView * dwebview=[[DWKWebView alloc] initWithFrame:bounds]; - // register api object without namespace - [dwebview addJavascriptObject:[[JsApiTest alloc] init] namespace:nil]; - ``` - -3. 在Javascript中调用原生 (Java/Object-c/swift) API ,并注册一个 javascript API供原生调用. - - - 初始化 dsBridge - - ```javascript - //cdn方式引入初始化代码(中国地区慢,建议下载到本地工程) - // - //npm方式安装初始化代码 - //npm install dsbridge@3.1.4 - var dsBridge=require("dsbridge") - ``` - - - 调用原生API ,并注册一个 javascript API供原生调用. - - ```javascript - - //同步调用 - var str=dsBridge.call("testSyn","testSyn"); - - //异步调用 - dsBridge.call("testAsyn","testAsyn", function (v) { - alert(v); - }) - - //注册 javascript API - dsBridge.register('addValue',function(l,r){ - return l+r; - }) - ``` - -4. 在Object-c中调用Javascript API - - ```objective-c - [dwebview callHandler:@"addValue" arguments:@[@3,@4] completionHandler:^(NSNumber* value){ - NSLog(@"%@",value); - }]; - ``` - - -## Object-C API 签名 - - OC API 必须符合如下签名: - -1. 同步API. - - **`(id) handler:(id) msg`** - - 参数可以是任何类型, 但是返回值类型不能为 **void。** **如果不需要参数,也必须声明**,声明后不使用就行。 - > 如果同步API返回值类型为void,调用时则会导致Crash,请务必遵守签名规范。 - -2. 异步 API. - - **` (void) handler:(id)arg :(void (^)( id result,BOOL complete))completionHandler)`** - - `JSCallback` 是一个block类型: - - ```objective-c - typedef void (^JSCallback)(NSString * _Nullable result,BOOL complete); - ``` - -> 注意:API名字**不能**以"init"开始,因为oc的类中是被预留的, 如果以"init"开始,执行结果将无法预期(很多时候会crash)。 -> - - - -## 在Swift中使用 - -在 Swift中,你应该按照如下方式声明APIs: - -```swift -//必须给第一个参数前添加下划线"_"来显式忽略参数名。 -@objc func testSyn( _ arg:String) -> String { - return String(format:"%@[Swift sync call:%@]", arg, "test") -} - -@objc func testAsyn( _ arg:String, handler: (String, Bool)->Void) { - handler(String(format:"%@[Swift async call:%@]", arg, "test"), true) -} -``` - -有两点必须注意: - -- 必须给Swift API添加 "@objc" 标注。 -- 必须给第一个参数前添加下划线"_"来显式忽略参数名 - -完整的示例在 [这里](https://github.com/wendux/DSBridge-IOS/blob/master/dsbridgedemo/JsApiTestSwift.swift) . - -## 命名空间 - -命名空间可以帮助你更好的管理API,这在API数量多的时候非常实用,比如在混合应用中。DSBridge (>= v3.0.0) 支持你通过命名空间将API分类管理,并且命名空间支持多级的,不同级之间只需用'.' 分隔即可。 - - - -## 调试模式 - -在调试模式时,发生一些错误时,将会以弹窗形式提示,并且原生API如果触发异常将不会被自动捕获,因为在调试阶段应该将问题暴露出来。如果调试模式关闭,错误将不会弹窗,并且会自动捕获API触发的异常,防止crash。强烈建议在开发阶段开启调试模式,可以通过如下代码开启调试模式: - -```objective-c -// open debug mode -[dwebview setDebugMode:true]; -``` - - - -## 进度回调 - -通常情况下,调用一个方法结束后会返回一个结果,是一一对应的。但是有时会遇到一次调用需要多次返回的场景,比如在javascript钟调用端上的一个下载文件功能,端上在下载过程中会多次通知javascript进度, 然后javascript将进度信息展示在h5页面上,这是一个典型的一次调用,多次返回的场景,如果使用其它Javascript bridge, 你将会发现要实现这个功能会比较麻烦,而DSBridge本省支持进度回调,你可以非常简单方便的实现一次调用需要多次返回的场景,下面我们实现一个倒计时的例子: - -In Object-c - -```objective-c -- ( void )callProgress:(NSDictionary *) args :(JSCallback)completionHandler -{ - value=10; - hanlder=completionHandler; - timer = [NSTimer scheduledTimerWithTimeInterval:1.0 - target:self - selector:@selector(onTimer:) - userInfo:nil - repeats:YES]; -} --(void)onTimer:t{ - if(value!=-1){ - hanlder([NSNumber numberWithInt:value--],NO); - }else{ - hanlder(@"",YES); - [timer invalidate]; - } -} -``` - -In javascript - -```javascript -dsBridge.call("callProgress", function (value) { - document.getElementById("progress").innerText = value -}) -``` - -完整的示例代码请参考demo工程。 - - - -## Javascript 弹出框 - -DSBridge已经实现了 Javascript的弹出框函数(alert/confirm/prompt),这些对话框按钮、标签文字默认都是中文的,如果你想自定义这些文本可以参考 `customJavascriptDialogLabelTitles` API,如果你不想使用DSBridge实现的对话框,你可以通过设置`DSUIDelegate` 属性(是WKUIDelegate的代理属性)完全自定义。 - -另外注意,DSBridge实现的弹出框都是模态的,这会阻塞UI线程,如果你需要非模态的对话框,请参考`disableJavascriptDialogBlock` API. - - - -# WKUIDelegate - -在 `DWKWebView ` 中,请使用` DSUIDelegate` 代替 `UIDelegate` , 因为在`DWKWebView ` 内部 `UIDelegate`已经设置过了,而 `DSUIDelegate` 正是 `UIDelegate` 的一个代理。 - - - -## API 列表 - -### Object-C API - -在Object-c中我们把实现了供 javascript调用的 API类的实例 成为 **Object-c API object**. - -##### `addJavascriptObject:(id) object namespace:(NSString *) namespace` - -添加一个 Object-c API object 到DWKWebView,并为它指定一个命名空间. 然后,在 javascript 中就可以通过`bridge.call("namespace.api",...)`来调用Object-c API object中的原生API了。 - -如果命名空间是空(nil或空字符串), 那么这个添加的 Object-c API object就没有命名空间。在 javascript 通过 `bridge.call("api",...)`调用。 - -示例: - -**In Object-c** - -```objective-c -@implementation JsEchoApi -- (id) syn:(id) arg -{ - return arg; -} -- (void) asyn: (id) arg :(JSCallback)completionHandler -{ - completionHandler(arg,YES); -} -@end -// register api object with namespace "echo" -[dwebview addJavascriptObject:[[JsEchoApi alloc] init] namespace:@"echo"]; -``` - -**In Javascript** - -```javascript -// call echo.syn -var ret=dsBridge.call("echo.syn",{msg:" I am echoSyn call", tag:1}) -alert(JSON.stringify(ret)) -// call echo.asyn -dsBridge.call("echo.asyn",{msg:" I am echoAsyn call",tag:2},function (ret) { - alert(JSON.stringify(ret)); -}) -``` - - - -##### `removeJavascriptObject:(NSString *) namespace` - -通过命名空间名称移除相应的 Object-c API object. - - - -##### `callHandler:(NSString *) methodName arguments:(NSArray *) args` - -##### `callHandler:(NSString *) methodName completionHandler:(void (^)(id value))completionHandler` - -##### `callHandler:(NSString *) methodName arguments:(NSArray *) args completionHandler:(void (^ )(id value))completionHandler` - -调用 javascript API.`methodName` 为javascript API 的名称,可以包含命名空间;参数以数组传递,`argumentss`数组中的元素依次对应javascript API的形参; `completionHandler` 用于接收javascript API的返回值,**注意: `completionHandler`将在主线程中被执行**。 - -示例: - -```objective-c -[dwebview callHandler:@"append" arguments:@[@"I",@"love",@"you"] - completionHandler:^(NSString * _Nullable value) { - NSLog(@"call succeed, append string is: %@",value); -}]; -// call with namespace 'syn', More details to see the Demo project -[dwebview callHandler:@"syn.getInfo" completionHandler:^(NSDictionary * _Nullable value) { - NSLog(@"Namespace syn.getInfo: %@",value); -}]; -``` - - - -##### `disableJavascriptDialogBlock:(bool) disable` - -**小心使用**. 如果你再javascript中调用弹窗函数(`alert`,` confirm`, 或 `prompt`), 那么APP将会挂起,因为这些弹窗都是**模态**的,会阻塞APP主线程,此时javascript执行流也会阻塞。如果你想避免阻塞,可以通过此API禁止,禁止后,一旦 javascript中调用了这些弹窗函数,APP将弹出**非模态**对话框,并立即返回,( `confirm` 会返回 `true`, `prompt` 返回空字符串)。 - -如: - -```objective-c -[dwebview disableJavascriptDialogBlock: true] -``` - -如果你想恢复**模态**对话框,传 `false` 调用即可. - - - -##### `setJavascriptCloseWindowListener:(void(^_Nullable)(void))callback` - -当 Javascript中调用`window.close`时,DWKWebView会触发此监听器: - -Example: - -```objective-c -[dwebview setJavascriptCloseWindowListener:^{ - NSLog(@"window.close called"); -}]; -``` - - - -##### `hasJavascriptMethod:(NSString*) handlerName methodExistCallback:(void(^)(bool exist))callback` - -检测是否存在指定的 javascript API,`handlerName`可以包含命名空间. - -Example: - -```objective-c -// test if javascript method exists. -[dwebview hasJavascriptMethod:@"addValue" methodExistCallback:^(bool exist) { - NSLog(@"method 'addValue' exist : %d",exist); -}]; -``` - - - -##### `setDebugMode:(bool) debug` - -设置调试模式。在调试模式时,发生一些错误时,将会以弹窗形式提示,并且原生API如果触发异常将不会被自动捕获,因为在调试阶段应该将问题暴露出来。如果调试模式关闭,错误将不会弹窗,并且会自动捕获API触发的异常,防止crash。强烈建议在开发阶段开启调试模式。 - - - -##### `customJavascriptDialogLabelTitles:(NSDictionary*) dic` - -custom the label text of javascript dialog that includes alert/confirm/prompt, the default text language is Chinese. - -自定义 javascript对话框上按钮、标签的文本,默认的文本语言是中文,你可以自定义英文,如: - -```objective-c -[dwebview customJavascriptDialogLabelTitles:@{ - @"alertTitle":@"Notification", - @"alertBtn":@"OK", - @"confirmTitle":@"", - @"confirmCancelBtn":@"CANCEL", - @"confirmOkBtn":@"OK", - @"promptCancelBtn":@"CANCEL", - @"promptOkBtn":@"OK" -}]; -``` - - - -### Javascript API - -##### dsBridge - -"dsBridge" 在初始化之后可用 . - -##### `dsBridge.call(method,[arg,callback])` - -同步或异步的调用Java API。 - -`method`: Java API 名称, 可以包含命名空间。 - -`arg`:传递给Java API 的参数。只能传一个,如果需要多个参数时,可以合并成一个json对象参数。 - -`callback(String returnValue)`: 处理Java API的返回结果. 可选参数,**只有异步调用时才需要提供**. - - - -##### `dsBridge.register(methodName|namespace,function|synApiObject)` - -##### `dsBridge.registerAsyn(methodName|namespace,function|asyApiObject)` - -注册同步/异步的Javascript API. 这两个方法都有两种调用形式: - -1. 注册一个普通的方法,如: - - **In Javascript** - - ```javascript - dsBridge.register('addValue',function(l,r){ - return l+r; - }) - dsBridge.registerAsyn('append',function(arg1,arg2,arg3,responseCallback){ - responseCallback(arg1+" "+arg2+" "+arg3); - }) - ``` - - **In Object-c** - - ```objective-c - // call javascript method - [dwebview callHandler:@"addValue" arguments:@[@3,@4] completionHandler:^(NSNumber * value){ - NSLog(@"%@",value); - }]; - - [dwebview callHandler:@"append" arguments:@[@"I",@"love",@"you"] completionHandler:^(NSString * _Nullable value) { - NSLog(@"call succeed, append string is: %@",value); - }]; - ``` - - ​ - -2. 注册一个对象,指定一个命名空间: - - **In Javascript** - - ```java - //namespace test for synchronous - dsBridge.register("test",{ - tag:"test", - test1:function(){ - return this.tag+"1" - }, - test2:function(){ - return this.tag+"2" - } - }) - - //namespace test1 for asynchronous calls - dsBridge.registerAsyn("test1",{ - tag:"test1", - test1:function(responseCallback){ - return responseCallback(this.tag+"1") - }, - test2:function(responseCallback){ - return responseCallback(this.tag+"2") - } - }) - ``` - - > 因为Javascript并不支持函数重载,所以不能在同一个Javascript对象中定义同名的同步函数和异步函数 - - **In Object-c** - - ```objective-c - [dwebview callHandler:@"test.test1" completionHandler:^(NSString * _Nullable value) { - NSLog(@"Namespace test.test1: %@",value); - }]; - - [dwebview callHandler:@"test1.test1" completionHandler:^(NSString * _Nullable value) { - NSLog(@"Namespace test1.test1: %@",value); - }]; - ``` - - - - -##### `dsBridge.hasNativeMethod(handlerName,[type])` - -检测Java中是否存在名为`handlerName`的API, `handlerName` 可以包含命名空间. - -`type`: 可选参数,`["all"|"syn"|"asyn" ]`, 默认是 "all". - -```javascript -//检测是否存在一个名为'testAsyn'的API(无论同步还是异步) -dsBridge.hasNativeMethod('testAsyn') -//检测test命名空间下是否存在一个’testAsyn’的API -dsBridge.hasNativeMethod('test.testAsyn') -// 检测是否存在一个名为"testSyn"的异步API -dsBridge.hasNativeMethod('testSyn','asyn') //false -``` - - -##### `dsBridge.disableJavascriptDialogBlock(disable)` - -调用 `dsBridge.disableJavascriptDialogBlock(...)` 和在Java中调用 `dwebview.disableJavascriptDialogBlock(...)` 作用一样. - -示例: - -```javascript -//disable -dsBridge.disableJavascriptDialogBlock() -//enable -dsBridge.disableJavascriptDialogBlock(false) -``` - - -## 和 fly.js一起使用 - -当dsBridge遇见 [Fly.js](https://github.com/wendux/fly) 时,将会打开一个新的世界。[fly.js传送门](https://github.com/wendux/fly) - -正如我们所知,在浏览器中,ajax请求受同源策略限制,不能跨域请求资源。然而, [Fly.js](https://github.com/wendux/fly) 有一个强大的功能就是支持请求重定向:将ajax请求通过任何Javascript bridge重定向到端上,并且 [Fly.js](https://github.com/wendux/fly) 官方已经提供的 dsBridge 的 adapter, 可以非常方便的协同dsBridge一起使用。由于端上没有同源策略的限制,所以 fly.js可以请求任何域的资源。 - -另一个典型的使用场景是在混合APP中,由于[Fly.js](https://github.com/wendux/fly) 可以将所有ajax请求转发到端上,所以,开发者就可以在端上进行统一的请求管理、证书校验、cookie管理、访问控制等。 - -详情请参考 https://github.com/wendux/fly. (DSBridge Android版 demo中包含fly.js的示例) - - - -## 最后 - -如果你喜欢 DSBridge, 欢迎star,以便更多的人知道它, 谢谢 ! - -