一. WKWebView与JS交互
首先使用WKWebView.你需要导入WebKit。关于WKWebView其他基础使用不在本篇研究范围。
1. iOS端执行一段js代码。
使用1
2
3
4
5```
//OC代码
[webView evaluateJavaScript:jsStr completionHandler:^(id _Nullable response, NSError * _Nullable error) {
}];
1 | //swift代码 |
一般使用以上方法,是在网页加载完成(1
2###### 2. 通过使用userContentController向网页注入js。
注入的js可以取名字,将会在```WKScriptMessageHandler```的代理方法```didReceiveScriptMessage```中被回掉。
//OC代码
NSString js = @”I am JS Code”;
//初始化WKUserScript对象
//WKUserScriptInjectionTimeAtDocumentEnd为网页加载完成时注入
WKUserScript script = [[WKUserScript alloc] initWithSource:js injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
//根据生成的WKUserScript对象,初始化WKWebViewConfiguration
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
[config.userContentController addUserScript:script];
//设置ScriptMessageHandler为self
[config.userContentController addScriptMessageHandler:self name:@”APPJS”];
self.webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config];1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16```
//swift代码
//从js文件加载js代码
let path = (bundlePath) + ("/" + "Contents/Resources/ContextMenu.js")
let source = try! NSString(contentsOfFile: path, encoding: String.Encoding.utf8.rawValue) as String
let path2 = (bundlePath) + ("/" + "Contents/Resources/JSBridge.js")
let source2 = try! NSString(contentsOfFile: path2, encoding: String.Encoding.utf8.rawValue) as String
let js = source + source2
let userScript = WKUserScript(source: js, injectionTime: WKUserScriptInjectionTime.atDocumentStart, forMainFrameOnly: false)
configuration!.userContentController.addUserScript(userScript)
//设置ScriptMessageHandler为self
configuration.userContentController.add(TabManager.sharedInstance, name: "APPJS")
let newWebView = WKWebView(frame: CGRect.zero, configuration: configuration)
self.webView = newWebView
3. 在网页里,使用js代码进行与原生交互。
比如js需要app已经登陆了用户,js调用原生的方法:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17//js代码
function callLogin() {
// APPJS是我们所注入的对象
window.webkit.messageHandlers.APPJS.postMessage("shouldLogin");
}
//复杂点的js方法,参数中约定好格式。
//比如:fun代表方法名,arg代表参数,callback是原生处理完需要回掉js的方法名
function callLogin() {
// APPJS是我们所注入的对象
window.webkit.messageHandlers.APPJS.postMessage({
fun: 'notifyLoginOut',
arg: {
callback: ""
}
});
}
4. 原生处理js内容。
通过在1
2
3
4
5
6
7
8
9
10
11
12
13```
//OC代码
- (void)userContentController:(WKUserContentController *)userContentController
didReceiveScriptMessage:(WKScriptMessage *)message {
if ([message.name isEqualToString:@"APPJS"]) {
// 打印所传过来的参数,只支持NSNumber, NSString, NSDate, NSArray,
// NSDictionary, and NSNull类型
//do something
NSLog(@"%@", message.body);
}else if ([message.name isEqualToString:@"AppModel"]){
NSLog(@"%@", message.body);
}
}
1 | //swift代码,这个对应上面比较复杂js的处理,按约定格式 |
5. 原生调用js方法。
上面的这些足够应付大多数的js和原生交互了。
当然,你还有一个使用场景,想要原生某些控件去调用网页里的某个js方法。比如:点击toolBar上的目录按钮,让网页左侧显示出章节目录。
已知js的显示章节目录的js方法名为1
2
//OC代码
NSString jsStr = @”window.toggleCatalog();”;
[self.webView evaluateJavaScript:jsStr completionHandler:^(id _Nullable, NSError _Nullable error) {
}]1
2
3
4```
//swift代码
let js ="window.toggleCatalog();"
webView?.evaluateJavaScript(js, completionHandler: nil)
二. WKWebView的cookie注入与清除
WKWebView与UIWebview的一个区别,就是WKWebView实例将会忽略任何的默认网络存储器(NSURLCache, NSHTTPCookieStorage, NSCredentialStorage) 和一些标准的自定义网络请求类(NSURLProtocol,等等.).
WKWebView实例不会把Cookie存入到App标准的的Cookie容器(NSHTTPCookieStorage)中,因为 NSURLSession/NSURLConnection等网络请求使用NSHTTPCookieStorage进行访问Cookie,所以不能访问WKWebView的Cookie,现象就是WKWebView存了Cookie,其他的网络类如NSURLSession/NSURLConnection却看不到
与Cookie相同的情况就是WKWebView的缓存,凭据等。WKWebView都拥有自己的私有存储,因此和标准cocoa网络类兼容的不是那么好
NSHTTPCookieStorage 实现管理cookie的单利,每个cookie都是NSHTTPCookie类的实例,做为一个规则,cookie在所有应用 之间共享并在不同进程之间保持同步。
上面引入了网页需要用户登陆,然后让app跳转登陆界面进行登陆,app登陆之后自然要向网页注入cookie,来让网页继续剩下的功能。
1. 在webview发起请求的时候附带cookie。
这个适用首次发起网页请求,同样适用点击,在webview代理方法里,判断是否需要注入cookie的域名,如果是,截断请求,重新发起注入了cookie的请求。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17//oc代码
NSMutableDictionary *cookieDic = [NSMutableDictionary dictionary];
NSMutableString *cookieValue = [NSMutableString stringWithFormat:@""];
NSHTTPCookieStorage *cookieJar = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (NSHTTPCookie *cookie in [cookieJar cookies]) {
[cookieDic setObject:cookie.value forKey:cookie.name];
}
// cookie重复,先放到字典进行去重,再进行拼接
for (NSString *key in cookieDic) {
NSString *appendString = [NSString stringWithFormat:@"%@=%@;", key, [cookieDic valueForKey:key]];
[cookieValue appendString:appendString];
}
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.url]];
[request addValue:cookieValue forHTTPHeaderField:@"Cookie"];
NSLog(@"添加cookie");
[self.webView loadRequest:request];
1 | //swift代码 |
2. 在webview创建的时候js注入cookie。
其中js的写法问题,有可能有多个写法是cookie之间用1
2
//OC代码
WKUserContentController* userContentController = WKUserContentController.new;
WKUserScript * cookieScript = [[WKUserScript alloc] initWithSource: @”document.cookie =’TeskCookieKey1=TeskCookieValue1’;document.cookie = ‘TeskCookieKey2=TeskCookieValue2’;”injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
[userContentController addUserScript:cookieScript];
WKWebViewConfiguration* webViewConfig = WKWebViewConfiguration.new;
webViewConfig.userContentController = userContentController;
WKWebView webView = [[WKWebView alloc] initWithFrame:CGRectMake(/set your values*/) configuration:webViewConfig];1
2
3
4
5
6
7
8
9
10
11```
//swift代码
let userContent = WKUserContentController()
let jsStr = "document.cookie ='TeskCookieKey1=TeskCookieValue1';document.cookie = 'TeskCookieKey2=TeskCookieValue2';"
let cookieScript = WKUserScript.init(source: jsStr, injectionTime: WKUserScriptInjectionTime.atDocumentStart, forMainFrameOnly: false)
userContent.addUserScript(cookieScript)
let webViewConfig = WKWebViewConfiguration()
webViewConfig.userContentController = userContent
let webview = WKWebView.init(frame: CGRect(x: 0, y: 0, width: 300, height: 300), configuration: webViewConfig)
3. 在webview加载内容时js注入cookie。
1 | //swift代码 |
经过验证,最好是第一点和第三点同时使用,第二点每次截断请求总觉得浪费资源 -。-
4. 清理注入的cookie。
当用户退出登陆的时候,需要清理已经注入的cookie。在iOS9之前,wkwebview是没有清理cookie的方法的,所以需要对不同的版本进行不同的操作。
那iOS9之前的如何操作?可以预见,既然是缓存,肯定是放在沙盒里的。找到沙盒的目录,删除文件即可。
```
//swift代码
/// 清理cookie缓存数据
func ClearCache() {
if #available(iOS 9.0, *) {
let websiteDataTypes = WKWebsiteDataStore.allWebsiteDataTypes()
let dateFrom = Date.init(timeIntervalSince1970: 0)
// NSDate.init(timeIntervalSince1970: 0)
// let websiteDataTypes: NSSet = WKWebsiteDataStore.allWebsiteDataTypes()
WKWebsiteDataStore.default().removeData(ofTypes: websiteDataTypes, modifiedSince: dateFrom, completionHandler: {
//done
})
} else {
let libraryPath = NSSearchPathForDirectoriesInDomains(.libraryDirectory, .userDomainMask, true)[0]
let cookiesFolderPath = libraryPath+"/Cookies"
try? FileManager.default.removeItem(atPath: cookiesFolderPath)
// try? NSFileManager.defaultManager().removeItemAtPath(cookiesFolderPath)
}
}