From 01b56425d07c2c91554aea0d1f3b31d83089b976 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BE=AF=E6=A3=AE=E9=AD=81?= Date: Fri, 4 Oct 2019 15:35:09 +0800 Subject: [PATCH 01/44] Delete UIWebView API that Apple have fidden to use. --- Example Apps/ExampleApp-iOS/ExampleAppDelegate.m | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/Example Apps/ExampleApp-iOS/ExampleAppDelegate.m b/Example Apps/ExampleApp-iOS/ExampleAppDelegate.m index ff9f5bef..7b893ff4 100644 --- a/Example Apps/ExampleApp-iOS/ExampleAppDelegate.m +++ b/Example Apps/ExampleApp-iOS/ExampleAppDelegate.m @@ -1,25 +1,15 @@ #import "ExampleAppDelegate.h" -#import "ExampleUIWebViewController.h" #import "ExampleWKWebViewController.h" @implementation ExampleAppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - // 1. Create the UIWebView example - ExampleUIWebViewController* UIWebViewExampleController = [[ExampleUIWebViewController alloc] init]; - UIWebViewExampleController.tabBarItem.title = @"UIWebView"; - - // 2. Create the tab footer and add the UIWebView example UITabBarController *tabBarController = [[UITabBarController alloc] init]; - [tabBarController addChildViewController:UIWebViewExampleController]; - // 3. Create the WKWebView example for devices >= iOS 8 - if([WKWebView class]) { - ExampleWKWebViewController* WKWebViewExampleController = [[ExampleWKWebViewController alloc] init]; - WKWebViewExampleController.tabBarItem.title = @"WKWebView"; - [tabBarController addChildViewController:WKWebViewExampleController]; - } + ExampleWKWebViewController* WKWebViewExampleController = [[ExampleWKWebViewController alloc] init]; + WKWebViewExampleController.tabBarItem.title = @"WKWebView"; + [tabBarController addChildViewController:WKWebViewExampleController]; self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; self.window.rootViewController = tabBarController; From 7e1b997c3671e632aa53deb0c7c06eb7e23967e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BE=AF=E6=A3=AE=E9=AD=81?= Date: Fri, 4 Oct 2019 17:34:18 +0800 Subject: [PATCH 02/44] 1.Redesign core bridge codes. 2.Delete some files. 3.Arrangement Example.html --- .../ExampleApp-iOS.xcodeproj/project.pbxproj | 37 +-- .../ExampleUIWebViewController.h | 13 - .../ExampleUIWebViewController.m | 90 ----- .../ExampleWKWebViewController.m | 48 ++- Example Apps/ExampleApp.html | 49 ++- .../WKWebView+JavaScriptBridge.h | 32 ++ .../WKWebView+JavaScriptBridge.m | 308 ++++++++++++++++++ .../WKWebViewJavascriptBridge.h | 34 -- .../WKWebViewJavascriptBridge.m | 198 ----------- .../WebViewJavascriptBridge.h | 50 --- .../WebViewJavascriptBridge.m | 211 ------------ .../WebViewJavascriptBridgeBase.h | 46 --- .../WebViewJavascriptBridgeBase.m | 221 ------------- .../WebViewJavascriptBridge_JS.h | 3 - .../WebViewJavascriptBridge_JS.m | 139 -------- 15 files changed, 390 insertions(+), 1089 deletions(-) delete mode 100644 Example Apps/ExampleApp-iOS/ExampleUIWebViewController.h delete mode 100644 Example Apps/ExampleApp-iOS/ExampleUIWebViewController.m create mode 100644 WebViewJavascriptBridge/WKWebView+JavaScriptBridge.h create mode 100644 WebViewJavascriptBridge/WKWebView+JavaScriptBridge.m delete mode 100644 WebViewJavascriptBridge/WKWebViewJavascriptBridge.h delete mode 100644 WebViewJavascriptBridge/WKWebViewJavascriptBridge.m delete mode 100755 WebViewJavascriptBridge/WebViewJavascriptBridge.h delete mode 100755 WebViewJavascriptBridge/WebViewJavascriptBridge.m delete mode 100755 WebViewJavascriptBridge/WebViewJavascriptBridgeBase.h delete mode 100755 WebViewJavascriptBridge/WebViewJavascriptBridgeBase.m delete mode 100644 WebViewJavascriptBridge/WebViewJavascriptBridge_JS.h delete mode 100644 WebViewJavascriptBridge/WebViewJavascriptBridge_JS.m diff --git a/Example Apps/ExampleApp-iOS.xcodeproj/project.pbxproj b/Example Apps/ExampleApp-iOS.xcodeproj/project.pbxproj index 324a5b06..0e6ddccf 100644 --- a/Example Apps/ExampleApp-iOS.xcodeproj/project.pbxproj +++ b/Example Apps/ExampleApp-iOS.xcodeproj/project.pbxproj @@ -8,34 +8,20 @@ /* Begin PBXBuildFile section */ 0E4E9D4C1A101E0B00043087 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E8082DC19EDD98700479452 /* WebKit.framework */; }; - 0E50601C1A01B442000BEEEA /* WebViewJavascriptBridgeBase.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E50601B1A01B442000BEEEA /* WebViewJavascriptBridgeBase.m */; }; - 0E8082DB19EDC32300479452 /* WKWebViewJavascriptBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E8082DA19EDC32300479452 /* WKWebViewJavascriptBridge.m */; }; 0ECB01441A0EE1F20037FF4E /* ExampleWKWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0ECB01431A0EE1F20037FF4E /* ExampleWKWebViewController.m */; }; - 2C1562C0176BA63500B4AE50 /* WebViewJavascriptBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C1562A9176B9F6200B4AE50 /* WebViewJavascriptBridge.m */; }; - 2C3E7C461C5A890A00A1E322 /* WebViewJavascriptBridge_JS.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C3E7C451C5A890A00A1E322 /* WebViewJavascriptBridge_JS.m */; }; - 2C45CA2C1884AD520002A4E2 /* ExampleUIWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C45CA2B1884AD520002A4E2 /* ExampleUIWebViewController.m */; }; 2CA045BF17117439006DEE8B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 2CA045B717117439006DEE8B /* InfoPlist.strings */; }; 2CA045C217117439006DEE8B /* ExampleAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CA045BD17117439006DEE8B /* ExampleAppDelegate.m */; }; 2CA045C317117439006DEE8B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 2CA045BE17117439006DEE8B /* main.m */; }; 2CA0465C1711AC8E006DEE8B /* ExampleApp.html in Resources */ = {isa = PBXBuildFile; fileRef = 2CA0465B1711AC8D006DEE8B /* ExampleApp.html */; }; 2CAB869B1727684300BD9ED1 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 2CAB869A1727684300BD9ED1 /* Default-568h@2x.png */; }; 2CEB3EC01602563600548120 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2CEB3EBF1602563600548120 /* UIKit.framework */; }; + EF5DC17323472E0B00F81F96 /* WKWebView+JavaScriptBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = EF5DC17223472E0B00F81F96 /* WKWebView+JavaScriptBridge.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - 0E50601B1A01B442000BEEEA /* WebViewJavascriptBridgeBase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WebViewJavascriptBridgeBase.m; sourceTree = ""; }; - 0E50601D1A01B44C000BEEEA /* WebViewJavascriptBridgeBase.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WebViewJavascriptBridgeBase.h; sourceTree = ""; }; - 0E8082D919EDC32300479452 /* WKWebViewJavascriptBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKWebViewJavascriptBridge.h; sourceTree = ""; }; - 0E8082DA19EDC32300479452 /* WKWebViewJavascriptBridge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WKWebViewJavascriptBridge.m; sourceTree = ""; }; 0E8082DC19EDD98700479452 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; }; 0ECB01421A0EE1BA0037FF4E /* ExampleWKWebViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ExampleWKWebViewController.h; sourceTree = ""; }; 0ECB01431A0EE1F20037FF4E /* ExampleWKWebViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ExampleWKWebViewController.m; sourceTree = ""; }; - 2C1562A8176B9F6200B4AE50 /* WebViewJavascriptBridge.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WebViewJavascriptBridge.h; sourceTree = ""; }; - 2C1562A9176B9F6200B4AE50 /* WebViewJavascriptBridge.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WebViewJavascriptBridge.m; sourceTree = ""; }; - 2C3E7C441C5A890A00A1E322 /* WebViewJavascriptBridge_JS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebViewJavascriptBridge_JS.h; sourceTree = ""; }; - 2C3E7C451C5A890A00A1E322 /* WebViewJavascriptBridge_JS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WebViewJavascriptBridge_JS.m; sourceTree = ""; }; - 2C45CA2A1884AD520002A4E2 /* ExampleUIWebViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExampleUIWebViewController.h; sourceTree = ""; }; - 2C45CA2B1884AD520002A4E2 /* ExampleUIWebViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ExampleUIWebViewController.m; sourceTree = ""; }; 2CA045B817117439006DEE8B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 2CA045B917117439006DEE8B /* ExampleApp-iOS-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "ExampleApp-iOS-Info.plist"; sourceTree = ""; }; 2CA045BA17117439006DEE8B /* ExampleApp-iOS-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ExampleApp-iOS-Prefix.pch"; sourceTree = ""; }; @@ -48,6 +34,8 @@ 2CEB3EBF1602563600548120 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 2CEB3EC11602563600548120 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 2CEB3EC31602563600548120 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + EF5DC17123472E0B00F81F96 /* WKWebView+JavaScriptBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "WKWebView+JavaScriptBridge.h"; sourceTree = ""; }; + EF5DC17223472E0B00F81F96 /* WKWebView+JavaScriptBridge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "WKWebView+JavaScriptBridge.m"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -66,14 +54,8 @@ 2C1562A7176B9F5400B4AE50 /* WebViewJavascriptBridge */ = { isa = PBXGroup; children = ( - 2C3E7C441C5A890A00A1E322 /* WebViewJavascriptBridge_JS.h */, - 2C3E7C451C5A890A00A1E322 /* WebViewJavascriptBridge_JS.m */, - 2C1562A8176B9F6200B4AE50 /* WebViewJavascriptBridge.h */, - 2C1562A9176B9F6200B4AE50 /* WebViewJavascriptBridge.m */, - 0E8082DA19EDC32300479452 /* WKWebViewJavascriptBridge.m */, - 0E8082D919EDC32300479452 /* WKWebViewJavascriptBridge.h */, - 0E50601B1A01B442000BEEEA /* WebViewJavascriptBridgeBase.m */, - 0E50601D1A01B44C000BEEEA /* WebViewJavascriptBridgeBase.h */, + EF5DC17123472E0B00F81F96 /* WKWebView+JavaScriptBridge.h */, + EF5DC17223472E0B00F81F96 /* WKWebView+JavaScriptBridge.m */, ); name = WebViewJavascriptBridge; path = ../../WebViewJavascriptBridge; @@ -85,8 +67,6 @@ 2CA0465B1711AC8D006DEE8B /* ExampleApp.html */, 2CA045BC17117439006DEE8B /* ExampleAppDelegate.h */, 2CA045BD17117439006DEE8B /* ExampleAppDelegate.m */, - 2C45CA2A1884AD520002A4E2 /* ExampleUIWebViewController.h */, - 2C45CA2B1884AD520002A4E2 /* ExampleUIWebViewController.m */, 0ECB01421A0EE1BA0037FF4E /* ExampleWKWebViewController.h */, 0ECB01431A0EE1F20037FF4E /* ExampleWKWebViewController.m */, 2C1562A7176B9F5400B4AE50 /* WebViewJavascriptBridge */, @@ -182,6 +162,7 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, ); mainGroup = 2CEB3EB01602563600548120; @@ -212,13 +193,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 2C3E7C461C5A890A00A1E322 /* WebViewJavascriptBridge_JS.m in Sources */, - 2C1562C0176BA63500B4AE50 /* WebViewJavascriptBridge.m in Sources */, - 0E8082DB19EDC32300479452 /* WKWebViewJavascriptBridge.m in Sources */, - 2C45CA2C1884AD520002A4E2 /* ExampleUIWebViewController.m in Sources */, 0ECB01441A0EE1F20037FF4E /* ExampleWKWebViewController.m in Sources */, 2CA045C217117439006DEE8B /* ExampleAppDelegate.m in Sources */, - 0E50601C1A01B442000BEEEA /* WebViewJavascriptBridgeBase.m in Sources */, + EF5DC17323472E0B00F81F96 /* WKWebView+JavaScriptBridge.m in Sources */, 2CA045C317117439006DEE8B /* main.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Example Apps/ExampleApp-iOS/ExampleUIWebViewController.h b/Example Apps/ExampleApp-iOS/ExampleUIWebViewController.h deleted file mode 100644 index 71364e80..00000000 --- a/Example Apps/ExampleApp-iOS/ExampleUIWebViewController.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// ExampleUIWebViewController.h -// ExampleApp-iOS -// -// Created by Marcus Westin on 1/13/14. -// Copyright (c) 2014 Marcus Westin. All rights reserved. -// - -#import - -@interface ExampleUIWebViewController : UINavigationController - -@end \ No newline at end of file diff --git a/Example Apps/ExampleApp-iOS/ExampleUIWebViewController.m b/Example Apps/ExampleApp-iOS/ExampleUIWebViewController.m deleted file mode 100644 index f988a014..00000000 --- a/Example Apps/ExampleApp-iOS/ExampleUIWebViewController.m +++ /dev/null @@ -1,90 +0,0 @@ -// -// ExampleUIWebViewController.m -// ExampleApp-iOS -// -// Created by Marcus Westin on 1/13/14. -// Copyright (c) 2014 Marcus Westin. All rights reserved. -// - -#import "ExampleUIWebViewController.h" -#import "WebViewJavascriptBridge.h" - -@interface ExampleUIWebViewController () -@property WebViewJavascriptBridge* bridge; -@end - -@implementation ExampleUIWebViewController - -- (void)viewWillAppear:(BOOL)animated { - if (_bridge) { return; } - - UIWebView* webView = [[UIWebView alloc] initWithFrame:self.view.bounds]; - [self.view addSubview:webView]; - - [WebViewJavascriptBridge enableLogging]; - - _bridge = [WebViewJavascriptBridge bridgeForWebView:webView]; - [_bridge setWebViewDelegate:self]; - - [_bridge registerHandler:@"testObjcCallback" handler:^(id data, WVJBResponseCallback responseCallback) { - NSLog(@"testObjcCallback called: %@", data); - responseCallback(@"Response from testObjcCallback"); - }]; - - [_bridge callHandler:@"testJavascriptHandler" data:@{ @"foo":@"before ready" }]; - - [self renderButtons:webView]; - [self loadExamplePage:webView]; -} - -- (void)webViewDidStartLoad:(UIWebView *)webView { - NSLog(@"webViewDidStartLoad"); -} - -- (void)webViewDidFinishLoad:(UIWebView *)webView { - NSLog(@"webViewDidFinishLoad"); -} - -- (void)renderButtons:(UIWebView*)webView { - UIFont* font = [UIFont fontWithName:@"HelveticaNeue" size:11.0]; - - UIButton *callbackButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; - [callbackButton setTitle:@"Call handler" forState:UIControlStateNormal]; - [callbackButton addTarget:self action:@selector(callHandler:) forControlEvents:UIControlEventTouchUpInside]; - [self.view insertSubview:callbackButton aboveSubview:webView]; - callbackButton.frame = CGRectMake(0, 400, 100, 35); - callbackButton.titleLabel.font = font; - - UIButton* reloadButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; - [reloadButton setTitle:@"Reload webview" forState:UIControlStateNormal]; - [reloadButton addTarget:webView action:@selector(reload) forControlEvents:UIControlEventTouchUpInside]; - [self.view insertSubview:reloadButton aboveSubview:webView]; - reloadButton.frame = CGRectMake(90, 400, 100, 35); - reloadButton.titleLabel.font = font; - - UIButton* safetyTimeoutButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; - [safetyTimeoutButton setTitle:@"Disable safety timeout" forState:UIControlStateNormal]; - [safetyTimeoutButton addTarget:self action:@selector(disableSafetyTimeout) forControlEvents:UIControlEventTouchUpInside]; - [self.view insertSubview:safetyTimeoutButton aboveSubview:webView]; - safetyTimeoutButton.frame = CGRectMake(190, 400, 120, 35); - safetyTimeoutButton.titleLabel.font = font; -} - -- (void)disableSafetyTimeout { - [self.bridge disableJavscriptAlertBoxSafetyTimeout]; -} - -- (void)callHandler:(id)sender { - id data = @{ @"greetingFromObjC": @"Hi there, JS!" }; - [_bridge callHandler:@"testJavascriptHandler" data:data responseCallback:^(id response) { - NSLog(@"testJavascriptHandler responded: %@", response); - }]; -} - -- (void)loadExamplePage:(UIWebView*)webView { - NSString* htmlPath = [[NSBundle mainBundle] pathForResource:@"ExampleApp" ofType:@"html"]; - NSString* appHtml = [NSString stringWithContentsOfFile:htmlPath encoding:NSUTF8StringEncoding error:nil]; - NSURL *baseURL = [NSURL fileURLWithPath:htmlPath]; - [webView loadHTMLString:appHtml baseURL:baseURL]; -} -@end diff --git a/Example Apps/ExampleApp-iOS/ExampleWKWebViewController.m b/Example Apps/ExampleApp-iOS/ExampleWKWebViewController.m index f9d4ac79..c95bfd49 100644 --- a/Example Apps/ExampleApp-iOS/ExampleWKWebViewController.m +++ b/Example Apps/ExampleApp-iOS/ExampleWKWebViewController.m @@ -7,43 +7,23 @@ // #import "ExampleWKWebViewController.h" -#import "WebViewJavascriptBridge.h" +#import "WKWebView+JavaScriptBridge.h" @interface ExampleWKWebViewController () - -@property WebViewJavascriptBridge* bridge; - +@property (nonatomic, strong) WKWebView *webView; @end @implementation ExampleWKWebViewController -- (void)viewWillAppear:(BOOL)animated { - if (_bridge) { return; } - - WKWebView* webView = [[NSClassFromString(@"WKWebView") alloc] initWithFrame:self.view.bounds]; - webView.navigationDelegate = self; - [self.view addSubview:webView]; - [WebViewJavascriptBridge enableLogging]; - _bridge = [WebViewJavascriptBridge bridgeForWebView:webView]; - [_bridge setWebViewDelegate:self]; - - [_bridge registerHandler:@"testObjcCallback" handler:^(id data, WVJBResponseCallback responseCallback) { +- (void)viewDidLoad { + [super viewDidLoad]; + + [self.webView registerHandler:@"testObjcCallback" handler:^(id data, WVJBResponseCallback responseCallback) { NSLog(@"testObjcCallback called: %@", data); responseCallback(@"Response from testObjcCallback"); }]; - - [_bridge callHandler:@"testJavascriptHandler" data:@{ @"foo":@"before ready" }]; - - [self renderButtons:webView]; - [self loadExamplePage:webView]; -} - -- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation { - NSLog(@"webViewDidStartLoad"); -} - -- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation { - NSLog(@"webViewDidFinishLoad"); + [self renderButtons:self.webView]; + [self loadExamplePage:self.webView]; } - (void)renderButtons:(WKWebView*)webView { @@ -66,7 +46,7 @@ - (void)renderButtons:(WKWebView*)webView { - (void)callHandler:(id)sender { id data = @{ @"greetingFromObjC": @"Hi there, JS!" }; - [_bridge callHandler:@"testJavascriptHandler" data:data responseCallback:^(id response) { + [self.webView callHandler:@"testJavascriptHandler" data:data responseCallback:^(id response) { NSLog(@"testJavascriptHandler responded: %@", response); }]; } @@ -77,4 +57,14 @@ - (void)loadExamplePage:(WKWebView*)webView { NSURL *baseURL = [NSURL fileURLWithPath:htmlPath]; [webView loadHTMLString:appHtml baseURL:baseURL]; } +- (WKWebView *) webView { + if (!_webView) { + WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init]; + _webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config]; + [self.view addSubview:_webView]; + //If you set LogginglevelAll ,Xcode command Line will show all JavaScript console.log. + [WKWebView enableLogging:LogginglevelAll]; + } + return _webView; +} @end diff --git a/Example Apps/ExampleApp.html b/Example Apps/ExampleApp.html index 06e7dc8a..322b1773 100644 --- a/Example Apps/ExampleApp.html +++ b/Example Apps/ExampleApp.html @@ -10,19 +10,8 @@

WebViewJavascriptBridge Demo

-
+
diff --git a/WebViewJavascriptBridge/WKWebView+JavaScriptBridge.h b/WebViewJavascriptBridge/WKWebView+JavaScriptBridge.h new file mode 100644 index 00000000..e8d518e0 --- /dev/null +++ b/WebViewJavascriptBridge/WKWebView+JavaScriptBridge.h @@ -0,0 +1,32 @@ +// +// WKWebView+JavaScriptBridge.h +// WKWebView+Console +// +// Created by 侯森魁 on 2019/10/3. +// Copyright © 2019 housenkui. All rights reserved. +// + +#import +NS_ASSUME_NONNULL_BEGIN +typedef enum { + //Only printf JSON In Xcode Command line + LogginglevelJSONOnly = 1 << 0, + //All String printf In Xcode Command line + LogginglevelAll = 1 << 1, +}Logginglevel; +typedef NSDictionary WVJBMessage; +typedef void (^WVJBResponseCallback)(id responseData); +typedef void (^WVJBHandler)(id data, WVJBResponseCallback responseCallback); + +@interface WKWebView (JavaScriptBridge) +@property (strong, nonatomic) NSMutableDictionary* responseCallbacks; +@property (strong, nonatomic) NSMutableDictionary* messageHandlers; +@property (strong, nonatomic) WVJBHandler messageHandler; + +- (void)registerHandler:(NSString *) handlerName handler:(WVJBHandler)handler; +- (void)removeHandler:(NSString *) handlerName; +- (void)callHandler:(NSString *) handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback; ++ (void)enableLogging:(Logginglevel)logginglevel; +@end + +NS_ASSUME_NONNULL_END diff --git a/WebViewJavascriptBridge/WKWebView+JavaScriptBridge.m b/WebViewJavascriptBridge/WKWebView+JavaScriptBridge.m new file mode 100644 index 00000000..81911c24 --- /dev/null +++ b/WebViewJavascriptBridge/WKWebView+JavaScriptBridge.m @@ -0,0 +1,308 @@ +// +// WKWebView+JavaScriptBridge.m +// WKWebView+Console +// +// Created by 侯森魁 on 2019/10/3. +// Copyright © 2019 housenkui. All rights reserved. +// + +#import "WKWebView+JavaScriptBridge.h" +#import +#define kBridgePrefix @"__bridge__" +static long _uniqueId = 0; +static Logginglevel loggingLevel = 0; +void _swizzleMethod(Class class, SEL originalSelector, SEL swizzledSelector) +{ + // the method might not exist in the class, but in its superclass + Method originalMethod = class_getInstanceMethod(class, originalSelector); + Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); + + // class_addMethod will fail if original method already exists + BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); + + // the method doesn’t exist and we just added one + if (didAddMethod) { + class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); + } + else { + method_exchangeImplementations(originalMethod, swizzledMethod); + } +} + +@implementation WKWebView (JavaScriptBridge) + ++(void)load { + _swizzleMethod([self class], @selector(initWithFrame:configuration:), @selector(initWithJavaScriptBridgeFrame:configuration:)); +} +- (instancetype)initWithJavaScriptBridgeFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration { + if (self = [self initWithJavaScriptBridgeFrame:frame configuration:configuration]) { + self.messageHandlers = [NSMutableDictionary dictionary]; + self.responseCallbacks = [NSMutableDictionary dictionary]; + + WKUserContentController *userCC = self.configuration.userContentController; + [userCC addScriptMessageHandler:self name:@"log"]; + [self _injectJavascriptFile]; + } + return self; +} + +- (void)_injectJavascriptFile { + NSString *jsCode = _WebViewJavascriptBridge_js(); + //injected the method when H5 starts to create the DOM tree + [self.configuration.userContentController addUserScript:[[WKUserScript alloc] initWithSource:jsCode injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES]]; +} + +- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message { + NSString * body = (NSString * )message.body; + if ([self _filterMessage:body]) { + NSMutableString *mstr = [NSMutableString stringWithString:body]; + [mstr replaceOccurrencesOfString:kBridgePrefix withString:@"" options:0 range:NSMakeRange(0, 10)]; + [self _flushMessageQueue:mstr]; + } +} + +- (void)registerHandler:(NSString *)handlerName handler:(WVJBHandler)handler { + self.messageHandlers[handlerName] = handler; +} + +- (void)removeHandler:(NSString *)handlerName { + [self.messageHandlers removeObjectForKey:handlerName]; +} + +- (void)callHandler:(NSString *)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback { + [self _sendData:data responseCallback:responseCallback handlerName:handlerName]; +} +- (void)_sendData:(id)data responseCallback:(WVJBResponseCallback)responseCallback handlerName:(NSString*)handlerName { + NSMutableDictionary* message = [NSMutableDictionary dictionary]; + + if (data) { + message[@"data"] = data; + } + + if (responseCallback) { + NSString* callbackId = [NSString stringWithFormat:@"objc_cb_%ld", ++_uniqueId]; + self.responseCallbacks[callbackId] = [responseCallback copy]; + message[@"callbackId"] = callbackId; + } + + if (handlerName) { + message[@"handlerName"] = handlerName; + } + [self _queueMessage:message]; +} + +- (void)_queueMessage:(WVJBMessage*)message { + [self _dispatchMessage:message]; +} +- (void)_flushMessageQueue:(NSString *)messageQueueString{ + if (messageQueueString == nil || messageQueueString.length == 0) { + NSLog(@"WebViewJavascriptBridge: WARNING: ObjC got nil while fetching the message queue JSON from webview. This can happen if the WebViewJavascriptBridge JS is not currently present in the webview, e.g if the webview just loaded a new page."); + return; + } + + id messages = [self _deserializeMessageJSON:messageQueueString]; + for (WVJBMessage* message in messages) { + if (![message isKindOfClass:[WVJBMessage class]]) { + NSLog(@"WebViewJavascriptBridge: WARNING: Invalid %@ received: %@", [message class], message); + continue; + } + [self _log:@"RCVD" json:message]; + + NSString* responseId = message[@"responseId"]; + if (responseId) { + WVJBResponseCallback responseCallback = self.responseCallbacks[responseId]; + responseCallback(message[@"responseData"]); + [self.responseCallbacks removeObjectForKey:responseId]; + } else { + WVJBResponseCallback responseCallback = NULL; + NSString* callbackId = message[@"callbackId"]; + if (callbackId) { + responseCallback = ^(id responseData) { + if (responseData == nil) { + responseData = [NSNull null]; + } + WVJBMessage* msg = @{ @"responseId":callbackId, @"responseData":responseData }; + [self _queueMessage:msg]; + }; + } else { + responseCallback = ^(id ignoreResponseData) { + // Do nothing + }; + } + + WVJBHandler handler = self.messageHandlers[message[@"handlerName"]]; + + if (!handler) { + NSLog(@"WVJBNoHandlerException, No handler for message from JS: %@", message); + continue; + } + handler(message[@"data"], responseCallback); + } + } +} +- (void)_dispatchMessage:(WVJBMessage*)message { + NSString *messageJSON = [self _serializeMessage:message pretty:NO]; + [self _log:@"SEND" json:messageJSON]; + messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\\" withString:@"\\\\"]; + messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""]; + messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\'" withString:@"\\\'"]; + messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"]; + messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\r" withString:@"\\r"]; + messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\f" withString:@"\\f"]; + messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\u2028" withString:@"\\u2028"]; + messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\u2029" withString:@"\\u2029"]; + NSString* javascriptCommand = [NSString stringWithFormat:@"WebViewJavascriptBridge._handleMessageFromObjC('%@');", messageJSON]; + if ([[NSThread currentThread] isMainThread]) { + [self _evaluateJavascript:javascriptCommand]; + + } else { + dispatch_sync(dispatch_get_main_queue(), ^{ + [self _evaluateJavascript:javascriptCommand]; + }); + } +} ++ (void)enableLogging:(Logginglevel)logginglevel { + loggingLevel = logginglevel; +} + +- (void)_log:(NSString *)action json:(id)json { + if (!loggingLevel) { return; } + if (![json isKindOfClass:[NSString class]]) { + json = [self _serializeMessage:json pretty:YES]; + } + NSLog(@"WVJB %@: %@", action, json); +} +- (NSString *)_serializeMessage:(id)message pretty:(BOOL)pretty{ + return [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:message options:(NSJSONWritingOptions)(pretty ? NSJSONWritingPrettyPrinted : 0) error:nil] encoding:NSUTF8StringEncoding]; +} +- (NSArray*)_deserializeMessageJSON:(NSString *)messageJSON { + return [NSJSONSerialization JSONObjectWithData:[messageJSON dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:nil]; +} + +- (NSString *)_filterMessage:(NSString *) message { + if (loggingLevel & LogginglevelAll) { + NSLog(@"All WVJB RCVD:%@",message); + } + if (message&& [message isKindOfClass:[NSString class]] && [message containsString:kBridgePrefix]) + { + return message; + } + return nil; +} +- (NSString * )_evaluateJavascript:(NSString*)javascriptCommand { + [self evaluateJavaScript:javascriptCommand completionHandler:nil]; + return nil; +} + +NSString * _WebViewJavascriptBridge_js() { +#define __WVJB_js_func__(x) #x + + // BEGIN preprocessorJSCode + static NSString * preprocessorJSCode = @__WVJB_js_func__( + ;(function() { + + console.log = (function (oriLogFunc) { + return function (str) { + window.webkit.messageHandlers.log.postMessage(str); + oriLogFunc.call(console, str); + } + })(console.log); + window.WebViewJavascriptBridge = { + registerHandler: registerHandler, + callHandler: callHandler, + _handleMessageFromObjC: _handleMessageFromObjC + }; + + var sendMessageQueue = []; + var messageHandlers = {}; + var responseCallbacks = {}; + var uniqueId = 1; + + function registerHandler(handlerName, handler) { + messageHandlers[handlerName] = handler; + } + + function callHandler(handlerName, data, responseCallback) { + if (arguments.length === 2 && typeof data == 'function') { + responseCallback = data; + data = null; + } + _doSend({ handlerName:handlerName, data:data }, responseCallback); + } + function _doSend(message, responseCallback) { + if (responseCallback) { + var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime(); + responseCallbacks[callbackId] = responseCallback; + message['callbackId'] = callbackId; + } + sendMessageQueue.push(message); + console.log('__bridge__'+ JSON.stringify(sendMessageQueue)); + sendMessageQueue = []; + } + + function _dispatchMessageFromObjC(messageJSON) { + _doDispatchMessageFromObjC(); + + function _doDispatchMessageFromObjC() { + var message = JSON.parse(messageJSON); + var messageHandler; + var responseCallback; + + if (message.responseId) { + responseCallback = responseCallbacks[message.responseId]; + if (!responseCallback) { + return; + } + responseCallback(message.responseData); + delete responseCallbacks[message.responseId]; + } else { + if (message.callbackId) { + var callbackResponseId = message.callbackId; + responseCallback = function(responseData) { + _doSend({ handlerName:message.handlerName, responseId:callbackResponseId, responseData:responseData }); + }; + } + var handler = messageHandlers[message.handlerName]; + if (!handler) { + console.log("WebViewJavascriptBridge: WARNING: no handler for message from ObjC:", message); + } else { + handler(message.data, responseCallback); + } + } + } + } + function _handleMessageFromObjC(messageJSON) { + _dispatchMessageFromObjC(messageJSON); + } + })(); + ); // END preprocessorJSCode + +#undef __WVJB_js_func__ + return preprocessorJSCode; +}; + +- (void)setResponseCallbacks:(NSMutableDictionary *)responseCallbacks { + objc_setAssociatedObject(self, @selector(responseCallbacks), responseCallbacks, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (NSMutableDictionary *)responseCallbacks { + NSMutableDictionary * responseCallbacks = objc_getAssociatedObject(self, _cmd); + return responseCallbacks; +} + +- (void)setMessageHandlers:(NSMutableDictionary *)messageHandlers { + objc_setAssociatedObject(self, @selector(messageHandlers), messageHandlers, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (NSMutableDictionary *)messageHandlers { + return objc_getAssociatedObject(self, _cmd); +} + +- (void)setMessageHandler:(WVJBHandler)messageHandler { + objc_setAssociatedObject(self, @selector(messageHandler), messageHandler, OBJC_ASSOCIATION_COPY_NONATOMIC); +} + +- (WVJBHandler)messageHandler { + return objc_getAssociatedObject(self, _cmd); +} +@end diff --git a/WebViewJavascriptBridge/WKWebViewJavascriptBridge.h b/WebViewJavascriptBridge/WKWebViewJavascriptBridge.h deleted file mode 100644 index 4e3404fc..00000000 --- a/WebViewJavascriptBridge/WKWebViewJavascriptBridge.h +++ /dev/null @@ -1,34 +0,0 @@ -// -// WKWebViewJavascriptBridge.h -// -// Created by @LokiMeyburg on 10/15/14. -// Copyright (c) 2014 @LokiMeyburg. All rights reserved. -// - -#if (__MAC_OS_X_VERSION_MAX_ALLOWED > __MAC_10_9 || __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_7_1) -#define supportsWKWebView -#endif - -#if defined supportsWKWebView - -#import -#import "WebViewJavascriptBridgeBase.h" -#import - -@interface WKWebViewJavascriptBridge : NSObject - -+ (instancetype)bridgeForWebView:(WKWebView*)webView; -+ (void)enableLogging; - -- (void)registerHandler:(NSString*)handlerName handler:(WVJBHandler)handler; -- (void)removeHandler:(NSString*)handlerName; -- (void)callHandler:(NSString*)handlerName; -- (void)callHandler:(NSString*)handlerName data:(id)data; -- (void)callHandler:(NSString*)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback; -- (void)reset; -- (void)setWebViewDelegate:(id)webViewDelegate; -- (void)disableJavscriptAlertBoxSafetyTimeout; - -@end - -#endif diff --git a/WebViewJavascriptBridge/WKWebViewJavascriptBridge.m b/WebViewJavascriptBridge/WKWebViewJavascriptBridge.m deleted file mode 100644 index 73c923db..00000000 --- a/WebViewJavascriptBridge/WKWebViewJavascriptBridge.m +++ /dev/null @@ -1,198 +0,0 @@ -// -// WKWebViewJavascriptBridge.m -// -// Created by @LokiMeyburg on 10/15/14. -// Copyright (c) 2014 @LokiMeyburg. All rights reserved. -// - - -#import "WKWebViewJavascriptBridge.h" - -#if defined supportsWKWebView - -@implementation WKWebViewJavascriptBridge { - __weak WKWebView* _webView; - __weak id _webViewDelegate; - long _uniqueId; - WebViewJavascriptBridgeBase *_base; -} - -/* API - *****/ - -+ (void)enableLogging { [WebViewJavascriptBridgeBase enableLogging]; } - -+ (instancetype)bridgeForWebView:(WKWebView*)webView { - WKWebViewJavascriptBridge* bridge = [[self alloc] init]; - [bridge _setupInstance:webView]; - [bridge reset]; - return bridge; -} - -- (void)send:(id)data { - [self send:data responseCallback:nil]; -} - -- (void)send:(id)data responseCallback:(WVJBResponseCallback)responseCallback { - [_base sendData:data responseCallback:responseCallback handlerName:nil]; -} - -- (void)callHandler:(NSString *)handlerName { - [self callHandler:handlerName data:nil responseCallback:nil]; -} - -- (void)callHandler:(NSString *)handlerName data:(id)data { - [self callHandler:handlerName data:data responseCallback:nil]; -} - -- (void)callHandler:(NSString *)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback { - [_base sendData:data responseCallback:responseCallback handlerName:handlerName]; -} - -- (void)registerHandler:(NSString *)handlerName handler:(WVJBHandler)handler { - _base.messageHandlers[handlerName] = [handler copy]; -} - -- (void)removeHandler:(NSString *)handlerName { - [_base.messageHandlers removeObjectForKey:handlerName]; -} - -- (void)reset { - [_base reset]; -} - -- (void)setWebViewDelegate:(id)webViewDelegate { - _webViewDelegate = webViewDelegate; -} - -- (void)disableJavscriptAlertBoxSafetyTimeout { - [_base disableJavscriptAlertBoxSafetyTimeout]; -} - -/* Internals - ***********/ - -- (void)dealloc { - _base = nil; - _webView = nil; - _webViewDelegate = nil; - _webView.navigationDelegate = nil; -} - - -/* WKWebView Specific Internals - ******************************/ - -- (void) _setupInstance:(WKWebView*)webView { - _webView = webView; - _webView.navigationDelegate = self; - _base = [[WebViewJavascriptBridgeBase alloc] init]; - _base.delegate = self; -} - - -- (void)WKFlushMessageQueue { - [_webView evaluateJavaScript:[_base webViewJavascriptFetchQueyCommand] completionHandler:^(NSString* result, NSError* error) { - if (error != nil) { - NSLog(@"WebViewJavascriptBridge: WARNING: Error when trying to fetch data from WKWebView: %@", error); - } - [_base flushMessageQueue:result]; - }]; -} - -- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation { - if (webView != _webView) { return; } - - __strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate; - if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:didFinishNavigation:)]) { - [strongDelegate webView:webView didFinishNavigation:navigation]; - } -} - - -- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler { - if (webView != _webView) { return; } - - __strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate; - if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:decidePolicyForNavigationResponse:decisionHandler:)]) { - [strongDelegate webView:webView decidePolicyForNavigationResponse:navigationResponse decisionHandler:decisionHandler]; - } - else { - decisionHandler(WKNavigationResponsePolicyAllow); - } -} - -- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler { - if (webView != _webView) { return; } - - __strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate; - if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:didReceiveAuthenticationChallenge:completionHandler:)]) { - [strongDelegate webView:webView didReceiveAuthenticationChallenge:challenge completionHandler:completionHandler]; - } else { - completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil); - } -} - -- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { - if (webView != _webView) { return; } - NSURL *url = navigationAction.request.URL; - __strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate; - - if ([_base isWebViewJavascriptBridgeURL:url]) { - if ([_base isBridgeLoadedURL:url]) { - [_base injectJavascriptFile]; - } else if ([_base isQueueMessageURL:url]) { - [self WKFlushMessageQueue]; - } else { - [_base logUnkownMessage:url]; - } - decisionHandler(WKNavigationActionPolicyCancel); - return; - } - - if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:decidePolicyForNavigationAction:decisionHandler:)]) { - [_webViewDelegate webView:webView decidePolicyForNavigationAction:navigationAction decisionHandler:decisionHandler]; - } else { - decisionHandler(WKNavigationActionPolicyAllow); - } -} - -- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation { - if (webView != _webView) { return; } - - __strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate; - if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:didStartProvisionalNavigation:)]) { - [strongDelegate webView:webView didStartProvisionalNavigation:navigation]; - } -} - - -- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error { - if (webView != _webView) { return; } - - __strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate; - if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:didFailNavigation:withError:)]) { - [strongDelegate webView:webView didFailNavigation:navigation withError:error]; - } -} - -- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error { - if (webView != _webView) { return; } - - __strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate; - if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:didFailProvisionalNavigation:withError:)]) { - [strongDelegate webView:webView didFailProvisionalNavigation:navigation withError:error]; - } -} - -- (NSString*) _evaluateJavascript:(NSString*)javascriptCommand { - [_webView evaluateJavaScript:javascriptCommand completionHandler:nil]; - return NULL; -} - - - -@end - - -#endif diff --git a/WebViewJavascriptBridge/WebViewJavascriptBridge.h b/WebViewJavascriptBridge/WebViewJavascriptBridge.h deleted file mode 100755 index 1b64bb4e..00000000 --- a/WebViewJavascriptBridge/WebViewJavascriptBridge.h +++ /dev/null @@ -1,50 +0,0 @@ -// -// WebViewJavascriptBridge.h -// ExampleApp-iOS -// -// Created by Marcus Westin on 6/14/13. -// Copyright (c) 2013 Marcus Westin. All rights reserved. -// - -#import -#import "WebViewJavascriptBridgeBase.h" - -#if (__MAC_OS_X_VERSION_MAX_ALLOWED > __MAC_10_9 || __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_7_1) -#define supportsWKWebView -#endif - -#if defined supportsWKWebView -#import -#endif - -#if defined __MAC_OS_X_VERSION_MAX_ALLOWED - #define WVJB_PLATFORM_OSX - #define WVJB_WEBVIEW_TYPE WebView - #define WVJB_WEBVIEW_DELEGATE_TYPE NSObject - #define WVJB_WEBVIEW_DELEGATE_INTERFACE NSObject -#elif defined __IPHONE_OS_VERSION_MAX_ALLOWED - #import - #define WVJB_PLATFORM_IOS - #define WVJB_WEBVIEW_TYPE UIWebView - #define WVJB_WEBVIEW_DELEGATE_TYPE NSObject - #define WVJB_WEBVIEW_DELEGATE_INTERFACE NSObject -#endif - -@interface WebViewJavascriptBridge : WVJB_WEBVIEW_DELEGATE_INTERFACE - - -+ (instancetype)bridgeForWebView:(id)webView; -+ (instancetype)bridge:(id)webView; - -+ (void)enableLogging; -+ (void)setLogMaxLength:(int)length; - -- (void)registerHandler:(NSString*)handlerName handler:(WVJBHandler)handler; -- (void)removeHandler:(NSString*)handlerName; -- (void)callHandler:(NSString*)handlerName; -- (void)callHandler:(NSString*)handlerName data:(id)data; -- (void)callHandler:(NSString*)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback; -- (void)setWebViewDelegate:(id)webViewDelegate; -- (void)disableJavscriptAlertBoxSafetyTimeout; - -@end diff --git a/WebViewJavascriptBridge/WebViewJavascriptBridge.m b/WebViewJavascriptBridge/WebViewJavascriptBridge.m deleted file mode 100755 index e74a6e24..00000000 --- a/WebViewJavascriptBridge/WebViewJavascriptBridge.m +++ /dev/null @@ -1,211 +0,0 @@ -// -// WebViewJavascriptBridge.m -// ExampleApp-iOS -// -// Created by Marcus Westin on 6/14/13. -// Copyright (c) 2013 Marcus Westin. All rights reserved. -// - -#import "WebViewJavascriptBridge.h" - -#if defined(supportsWKWebView) -#import "WKWebViewJavascriptBridge.h" -#endif - -#if __has_feature(objc_arc_weak) - #define WVJB_WEAK __weak -#else - #define WVJB_WEAK __unsafe_unretained -#endif - -@implementation WebViewJavascriptBridge { - WVJB_WEAK WVJB_WEBVIEW_TYPE* _webView; - WVJB_WEAK id _webViewDelegate; - long _uniqueId; - WebViewJavascriptBridgeBase *_base; -} - -/* API - *****/ - -+ (void)enableLogging { - [WebViewJavascriptBridgeBase enableLogging]; -} -+ (void)setLogMaxLength:(int)length { - [WebViewJavascriptBridgeBase setLogMaxLength:length]; -} - -+ (instancetype)bridgeForWebView:(id)webView { - return [self bridge:webView]; -} -+ (instancetype)bridge:(id)webView { -#if defined supportsWKWebView - if ([webView isKindOfClass:[WKWebView class]]) { - return (WebViewJavascriptBridge*) [WKWebViewJavascriptBridge bridgeForWebView:webView]; - } -#endif - if ([webView isKindOfClass:[WVJB_WEBVIEW_TYPE class]]) { - WebViewJavascriptBridge* bridge = [[self alloc] init]; - [bridge _platformSpecificSetup:webView]; - return bridge; - } - [NSException raise:@"BadWebViewType" format:@"Unknown web view type."]; - return nil; -} - -- (void)setWebViewDelegate:(WVJB_WEBVIEW_DELEGATE_TYPE*)webViewDelegate { - _webViewDelegate = webViewDelegate; -} - -- (void)send:(id)data { - [self send:data responseCallback:nil]; -} - -- (void)send:(id)data responseCallback:(WVJBResponseCallback)responseCallback { - [_base sendData:data responseCallback:responseCallback handlerName:nil]; -} - -- (void)callHandler:(NSString *)handlerName { - [self callHandler:handlerName data:nil responseCallback:nil]; -} - -- (void)callHandler:(NSString *)handlerName data:(id)data { - [self callHandler:handlerName data:data responseCallback:nil]; -} - -- (void)callHandler:(NSString *)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback { - [_base sendData:data responseCallback:responseCallback handlerName:handlerName]; -} - -- (void)registerHandler:(NSString *)handlerName handler:(WVJBHandler)handler { - _base.messageHandlers[handlerName] = [handler copy]; -} - -- (void)removeHandler:(NSString *)handlerName { - [_base.messageHandlers removeObjectForKey:handlerName]; -} - -- (void)disableJavscriptAlertBoxSafetyTimeout { - [_base disableJavscriptAlertBoxSafetyTimeout]; -} - - -/* Platform agnostic internals - *****************************/ - -- (void)dealloc { - [self _platformSpecificDealloc]; - _base = nil; - _webView = nil; - _webViewDelegate = nil; -} - -- (NSString*) _evaluateJavascript:(NSString*)javascriptCommand { - return [_webView stringByEvaluatingJavaScriptFromString:javascriptCommand]; -} - -#if defined WVJB_PLATFORM_OSX -/* Platform specific internals: OSX - **********************************/ - -- (void) _platformSpecificSetup:(WVJB_WEBVIEW_TYPE*)webView { - _webView = webView; - _webView.policyDelegate = self; - _base = [[WebViewJavascriptBridgeBase alloc] init]; - _base.delegate = self; -} - -- (void) _platformSpecificDealloc { - _webView.policyDelegate = nil; -} - -- (void)webView:(WebView *)webView decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id)listener { - if (webView != _webView) { return; } - - NSURL *url = [request URL]; - if ([_base isWebViewJavascriptBridgeURL:url]) { - if ([_base isBridgeLoadedURL:url]) { - [_base injectJavascriptFile]; - } else if ([_base isQueueMessageURL:url]) { - NSString *messageQueueString = [self _evaluateJavascript:[_base webViewJavascriptFetchQueyCommand]]; - [_base flushMessageQueue:messageQueueString]; - } else { - [_base logUnkownMessage:url]; - } - [listener ignore]; - } else if (_webViewDelegate && [_webViewDelegate respondsToSelector:@selector(webView:decidePolicyForNavigationAction:request:frame:decisionListener:)]) { - [_webViewDelegate webView:webView decidePolicyForNavigationAction:actionInformation request:request frame:frame decisionListener:listener]; - } else { - [listener use]; - } -} - - - -#elif defined WVJB_PLATFORM_IOS -/* Platform specific internals: iOS - **********************************/ - -- (void) _platformSpecificSetup:(WVJB_WEBVIEW_TYPE*)webView { - _webView = webView; - _webView.delegate = self; - _base = [[WebViewJavascriptBridgeBase alloc] init]; - _base.delegate = self; -} - -- (void) _platformSpecificDealloc { - _webView.delegate = nil; -} - -- (void)webViewDidFinishLoad:(UIWebView *)webView { - if (webView != _webView) { return; } - - __strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate; - if (strongDelegate && [strongDelegate respondsToSelector:@selector(webViewDidFinishLoad:)]) { - [strongDelegate webViewDidFinishLoad:webView]; - } -} - -- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error { - if (webView != _webView) { return; } - - __strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate; - if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:didFailLoadWithError:)]) { - [strongDelegate webView:webView didFailLoadWithError:error]; - } -} - -- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { - if (webView != _webView) { return YES; } - - NSURL *url = [request URL]; - __strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate; - if ([_base isWebViewJavascriptBridgeURL:url]) { - if ([_base isBridgeLoadedURL:url]) { - [_base injectJavascriptFile]; - } else if ([_base isQueueMessageURL:url]) { - NSString *messageQueueString = [self _evaluateJavascript:[_base webViewJavascriptFetchQueyCommand]]; - [_base flushMessageQueue:messageQueueString]; - } else { - [_base logUnkownMessage:url]; - } - return NO; - } else if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:shouldStartLoadWithRequest:navigationType:)]) { - return [strongDelegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType]; - } else { - return YES; - } -} - -- (void)webViewDidStartLoad:(UIWebView *)webView { - if (webView != _webView) { return; } - - __strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate; - if (strongDelegate && [strongDelegate respondsToSelector:@selector(webViewDidStartLoad:)]) { - [strongDelegate webViewDidStartLoad:webView]; - } -} - -#endif - -@end diff --git a/WebViewJavascriptBridge/WebViewJavascriptBridgeBase.h b/WebViewJavascriptBridge/WebViewJavascriptBridgeBase.h deleted file mode 100755 index 54d80acc..00000000 --- a/WebViewJavascriptBridge/WebViewJavascriptBridgeBase.h +++ /dev/null @@ -1,46 +0,0 @@ -// -// WebViewJavascriptBridgeBase.h -// -// Created by @LokiMeyburg on 10/15/14. -// Copyright (c) 2014 @LokiMeyburg. All rights reserved. -// - -#import - -#define kOldProtocolScheme @"wvjbscheme" -#define kNewProtocolScheme @"https" -#define kQueueHasMessage @"__wvjb_queue_message__" -#define kBridgeLoaded @"__bridge_loaded__" - -typedef void (^WVJBResponseCallback)(id responseData); -typedef void (^WVJBHandler)(id data, WVJBResponseCallback responseCallback); -typedef NSDictionary WVJBMessage; - -@protocol WebViewJavascriptBridgeBaseDelegate -- (NSString*) _evaluateJavascript:(NSString*)javascriptCommand; -@end - -@interface WebViewJavascriptBridgeBase : NSObject - - -@property (weak, nonatomic) id delegate; -@property (strong, nonatomic) NSMutableArray* startupMessageQueue; -@property (strong, nonatomic) NSMutableDictionary* responseCallbacks; -@property (strong, nonatomic) NSMutableDictionary* messageHandlers; -@property (strong, nonatomic) WVJBHandler messageHandler; - -+ (void)enableLogging; -+ (void)setLogMaxLength:(int)length; -- (void)reset; -- (void)sendData:(id)data responseCallback:(WVJBResponseCallback)responseCallback handlerName:(NSString*)handlerName; -- (void)flushMessageQueue:(NSString *)messageQueueString; -- (void)injectJavascriptFile; -- (BOOL)isWebViewJavascriptBridgeURL:(NSURL*)url; -- (BOOL)isQueueMessageURL:(NSURL*)urll; -- (BOOL)isBridgeLoadedURL:(NSURL*)urll; -- (void)logUnkownMessage:(NSURL*)url; -- (NSString *)webViewJavascriptCheckCommand; -- (NSString *)webViewJavascriptFetchQueyCommand; -- (void)disableJavscriptAlertBoxSafetyTimeout; - -@end diff --git a/WebViewJavascriptBridge/WebViewJavascriptBridgeBase.m b/WebViewJavascriptBridge/WebViewJavascriptBridgeBase.m deleted file mode 100755 index 3ec26ed4..00000000 --- a/WebViewJavascriptBridge/WebViewJavascriptBridgeBase.m +++ /dev/null @@ -1,221 +0,0 @@ -// -// WebViewJavascriptBridgeBase.m -// -// Created by @LokiMeyburg on 10/15/14. -// Copyright (c) 2014 @LokiMeyburg. All rights reserved. -// - -#import -#import "WebViewJavascriptBridgeBase.h" -#import "WebViewJavascriptBridge_JS.h" - -@implementation WebViewJavascriptBridgeBase { - __weak id _webViewDelegate; - long _uniqueId; -} - -static bool logging = false; -static int logMaxLength = 500; - -+ (void)enableLogging { logging = true; } -+ (void)setLogMaxLength:(int)length { logMaxLength = length;} - -- (id)init { - if (self = [super init]) { - self.messageHandlers = [NSMutableDictionary dictionary]; - self.startupMessageQueue = [NSMutableArray array]; - self.responseCallbacks = [NSMutableDictionary dictionary]; - _uniqueId = 0; - } - return self; -} - -- (void)dealloc { - self.startupMessageQueue = nil; - self.responseCallbacks = nil; - self.messageHandlers = nil; -} - -- (void)reset { - self.startupMessageQueue = [NSMutableArray array]; - self.responseCallbacks = [NSMutableDictionary dictionary]; - _uniqueId = 0; -} - -- (void)sendData:(id)data responseCallback:(WVJBResponseCallback)responseCallback handlerName:(NSString*)handlerName { - NSMutableDictionary* message = [NSMutableDictionary dictionary]; - - if (data) { - message[@"data"] = data; - } - - if (responseCallback) { - NSString* callbackId = [NSString stringWithFormat:@"objc_cb_%ld", ++_uniqueId]; - self.responseCallbacks[callbackId] = [responseCallback copy]; - message[@"callbackId"] = callbackId; - } - - if (handlerName) { - message[@"handlerName"] = handlerName; - } - [self _queueMessage:message]; -} - -- (void)flushMessageQueue:(NSString *)messageQueueString{ - if (messageQueueString == nil || messageQueueString.length == 0) { - NSLog(@"WebViewJavascriptBridge: WARNING: ObjC got nil while fetching the message queue JSON from webview. This can happen if the WebViewJavascriptBridge JS is not currently present in the webview, e.g if the webview just loaded a new page."); - return; - } - - id messages = [self _deserializeMessageJSON:messageQueueString]; - for (WVJBMessage* message in messages) { - if (![message isKindOfClass:[WVJBMessage class]]) { - NSLog(@"WebViewJavascriptBridge: WARNING: Invalid %@ received: %@", [message class], message); - continue; - } - [self _log:@"RCVD" json:message]; - - NSString* responseId = message[@"responseId"]; - if (responseId) { - WVJBResponseCallback responseCallback = _responseCallbacks[responseId]; - responseCallback(message[@"responseData"]); - [self.responseCallbacks removeObjectForKey:responseId]; - } else { - WVJBResponseCallback responseCallback = NULL; - NSString* callbackId = message[@"callbackId"]; - if (callbackId) { - responseCallback = ^(id responseData) { - if (responseData == nil) { - responseData = [NSNull null]; - } - - WVJBMessage* msg = @{ @"responseId":callbackId, @"responseData":responseData }; - [self _queueMessage:msg]; - }; - } else { - responseCallback = ^(id ignoreResponseData) { - // Do nothing - }; - } - - WVJBHandler handler = self.messageHandlers[message[@"handlerName"]]; - - if (!handler) { - NSLog(@"WVJBNoHandlerException, No handler for message from JS: %@", message); - continue; - } - - handler(message[@"data"], responseCallback); - } - } -} - -- (void)injectJavascriptFile { - NSString *js = WebViewJavascriptBridge_js(); - [self _evaluateJavascript:js]; - if (self.startupMessageQueue) { - NSArray* queue = self.startupMessageQueue; - self.startupMessageQueue = nil; - for (id queuedMessage in queue) { - [self _dispatchMessage:queuedMessage]; - } - } -} - -- (BOOL)isWebViewJavascriptBridgeURL:(NSURL*)url { - if (![self isSchemeMatch:url]) { - return NO; - } - return [self isBridgeLoadedURL:url] || [self isQueueMessageURL:url]; -} - -- (BOOL)isSchemeMatch:(NSURL*)url { - NSString* scheme = url.scheme.lowercaseString; - return [scheme isEqualToString:kNewProtocolScheme] || [scheme isEqualToString:kOldProtocolScheme]; -} - -- (BOOL)isQueueMessageURL:(NSURL*)url { - NSString* host = url.host.lowercaseString; - return [self isSchemeMatch:url] && [host isEqualToString:kQueueHasMessage]; -} - -- (BOOL)isBridgeLoadedURL:(NSURL*)url { - NSString* host = url.host.lowercaseString; - return [self isSchemeMatch:url] && [host isEqualToString:kBridgeLoaded]; -} - -- (void)logUnkownMessage:(NSURL*)url { - NSLog(@"WebViewJavascriptBridge: WARNING: Received unknown WebViewJavascriptBridge command %@", [url absoluteString]); -} - -- (NSString *)webViewJavascriptCheckCommand { - return @"typeof WebViewJavascriptBridge == \'object\';"; -} - -- (NSString *)webViewJavascriptFetchQueyCommand { - return @"WebViewJavascriptBridge._fetchQueue();"; -} - -- (void)disableJavscriptAlertBoxSafetyTimeout { - [self sendData:nil responseCallback:nil handlerName:@"_disableJavascriptAlertBoxSafetyTimeout"]; -} - -// Private -// ------------------------------------------- - -- (void) _evaluateJavascript:(NSString *)javascriptCommand { - [self.delegate _evaluateJavascript:javascriptCommand]; -} - -- (void)_queueMessage:(WVJBMessage*)message { - if (self.startupMessageQueue) { - [self.startupMessageQueue addObject:message]; - } else { - [self _dispatchMessage:message]; - } -} - -- (void)_dispatchMessage:(WVJBMessage*)message { - NSString *messageJSON = [self _serializeMessage:message pretty:NO]; - [self _log:@"SEND" json:messageJSON]; - messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\\" withString:@"\\\\"]; - messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""]; - messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\'" withString:@"\\\'"]; - messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"]; - messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\r" withString:@"\\r"]; - messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\f" withString:@"\\f"]; - messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\u2028" withString:@"\\u2028"]; - messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\u2029" withString:@"\\u2029"]; - - NSString* javascriptCommand = [NSString stringWithFormat:@"WebViewJavascriptBridge._handleMessageFromObjC('%@');", messageJSON]; - if ([[NSThread currentThread] isMainThread]) { - [self _evaluateJavascript:javascriptCommand]; - - } else { - dispatch_sync(dispatch_get_main_queue(), ^{ - [self _evaluateJavascript:javascriptCommand]; - }); - } -} - -- (NSString *)_serializeMessage:(id)message pretty:(BOOL)pretty{ - return [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:message options:(NSJSONWritingOptions)(pretty ? NSJSONWritingPrettyPrinted : 0) error:nil] encoding:NSUTF8StringEncoding]; -} - -- (NSArray*)_deserializeMessageJSON:(NSString *)messageJSON { - return [NSJSONSerialization JSONObjectWithData:[messageJSON dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:nil]; -} - -- (void)_log:(NSString *)action json:(id)json { - if (!logging) { return; } - if (![json isKindOfClass:[NSString class]]) { - json = [self _serializeMessage:json pretty:YES]; - } - if ([json length] > logMaxLength) { - NSLog(@"WVJB %@: %@ [...]", action, [json substringToIndex:logMaxLength]); - } else { - NSLog(@"WVJB %@: %@", action, json); - } -} - -@end diff --git a/WebViewJavascriptBridge/WebViewJavascriptBridge_JS.h b/WebViewJavascriptBridge/WebViewJavascriptBridge_JS.h deleted file mode 100644 index 9c857f16..00000000 --- a/WebViewJavascriptBridge/WebViewJavascriptBridge_JS.h +++ /dev/null @@ -1,3 +0,0 @@ -#import - -NSString * WebViewJavascriptBridge_js(void); diff --git a/WebViewJavascriptBridge/WebViewJavascriptBridge_JS.m b/WebViewJavascriptBridge/WebViewJavascriptBridge_JS.m deleted file mode 100644 index 670a552f..00000000 --- a/WebViewJavascriptBridge/WebViewJavascriptBridge_JS.m +++ /dev/null @@ -1,139 +0,0 @@ -// This file contains the source for the Javascript side of the -// WebViewJavascriptBridge. It is plaintext, but converted to an NSString -// via some preprocessor tricks. -// -// Previous implementations of WebViewJavascriptBridge loaded the javascript source -// from a resource. This worked fine for app developers, but library developers who -// included the bridge into their library, awkwardly had to ask consumers of their -// library to include the resource, violating their encapsulation. By including the -// Javascript as a string resource, the encapsulation of the library is maintained. - -#import "WebViewJavascriptBridge_JS.h" - -NSString * WebViewJavascriptBridge_js() { - #define __wvjb_js_func__(x) #x - - // BEGIN preprocessorJSCode - static NSString * preprocessorJSCode = @__wvjb_js_func__( -;(function() { - if (window.WebViewJavascriptBridge) { - return; - } - - if (!window.onerror) { - window.onerror = function(msg, url, line) { - console.log("WebViewJavascriptBridge: ERROR:" + msg + "@" + url + ":" + line); - } - } - window.WebViewJavascriptBridge = { - registerHandler: registerHandler, - callHandler: callHandler, - disableJavscriptAlertBoxSafetyTimeout: disableJavscriptAlertBoxSafetyTimeout, - _fetchQueue: _fetchQueue, - _handleMessageFromObjC: _handleMessageFromObjC - }; - - var messagingIframe; - var sendMessageQueue = []; - var messageHandlers = {}; - - var CUSTOM_PROTOCOL_SCHEME = 'https'; - var QUEUE_HAS_MESSAGE = '__wvjb_queue_message__'; - - var responseCallbacks = {}; - var uniqueId = 1; - var dispatchMessagesWithTimeoutSafety = true; - - function registerHandler(handlerName, handler) { - messageHandlers[handlerName] = handler; - } - - function callHandler(handlerName, data, responseCallback) { - if (arguments.length == 2 && typeof data == 'function') { - responseCallback = data; - data = null; - } - _doSend({ handlerName:handlerName, data:data }, responseCallback); - } - function disableJavscriptAlertBoxSafetyTimeout() { - dispatchMessagesWithTimeoutSafety = false; - } - - function _doSend(message, responseCallback) { - if (responseCallback) { - var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime(); - responseCallbacks[callbackId] = responseCallback; - message['callbackId'] = callbackId; - } - sendMessageQueue.push(message); - messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE; - } - - function _fetchQueue() { - var messageQueueString = JSON.stringify(sendMessageQueue); - sendMessageQueue = []; - return messageQueueString; - } - - function _dispatchMessageFromObjC(messageJSON) { - if (dispatchMessagesWithTimeoutSafety) { - setTimeout(_doDispatchMessageFromObjC); - } else { - _doDispatchMessageFromObjC(); - } - - function _doDispatchMessageFromObjC() { - var message = JSON.parse(messageJSON); - var messageHandler; - var responseCallback; - - if (message.responseId) { - responseCallback = responseCallbacks[message.responseId]; - if (!responseCallback) { - return; - } - responseCallback(message.responseData); - delete responseCallbacks[message.responseId]; - } else { - if (message.callbackId) { - var callbackResponseId = message.callbackId; - responseCallback = function(responseData) { - _doSend({ handlerName:message.handlerName, responseId:callbackResponseId, responseData:responseData }); - }; - } - - var handler = messageHandlers[message.handlerName]; - if (!handler) { - console.log("WebViewJavascriptBridge: WARNING: no handler for message from ObjC:", message); - } else { - handler(message.data, responseCallback); - } - } - } - } - - function _handleMessageFromObjC(messageJSON) { - _dispatchMessageFromObjC(messageJSON); - } - - messagingIframe = document.createElement('iframe'); - messagingIframe.style.display = 'none'; - messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE; - document.documentElement.appendChild(messagingIframe); - - registerHandler("_disableJavascriptAlertBoxSafetyTimeout", disableJavscriptAlertBoxSafetyTimeout); - - setTimeout(_callWVJBCallbacks, 0); - function _callWVJBCallbacks() { - var callbacks = window.WVJBCallbacks; - delete window.WVJBCallbacks; - for (var i=0; i Date: Fri, 4 Oct 2019 17:47:31 +0800 Subject: [PATCH 03/44] 2.Delete some files. --- .../ExampleApp-OSX.xcodeproj/project.pbxproj | 31 ++------ Example Apps/ExampleApp-OSX/AppDelegate.m | 78 ++----------------- 2 files changed, 13 insertions(+), 96 deletions(-) diff --git a/Example Apps/ExampleApp-OSX.xcodeproj/project.pbxproj b/Example Apps/ExampleApp-OSX.xcodeproj/project.pbxproj index d08ca0ab..53d6c361 100644 --- a/Example Apps/ExampleApp-OSX.xcodeproj/project.pbxproj +++ b/Example Apps/ExampleApp-OSX.xcodeproj/project.pbxproj @@ -7,8 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - 0ECB01491A0EEB3A0037FF4E /* WebViewJavascriptBridgeBase.m in Sources */ = {isa = PBXBuildFile; fileRef = 0ECB01461A0EEB3A0037FF4E /* WebViewJavascriptBridgeBase.m */; }; - 0ECB014A1A0EEB3A0037FF4E /* WKWebViewJavascriptBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 0ECB01481A0EEB3A0037FF4E /* WKWebViewJavascriptBridge.m */; }; 2C136A2517641106004C7401 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2C136A2417641106004C7401 /* Cocoa.framework */; }; 2C136A2F17641106004C7401 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 2C136A2D17641106004C7401 /* InfoPlist.strings */; }; 2C136A3117641106004C7401 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C136A3017641106004C7401 /* main.m */; }; @@ -16,16 +14,11 @@ 2C136A3817641106004C7401 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C136A3717641106004C7401 /* AppDelegate.m */; }; 2C136A4217641236004C7401 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2C136A4117641236004C7401 /* WebKit.framework */; }; 2C136A5A17642704004C7401 /* ExampleApp.html in Resources */ = {isa = PBXBuildFile; fileRef = 2C136A5917642704004C7401 /* ExampleApp.html */; }; - 2C1562C6176BA9FF00B4AE50 /* WebViewJavascriptBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C1562C4176BA9FF00B4AE50 /* WebViewJavascriptBridge.m */; }; - 2C3E7C491C5A8B8D00A1E322 /* WebViewJavascriptBridge_JS.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C3E7C481C5A8B8D00A1E322 /* WebViewJavascriptBridge_JS.m */; }; 2CF17F5317D8AACF006E828B /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2CF17F5217D8AACF006E828B /* MainMenu.xib */; }; + EF5DC17623474A0A00F81F96 /* WKWebView+JavaScriptBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = EF5DC17523474A0A00F81F96 /* WKWebView+JavaScriptBridge.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - 0ECB01451A0EEB3A0037FF4E /* WebViewJavascriptBridgeBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebViewJavascriptBridgeBase.h; sourceTree = ""; }; - 0ECB01461A0EEB3A0037FF4E /* WebViewJavascriptBridgeBase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WebViewJavascriptBridgeBase.m; sourceTree = ""; }; - 0ECB01471A0EEB3A0037FF4E /* WKWebViewJavascriptBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKWebViewJavascriptBridge.h; sourceTree = ""; }; - 0ECB01481A0EEB3A0037FF4E /* WKWebViewJavascriptBridge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WKWebViewJavascriptBridge.m; sourceTree = ""; }; 2C136A2117641106004C7401 /* ExampleApp-OSX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "ExampleApp-OSX.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 2C136A2417641106004C7401 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 2C136A2717641106004C7401 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; @@ -40,11 +33,9 @@ 2C136A3717641106004C7401 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 2C136A4117641236004C7401 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; }; 2C136A5917642704004C7401 /* ExampleApp.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = ExampleApp.html; sourceTree = SOURCE_ROOT; }; - 2C1562C2176BA9FF00B4AE50 /* WebViewJavascriptBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebViewJavascriptBridge.h; sourceTree = ""; }; - 2C1562C4176BA9FF00B4AE50 /* WebViewJavascriptBridge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WebViewJavascriptBridge.m; sourceTree = ""; }; - 2C3E7C471C5A8B8D00A1E322 /* WebViewJavascriptBridge_JS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebViewJavascriptBridge_JS.h; sourceTree = ""; }; - 2C3E7C481C5A8B8D00A1E322 /* WebViewJavascriptBridge_JS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WebViewJavascriptBridge_JS.m; sourceTree = ""; }; 2CF17F5217D8AACF006E828B /* MainMenu.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MainMenu.xib; sourceTree = ""; }; + EF5DC17423474A0A00F81F96 /* WKWebView+JavaScriptBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "WKWebView+JavaScriptBridge.h"; sourceTree = ""; }; + EF5DC17523474A0A00F81F96 /* WKWebView+JavaScriptBridge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "WKWebView+JavaScriptBridge.m"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -125,14 +116,8 @@ 2C1562C1176BA9FF00B4AE50 /* WebViewJavascriptBridge */ = { isa = PBXGroup; children = ( - 2C3E7C471C5A8B8D00A1E322 /* WebViewJavascriptBridge_JS.h */, - 2C3E7C481C5A8B8D00A1E322 /* WebViewJavascriptBridge_JS.m */, - 0ECB01451A0EEB3A0037FF4E /* WebViewJavascriptBridgeBase.h */, - 0ECB01461A0EEB3A0037FF4E /* WebViewJavascriptBridgeBase.m */, - 0ECB01471A0EEB3A0037FF4E /* WKWebViewJavascriptBridge.h */, - 0ECB01481A0EEB3A0037FF4E /* WKWebViewJavascriptBridge.m */, - 2C1562C2176BA9FF00B4AE50 /* WebViewJavascriptBridge.h */, - 2C1562C4176BA9FF00B4AE50 /* WebViewJavascriptBridge.m */, + EF5DC17423474A0A00F81F96 /* WKWebView+JavaScriptBridge.h */, + EF5DC17523474A0A00F81F96 /* WKWebView+JavaScriptBridge.m */, ); name = WebViewJavascriptBridge; path = ../../WebViewJavascriptBridge; @@ -172,6 +157,7 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, ); mainGroup = 2C136A1817641106004C7401; @@ -203,11 +189,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 0ECB01491A0EEB3A0037FF4E /* WebViewJavascriptBridgeBase.m in Sources */, - 0ECB014A1A0EEB3A0037FF4E /* WKWebViewJavascriptBridge.m in Sources */, - 2C3E7C491C5A8B8D00A1E322 /* WebViewJavascriptBridge_JS.m in Sources */, + EF5DC17623474A0A00F81F96 /* WKWebView+JavaScriptBridge.m in Sources */, 2C136A3117641106004C7401 /* main.m in Sources */, - 2C1562C6176BA9FF00B4AE50 /* WebViewJavascriptBridge.m in Sources */, 2C136A3817641106004C7401 /* AppDelegate.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Example Apps/ExampleApp-OSX/AppDelegate.m b/Example Apps/ExampleApp-OSX/AppDelegate.m index 5677342a..46df6bae 100644 --- a/Example Apps/ExampleApp-OSX/AppDelegate.m +++ b/Example Apps/ExampleApp-OSX/AppDelegate.m @@ -8,69 +8,27 @@ #import "AppDelegate.h" #import -#import "WebViewJavascriptBridge.h" +#import "WKWebView+JavaScriptBridge.h" @implementation AppDelegate { - WebView* _webView; WKWebView *_WKWebView; - WebViewJavascriptBridge* _bridge; - WebViewJavascriptBridge* _WKBridge; NSView* _WKWebViewWrapper; } - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { [self _createViews]; - [self _configureWebview]; [self _configureWKWebview]; } -- (void)_configureWebview { - // Create Bridge - _bridge = [WebViewJavascriptBridge bridgeForWebView:_webView]; - - [_bridge registerHandler:@"testObjcCallback" handler:^(id data, WVJBResponseCallback responseCallback) { - NSLog(@"testObjcCallback called: %@", data); - responseCallback(@"Response from testObjcCallback"); - }]; - - [_bridge callHandler:@"testJavascriptHandler" data:@{ @"foo":@"before ready" }]; - - // Create Buttons - NSButton *callbackButton = [[NSButton alloc] initWithFrame:NSMakeRect(5, 0, 120, 40)]; - [callbackButton setTitle:@"Call handler"]; - [callbackButton setBezelStyle:NSRoundedBezelStyle]; - [callbackButton setTarget:self]; - [callbackButton setAction:@selector(_callHandler)]; - [_webView addSubview:callbackButton]; - - NSButton *webViewToggleButton = [[NSButton alloc] initWithFrame:NSMakeRect(120, 0, 180, 40)]; - [webViewToggleButton setTitle:@"Switch to WKWebView"]; - [webViewToggleButton setBezelStyle:NSRoundedBezelStyle]; - [webViewToggleButton setTarget:self]; - [webViewToggleButton setAction:@selector(_toggleExample)]; - [_webView addSubview:webViewToggleButton]; - - - // Load Page - NSString* htmlPath = [[NSBundle mainBundle] pathForResource:@"ExampleApp" ofType:@"html"]; - NSString* html = [NSString stringWithContentsOfFile:htmlPath encoding:NSUTF8StringEncoding error:nil]; - NSURL *baseURL = [NSURL fileURLWithPath:htmlPath]; - [[_webView mainFrame] loadHTMLString:html baseURL: baseURL]; -} - - - (void)_configureWKWebview { // Create Bridge - _WKBridge = [WebViewJavascriptBridge bridgeForWebView:_WKWebView]; - - [_WKBridge registerHandler:@"testObjcCallback" handler:^(id data, WVJBResponseCallback responseCallback) { + [WKWebView enableLogging:LogginglevelAll]; + [_WKWebView registerHandler:@"testObjcCallback" handler:^(id data, WVJBResponseCallback responseCallback) { NSLog(@"testObjcCallback called: %@", data); responseCallback(@"Response from testObjcCallback"); }]; - [_WKBridge callHandler:@"testJavascriptHandler" data:@{ @"foo":@"before ready" }]; - // Create Buttons NSButton *callbackButton = [[NSButton alloc] initWithFrame:NSMakeRect(5, 0, 120, 40)]; [callbackButton setTitle:@"Call handler"]; @@ -79,12 +37,6 @@ - (void)_configureWKWebview { [callbackButton setAction:@selector(_WKCallHandler)]; [_WKWebView addSubview:callbackButton]; - NSButton *webViewToggleButton = [[NSButton alloc] initWithFrame:NSMakeRect(120, 0, 180, 40)]; - [webViewToggleButton setTitle:@"Switch to WebView"]; - [webViewToggleButton setBezelStyle:NSRoundedBezelStyle]; - [webViewToggleButton setTarget:self]; - [webViewToggleButton setAction:@selector(_toggleExample)]; - [_WKWebView addSubview:webViewToggleButton]; // Load Page NSString* htmlPath = [[NSBundle mainBundle] pathForResource:@"ExampleApp" ofType:@"html"]; @@ -92,39 +44,21 @@ - (void)_configureWKWebview { NSURL *baseURL = [NSURL fileURLWithPath:htmlPath]; [_WKWebView loadHTMLString:html baseURL:baseURL]; } - --(void)_toggleExample { - _WKWebView.hidden = !_WKWebView.isHidden; - _webView.hidden = !_webView.isHidden; -} - -- (void)_callHandler { - id data = @{ @"greetingFromObjC": @"Hi there, JS!" }; - [_bridge callHandler:@"testJavascriptHandler" data:data responseCallback:^(id response) { - NSLog(@"testJavascriptHandler responded: %@", response); - }]; -} - - (void)_WKCallHandler { id data = @{ @"greetingFromObjC": @"Hi there, JS!" }; - [_WKBridge callHandler:@"testJavascriptHandler" data:data responseCallback:^(id response) { + [_WKWebView callHandler:@"testJavascriptHandler" data:data responseCallback:^(id response) { NSLog(@"testJavascriptHandler responded: %@", response); }]; } - (void)_createViews { NSView* contentView = _window.contentView; - // WebView - _webView = [[WebView alloc] initWithFrame:contentView.frame]; - [_webView setAutoresizingMask:(NSViewHeightSizable | NSViewWidthSizable)]; - _webView.hidden = YES; - // WKWebView - _WKWebView = [[WKWebView alloc] initWithFrame:contentView.frame]; + WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init]; + _WKWebView = [[WKWebView alloc] initWithFrame:contentView.frame configuration:config]; [_WKWebView setAutoresizingMask:(NSViewHeightSizable | NSViewWidthSizable)]; [contentView addSubview:_WKWebView]; - [contentView addSubview:_webView]; } From 1b5fbc550ee062b237aa309afa464de0a0c9c38f Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Fri, 4 Oct 2019 17:55:20 +0800 Subject: [PATCH 04/44] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fb14d2de..e25f23e4 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ WebViewJavascriptBridge [![Circle CI](https://img.shields.io/circleci/project/github/marcuswestin/WebViewJavascriptBridge.svg)](https://circleci.com/gh/marcuswestin/WebViewJavascriptBridge) -An iOS/OSX bridge for sending messages between Obj-C and JavaScript in WKWebViews, UIWebViews & WebViews. +An iOS/OSX bridge for sending messages between Obj-C and JavaScript in WKWebViews. Migration Guide --------------- From db65afe90780031e01878d8f2115fe7ff4b4afe8 Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Fri, 4 Oct 2019 17:57:18 +0800 Subject: [PATCH 05/44] Update README.md --- README.md | 230 ------------------------------------------------------ 1 file changed, 230 deletions(-) diff --git a/README.md b/README.md index e25f23e4..fb055525 100644 --- a/README.md +++ b/README.md @@ -5,234 +5,4 @@ WebViewJavascriptBridge An iOS/OSX bridge for sending messages between Obj-C and JavaScript in WKWebViews. -Migration Guide ---------------- -When upgrading from v5.0.x to 6.0.x you will have to update the `setupWebViewJavascriptBridge` javascript snippet. See https://github.com/marcuswestin/WebViewJavascriptBridge#usage part 4). - -Who uses WebViewJavascriptBridge? ---------------------------------- -WebViewJavascriptBridge is used by a range of companies and projects. This is a small and incomplete sample list: - -- [Facebook Messenger](https://www.facebook.com/mobile/messenger) -- [Facebook Paper](https://facebook.com/paper) -- [Yardsale](http://www.getyardsale.com/) -- [EverTrue](http://www.evertrue.com/) -- [Game Insight](http://www.game-insight.com/) -- [Sush.io](http://www.sush.io) -- [Imbed](http://imbed.github.io/) -- [CareZone](https://carezone.com) -- [Hemlig](http://www.hemlig.co) -- [Altralogica](http://www.altralogica.it) -- [鼎盛中华](https://itunes.apple.com/us/app/ding-sheng-zhong-hua/id537273940?mt=8) -- [FRIL](https://fril.jp) -- [留白·WHITE](http://liubaiapp.com) -- [BrowZine](http://thirdiron.com/browzine/) -- ... & many more! - -Installation (iOS & OSX) ------------------------- - -### Installation with CocoaPods -Add this to your [podfile](https://guides.cocoapods.org/using/getting-started.html) and run `pod install` to install: - -```ruby -pod 'WebViewJavascriptBridge', '~> 6.0' -``` - -### Manual installation - -Drag the `WebViewJavascriptBridge` folder into your project. - -In the dialog that appears, uncheck "Copy items into destination group's folder" and select "Create groups for any folders". - -Examples --------- - -See the `Example Apps/` folder. Open either the iOS or OSX project and hit run to see it in action. - -To use a WebViewJavascriptBridge in your own project: - -Usage ------ - -1) Import the header file and declare an ivar property: - -```objc -#import "WebViewJavascriptBridge.h" -``` - -... - -```objc -@property WebViewJavascriptBridge* bridge; -``` - -2) Instantiate WebViewJavascriptBridge with a WKWebView, UIWebView (iOS) or WebView (OSX): - -```objc -self.bridge = [WebViewJavascriptBridge bridgeForWebView:webView]; -``` - -3) Register a handler in ObjC, and call a JS handler: - -```objc -[self.bridge registerHandler:@"ObjC Echo" handler:^(id data, WVJBResponseCallback responseCallback) { - NSLog(@"ObjC Echo called with: %@", data); - responseCallback(data); -}]; -[self.bridge callHandler:@"JS Echo" data:nil responseCallback:^(id responseData) { - NSLog(@"ObjC received response: %@", responseData); -}]; -``` - -4) Copy and paste `setupWebViewJavascriptBridge` into your JS: - -```javascript -function setupWebViewJavascriptBridge(callback) { - if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); } - if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); } - window.WVJBCallbacks = [callback]; - var WVJBIframe = document.createElement('iframe'); - WVJBIframe.style.display = 'none'; - WVJBIframe.src = 'https://__bridge_loaded__'; - document.documentElement.appendChild(WVJBIframe); - setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0) -} -``` - -5) Finally, call `setupWebViewJavascriptBridge` and then use the bridge to register handlers and call ObjC handlers: - -```javascript -setupWebViewJavascriptBridge(function(bridge) { - - /* Initialize your app here */ - - bridge.registerHandler('JS Echo', function(data, responseCallback) { - console.log("JS Echo called with:", data) - responseCallback(data) - }) - bridge.callHandler('ObjC Echo', {'key':'value'}, function responseCallback(responseData) { - console.log("JS received response:", responseData) - }) -}) -``` - -Automatic reference counting (ARC) ----------------------------------- -This library relies on ARC, so if you use ARC in you project, all works fine. -But if your project have no ARC support, be sure to do next steps: - -1) In your Xcode project open project settings -> 'Build Phases' - -2) Expand 'Compile Sources' header and find all *.m files which are belongs to this library. Make attention on the 'Compiler Flags' in front of each source file in this list - -3) For each file add '-fobjc-arc' flag - -Now all WVJB files will be compiled with ARC support. - -Contributors & Forks --------------------- -Contributors: https://github.com/marcuswestin/WebViewJavascriptBridge/graphs/contributors - -Forks: https://github.com/marcuswestin/WebViewJavascriptBridge/network/members - -API Reference -------------- - -### ObjC API - -##### `[WebViewJavascriptBridge bridgeForWebView:(WKWebVIew/UIWebView/WebView*)webview` - -Create a javascript bridge for the given web view. - -Example: - -```objc -[WebViewJavascriptBridge bridgeForWebView:webView]; -``` - -##### `[bridge registerHandler:(NSString*)handlerName handler:(WVJBHandler)handler]` - -Register a handler called `handlerName`. The javascript can then call this handler with `WebViewJavascriptBridge.callHandler("handlerName")`. - -Example: - -```objc -[self.bridge registerHandler:@"getScreenHeight" handler:^(id data, WVJBResponseCallback responseCallback) { - responseCallback([NSNumber numberWithInt:[UIScreen mainScreen].bounds.size.height]); -}]; -[self.bridge registerHandler:@"log" handler:^(id data, WVJBResponseCallback responseCallback) { - NSLog(@"Log: %@", data); -}]; - -``` - -##### `[bridge callHandler:(NSString*)handlerName data:(id)data]` -##### `[bridge callHandler:(NSString*)handlerName data:(id)data responseCallback:(WVJBResponseCallback)callback]` - -Call the javascript handler called `handlerName`. If a `responseCallback` block is given the javascript handler can respond. - -Example: - -```objc -[self.bridge callHandler:@"showAlert" data:@"Hi from ObjC to JS!"]; -[self.bridge callHandler:@"getCurrentPageUrl" data:nil responseCallback:^(id responseData) { - NSLog(@"Current UIWebView page URL is: %@", responseData); -}]; -``` - -#### `[bridge setWebViewDelegate:(id)webViewDelegate]` - -Optionally, set a `WKNavigationDelegate/UIWebViewDelegate` if you need to respond to the [web view's lifecycle events](https://developer.apple.com/reference/uikit/uiwebviewdelegate). - -##### `[bridge disableJavscriptAlertBoxSafetyTimeout]` - -UNSAFE. Speed up bridge message passing by disabling the setTimeout safety check. It is only safe to disable this safety check if you do not call any of the javascript popup box functions (alert, confirm, and prompt). If you call any of these functions from the bridged javascript code, the app will hang. - -Example: - - [self.bridge disableJavscriptAlertBoxSafetyTimeout]; - - - -### Javascript API - -##### `bridge.registerHandler("handlerName", function(responseData) { ... })` - -Register a handler called `handlerName`. The ObjC can then call this handler with `[bridge callHandler:"handlerName" data:@"Foo"]` and `[bridge callHandler:"handlerName" data:@"Foo" responseCallback:^(id responseData) { ... }]` - -Example: - -```javascript -bridge.registerHandler("showAlert", function(data) { alert(data) }) -bridge.registerHandler("getCurrentPageUrl", function(data, responseCallback) { - responseCallback(document.location.toString()) -}) -``` - - -##### `bridge.callHandler("handlerName", data)` -##### `bridge.callHandler("handlerName", data, function responseCallback(responseData) { ... })` - -Call an ObjC handler called `handlerName`. If a `responseCallback` function is given the ObjC handler can respond. - -Example: - -```javascript -bridge.callHandler("Log", "Foo") -bridge.callHandler("getScreenHeight", null, function(response) { - alert('Screen height:' + response) -}) -``` - - -##### `bridge.disableJavscriptAlertBoxSafetyTimeout()` - -Calling `bridge.disableJavscriptAlertBoxSafetyTimeout()` has the same effect as calling `[bridge disableJavscriptAlertBoxSafetyTimeout];` in ObjC. - -Example: - -```javascript -bridge.disableJavscriptAlertBoxSafetyTimeout() -``` From 538373750f3719f960f0bd34216f17199d2fd2cd Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Fri, 4 Oct 2019 18:03:13 +0800 Subject: [PATCH 06/44] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index fb055525..dbfdf0b4 100644 --- a/README.md +++ b/README.md @@ -5,4 +5,7 @@ WebViewJavascriptBridge An iOS/OSX bridge for sending messages between Obj-C and JavaScript in WKWebViews. +I had redesign the core bridge codes with AOP. +#more simple more light. + From 188d6c5fc3bf1b8d87448bd741ffcae04d725300 Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Fri, 4 Oct 2019 18:04:18 +0800 Subject: [PATCH 07/44] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index dbfdf0b4..309393ee 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ WebViewJavascriptBridge An iOS/OSX bridge for sending messages between Obj-C and JavaScript in WKWebViews. -I had redesign the core bridge codes with AOP. -#more simple more light. +More simple more light.I had redesign the core bridge codes with AOP. +========================== From 74452ac27a4b01956f3731dbe8146ca70e47c724 Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Fri, 4 Oct 2019 19:11:42 +0800 Subject: [PATCH 08/44] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 309393ee..9c52a1a3 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ WebViewJavascriptBridge An iOS/OSX bridge for sending messages between Obj-C and JavaScript in WKWebViews. -More simple more light.I had redesign the core bridge codes with AOP. +More simple more light. ========================== From 50594fb9a7d681baf02d98b3983970bc759866f6 Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Fri, 4 Oct 2019 19:12:30 +0800 Subject: [PATCH 09/44] Update README.md --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 9c52a1a3..a3ad1e3a 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ WebViewJavascriptBridge ======================= -[![Circle CI](https://img.shields.io/circleci/project/github/marcuswestin/WebViewJavascriptBridge.svg)](https://circleci.com/gh/marcuswestin/WebViewJavascriptBridge) - An iOS/OSX bridge for sending messages between Obj-C and JavaScript in WKWebViews. More simple more light. From 38a1d636ab7fd29b3172dc6d92689d89889f27c4 Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Fri, 4 Oct 2019 20:18:39 +0800 Subject: [PATCH 10/44] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a3ad1e3a..ae17eb09 100644 --- a/README.md +++ b/README.md @@ -6,4 +6,6 @@ An iOS/OSX bridge for sending messages between Obj-C and JavaScript in WKWebView More simple more light. ========================== - +How to use ? +========================== +On Native side: From ace5c91ba89152041f091fdc19da62a494ccb150 Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Fri, 4 Oct 2019 20:20:41 +0800 Subject: [PATCH 11/44] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ae17eb09..389efda0 100644 --- a/README.md +++ b/README.md @@ -9,3 +9,4 @@ More simple more light. How to use ? ========================== On Native side: + ![shell文件](http://upload-images.jianshu.io/upload_images/1485140-22eacdbae71ab997.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) From 9efe2565a21d1076552d6aeef350926c88df207d Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Fri, 4 Oct 2019 20:20:53 +0800 Subject: [PATCH 12/44] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 389efda0..c0218f8e 100644 --- a/README.md +++ b/README.md @@ -9,4 +9,4 @@ More simple more light. How to use ? ========================== On Native side: - ![shell文件](http://upload-images.jianshu.io/upload_images/1485140-22eacdbae71ab997.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + From 8670bfa0c6245e358ae923a3668279d81aa8ab87 Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Fri, 4 Oct 2019 20:48:02 +0800 Subject: [PATCH 13/44] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c0218f8e..693107bf 100644 --- a/README.md +++ b/README.md @@ -9,4 +9,4 @@ More simple more light. How to use ? ========================== On Native side: - + ![shell文件](https://upload-images.jianshu.io/upload_images/1485140-039a71e6e602bf15.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/1200/format/webp) From 883f4c29fd3201051f8a1b0fa7fcb15f07169b7a Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Fri, 4 Oct 2019 20:50:36 +0800 Subject: [PATCH 14/44] Update README.md --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 693107bf..9fe7d3c3 100644 --- a/README.md +++ b/README.md @@ -10,3 +10,11 @@ How to use ? ========================== On Native side: ![shell文件](https://upload-images.jianshu.io/upload_images/1485140-039a71e6e602bf15.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/1200/format/webp) + +On JavaScript side: + ![shell文件](https://upload-images.jianshu.io/upload_images/1485140-c759d9499766b8b9.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/1080/format/webp) + + Any question you can join my Chat group: + ![shell文件](https://upload-images.jianshu.io/upload_images/1485140-ccada880a92988df.JPG?imageMogr2/auto-orient/strip|imageView2/2/w/752/format/webp) + + Or contact me with:840737320@qq.com. From 809cbb23433be764804da111c60304766bee471b Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Fri, 4 Oct 2019 20:53:38 +0800 Subject: [PATCH 15/44] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9fe7d3c3..039025bc 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ WebViewJavascriptBridge An iOS/OSX bridge for sending messages between Obj-C and JavaScript in WKWebViews. -More simple more light. +More simple more light. Refactor WebViewJavascriptBridge with AOP ========================== How to use ? From 85192f7ea3e0824a7dda9e4a232921fa94957ba7 Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Sat, 5 Oct 2019 15:19:56 +0800 Subject: [PATCH 16/44] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 039025bc..42d8c405 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ On Native side: On JavaScript side: ![shell文件](https://upload-images.jianshu.io/upload_images/1485140-c759d9499766b8b9.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/1080/format/webp) - Any question you can join my Chat group: + Any question you can join my Group Chat: ![shell文件](https://upload-images.jianshu.io/upload_images/1485140-ccada880a92988df.JPG?imageMogr2/auto-orient/strip|imageView2/2/w/752/format/webp) Or contact me with:840737320@qq.com. From 1e70e2bbd938dd16317436ec55563e33639fefbd Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Sun, 6 Oct 2019 14:42:57 +0800 Subject: [PATCH 17/44] Update README.md --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 42d8c405..c8beab3e 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,16 @@ More simple more light. Refactor WebViewJavascriptBridge with AOP How to use ? ========================== + +### Installation with CocoaPods +Add this to your [podfile](https://guides.cocoapods.org/using/getting-started.html) and run `pod install` to install: + +```ruby +pod 'SKJavaScriptBridge', '~> 1.0.1' +``` + +### Manual installation + On Native side: ![shell文件](https://upload-images.jianshu.io/upload_images/1485140-039a71e6e602bf15.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/1200/format/webp) From 4fd55f76304ac508f94bda72dd194af9c77aae11 Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Sun, 6 Oct 2019 15:23:15 +0800 Subject: [PATCH 18/44] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c8beab3e..cda3b5ed 100644 --- a/README.md +++ b/README.md @@ -27,4 +27,4 @@ On JavaScript side: Any question you can join my Group Chat: ![shell文件](https://upload-images.jianshu.io/upload_images/1485140-ccada880a92988df.JPG?imageMogr2/auto-orient/strip|imageView2/2/w/752/format/webp) - Or contact me with:840737320@qq.com. + Or contact me with:housenkui@gmail.com. From 04bcb038c4ac4728a62bc6a7e39b399e05d63d31 Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Tue, 8 Oct 2019 09:38:11 +0800 Subject: [PATCH 19/44] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index cda3b5ed..3e4595f2 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,8 @@ On Native side: On JavaScript side: ![shell文件](https://upload-images.jianshu.io/upload_images/1485140-c759d9499766b8b9.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/1080/format/webp) + In fact,just one console.log('callNative') is enough to call native method.I had hack the Javascript method 'console.log'. + Any question you can join my Group Chat: ![shell文件](https://upload-images.jianshu.io/upload_images/1485140-ccada880a92988df.JPG?imageMogr2/auto-orient/strip|imageView2/2/w/752/format/webp) From 4a9e94c060f56a1117f4f3b3c15cb59b9adb98f9 Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Tue, 8 Oct 2019 09:40:06 +0800 Subject: [PATCH 20/44] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 3e4595f2..f598251b 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,7 @@ On Native side: On JavaScript side: ![shell文件](https://upload-images.jianshu.io/upload_images/1485140-c759d9499766b8b9.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/1080/format/webp) - In fact,just one console.log('callNative') is enough to call native method.I had hack the Javascript method 'console.log'. - + In fact,just one ```console.log('callNative')``` is enough to call native method.I had hack the Javascript method ```console.log```.. Any question you can join my Group Chat: ![shell文件](https://upload-images.jianshu.io/upload_images/1485140-ccada880a92988df.JPG?imageMogr2/auto-orient/strip|imageView2/2/w/752/format/webp) From 892c157030187fe93c80dc856529b4a6ce963127 Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Tue, 8 Oct 2019 09:40:57 +0800 Subject: [PATCH 21/44] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f598251b..a6b4db51 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ On Native side: On JavaScript side: ![shell文件](https://upload-images.jianshu.io/upload_images/1485140-c759d9499766b8b9.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/1080/format/webp) - In fact,just one ```console.log('callNative')``` is enough to call native method.I had hack the Javascript method ```console.log```.. + In fact,just one ```console.log('callNative')``` is enough to call native method.I had hack the Javascript method ```console.log```. Any question you can join my Group Chat: ![shell文件](https://upload-images.jianshu.io/upload_images/1485140-ccada880a92988df.JPG?imageMogr2/auto-orient/strip|imageView2/2/w/752/format/webp) From d1c61824212aaff2205b573c50a097a48be0aa22 Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Thu, 10 Oct 2019 09:49:20 +0800 Subject: [PATCH 22/44] Update WKWebView+JavaScriptBridge.m --- WebViewJavascriptBridge/WKWebView+JavaScriptBridge.m | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/WebViewJavascriptBridge/WKWebView+JavaScriptBridge.m b/WebViewJavascriptBridge/WKWebView+JavaScriptBridge.m index 81911c24..360f54a8 100644 --- a/WebViewJavascriptBridge/WKWebView+JavaScriptBridge.m +++ b/WebViewJavascriptBridge/WKWebView+JavaScriptBridge.m @@ -280,7 +280,10 @@ function _handleMessageFromObjC(messageJSON) { #undef __WVJB_js_func__ return preprocessorJSCode; }; - +- (void)dealloc +{ + [self.configuration.userContentController removeScriptMessageHandlerForName:@"log"]; +} - (void)setResponseCallbacks:(NSMutableDictionary *)responseCallbacks { objc_setAssociatedObject(self, @selector(responseCallbacks), responseCallbacks, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } From 250657e0f55b871b04c5bb415114877e77e4da4d Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Sun, 13 Oct 2019 18:06:29 +0800 Subject: [PATCH 23/44] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a6b4db51..c0ed72c7 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ Add this to your [podfile](https://guides.cocoapods.org/using/getting-started.ht ```ruby pod 'SKJavaScriptBridge', '~> 1.0.1' ``` +If native want to get console.log in WKWebView just ```[WKWebView enableLogging:LogginglevelAll];``` is enough. ### Manual installation From 9e56e5d538132c7c976e56969320275135d1c51e Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Thu, 26 Dec 2019 23:34:39 +0800 Subject: [PATCH 24/44] Update README.md --- README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index c0ed72c7..c7204a38 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,4 @@ On JavaScript side: ![shell文件](https://upload-images.jianshu.io/upload_images/1485140-c759d9499766b8b9.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/1080/format/webp) In fact,just one ```console.log('callNative')``` is enough to call native method.I had hack the Javascript method ```console.log```. - Any question you can join my Group Chat: - ![shell文件](https://upload-images.jianshu.io/upload_images/1485140-ccada880a92988df.JPG?imageMogr2/auto-orient/strip|imageView2/2/w/752/format/webp) - - Or contact me with:housenkui@gmail.com. + Any question you can contact me with:housenkui@gmail.com or WeChat :housenkui From 27814d45e2d3b85f7dc01c944a57515e59e643e7 Mon Sep 17 00:00:00 2001 From: housenkui Date: Wed, 25 Mar 2020 12:46:19 +0800 Subject: [PATCH 25/44] =?UTF-8?q?=E6=8A=BD=E5=8F=96=E9=9D=9E=E6=A0=B8?= =?UTF-8?q?=E5=BF=83=E9=80=BB=E8=BE=91=E5=88=B0=E4=B8=8D=E5=90=8C=E7=9A=84?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExampleApp-iOS.xcodeproj/project.pbxproj | 12 ++ .../ExampleApp-iOS/ExampleAppDelegate.m | 1 + .../ExampleWKWebViewController.h | 2 +- .../ExampleWKWebViewController.m | 10 +- Example Apps/ExampleApp.html | 4 +- WebViewJavascriptBridge/Logginglevel.h | 15 ++ WebViewJavascriptBridge/Tool.h | 27 ++++ WebViewJavascriptBridge/Utils.h | 20 +++ WebViewJavascriptBridge/Utils.m | 39 +++++ .../WKWebView+JavaScriptBridge.h | 8 +- .../WKWebView+JavaScriptBridge.m | 142 ++---------------- .../WKWebViewJavascriptBridge_JS.h | 95 ++++++++++++ 12 files changed, 234 insertions(+), 141 deletions(-) create mode 100644 WebViewJavascriptBridge/Logginglevel.h create mode 100644 WebViewJavascriptBridge/Tool.h create mode 100644 WebViewJavascriptBridge/Utils.h create mode 100644 WebViewJavascriptBridge/Utils.m create mode 100644 WebViewJavascriptBridge/WKWebViewJavascriptBridge_JS.h diff --git a/Example Apps/ExampleApp-iOS.xcodeproj/project.pbxproj b/Example Apps/ExampleApp-iOS.xcodeproj/project.pbxproj index 0e6ddccf..4448a290 100644 --- a/Example Apps/ExampleApp-iOS.xcodeproj/project.pbxproj +++ b/Example Apps/ExampleApp-iOS.xcodeproj/project.pbxproj @@ -16,6 +16,7 @@ 2CAB869B1727684300BD9ED1 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 2CAB869A1727684300BD9ED1 /* Default-568h@2x.png */; }; 2CEB3EC01602563600548120 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2CEB3EBF1602563600548120 /* UIKit.framework */; }; EF5DC17323472E0B00F81F96 /* WKWebView+JavaScriptBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = EF5DC17223472E0B00F81F96 /* WKWebView+JavaScriptBridge.m */; }; + EFE398AD242B15EF0027857D /* Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = EFE398AC242B15EF0027857D /* Utils.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -36,6 +37,11 @@ 2CEB3EC31602563600548120 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; EF5DC17123472E0B00F81F96 /* WKWebView+JavaScriptBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "WKWebView+JavaScriptBridge.h"; sourceTree = ""; }; EF5DC17223472E0B00F81F96 /* WKWebView+JavaScriptBridge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "WKWebView+JavaScriptBridge.m"; sourceTree = ""; }; + EFE398A8242B12C90027857D /* Logginglevel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Logginglevel.h; sourceTree = ""; }; + EFE398A9242B14340027857D /* WKWebViewJavascriptBridge_JS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WKWebViewJavascriptBridge_JS.h; sourceTree = ""; }; + EFE398AA242B151E0027857D /* Tool.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Tool.h; sourceTree = ""; }; + EFE398AB242B15EF0027857D /* Utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Utils.h; sourceTree = ""; }; + EFE398AC242B15EF0027857D /* Utils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Utils.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -54,8 +60,13 @@ 2C1562A7176B9F5400B4AE50 /* WebViewJavascriptBridge */ = { isa = PBXGroup; children = ( + EFE398A8242B12C90027857D /* Logginglevel.h */, EF5DC17123472E0B00F81F96 /* WKWebView+JavaScriptBridge.h */, EF5DC17223472E0B00F81F96 /* WKWebView+JavaScriptBridge.m */, + EFE398A9242B14340027857D /* WKWebViewJavascriptBridge_JS.h */, + EFE398AA242B151E0027857D /* Tool.h */, + EFE398AB242B15EF0027857D /* Utils.h */, + EFE398AC242B15EF0027857D /* Utils.m */, ); name = WebViewJavascriptBridge; path = ../../WebViewJavascriptBridge; @@ -196,6 +207,7 @@ 0ECB01441A0EE1F20037FF4E /* ExampleWKWebViewController.m in Sources */, 2CA045C217117439006DEE8B /* ExampleAppDelegate.m in Sources */, EF5DC17323472E0B00F81F96 /* WKWebView+JavaScriptBridge.m in Sources */, + EFE398AD242B15EF0027857D /* Utils.m in Sources */, 2CA045C317117439006DEE8B /* main.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Example Apps/ExampleApp-iOS/ExampleAppDelegate.m b/Example Apps/ExampleApp-iOS/ExampleAppDelegate.m index 7b893ff4..9b2ac6e8 100644 --- a/Example Apps/ExampleApp-iOS/ExampleAppDelegate.m +++ b/Example Apps/ExampleApp-iOS/ExampleAppDelegate.m @@ -14,6 +14,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; self.window.rootViewController = tabBarController; [self.window makeKeyAndVisible]; + return YES; } diff --git a/Example Apps/ExampleApp-iOS/ExampleWKWebViewController.h b/Example Apps/ExampleApp-iOS/ExampleWKWebViewController.h index 7dd92b8e..cf0f1c42 100644 --- a/Example Apps/ExampleApp-iOS/ExampleWKWebViewController.h +++ b/Example Apps/ExampleApp-iOS/ExampleWKWebViewController.h @@ -11,4 +11,4 @@ @interface ExampleWKWebViewController : UINavigationController -@end \ No newline at end of file +@end diff --git a/Example Apps/ExampleApp-iOS/ExampleWKWebViewController.m b/Example Apps/ExampleApp-iOS/ExampleWKWebViewController.m index c95bfd49..bda789b7 100644 --- a/Example Apps/ExampleApp-iOS/ExampleWKWebViewController.m +++ b/Example Apps/ExampleApp-iOS/ExampleWKWebViewController.m @@ -24,6 +24,7 @@ - (void)viewDidLoad { }]; [self renderButtons:self.webView]; [self loadExamplePage:self.webView]; + } - (void)renderButtons:(WKWebView*)webView { @@ -42,6 +43,13 @@ - (void)renderButtons:(WKWebView*)webView { [self.view insertSubview:reloadButton aboveSubview:webView]; reloadButton.frame = CGRectMake(110, 400, 100, 35); reloadButton.titleLabel.font = font; + + UIButton *callBackButton = [UIButton buttonWithType:UIButtonTypeCustom]; + [callBackButton setTitle:@"测试按钮" forState:UIControlStateNormal]; + [callBackButton setTitleColor:[UIColor redColor] forState:UIControlStateNormal]; + [callBackButton addTarget:self action:@selector(callHandler:) forControlEvents:UIControlEventTouchUpInside]; + callBackButton.frame = CGRectMake(210, 400, 100, 35); + [self.view insertSubview:callBackButton aboveSubview:webView]; } - (void)callHandler:(id)sender { @@ -63,7 +71,7 @@ - (WKWebView *) webView { _webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config]; [self.view addSubview:_webView]; //If you set LogginglevelAll ,Xcode command Line will show all JavaScript console.log. - [WKWebView enableLogging:LogginglevelAll]; + [WKWebView enableLogging:LogginglevelJSONOnly]; } return _webView; } diff --git a/Example Apps/ExampleApp.html b/Example Apps/ExampleApp.html index 322b1773..9f5fc509 100644 --- a/Example Apps/ExampleApp.html +++ b/Example Apps/ExampleApp.html @@ -11,7 +11,7 @@

WebViewJavascriptBridge Demo

diff --git a/WebViewJavascriptBridge/Logginglevel.h b/WebViewJavascriptBridge/Logginglevel.h new file mode 100644 index 00000000..3ac4ba11 --- /dev/null +++ b/WebViewJavascriptBridge/Logginglevel.h @@ -0,0 +1,15 @@ +// +// Logginglevel.h +// ExampleApp-iOS +// +// Created by 侯森魁 on 2020/3/25. +// Copyright © 2020 Marcus Westin. All rights reserved. +// + +typedef enum { + //Only printf JSON In Xcode Command line + LogginglevelJSONOnly = 1 << 0, + //All String printf In Xcode Command line + LogginglevelAll = 1 << 1, +}Logginglevel; + diff --git a/WebViewJavascriptBridge/Tool.h b/WebViewJavascriptBridge/Tool.h new file mode 100644 index 00000000..e735b7a6 --- /dev/null +++ b/WebViewJavascriptBridge/Tool.h @@ -0,0 +1,27 @@ +// +// Tool.h +// ExampleApp-iOS +// +// Created by 侯森魁 on 2020/3/25. +// Copyright © 2020 Marcus Westin. All rights reserved. +// + +#import +void swizzleMethod(Class class, SEL originalSelector, SEL swizzledSelector) +{ + // the method might not exist in the class, but in its superclass + Method originalMethod = class_getInstanceMethod(class, originalSelector); + Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); + + // class_addMethod will fail if original method already exists + BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); + + // the method doesn’t exist and we just added one + if (didAddMethod) { + class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); + } + else { + method_exchangeImplementations(originalMethod, swizzledMethod); + } +} + diff --git a/WebViewJavascriptBridge/Utils.h b/WebViewJavascriptBridge/Utils.h new file mode 100644 index 00000000..607ed119 --- /dev/null +++ b/WebViewJavascriptBridge/Utils.h @@ -0,0 +1,20 @@ +// +// Utils.h +// ExampleApp-iOS +// +// Created by 侯森魁 on 2020/3/25. +// Copyright © 2020 Marcus Westin. All rights reserved. +// + +#import +#import "Logginglevel.h" +NS_ASSUME_NONNULL_BEGIN + +@interface Utils : NSObject ++ (NSString *)serializeMessage:(id)message pretty:(BOOL)pretty; ++ (NSArray*)deserializeMessageJSON:(NSString *)messageJSON; ++ (void)log:(NSString *)action json:(id)json loggingLevel:(Logginglevel)loggingLevel; ++ (NSString *)replacingJSONString:(NSString *)messageJSON; +@end + +NS_ASSUME_NONNULL_END diff --git a/WebViewJavascriptBridge/Utils.m b/WebViewJavascriptBridge/Utils.m new file mode 100644 index 00000000..2baab468 --- /dev/null +++ b/WebViewJavascriptBridge/Utils.m @@ -0,0 +1,39 @@ +// +// Utils.m +// ExampleApp-iOS +// +// Created by 侯森魁 on 2020/3/25. +// Copyright © 2020 Marcus Westin. All rights reserved. +// + +#import "Utils.h" + +@implementation Utils ++ (NSString *)serializeMessage:(id)message pretty:(BOOL)pretty{ + return [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:message options:(NSJSONWritingOptions)(pretty ? NSJSONWritingPrettyPrinted : 0) error:nil] encoding:NSUTF8StringEncoding]; +} ++ (NSArray*)deserializeMessageJSON:(NSString *)messageJSON { + return [NSJSONSerialization JSONObjectWithData:[messageJSON dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:nil]; +} + ++ (void)log:(NSString *)action json:(id)json loggingLevel:(Logginglevel)loggingLevel{ + if (!loggingLevel) { return; } + if (![json isKindOfClass:[NSString class]]) { + json = [Utils serializeMessage:json pretty:YES]; + } + NSLog(@"WVJB %@: %@", action, json); +} + ++ (NSString *)replacingJSONString:(NSString *)messageJSON { + + messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\\" withString:@"\\\\"]; + messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""]; + messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\'" withString:@"\\\'"]; + messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"]; + messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\r" withString:@"\\r"]; + messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\f" withString:@"\\f"]; + messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\u2028" withString:@"\\u2028"]; + messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\u2029" withString:@"\\u2029"]; + return messageJSON; +} +@end diff --git a/WebViewJavascriptBridge/WKWebView+JavaScriptBridge.h b/WebViewJavascriptBridge/WKWebView+JavaScriptBridge.h index e8d518e0..9998e070 100644 --- a/WebViewJavascriptBridge/WKWebView+JavaScriptBridge.h +++ b/WebViewJavascriptBridge/WKWebView+JavaScriptBridge.h @@ -7,13 +7,9 @@ // #import +#import "Logginglevel.h" NS_ASSUME_NONNULL_BEGIN -typedef enum { - //Only printf JSON In Xcode Command line - LogginglevelJSONOnly = 1 << 0, - //All String printf In Xcode Command line - LogginglevelAll = 1 << 1, -}Logginglevel; + typedef NSDictionary WVJBMessage; typedef void (^WVJBResponseCallback)(id responseData); typedef void (^WVJBHandler)(id data, WVJBResponseCallback responseCallback); diff --git a/WebViewJavascriptBridge/WKWebView+JavaScriptBridge.m b/WebViewJavascriptBridge/WKWebView+JavaScriptBridge.m index 360f54a8..a9feb6a9 100644 --- a/WebViewJavascriptBridge/WKWebView+JavaScriptBridge.m +++ b/WebViewJavascriptBridge/WKWebView+JavaScriptBridge.m @@ -7,32 +7,18 @@ // #import "WKWebView+JavaScriptBridge.h" -#import +#import "WKWebViewJavascriptBridge_JS.h" +#import "Tool.h" +#import "Utils.h" #define kBridgePrefix @"__bridge__" + static long _uniqueId = 0; static Logginglevel loggingLevel = 0; -void _swizzleMethod(Class class, SEL originalSelector, SEL swizzledSelector) -{ - // the method might not exist in the class, but in its superclass - Method originalMethod = class_getInstanceMethod(class, originalSelector); - Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); - - // class_addMethod will fail if original method already exists - BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); - - // the method doesn’t exist and we just added one - if (didAddMethod) { - class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); - } - else { - method_exchangeImplementations(originalMethod, swizzledMethod); - } -} @implementation WKWebView (JavaScriptBridge) +(void)load { - _swizzleMethod([self class], @selector(initWithFrame:configuration:), @selector(initWithJavaScriptBridgeFrame:configuration:)); + swizzleMethod([self class], @selector(initWithFrame:configuration:), @selector(initWithJavaScriptBridgeFrame:configuration:)); } - (instancetype)initWithJavaScriptBridgeFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration { if (self = [self initWithJavaScriptBridgeFrame:frame configuration:configuration]) { @@ -100,13 +86,13 @@ - (void)_flushMessageQueue:(NSString *)messageQueueString{ return; } - id messages = [self _deserializeMessageJSON:messageQueueString]; + id messages = [Utils deserializeMessageJSON:messageQueueString]; for (WVJBMessage* message in messages) { if (![message isKindOfClass:[WVJBMessage class]]) { NSLog(@"WebViewJavascriptBridge: WARNING: Invalid %@ received: %@", [message class], message); continue; } - [self _log:@"RCVD" json:message]; + [Utils log:@"RCVD" json:message loggingLevel:loggingLevel]; NSString* responseId = message[@"responseId"]; if (responseId) { @@ -141,16 +127,10 @@ - (void)_flushMessageQueue:(NSString *)messageQueueString{ } } - (void)_dispatchMessage:(WVJBMessage*)message { - NSString *messageJSON = [self _serializeMessage:message pretty:NO]; - [self _log:@"SEND" json:messageJSON]; - messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\\" withString:@"\\\\"]; - messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""]; - messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\'" withString:@"\\\'"]; - messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"]; - messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\r" withString:@"\\r"]; - messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\f" withString:@"\\f"]; - messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\u2028" withString:@"\\u2028"]; - messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\u2029" withString:@"\\u2029"]; + + NSString *messageJSON = [Utils serializeMessage:message pretty:NO]; + [Utils log:@"SEND" json:messageJSON loggingLevel:loggingLevel]; + messageJSON = [Utils replacingJSONString:messageJSON]; NSString* javascriptCommand = [NSString stringWithFormat:@"WebViewJavascriptBridge._handleMessageFromObjC('%@');", messageJSON]; if ([[NSThread currentThread] isMainThread]) { [self _evaluateJavascript:javascriptCommand]; @@ -165,20 +145,6 @@ + (void)enableLogging:(Logginglevel)logginglevel { loggingLevel = logginglevel; } -- (void)_log:(NSString *)action json:(id)json { - if (!loggingLevel) { return; } - if (![json isKindOfClass:[NSString class]]) { - json = [self _serializeMessage:json pretty:YES]; - } - NSLog(@"WVJB %@: %@", action, json); -} -- (NSString *)_serializeMessage:(id)message pretty:(BOOL)pretty{ - return [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:message options:(NSJSONWritingOptions)(pretty ? NSJSONWritingPrettyPrinted : 0) error:nil] encoding:NSUTF8StringEncoding]; -} -- (NSArray*)_deserializeMessageJSON:(NSString *)messageJSON { - return [NSJSONSerialization JSONObjectWithData:[messageJSON dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:nil]; -} - - (NSString *)_filterMessage:(NSString *) message { if (loggingLevel & LogginglevelAll) { NSLog(@"All WVJB RCVD:%@",message); @@ -194,92 +160,6 @@ - (NSString * )_evaluateJavascript:(NSString*)javascriptCommand { return nil; } -NSString * _WebViewJavascriptBridge_js() { -#define __WVJB_js_func__(x) #x - - // BEGIN preprocessorJSCode - static NSString * preprocessorJSCode = @__WVJB_js_func__( - ;(function() { - - console.log = (function (oriLogFunc) { - return function (str) { - window.webkit.messageHandlers.log.postMessage(str); - oriLogFunc.call(console, str); - } - })(console.log); - window.WebViewJavascriptBridge = { - registerHandler: registerHandler, - callHandler: callHandler, - _handleMessageFromObjC: _handleMessageFromObjC - }; - - var sendMessageQueue = []; - var messageHandlers = {}; - var responseCallbacks = {}; - var uniqueId = 1; - - function registerHandler(handlerName, handler) { - messageHandlers[handlerName] = handler; - } - - function callHandler(handlerName, data, responseCallback) { - if (arguments.length === 2 && typeof data == 'function') { - responseCallback = data; - data = null; - } - _doSend({ handlerName:handlerName, data:data }, responseCallback); - } - function _doSend(message, responseCallback) { - if (responseCallback) { - var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime(); - responseCallbacks[callbackId] = responseCallback; - message['callbackId'] = callbackId; - } - sendMessageQueue.push(message); - console.log('__bridge__'+ JSON.stringify(sendMessageQueue)); - sendMessageQueue = []; - } - - function _dispatchMessageFromObjC(messageJSON) { - _doDispatchMessageFromObjC(); - - function _doDispatchMessageFromObjC() { - var message = JSON.parse(messageJSON); - var messageHandler; - var responseCallback; - - if (message.responseId) { - responseCallback = responseCallbacks[message.responseId]; - if (!responseCallback) { - return; - } - responseCallback(message.responseData); - delete responseCallbacks[message.responseId]; - } else { - if (message.callbackId) { - var callbackResponseId = message.callbackId; - responseCallback = function(responseData) { - _doSend({ handlerName:message.handlerName, responseId:callbackResponseId, responseData:responseData }); - }; - } - var handler = messageHandlers[message.handlerName]; - if (!handler) { - console.log("WebViewJavascriptBridge: WARNING: no handler for message from ObjC:", message); - } else { - handler(message.data, responseCallback); - } - } - } - } - function _handleMessageFromObjC(messageJSON) { - _dispatchMessageFromObjC(messageJSON); - } - })(); - ); // END preprocessorJSCode - -#undef __WVJB_js_func__ - return preprocessorJSCode; -}; - (void)dealloc { [self.configuration.userContentController removeScriptMessageHandlerForName:@"log"]; diff --git a/WebViewJavascriptBridge/WKWebViewJavascriptBridge_JS.h b/WebViewJavascriptBridge/WKWebViewJavascriptBridge_JS.h new file mode 100644 index 00000000..9dde40ef --- /dev/null +++ b/WebViewJavascriptBridge/WKWebViewJavascriptBridge_JS.h @@ -0,0 +1,95 @@ +// +// WKWebViewJavascriptBridge_JS.h +// ExampleApp-iOS +// +// Created by 侯森魁 on 2020/3/25. +// Copyright © 2020 Marcus Westin. All rights reserved. +// + + +NSString * _WebViewJavascriptBridge_js() { +#define __WVJB_js_func__(x) #x + + // BEGIN preprocessorJSCode + static NSString * preprocessorJSCode = @__WVJB_js_func__( + ;(function(window) { + + console.log = (function (oriLogFunc) { + return function (str) { + window.webkit.messageHandlers.log.postMessage(str); + oriLogFunc.call(console, str); + } + })(console.log); + window.WebViewJavascriptBridge = { + registerHandler: registerHandler, + callHandler: callHandler, + _handleMessageFromObjC: _handleMessageFromObjC + }; + + var sendMessageQueue = []; + var messageHandlers = {}; + var responseCallbacks = {}; + var uniqueId = 1; + + function registerHandler(handlerName, handler) { + messageHandlers[handlerName] = handler; + } + + function callHandler(handlerName, data, responseCallback) { + if (arguments.length === 2 && typeof data == 'function') { + responseCallback = data; + data = null; + } + _doSend({ handlerName:handlerName, data:data }, responseCallback); + } + function _doSend(message, responseCallback) { + if (responseCallback) { + var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime(); + responseCallbacks[callbackId] = responseCallback; + message['callbackId'] = callbackId; + } + sendMessageQueue.push(message); + console.log('__bridge__'+ JSON.stringify(sendMessageQueue)); + sendMessageQueue = []; + } + + function _dispatchMessageFromObjC(messageJSON) { + _doDispatchMessageFromObjC(); + + function _doDispatchMessageFromObjC() { + var message = JSON.parse(messageJSON); + var messageHandler; + var responseCallback; + + if (message.responseId) { + responseCallback = responseCallbacks[message.responseId]; + if (!responseCallback) { + return; + } + responseCallback(message.responseData); + delete responseCallbacks[message.responseId]; + } else { + if (message.callbackId) { + var callbackResponseId = message.callbackId; + responseCallback = function(responseData) { + _doSend({ handlerName:message.handlerName, responseId:callbackResponseId, responseData:responseData }); + }; + } + var handler = messageHandlers[message.handlerName]; + if (!handler) { + console.log("WebViewJavascriptBridge: WARNING: no handler for message from ObjC:", message); + } else { + handler(message.data, responseCallback); + } + } + } + } + function _handleMessageFromObjC(messageJSON) { + _dispatchMessageFromObjC(messageJSON); + } + })(window); + ); // END preprocessorJSCode + +#undef __WVJB_js_func__ + return preprocessorJSCode; +}; From 7f877dbf527a1b37ab4c0f35915ae368445f6bce Mon Sep 17 00:00:00 2001 From: housenkui Date: Thu, 26 Mar 2020 16:59:32 +0800 Subject: [PATCH 26/44] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=AF=B9NaN=E3=80=81nu?= =?UTF-8?q?ll=E3=80=81undefined=E3=80=81this=E7=9A=84=E6=89=93=E5=8D=B0?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExampleApp-iOS.xcodeproj/project.pbxproj | 4 ++ .../ExampleWKWebViewController.m | 10 +--- Example Apps/ExampleApp.html | 51 ++++++++++++------- Example Apps/TestConsole.html | 16 ++++++ .../WKWebViewJavascriptBridge_JS.h | 38 +++++++++++--- 5 files changed, 87 insertions(+), 32 deletions(-) create mode 100644 Example Apps/TestConsole.html diff --git a/Example Apps/ExampleApp-iOS.xcodeproj/project.pbxproj b/Example Apps/ExampleApp-iOS.xcodeproj/project.pbxproj index 4448a290..9e5a921a 100644 --- a/Example Apps/ExampleApp-iOS.xcodeproj/project.pbxproj +++ b/Example Apps/ExampleApp-iOS.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ 2CEB3EC01602563600548120 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2CEB3EBF1602563600548120 /* UIKit.framework */; }; EF5DC17323472E0B00F81F96 /* WKWebView+JavaScriptBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = EF5DC17223472E0B00F81F96 /* WKWebView+JavaScriptBridge.m */; }; EFE398AD242B15EF0027857D /* Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = EFE398AC242B15EF0027857D /* Utils.m */; }; + EFE398AF242C348C0027857D /* TestConsole.html in Resources */ = {isa = PBXBuildFile; fileRef = EFE398AE242C348C0027857D /* TestConsole.html */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -42,6 +43,7 @@ EFE398AA242B151E0027857D /* Tool.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Tool.h; sourceTree = ""; }; EFE398AB242B15EF0027857D /* Utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Utils.h; sourceTree = ""; }; EFE398AC242B15EF0027857D /* Utils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Utils.m; sourceTree = ""; }; + EFE398AE242C348C0027857D /* TestConsole.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = TestConsole.html; sourceTree = SOURCE_ROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -76,6 +78,7 @@ isa = PBXGroup; children = ( 2CA0465B1711AC8D006DEE8B /* ExampleApp.html */, + EFE398AE242C348C0027857D /* TestConsole.html */, 2CA045BC17117439006DEE8B /* ExampleAppDelegate.h */, 2CA045BD17117439006DEE8B /* ExampleAppDelegate.m */, 0ECB01421A0EE1BA0037FF4E /* ExampleWKWebViewController.h */, @@ -193,6 +196,7 @@ files = ( 2CA045BF17117439006DEE8B /* InfoPlist.strings in Resources */, 2CA0465C1711AC8E006DEE8B /* ExampleApp.html in Resources */, + EFE398AF242C348C0027857D /* TestConsole.html in Resources */, 2CAB869B1727684300BD9ED1 /* Default-568h@2x.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Example Apps/ExampleApp-iOS/ExampleWKWebViewController.m b/Example Apps/ExampleApp-iOS/ExampleWKWebViewController.m index bda789b7..af875d30 100644 --- a/Example Apps/ExampleApp-iOS/ExampleWKWebViewController.m +++ b/Example Apps/ExampleApp-iOS/ExampleWKWebViewController.m @@ -20,7 +20,7 @@ - (void)viewDidLoad { [self.webView registerHandler:@"testObjcCallback" handler:^(id data, WVJBResponseCallback responseCallback) { NSLog(@"testObjcCallback called: %@", data); - responseCallback(@"Response from testObjcCallback"); + responseCallback(@"Response from testObjcCallback111"); }]; [self renderButtons:self.webView]; [self loadExamplePage:self.webView]; @@ -44,12 +44,6 @@ - (void)renderButtons:(WKWebView*)webView { reloadButton.frame = CGRectMake(110, 400, 100, 35); reloadButton.titleLabel.font = font; - UIButton *callBackButton = [UIButton buttonWithType:UIButtonTypeCustom]; - [callBackButton setTitle:@"测试按钮" forState:UIControlStateNormal]; - [callBackButton setTitleColor:[UIColor redColor] forState:UIControlStateNormal]; - [callBackButton addTarget:self action:@selector(callHandler:) forControlEvents:UIControlEventTouchUpInside]; - callBackButton.frame = CGRectMake(210, 400, 100, 35); - [self.view insertSubview:callBackButton aboveSubview:webView]; } - (void)callHandler:(id)sender { @@ -71,7 +65,7 @@ - (WKWebView *) webView { _webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config]; [self.view addSubview:_webView]; //If you set LogginglevelAll ,Xcode command Line will show all JavaScript console.log. - [WKWebView enableLogging:LogginglevelJSONOnly]; + [WKWebView enableLogging:LogginglevelAll]; } return _webView; } diff --git a/Example Apps/ExampleApp.html b/Example Apps/ExampleApp.html index 9f5fc509..e685c89c 100644 --- a/Example Apps/ExampleApp.html +++ b/Example Apps/ExampleApp.html @@ -9,13 +9,28 @@

WebViewJavascriptBridge Demo

+
diff --git a/Example Apps/TestConsole.html b/Example Apps/TestConsole.html new file mode 100644 index 00000000..cb4555ac --- /dev/null +++ b/Example Apps/TestConsole.html @@ -0,0 +1,16 @@ + + + + + +

WebViewJavascriptBridge Demo Test console.log

+ + + diff --git a/WebViewJavascriptBridge/WKWebViewJavascriptBridge_JS.h b/WebViewJavascriptBridge/WKWebViewJavascriptBridge_JS.h index 9dde40ef..9b8b0de0 100644 --- a/WebViewJavascriptBridge/WKWebViewJavascriptBridge_JS.h +++ b/WebViewJavascriptBridge/WKWebViewJavascriptBridge_JS.h @@ -14,12 +14,36 @@ NSString * _WebViewJavascriptBridge_js() { static NSString * preprocessorJSCode = @__WVJB_js_func__( ;(function(window) { - console.log = (function (oriLogFunc) { - return function (str) { - window.webkit.messageHandlers.log.postMessage(str); - oriLogFunc.call(console, str); - } - })(console.log); + let printObject = function (obj) { + let output = ""; + if (obj === null) { + output += "null"; + } + else if (typeof(obj) == "undefined") { + output += "undefined"; + } + else if (typeof obj ==='object'){ + output+="{"; + for(let key in obj){ + let value = obj[key]; + output+= "\""+key+"\""+":"+"\""+value+"\""+","; + } + output = output.substr(0, output.length - 1); + output+="}"; + } + else { + output = "" + obj; + } + return output; + }; + console.log = (function (oriLogFunc,printObject) { + return function (str) { + str = printObject(str); + window.webkit.messageHandlers.log.postMessage(str); + oriLogFunc.call(console, str); + } + })(console.log,printObject); + window.WebViewJavascriptBridge = { registerHandler: registerHandler, callHandler: callHandler, @@ -64,8 +88,10 @@ NSString * _WebViewJavascriptBridge_js() { if (message.responseId) { responseCallback = responseCallbacks[message.responseId]; if (!responseCallback) { + return; } + responseCallback(message.responseData); delete responseCallbacks[message.responseId]; } else { From ea9265ea4aa729ca10cc04ea6508ae2a4f18edb7 Mon Sep 17 00:00:00 2001 From: housenkui Date: Fri, 27 Mar 2020 12:57:25 +0800 Subject: [PATCH 27/44] =?UTF-8?q?=E5=8D=87=E7=BA=A7=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c7204a38..7446d19c 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ How to use ? Add this to your [podfile](https://guides.cocoapods.org/using/getting-started.html) and run `pod install` to install: ```ruby -pod 'SKJavaScriptBridge', '~> 1.0.1' +pod 'SKJavaScriptBridge' ``` If native want to get console.log in WKWebView just ```[WKWebView enableLogging:LogginglevelAll];``` is enough. From dff60ccb1d3f5c4810d709d4da666b8b22488d3e Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Fri, 27 Mar 2020 18:08:42 +0800 Subject: [PATCH 28/44] add more information add more information --- README.md | 71 +++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 64 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 7446d19c..aa40af11 100644 --- a/README.md +++ b/README.md @@ -18,12 +18,69 @@ pod 'SKJavaScriptBridge' If native want to get console.log in WKWebView just ```[WKWebView enableLogging:LogginglevelAll];``` is enough. ### Manual installation +Drag the `WebViewJavascriptBridge` folder into your project. -On Native side: - ![shell文件](https://upload-images.jianshu.io/upload_images/1485140-039a71e6e602bf15.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/1200/format/webp) +In the dialog that appears, uncheck "Copy items into destination group's folder" and select "Create groups for any folders". -On JavaScript side: - ![shell文件](https://upload-images.jianshu.io/upload_images/1485140-c759d9499766b8b9.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/1080/format/webp) - - In fact,just one ```console.log('callNative')``` is enough to call native method.I had hack the Javascript method ```console.log```. - Any question you can contact me with:housenkui@gmail.com or WeChat :housenkui +Usage +----- +1) Import the header file and declare an ivar property: + +```objc +#import "WKWebView+JavaScriptBridge.h" +``` +```objc +@property (nonatomic, strong) WKWebView *webView; +``` + +```objc +- (WKWebView *) webView { + if (!_webView) { + WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init]; + _webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config]; + [self.view addSubview:_webView]; + //If you set LogginglevelAll ,Xcode command Line will show all JavaScript console.log. + [WKWebView enableLogging:LogginglevelAll]; + } + return _webView; +} +``` + +2) Register a handler in ObjC, and call a JS handler: + +```objc +[self.webView registerHandler:@"ObjC Echo" handler:^(id data, WVJBResponseCallback responseCallback) { + NSLog(@"ObjC Echo called with: %@", data); + responseCallback(data); +}]; +[self.webView callHandler:@"JS Echo" data:nil responseCallback:^(id responseData) { + NSLog(@"ObjC received response: %@", responseData); +}]; +``` +3) Copy and paste `setupWebViewJavascriptBridge` into your JS: + +```javascript +function setupWebViewJavascriptBridge(callback) { + return callback(WebViewJavascriptBridge); +} +``` +5) Finally, call `setupWebViewJavascriptBridge` and then use the bridge to register handlers and call ObjC handlers: + +```javascript +setupWebViewJavascriptBridge(function(bridge) { + + /* Initialize your app here */ + + bridge.registerHandler('JS Echo', function(data, responseCallback) { + console.log("JS Echo called with:", data) + responseCallback(data) + }) + bridge.callHandler('ObjC Echo', {'key':'value'}, function responseCallback(responseData) { + console.log("JS received response:", responseData) + }) +}) +``` +### Any question. +You can watch this chinese video https://www.youtube.com/watch?v=4JUNQkohh5E. +English video https://www.youtube.com/watch?v=POohaYA-ew0. +Also you can contact me with:housenkui@gmail.com or WeChat :housenkui From 4ec11adcb8436b78bcea120870d8e3177d0d51ae Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Fri, 27 Mar 2020 18:11:04 +0800 Subject: [PATCH 29/44] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index aa40af11..dfc3698e 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,6 @@ setupWebViewJavascriptBridge(function(bridge) { }) ``` ### Any question. -You can watch this chinese video https://www.youtube.com/watch?v=4JUNQkohh5E. +You can watch this chinese video https://www.youtube.com/watch?v=4JUNQkohh5E. English video https://www.youtube.com/watch?v=POohaYA-ew0. Also you can contact me with:housenkui@gmail.com or WeChat :housenkui From d532f6a944a1fe116eea7e4356373908c48a7ea7 Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Fri, 27 Mar 2020 18:11:28 +0800 Subject: [PATCH 30/44] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dfc3698e..a69d0366 100644 --- a/README.md +++ b/README.md @@ -82,5 +82,5 @@ setupWebViewJavascriptBridge(function(bridge) { ``` ### Any question. You can watch this chinese video https://www.youtube.com/watch?v=4JUNQkohh5E. -English video https://www.youtube.com/watch?v=POohaYA-ew0. +English video https://www.youtube.com/watch?v=POohaYA-ew0. Also you can contact me with:housenkui@gmail.com or WeChat :housenkui From bcaad18b0a8146d36cae05bd105ff9d52b4871cf Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Fri, 27 Mar 2020 18:12:29 +0800 Subject: [PATCH 31/44] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a69d0366..636cbfe6 100644 --- a/README.md +++ b/README.md @@ -83,4 +83,4 @@ setupWebViewJavascriptBridge(function(bridge) { ### Any question. You can watch this chinese video https://www.youtube.com/watch?v=4JUNQkohh5E. English video https://www.youtube.com/watch?v=POohaYA-ew0. -Also you can contact me with:housenkui@gmail.com or WeChat :housenkui +Also you can contact me with:housenkui@gmail.com or WeChat :[housenkui] From 40a76cf2b9afe0913d298005270de3103c9f8880 Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Fri, 27 Mar 2020 18:13:28 +0800 Subject: [PATCH 32/44] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 636cbfe6..e11dee49 100644 --- a/README.md +++ b/README.md @@ -83,4 +83,4 @@ setupWebViewJavascriptBridge(function(bridge) { ### Any question. You can watch this chinese video https://www.youtube.com/watch?v=4JUNQkohh5E. English video https://www.youtube.com/watch?v=POohaYA-ew0. -Also you can contact me with:housenkui@gmail.com or WeChat :[housenkui] +Also you can contact me with:housenkui@gmail.com or WeChat :[housenkui](https://github.com/housenkui/) From 4cff34a1974a0d0a130c459c07dbef60ece2cd9a Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Sat, 28 Mar 2020 09:41:56 +0800 Subject: [PATCH 33/44] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index e11dee49..bf104e59 100644 --- a/README.md +++ b/README.md @@ -84,3 +84,7 @@ setupWebViewJavascriptBridge(function(bridge) { You can watch this chinese video https://www.youtube.com/watch?v=4JUNQkohh5E. English video https://www.youtube.com/watch?v=POohaYA-ew0. Also you can contact me with:housenkui@gmail.com or WeChat :[housenkui](https://github.com/housenkui/) +### 如果你有疑问. +你可以观看中文版的视频介绍:https://www.youtube.com/watch?v=4JUNQkohh5E +或者英文版的:https://www.youtube.com/watch?v=POohaYA-ew0 https://v.youku.com/v_show/id_XNDYwNzUwODgwNA==.html +当然你也可以邮件联系我:housenkui@gmail.com 或者微信:[housenkui](https://github.com/housenkui/) From 4d59d06efab684720d5c4bd8a9aec274fe0a60c8 Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Sat, 28 Mar 2020 09:42:24 +0800 Subject: [PATCH 34/44] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bf104e59..5e4c5379 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,6 @@ You can watch this chinese video https://www.youtube.com/watch?v=4JUNQkohh5E. English video https://www.youtube.com/watch?v=POohaYA-ew0. Also you can contact me with:housenkui@gmail.com or WeChat :[housenkui](https://github.com/housenkui/) ### 如果你有疑问. -你可以观看中文版的视频介绍:https://www.youtube.com/watch?v=4JUNQkohh5E -或者英文版的:https://www.youtube.com/watch?v=POohaYA-ew0 https://v.youku.com/v_show/id_XNDYwNzUwODgwNA==.html +你可以观看中文版的视频介绍:https://www.youtube.com/watch?v=4JUNQkohh5E +或者英文版的:https://www.youtube.com/watch?v=POohaYA-ew0 https://v.youku.com/v_show/id_XNDYwNzUwODgwNA==.html 当然你也可以邮件联系我:housenkui@gmail.com 或者微信:[housenkui](https://github.com/housenkui/) From adf616d634aa93a7c3e2811ae14001b2b26b40c2 Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Sat, 28 Mar 2020 09:42:40 +0800 Subject: [PATCH 35/44] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5e4c5379..89a78741 100644 --- a/README.md +++ b/README.md @@ -86,5 +86,5 @@ English video https://www.youtube.com/watch?v=POohaYA-ew0. Also you can contact me with:housenkui@gmail.com or WeChat :[housenkui](https://github.com/housenkui/) ### 如果你有疑问. 你可以观看中文版的视频介绍:https://www.youtube.com/watch?v=4JUNQkohh5E -或者英文版的:https://www.youtube.com/watch?v=POohaYA-ew0 https://v.youku.com/v_show/id_XNDYwNzUwODgwNA==.html +或者英文版的:https://www.youtube.com/watch?v=POohaYA-ew0 https://v.youku.com/v_show/id_XNDYwNzUwODgwNA==.html 当然你也可以邮件联系我:housenkui@gmail.com 或者微信:[housenkui](https://github.com/housenkui/) From 2e426b39fde71325353554a0497ea03a10e07be8 Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Wed, 29 Apr 2020 18:23:54 +0800 Subject: [PATCH 36/44] Update README.md --- README.md | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 89a78741..872463c6 100644 --- a/README.md +++ b/README.md @@ -31,29 +31,26 @@ Usage ``` ```objc @property (nonatomic, strong) WKWebView *webView; +@property (nonatomic, strong) WebViewJavascriptBridge* bridge; ``` ```objc -- (WKWebView *) webView { - if (!_webView) { - WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init]; - _webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config]; - [self.view addSubview:_webView]; - //If you set LogginglevelAll ,Xcode command Line will show all JavaScript console.log. - [WKWebView enableLogging:LogginglevelAll]; - } - return _webView; -} +self.webView = [[WKWebView alloc] initWithFrame:self.view.bounds]; + [self.view addSubview:self.webView]; + + _bridge = [WebViewJavascriptBridge bridgeForWebView:self.webView + showJSconsole:YES + enableLogging:YES]; ``` 2) Register a handler in ObjC, and call a JS handler: ```objc -[self.webView registerHandler:@"ObjC Echo" handler:^(id data, WVJBResponseCallback responseCallback) { +[_bridge registerHandler:@"ObjC Echo" handler:^(id data, WVJBResponseCallback responseCallback) { NSLog(@"ObjC Echo called with: %@", data); responseCallback(data); }]; -[self.webView callHandler:@"JS Echo" data:nil responseCallback:^(id responseData) { +[_bridge callHandler:@"JS Echo" data:nil responseCallback:^(id responseData) { NSLog(@"ObjC received response: %@", responseData); }]; ``` From 29fcfae98238167744d8ed278dcc0e0d38b8a65f Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Wed, 29 Apr 2020 18:24:56 +0800 Subject: [PATCH 37/44] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 872463c6..0ea3528a 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ How to use ? Add this to your [podfile](https://guides.cocoapods.org/using/getting-started.html) and run `pod install` to install: ```ruby -pod 'SKJavaScriptBridge' +pod 'SKJavaScriptBridge', '~> 1.0.3' ``` If native want to get console.log in WKWebView just ```[WKWebView enableLogging:LogginglevelAll];``` is enough. @@ -27,7 +27,7 @@ Usage 1) Import the header file and declare an ivar property: ```objc -#import "WKWebView+JavaScriptBridge.h" +#import "WebViewJavascriptBridge.h" ``` ```objc @property (nonatomic, strong) WKWebView *webView; From 5c03c91d73cae79e2a82e26ba161ebcd60004219 Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Wed, 29 Apr 2020 18:25:30 +0800 Subject: [PATCH 38/44] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0ea3528a..afe7a3a5 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ Usage ``` ```objc -self.webView = [[WKWebView alloc] initWithFrame:self.view.bounds]; + self.webView = [[WKWebView alloc] initWithFrame:self.view.bounds]; [self.view addSubview:self.webView]; _bridge = [WebViewJavascriptBridge bridgeForWebView:self.webView From ca0d0b3276d6cfd8a38e21de51a5a21b37383837 Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Wed, 29 Apr 2020 18:40:57 +0800 Subject: [PATCH 39/44] Update README.md --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index afe7a3a5..d61b913d 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ WebViewJavascriptBridge ======================= -An iOS/OSX bridge for sending messages between Obj-C and JavaScript in WKWebViews. +An iOS/OSX bridge for sending messages between Obj-C and JavaScript in WKWebViews. Also easy to get js console.log. More simple more light. Refactor WebViewJavascriptBridge with AOP ========================== @@ -15,7 +15,10 @@ Add this to your [podfile](https://guides.cocoapods.org/using/getting-started.ht ```ruby pod 'SKJavaScriptBridge', '~> 1.0.3' ``` -If native want to get console.log in WKWebView just ```[WKWebView enableLogging:LogginglevelAll];``` is enough. +If you can't find the last version, maybe you need to update local pod repo. +```ruby +pod repo update +``` ### Manual installation Drag the `WebViewJavascriptBridge` folder into your project. From 7ef16af8117a50db8f84547fd915878280f259d4 Mon Sep 17 00:00:00 2001 From: housenkui Date: Wed, 29 Apr 2020 19:08:48 +0800 Subject: [PATCH 40/44] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExampleApp-iOS.xcodeproj/project.pbxproj | 45 ++-- .../xcschemes/ExampleApp-iOS.xcscheme | 78 ++++++ .../ExampleApp-iOS/ExampleAppDelegate.m | 5 +- .../ExampleWKWebViewController.h | 2 +- .../ExampleWKWebViewController.m | 25 +- .../ExampleApp-iOS/FirstViewController.h | 16 ++ .../ExampleApp-iOS/FirstViewController.m | 49 ++++ WebViewJavascriptBridge/Logginglevel.h | 15 -- WebViewJavascriptBridge/Tool.h | 27 -- WebViewJavascriptBridge/Utils.h | 20 -- WebViewJavascriptBridge/Utils.m | 39 --- .../WKWebView+JavaScriptBridge.h | 28 --- .../WKWebView+JavaScriptBridge.m | 191 -------------- .../WKWebViewJavascriptBridge_JS.h | 121 --------- .../WebViewJavascriptBridge.h | 26 ++ .../WebViewJavascriptBridge.m | 233 ++++++++++++++++++ .../WebViewJavascriptBridgeBase.h | 29 +++ .../WebViewJavascriptBridgeBase.m | 122 +++++++++ .../WebViewJavascriptLeakAvoider.h | 18 ++ .../WebViewJavascriptLeakAvoider.m | 21 ++ 20 files changed, 643 insertions(+), 467 deletions(-) create mode 100644 Example Apps/ExampleApp-iOS.xcodeproj/xcshareddata/xcschemes/ExampleApp-iOS.xcscheme create mode 100644 Example Apps/ExampleApp-iOS/FirstViewController.h create mode 100644 Example Apps/ExampleApp-iOS/FirstViewController.m delete mode 100644 WebViewJavascriptBridge/Logginglevel.h delete mode 100644 WebViewJavascriptBridge/Tool.h delete mode 100644 WebViewJavascriptBridge/Utils.h delete mode 100644 WebViewJavascriptBridge/Utils.m delete mode 100644 WebViewJavascriptBridge/WKWebView+JavaScriptBridge.h delete mode 100644 WebViewJavascriptBridge/WKWebView+JavaScriptBridge.m delete mode 100644 WebViewJavascriptBridge/WKWebViewJavascriptBridge_JS.h create mode 100644 WebViewJavascriptBridge/WebViewJavascriptBridge.h create mode 100644 WebViewJavascriptBridge/WebViewJavascriptBridge.m create mode 100644 WebViewJavascriptBridge/WebViewJavascriptBridgeBase.h create mode 100644 WebViewJavascriptBridge/WebViewJavascriptBridgeBase.m create mode 100644 WebViewJavascriptBridge/WebViewJavascriptLeakAvoider.h create mode 100644 WebViewJavascriptBridge/WebViewJavascriptLeakAvoider.m diff --git a/Example Apps/ExampleApp-iOS.xcodeproj/project.pbxproj b/Example Apps/ExampleApp-iOS.xcodeproj/project.pbxproj index 9e5a921a..5f180388 100644 --- a/Example Apps/ExampleApp-iOS.xcodeproj/project.pbxproj +++ b/Example Apps/ExampleApp-iOS.xcodeproj/project.pbxproj @@ -15,9 +15,11 @@ 2CA0465C1711AC8E006DEE8B /* ExampleApp.html in Resources */ = {isa = PBXBuildFile; fileRef = 2CA0465B1711AC8D006DEE8B /* ExampleApp.html */; }; 2CAB869B1727684300BD9ED1 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 2CAB869A1727684300BD9ED1 /* Default-568h@2x.png */; }; 2CEB3EC01602563600548120 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2CEB3EBF1602563600548120 /* UIKit.framework */; }; - EF5DC17323472E0B00F81F96 /* WKWebView+JavaScriptBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = EF5DC17223472E0B00F81F96 /* WKWebView+JavaScriptBridge.m */; }; - EFE398AD242B15EF0027857D /* Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = EFE398AC242B15EF0027857D /* Utils.m */; }; + EF5D0B3224599757008DC69C /* WebViewJavascriptBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = EF5D0B2E24599756008DC69C /* WebViewJavascriptBridge.m */; }; + EF5D0B3324599757008DC69C /* WebViewJavascriptBridgeBase.m in Sources */ = {isa = PBXBuildFile; fileRef = EF5D0B2F24599756008DC69C /* WebViewJavascriptBridgeBase.m */; }; + EF5D0B3424599757008DC69C /* WebViewJavascriptLeakAvoider.m in Sources */ = {isa = PBXBuildFile; fileRef = EF5D0B3024599757008DC69C /* WebViewJavascriptLeakAvoider.m */; }; EFE398AF242C348C0027857D /* TestConsole.html in Resources */ = {isa = PBXBuildFile; fileRef = EFE398AE242C348C0027857D /* TestConsole.html */; }; + EFF05EC5244C0FED0060F71C /* FirstViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EFF05EC4244C0FED0060F71C /* FirstViewController.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -36,14 +38,15 @@ 2CEB3EBF1602563600548120 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 2CEB3EC11602563600548120 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 2CEB3EC31602563600548120 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; - EF5DC17123472E0B00F81F96 /* WKWebView+JavaScriptBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "WKWebView+JavaScriptBridge.h"; sourceTree = ""; }; - EF5DC17223472E0B00F81F96 /* WKWebView+JavaScriptBridge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "WKWebView+JavaScriptBridge.m"; sourceTree = ""; }; - EFE398A8242B12C90027857D /* Logginglevel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Logginglevel.h; sourceTree = ""; }; - EFE398A9242B14340027857D /* WKWebViewJavascriptBridge_JS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WKWebViewJavascriptBridge_JS.h; sourceTree = ""; }; - EFE398AA242B151E0027857D /* Tool.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Tool.h; sourceTree = ""; }; - EFE398AB242B15EF0027857D /* Utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Utils.h; sourceTree = ""; }; - EFE398AC242B15EF0027857D /* Utils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Utils.m; sourceTree = ""; }; + EF5D0B2C24599756008DC69C /* WebViewJavascriptBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebViewJavascriptBridge.h; sourceTree = ""; }; + EF5D0B2D24599756008DC69C /* WebViewJavascriptLeakAvoider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebViewJavascriptLeakAvoider.h; sourceTree = ""; }; + EF5D0B2E24599756008DC69C /* WebViewJavascriptBridge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WebViewJavascriptBridge.m; sourceTree = ""; }; + EF5D0B2F24599756008DC69C /* WebViewJavascriptBridgeBase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WebViewJavascriptBridgeBase.m; sourceTree = ""; }; + EF5D0B3024599757008DC69C /* WebViewJavascriptLeakAvoider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WebViewJavascriptLeakAvoider.m; sourceTree = ""; }; + EF5D0B3124599757008DC69C /* WebViewJavascriptBridgeBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebViewJavascriptBridgeBase.h; sourceTree = ""; }; EFE398AE242C348C0027857D /* TestConsole.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = TestConsole.html; sourceTree = SOURCE_ROOT; }; + EFF05EC3244C0FED0060F71C /* FirstViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FirstViewController.h; sourceTree = ""; }; + EFF05EC4244C0FED0060F71C /* FirstViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FirstViewController.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -62,13 +65,12 @@ 2C1562A7176B9F5400B4AE50 /* WebViewJavascriptBridge */ = { isa = PBXGroup; children = ( - EFE398A8242B12C90027857D /* Logginglevel.h */, - EF5DC17123472E0B00F81F96 /* WKWebView+JavaScriptBridge.h */, - EF5DC17223472E0B00F81F96 /* WKWebView+JavaScriptBridge.m */, - EFE398A9242B14340027857D /* WKWebViewJavascriptBridge_JS.h */, - EFE398AA242B151E0027857D /* Tool.h */, - EFE398AB242B15EF0027857D /* Utils.h */, - EFE398AC242B15EF0027857D /* Utils.m */, + EF5D0B2C24599756008DC69C /* WebViewJavascriptBridge.h */, + EF5D0B2E24599756008DC69C /* WebViewJavascriptBridge.m */, + EF5D0B3124599757008DC69C /* WebViewJavascriptBridgeBase.h */, + EF5D0B2F24599756008DC69C /* WebViewJavascriptBridgeBase.m */, + EF5D0B2D24599756008DC69C /* WebViewJavascriptLeakAvoider.h */, + EF5D0B3024599757008DC69C /* WebViewJavascriptLeakAvoider.m */, ); name = WebViewJavascriptBridge; path = ../../WebViewJavascriptBridge; @@ -81,6 +83,8 @@ EFE398AE242C348C0027857D /* TestConsole.html */, 2CA045BC17117439006DEE8B /* ExampleAppDelegate.h */, 2CA045BD17117439006DEE8B /* ExampleAppDelegate.m */, + EFF05EC3244C0FED0060F71C /* FirstViewController.h */, + EFF05EC4244C0FED0060F71C /* FirstViewController.m */, 0ECB01421A0EE1BA0037FF4E /* ExampleWKWebViewController.h */, 0ECB01431A0EE1F20037FF4E /* ExampleWKWebViewController.m */, 2C1562A7176B9F5400B4AE50 /* WebViewJavascriptBridge */, @@ -167,6 +171,7 @@ ORGANIZATIONNAME = "Marcus Westin"; TargetAttributes = { 2CEB3EBA1602563600548120 = { + DevelopmentTeam = D9DMY5ZV46; LastSwiftMigration = 0820; }; }; @@ -209,10 +214,12 @@ buildActionMask = 2147483647; files = ( 0ECB01441A0EE1F20037FF4E /* ExampleWKWebViewController.m in Sources */, + EFF05EC5244C0FED0060F71C /* FirstViewController.m in Sources */, + EF5D0B3424599757008DC69C /* WebViewJavascriptLeakAvoider.m in Sources */, 2CA045C217117439006DEE8B /* ExampleAppDelegate.m in Sources */, - EF5DC17323472E0B00F81F96 /* WKWebView+JavaScriptBridge.m in Sources */, - EFE398AD242B15EF0027857D /* Utils.m in Sources */, 2CA045C317117439006DEE8B /* main.m in Sources */, + EF5D0B3324599757008DC69C /* WebViewJavascriptBridgeBase.m in Sources */, + EF5D0B3224599757008DC69C /* WebViewJavascriptBridge.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -309,6 +316,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; + DEVELOPMENT_TEAM = D9DMY5ZV46; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "ExampleApp-iOS/ExampleApp-iOS-Prefix.pch"; INFOPLIST_FILE = "ExampleApp-iOS/ExampleApp-iOS-Info.plist"; @@ -326,6 +334,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; + DEVELOPMENT_TEAM = D9DMY5ZV46; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "ExampleApp-iOS/ExampleApp-iOS-Prefix.pch"; INFOPLIST_FILE = "ExampleApp-iOS/ExampleApp-iOS-Info.plist"; diff --git a/Example Apps/ExampleApp-iOS.xcodeproj/xcshareddata/xcschemes/ExampleApp-iOS.xcscheme b/Example Apps/ExampleApp-iOS.xcodeproj/xcshareddata/xcschemes/ExampleApp-iOS.xcscheme new file mode 100644 index 00000000..a902b6a7 --- /dev/null +++ b/Example Apps/ExampleApp-iOS.xcodeproj/xcshareddata/xcschemes/ExampleApp-iOS.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example Apps/ExampleApp-iOS/ExampleAppDelegate.m b/Example Apps/ExampleApp-iOS/ExampleAppDelegate.m index 9b2ac6e8..c083d00f 100644 --- a/Example Apps/ExampleApp-iOS/ExampleAppDelegate.m +++ b/Example Apps/ExampleApp-iOS/ExampleAppDelegate.m @@ -1,5 +1,6 @@ #import "ExampleAppDelegate.h" #import "ExampleWKWebViewController.h" +#import "FirstViewController.h" @implementation ExampleAppDelegate @@ -12,7 +13,9 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( [tabBarController addChildViewController:WKWebViewExampleController]; self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; - self.window.rootViewController = tabBarController; + + UINavigationController *nav = [[UINavigationController alloc]initWithRootViewController:[FirstViewController new]]; + self.window.rootViewController = nav; [self.window makeKeyAndVisible]; return YES; diff --git a/Example Apps/ExampleApp-iOS/ExampleWKWebViewController.h b/Example Apps/ExampleApp-iOS/ExampleWKWebViewController.h index cf0f1c42..14b8cf9b 100644 --- a/Example Apps/ExampleApp-iOS/ExampleWKWebViewController.h +++ b/Example Apps/ExampleApp-iOS/ExampleWKWebViewController.h @@ -9,6 +9,6 @@ #import #import -@interface ExampleWKWebViewController : UINavigationController +@interface ExampleWKWebViewController : UIViewController @end diff --git a/Example Apps/ExampleApp-iOS/ExampleWKWebViewController.m b/Example Apps/ExampleApp-iOS/ExampleWKWebViewController.m index af875d30..f7ca6df5 100644 --- a/Example Apps/ExampleApp-iOS/ExampleWKWebViewController.m +++ b/Example Apps/ExampleApp-iOS/ExampleWKWebViewController.m @@ -7,18 +7,25 @@ // #import "ExampleWKWebViewController.h" -#import "WKWebView+JavaScriptBridge.h" - +#import "WebViewJavascriptBridge.h" @interface ExampleWKWebViewController () @property (nonatomic, strong) WKWebView *webView; +@property (nonatomic, strong) WebViewJavascriptBridge* bridge; @end @implementation ExampleWKWebViewController - (void)viewDidLoad { [super viewDidLoad]; + + self.webView = [[WKWebView alloc] initWithFrame:self.view.bounds]; + [self.view addSubview:self.webView]; + + _bridge = [WebViewJavascriptBridge bridgeForWebView:self.webView + showJSconsole:YES + enableLogging:YES]; - [self.webView registerHandler:@"testObjcCallback" handler:^(id data, WVJBResponseCallback responseCallback) { + [_bridge registerHandler:@"testObjcCallback" handler:^(id data, WVJBResponseCallback responseCallback) { NSLog(@"testObjcCallback called: %@", data); responseCallback(@"Response from testObjcCallback111"); }]; @@ -48,7 +55,7 @@ - (void)renderButtons:(WKWebView*)webView { - (void)callHandler:(id)sender { id data = @{ @"greetingFromObjC": @"Hi there, JS!" }; - [self.webView callHandler:@"testJavascriptHandler" data:data responseCallback:^(id response) { + [_bridge callHandler:@"testJavascriptHandler" data:data responseCallback:^(id response) { NSLog(@"testJavascriptHandler responded: %@", response); }]; } @@ -64,9 +71,15 @@ - (WKWebView *) webView { WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init]; _webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config]; [self.view addSubview:_webView]; - //If you set LogginglevelAll ,Xcode command Line will show all JavaScript console.log. - [WKWebView enableLogging:LogginglevelAll]; + } return _webView; } + +-(void)viewDidDisappear:(BOOL)animated { + [super viewDidDisappear:animated]; + + NSLog(@"viewDidDisappear``"); + [self.webView.configuration.userContentController removeAllUserScripts]; +} @end diff --git a/Example Apps/ExampleApp-iOS/FirstViewController.h b/Example Apps/ExampleApp-iOS/FirstViewController.h new file mode 100644 index 00000000..ecec350d --- /dev/null +++ b/Example Apps/ExampleApp-iOS/FirstViewController.h @@ -0,0 +1,16 @@ +// +// FirstViewController.h +// ExampleApp-iOS +// +// Created by 侯森魁 on 2020/4/19. +// Copyright © 2020 Marcus Westin. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface FirstViewController : UIViewController +@end + +NS_ASSUME_NONNULL_END diff --git a/Example Apps/ExampleApp-iOS/FirstViewController.m b/Example Apps/ExampleApp-iOS/FirstViewController.m new file mode 100644 index 00000000..f2313109 --- /dev/null +++ b/Example Apps/ExampleApp-iOS/FirstViewController.m @@ -0,0 +1,49 @@ +// +// FirstViewController.m +// ExampleApp-iOS +// +// Created by 侯森魁 on 2020/4/19. +// Copyright © 2020 Marcus Westin. All rights reserved. +// + +#import "FirstViewController.h" +#import "ExampleWKWebViewController.h" +@interface FirstViewController () + +@end + +@implementation FirstViewController + + +- (void)viewDidLoad { + [super viewDidLoad]; + self.title = @"first page"; + UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; + [button setTitle:@"click me" forState:UIControlStateNormal]; + [button addTarget:self action:@selector(jump) forControlEvents:UIControlEventTouchUpInside]; + button.frame = CGRectMake(100, 200, 100, 40); + button.center = self.view.center; + self.view.backgroundColor = [UIColor redColor]; + [self.view addSubview:button]; + // Do any additional setup after loading the view. +} +- (void)jump{ +// UITabBarController *tabBarController = [[UITabBarController alloc] init]; + + ExampleWKWebViewController* WKWebViewExampleController = [[ExampleWKWebViewController alloc] init]; +// WKWebViewExampleController.tabBarItem.title = @"WKWebView"; +// [tabBarController addChildViewController:WKWebViewExampleController]; +// [self presentViewController:WKWebViewExampleController animated:YES completion:nil]; + [self.navigationController pushViewController:WKWebViewExampleController animated:YES]; +} +/* +#pragma mark - Navigation + +// In a storyboard-based application, you will often want to do a little preparation before navigation +- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { + // Get the new view controller using [segue destinationViewController]. + // Pass the selected object to the new view controller. +} +*/ + +@end diff --git a/WebViewJavascriptBridge/Logginglevel.h b/WebViewJavascriptBridge/Logginglevel.h deleted file mode 100644 index 3ac4ba11..00000000 --- a/WebViewJavascriptBridge/Logginglevel.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Logginglevel.h -// ExampleApp-iOS -// -// Created by 侯森魁 on 2020/3/25. -// Copyright © 2020 Marcus Westin. All rights reserved. -// - -typedef enum { - //Only printf JSON In Xcode Command line - LogginglevelJSONOnly = 1 << 0, - //All String printf In Xcode Command line - LogginglevelAll = 1 << 1, -}Logginglevel; - diff --git a/WebViewJavascriptBridge/Tool.h b/WebViewJavascriptBridge/Tool.h deleted file mode 100644 index e735b7a6..00000000 --- a/WebViewJavascriptBridge/Tool.h +++ /dev/null @@ -1,27 +0,0 @@ -// -// Tool.h -// ExampleApp-iOS -// -// Created by 侯森魁 on 2020/3/25. -// Copyright © 2020 Marcus Westin. All rights reserved. -// - -#import -void swizzleMethod(Class class, SEL originalSelector, SEL swizzledSelector) -{ - // the method might not exist in the class, but in its superclass - Method originalMethod = class_getInstanceMethod(class, originalSelector); - Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); - - // class_addMethod will fail if original method already exists - BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); - - // the method doesn’t exist and we just added one - if (didAddMethod) { - class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); - } - else { - method_exchangeImplementations(originalMethod, swizzledMethod); - } -} - diff --git a/WebViewJavascriptBridge/Utils.h b/WebViewJavascriptBridge/Utils.h deleted file mode 100644 index 607ed119..00000000 --- a/WebViewJavascriptBridge/Utils.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// Utils.h -// ExampleApp-iOS -// -// Created by 侯森魁 on 2020/3/25. -// Copyright © 2020 Marcus Westin. All rights reserved. -// - -#import -#import "Logginglevel.h" -NS_ASSUME_NONNULL_BEGIN - -@interface Utils : NSObject -+ (NSString *)serializeMessage:(id)message pretty:(BOOL)pretty; -+ (NSArray*)deserializeMessageJSON:(NSString *)messageJSON; -+ (void)log:(NSString *)action json:(id)json loggingLevel:(Logginglevel)loggingLevel; -+ (NSString *)replacingJSONString:(NSString *)messageJSON; -@end - -NS_ASSUME_NONNULL_END diff --git a/WebViewJavascriptBridge/Utils.m b/WebViewJavascriptBridge/Utils.m deleted file mode 100644 index 2baab468..00000000 --- a/WebViewJavascriptBridge/Utils.m +++ /dev/null @@ -1,39 +0,0 @@ -// -// Utils.m -// ExampleApp-iOS -// -// Created by 侯森魁 on 2020/3/25. -// Copyright © 2020 Marcus Westin. All rights reserved. -// - -#import "Utils.h" - -@implementation Utils -+ (NSString *)serializeMessage:(id)message pretty:(BOOL)pretty{ - return [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:message options:(NSJSONWritingOptions)(pretty ? NSJSONWritingPrettyPrinted : 0) error:nil] encoding:NSUTF8StringEncoding]; -} -+ (NSArray*)deserializeMessageJSON:(NSString *)messageJSON { - return [NSJSONSerialization JSONObjectWithData:[messageJSON dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:nil]; -} - -+ (void)log:(NSString *)action json:(id)json loggingLevel:(Logginglevel)loggingLevel{ - if (!loggingLevel) { return; } - if (![json isKindOfClass:[NSString class]]) { - json = [Utils serializeMessage:json pretty:YES]; - } - NSLog(@"WVJB %@: %@", action, json); -} - -+ (NSString *)replacingJSONString:(NSString *)messageJSON { - - messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\\" withString:@"\\\\"]; - messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""]; - messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\'" withString:@"\\\'"]; - messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"]; - messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\r" withString:@"\\r"]; - messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\f" withString:@"\\f"]; - messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\u2028" withString:@"\\u2028"]; - messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\u2029" withString:@"\\u2029"]; - return messageJSON; -} -@end diff --git a/WebViewJavascriptBridge/WKWebView+JavaScriptBridge.h b/WebViewJavascriptBridge/WKWebView+JavaScriptBridge.h deleted file mode 100644 index 9998e070..00000000 --- a/WebViewJavascriptBridge/WKWebView+JavaScriptBridge.h +++ /dev/null @@ -1,28 +0,0 @@ -// -// WKWebView+JavaScriptBridge.h -// WKWebView+Console -// -// Created by 侯森魁 on 2019/10/3. -// Copyright © 2019 housenkui. All rights reserved. -// - -#import -#import "Logginglevel.h" -NS_ASSUME_NONNULL_BEGIN - -typedef NSDictionary WVJBMessage; -typedef void (^WVJBResponseCallback)(id responseData); -typedef void (^WVJBHandler)(id data, WVJBResponseCallback responseCallback); - -@interface WKWebView (JavaScriptBridge) -@property (strong, nonatomic) NSMutableDictionary* responseCallbacks; -@property (strong, nonatomic) NSMutableDictionary* messageHandlers; -@property (strong, nonatomic) WVJBHandler messageHandler; - -- (void)registerHandler:(NSString *) handlerName handler:(WVJBHandler)handler; -- (void)removeHandler:(NSString *) handlerName; -- (void)callHandler:(NSString *) handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback; -+ (void)enableLogging:(Logginglevel)logginglevel; -@end - -NS_ASSUME_NONNULL_END diff --git a/WebViewJavascriptBridge/WKWebView+JavaScriptBridge.m b/WebViewJavascriptBridge/WKWebView+JavaScriptBridge.m deleted file mode 100644 index a9feb6a9..00000000 --- a/WebViewJavascriptBridge/WKWebView+JavaScriptBridge.m +++ /dev/null @@ -1,191 +0,0 @@ -// -// WKWebView+JavaScriptBridge.m -// WKWebView+Console -// -// Created by 侯森魁 on 2019/10/3. -// Copyright © 2019 housenkui. All rights reserved. -// - -#import "WKWebView+JavaScriptBridge.h" -#import "WKWebViewJavascriptBridge_JS.h" -#import "Tool.h" -#import "Utils.h" -#define kBridgePrefix @"__bridge__" - -static long _uniqueId = 0; -static Logginglevel loggingLevel = 0; - -@implementation WKWebView (JavaScriptBridge) - -+(void)load { - swizzleMethod([self class], @selector(initWithFrame:configuration:), @selector(initWithJavaScriptBridgeFrame:configuration:)); -} -- (instancetype)initWithJavaScriptBridgeFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration { - if (self = [self initWithJavaScriptBridgeFrame:frame configuration:configuration]) { - self.messageHandlers = [NSMutableDictionary dictionary]; - self.responseCallbacks = [NSMutableDictionary dictionary]; - - WKUserContentController *userCC = self.configuration.userContentController; - [userCC addScriptMessageHandler:self name:@"log"]; - [self _injectJavascriptFile]; - } - return self; -} - -- (void)_injectJavascriptFile { - NSString *jsCode = _WebViewJavascriptBridge_js(); - //injected the method when H5 starts to create the DOM tree - [self.configuration.userContentController addUserScript:[[WKUserScript alloc] initWithSource:jsCode injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES]]; -} - -- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message { - NSString * body = (NSString * )message.body; - if ([self _filterMessage:body]) { - NSMutableString *mstr = [NSMutableString stringWithString:body]; - [mstr replaceOccurrencesOfString:kBridgePrefix withString:@"" options:0 range:NSMakeRange(0, 10)]; - [self _flushMessageQueue:mstr]; - } -} - -- (void)registerHandler:(NSString *)handlerName handler:(WVJBHandler)handler { - self.messageHandlers[handlerName] = handler; -} - -- (void)removeHandler:(NSString *)handlerName { - [self.messageHandlers removeObjectForKey:handlerName]; -} - -- (void)callHandler:(NSString *)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback { - [self _sendData:data responseCallback:responseCallback handlerName:handlerName]; -} -- (void)_sendData:(id)data responseCallback:(WVJBResponseCallback)responseCallback handlerName:(NSString*)handlerName { - NSMutableDictionary* message = [NSMutableDictionary dictionary]; - - if (data) { - message[@"data"] = data; - } - - if (responseCallback) { - NSString* callbackId = [NSString stringWithFormat:@"objc_cb_%ld", ++_uniqueId]; - self.responseCallbacks[callbackId] = [responseCallback copy]; - message[@"callbackId"] = callbackId; - } - - if (handlerName) { - message[@"handlerName"] = handlerName; - } - [self _queueMessage:message]; -} - -- (void)_queueMessage:(WVJBMessage*)message { - [self _dispatchMessage:message]; -} -- (void)_flushMessageQueue:(NSString *)messageQueueString{ - if (messageQueueString == nil || messageQueueString.length == 0) { - NSLog(@"WebViewJavascriptBridge: WARNING: ObjC got nil while fetching the message queue JSON from webview. This can happen if the WebViewJavascriptBridge JS is not currently present in the webview, e.g if the webview just loaded a new page."); - return; - } - - id messages = [Utils deserializeMessageJSON:messageQueueString]; - for (WVJBMessage* message in messages) { - if (![message isKindOfClass:[WVJBMessage class]]) { - NSLog(@"WebViewJavascriptBridge: WARNING: Invalid %@ received: %@", [message class], message); - continue; - } - [Utils log:@"RCVD" json:message loggingLevel:loggingLevel]; - - NSString* responseId = message[@"responseId"]; - if (responseId) { - WVJBResponseCallback responseCallback = self.responseCallbacks[responseId]; - responseCallback(message[@"responseData"]); - [self.responseCallbacks removeObjectForKey:responseId]; - } else { - WVJBResponseCallback responseCallback = NULL; - NSString* callbackId = message[@"callbackId"]; - if (callbackId) { - responseCallback = ^(id responseData) { - if (responseData == nil) { - responseData = [NSNull null]; - } - WVJBMessage* msg = @{ @"responseId":callbackId, @"responseData":responseData }; - [self _queueMessage:msg]; - }; - } else { - responseCallback = ^(id ignoreResponseData) { - // Do nothing - }; - } - - WVJBHandler handler = self.messageHandlers[message[@"handlerName"]]; - - if (!handler) { - NSLog(@"WVJBNoHandlerException, No handler for message from JS: %@", message); - continue; - } - handler(message[@"data"], responseCallback); - } - } -} -- (void)_dispatchMessage:(WVJBMessage*)message { - - NSString *messageJSON = [Utils serializeMessage:message pretty:NO]; - [Utils log:@"SEND" json:messageJSON loggingLevel:loggingLevel]; - messageJSON = [Utils replacingJSONString:messageJSON]; - NSString* javascriptCommand = [NSString stringWithFormat:@"WebViewJavascriptBridge._handleMessageFromObjC('%@');", messageJSON]; - if ([[NSThread currentThread] isMainThread]) { - [self _evaluateJavascript:javascriptCommand]; - - } else { - dispatch_sync(dispatch_get_main_queue(), ^{ - [self _evaluateJavascript:javascriptCommand]; - }); - } -} -+ (void)enableLogging:(Logginglevel)logginglevel { - loggingLevel = logginglevel; -} - -- (NSString *)_filterMessage:(NSString *) message { - if (loggingLevel & LogginglevelAll) { - NSLog(@"All WVJB RCVD:%@",message); - } - if (message&& [message isKindOfClass:[NSString class]] && [message containsString:kBridgePrefix]) - { - return message; - } - return nil; -} -- (NSString * )_evaluateJavascript:(NSString*)javascriptCommand { - [self evaluateJavaScript:javascriptCommand completionHandler:nil]; - return nil; -} - -- (void)dealloc -{ - [self.configuration.userContentController removeScriptMessageHandlerForName:@"log"]; -} -- (void)setResponseCallbacks:(NSMutableDictionary *)responseCallbacks { - objc_setAssociatedObject(self, @selector(responseCallbacks), responseCallbacks, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -- (NSMutableDictionary *)responseCallbacks { - NSMutableDictionary * responseCallbacks = objc_getAssociatedObject(self, _cmd); - return responseCallbacks; -} - -- (void)setMessageHandlers:(NSMutableDictionary *)messageHandlers { - objc_setAssociatedObject(self, @selector(messageHandlers), messageHandlers, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -- (NSMutableDictionary *)messageHandlers { - return objc_getAssociatedObject(self, _cmd); -} - -- (void)setMessageHandler:(WVJBHandler)messageHandler { - objc_setAssociatedObject(self, @selector(messageHandler), messageHandler, OBJC_ASSOCIATION_COPY_NONATOMIC); -} - -- (WVJBHandler)messageHandler { - return objc_getAssociatedObject(self, _cmd); -} -@end diff --git a/WebViewJavascriptBridge/WKWebViewJavascriptBridge_JS.h b/WebViewJavascriptBridge/WKWebViewJavascriptBridge_JS.h deleted file mode 100644 index 9b8b0de0..00000000 --- a/WebViewJavascriptBridge/WKWebViewJavascriptBridge_JS.h +++ /dev/null @@ -1,121 +0,0 @@ -// -// WKWebViewJavascriptBridge_JS.h -// ExampleApp-iOS -// -// Created by 侯森魁 on 2020/3/25. -// Copyright © 2020 Marcus Westin. All rights reserved. -// - - -NSString * _WebViewJavascriptBridge_js() { -#define __WVJB_js_func__(x) #x - - // BEGIN preprocessorJSCode - static NSString * preprocessorJSCode = @__WVJB_js_func__( - ;(function(window) { - - let printObject = function (obj) { - let output = ""; - if (obj === null) { - output += "null"; - } - else if (typeof(obj) == "undefined") { - output += "undefined"; - } - else if (typeof obj ==='object'){ - output+="{"; - for(let key in obj){ - let value = obj[key]; - output+= "\""+key+"\""+":"+"\""+value+"\""+","; - } - output = output.substr(0, output.length - 1); - output+="}"; - } - else { - output = "" + obj; - } - return output; - }; - console.log = (function (oriLogFunc,printObject) { - return function (str) { - str = printObject(str); - window.webkit.messageHandlers.log.postMessage(str); - oriLogFunc.call(console, str); - } - })(console.log,printObject); - - window.WebViewJavascriptBridge = { - registerHandler: registerHandler, - callHandler: callHandler, - _handleMessageFromObjC: _handleMessageFromObjC - }; - - var sendMessageQueue = []; - var messageHandlers = {}; - var responseCallbacks = {}; - var uniqueId = 1; - - function registerHandler(handlerName, handler) { - messageHandlers[handlerName] = handler; - } - - function callHandler(handlerName, data, responseCallback) { - if (arguments.length === 2 && typeof data == 'function') { - responseCallback = data; - data = null; - } - _doSend({ handlerName:handlerName, data:data }, responseCallback); - } - function _doSend(message, responseCallback) { - if (responseCallback) { - var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime(); - responseCallbacks[callbackId] = responseCallback; - message['callbackId'] = callbackId; - } - sendMessageQueue.push(message); - console.log('__bridge__'+ JSON.stringify(sendMessageQueue)); - sendMessageQueue = []; - } - - function _dispatchMessageFromObjC(messageJSON) { - _doDispatchMessageFromObjC(); - - function _doDispatchMessageFromObjC() { - var message = JSON.parse(messageJSON); - var messageHandler; - var responseCallback; - - if (message.responseId) { - responseCallback = responseCallbacks[message.responseId]; - if (!responseCallback) { - - return; - } - - responseCallback(message.responseData); - delete responseCallbacks[message.responseId]; - } else { - if (message.callbackId) { - var callbackResponseId = message.callbackId; - responseCallback = function(responseData) { - _doSend({ handlerName:message.handlerName, responseId:callbackResponseId, responseData:responseData }); - }; - } - var handler = messageHandlers[message.handlerName]; - if (!handler) { - console.log("WebViewJavascriptBridge: WARNING: no handler for message from ObjC:", message); - } else { - handler(message.data, responseCallback); - } - } - } - } - function _handleMessageFromObjC(messageJSON) { - _dispatchMessageFromObjC(messageJSON); - } - })(window); - ); // END preprocessorJSCode - -#undef __WVJB_js_func__ - return preprocessorJSCode; -}; diff --git a/WebViewJavascriptBridge/WebViewJavascriptBridge.h b/WebViewJavascriptBridge/WebViewJavascriptBridge.h new file mode 100644 index 00000000..002e64b3 --- /dev/null +++ b/WebViewJavascriptBridge/WebViewJavascriptBridge.h @@ -0,0 +1,26 @@ +// +// WebViewJavascriptBridge.h +// TestPod +// +// Created by 侯森魁 on 2020/4/29. +// Copyright © 2020 侯森魁. All rights reserved. +// + +#import +#import "WebViewJavascriptBridgeBase.h" +#import +NS_ASSUME_NONNULL_BEGIN + +@interface WebViewJavascriptBridge : NSObject + ++ (instancetype)bridgeForWebView:(WKWebView*)webView + showJSconsole:(BOOL)show + enableLogging:(BOOL)enable; +- (void)registerHandler:(NSString*)handlerName handler:(nullable WVJBHandler)handler; +- (void)removeHandler:( NSString* )handlerName; +- (void)callHandler:(NSString*)handlerName; +- (void)callHandler:(NSString*)handlerName data:(nullable id)data; +- (void)callHandler:(NSString*)handlerName data:(nullable id)data responseCallback:(nullable WVJBResponseCallback)responseCallback; +@end + +NS_ASSUME_NONNULL_END diff --git a/WebViewJavascriptBridge/WebViewJavascriptBridge.m b/WebViewJavascriptBridge/WebViewJavascriptBridge.m new file mode 100644 index 00000000..79f48f12 --- /dev/null +++ b/WebViewJavascriptBridge/WebViewJavascriptBridge.m @@ -0,0 +1,233 @@ +// +// WebViewJavascriptBridge.m +// TestPod +// +// Created by 侯森魁 on 2020/4/29. +// Copyright © 2020 侯森魁. All rights reserved. +// + +#import "WebViewJavascriptBridge.h" +#import "WebViewJavascriptLeakAvoider.h" +#define kBridgePrefix @"__bridge__" + +@implementation WebViewJavascriptBridge { + WKWebView* _webView; + long _uniqueId; + WebViewJavascriptBridgeBase *_base; + BOOL _showJSconsole; + BOOL _enableLogging; +} + ++ (instancetype)bridgeForWebView:(WKWebView*)webView + showJSconsole:(BOOL)show + enableLogging:(BOOL)enable { + WebViewJavascriptBridge* bridge = [[self alloc] init]; + [bridge _setupInstance:webView showJSconsole:show enableLogging:enable]; + return bridge; +} + +- (void)callHandler:(NSString *)handlerName { + [self callHandler:handlerName data:nil responseCallback:nil]; +} + +- (void)callHandler:(NSString *)handlerName data:(id)data { + [self callHandler:handlerName data:data responseCallback:nil]; +} + +- (void)callHandler:(NSString *)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback { + [_base sendData:data responseCallback:responseCallback handlerName:handlerName]; +} + +- (void)registerHandler:(NSString *)handlerName handler:(WVJBHandler)handler { + _base.messageHandlers[handlerName] = [handler copy]; +} + +- (void)removeHandler:(NSString *)handlerName { + [_base.messageHandlers removeObjectForKey:handlerName]; +} + + +- (void)_setupInstance:(WKWebView*)webView showJSconsole:(BOOL)show enableLogging:(BOOL)enable{ + _webView = webView; + _base = [[WebViewJavascriptBridgeBase alloc] init]; + _base.delegate = self; + _showJSconsole = show; + _enableLogging = enable; + + [self addScriptMessageHandler]; + [self _injectJavascriptFile]; +} +- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message { + NSString * body = (NSString * )message.body; + if ([self _filterMessage:body]) { + NSMutableString *mstr = [NSMutableString stringWithString:body]; + [mstr replaceOccurrencesOfString:kBridgePrefix withString:@"" options:0 range:NSMakeRange(0, 10)]; + [_base flushMessageQueue:mstr]; + } +} +- (void)_injectJavascriptFile { + NSString *bridge_js = WebViewJavascriptBridge_js(); + //injected the method when H5 starts to create the DOM tree + WKUserScript * bridge_userScript = [[WKUserScript alloc]initWithSource:bridge_js injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES]; + [_webView.configuration.userContentController addUserScript:bridge_userScript]; + if (_showJSconsole) { + NSString *console_log_js = WebViewJavascriptBridge_console_log_js(); + WKUserScript * console_log_userScript = [[WKUserScript alloc]initWithSource:console_log_js injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES]; + [_webView.configuration.userContentController addUserScript:console_log_userScript]; + } +} +- (void) addScriptMessageHandler { + [_webView.configuration.userContentController addScriptMessageHandler:[[WebViewJavascriptLeakAvoider alloc]initWithDelegate:self] name:@"pipe"]; +} + +- (void)removeScriptMessageHandler { + [_webView.configuration.userContentController removeScriptMessageHandlerForName:@"pipe"]; +} + +- (NSString*) _evaluateJavascript:(NSString*)javascriptCommand { + [_webView evaluateJavaScript:javascriptCommand completionHandler:nil]; + return NULL; +} + +- (NSString *)_filterMessage:(NSString *) message { + if (_enableLogging) { + NSLog(@"All WVJB RCVD:%@",message); + } + if (message&& [message isKindOfClass:[NSString class]] && [message containsString:kBridgePrefix]) + { + return message; + } + return nil; +} + +- (void)dealloc { + [self removeScriptMessageHandler]; +} + +NSString * WebViewJavascriptBridge_js() { +#define __WVJB_js_func__(x) #x + + // BEGIN preprocessorJSCode + static NSString * preprocessorJSCode = @__WVJB_js_func__( + ;(function(window) { + + window.WebViewJavascriptBridge = { + registerHandler: registerHandler, + callHandler: callHandler, + _handleMessageFromObjC: _handleMessageFromObjC + }; + + var sendMessageQueue = []; + var messageHandlers = {}; + var responseCallbacks = {}; + var uniqueId = 1; + + function registerHandler(handlerName, handler) { + messageHandlers[handlerName] = handler; + } + + function callHandler(handlerName, data, responseCallback) { + if (arguments.length === 2 && typeof data == 'function') { + responseCallback = data; + data = null; + } + _doSend({ handlerName:handlerName, data:data }, responseCallback); + } + function _doSend(message, responseCallback) { + if (responseCallback) { + var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime(); + responseCallbacks[callbackId] = responseCallback; + message['callbackId'] = callbackId; + } + sendMessageQueue.push(message); + window.webkit.messageHandlers.pipe.postMessage('__bridge__'+ JSON.stringify(sendMessageQueue)); + sendMessageQueue = []; + } + + function _dispatchMessageFromObjC(messageJSON) { + _doDispatchMessageFromObjC(); + + function _doDispatchMessageFromObjC() { + var message = JSON.parse(messageJSON); + var messageHandler; + var responseCallback; + + if (message.responseId) { + responseCallback = responseCallbacks[message.responseId]; + if (!responseCallback) { + + return; + } + + responseCallback(message.responseData); + delete responseCallbacks[message.responseId]; + } else { + if (message.callbackId) { + var callbackResponseId = message.callbackId; + responseCallback = function(responseData) { + _doSend({ handlerName:message.handlerName, responseId:callbackResponseId, responseData:responseData }); + }; + } + var handler = messageHandlers[message.handlerName]; + if (!handler) { + console.log("WebViewJavascriptBridge: WARNING: no handler for message from ObjC:", message); + } else { + handler(message.data, responseCallback); + } + } + } + } + function _handleMessageFromObjC(messageJSON) { + _dispatchMessageFromObjC(messageJSON); + } + })(window); + ); // END preprocessorJSCode + +#undef __WVJB_js_func__ + return preprocessorJSCode; +}; + +NSString * WebViewJavascriptBridge_console_log_js() { +#define __WVJB_js_func__(x) #x + + // BEGIN preprocessorJSCode + static NSString * preprocessorJSCode = @__WVJB_js_func__( + ;(function(window) { + let printObject = function (obj) { + let output = ""; + if (obj === null) { + output += "null"; + } + else if (typeof(obj) == "undefined") { + output += "undefined"; + } + else if (typeof obj ==='object'){ + output+="{"; + for(let key in obj){ + let value = obj[key]; + output+= "\""+key+"\""+":"+"\""+value+"\""+","; + } + output = output.substr(0, output.length - 1); + output+="}"; + } + else { + output = "" + obj; + } + return output; + }; + window.console.log = (function (oriLogFunc,printObject) { + return function (str) { + str = printObject(str); + window.webkit.messageHandlers.pipe.postMessage(str); + oriLogFunc.call(window.console, str); + } + })(window.console.log,printObject); + + })(window); + ); // END preprocessorJSCode + +#undef __WVJB_js_func__ + return preprocessorJSCode; +}; + +@end diff --git a/WebViewJavascriptBridge/WebViewJavascriptBridgeBase.h b/WebViewJavascriptBridge/WebViewJavascriptBridgeBase.h new file mode 100644 index 00000000..6b723cde --- /dev/null +++ b/WebViewJavascriptBridge/WebViewJavascriptBridgeBase.h @@ -0,0 +1,29 @@ +// +// WebViewJavascriptBridgeBase.h +// TestPod +// +// Created by 侯森魁 on 2020/4/29. +// Copyright © 2020 侯森魁. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN +@protocol WebViewJavascriptBridgeBaseDelegate +- (NSString*) _evaluateJavascript:(NSString*)javascriptCommand; +@end +typedef NSDictionary WVJBMessage; +typedef void (^WVJBResponseCallback)(id responseData); +typedef void (^WVJBHandler)(id data, WVJBResponseCallback responseCallback); + +@interface WebViewJavascriptBridgeBase : NSObject +@property (weak, nonatomic) id delegate; +@property (strong, nonatomic) NSMutableDictionary* responseCallbacks; +@property (strong, nonatomic) NSMutableDictionary* messageHandlers; +@property (strong, nonatomic) WVJBHandler messageHandler; + +- (void)sendData:(id)data responseCallback:(WVJBResponseCallback)responseCallback handlerName:(NSString*)handlerName; +- (void)flushMessageQueue:(NSString *)messageQueueString; +@end + +NS_ASSUME_NONNULL_END diff --git a/WebViewJavascriptBridge/WebViewJavascriptBridgeBase.m b/WebViewJavascriptBridge/WebViewJavascriptBridgeBase.m new file mode 100644 index 00000000..a519cb68 --- /dev/null +++ b/WebViewJavascriptBridge/WebViewJavascriptBridgeBase.m @@ -0,0 +1,122 @@ +// +// WebViewJavascriptBridgeBase.m +// TestPod +// +// Created by 侯森魁 on 2020/4/29. +// Copyright © 2020 侯森魁. All rights reserved. +// + +#import "WebViewJavascriptBridgeBase.h" +@implementation WebViewJavascriptBridgeBase { + long _uniqueId; +} +- (instancetype)init { + if (self = [super init]) { + self.messageHandlers = [NSMutableDictionary dictionary]; + self.responseCallbacks = [NSMutableDictionary dictionary]; + _uniqueId = 0; + } + return self; +} + +- (void)sendData:(id)data responseCallback:(WVJBResponseCallback)responseCallback handlerName:(NSString*)handlerName { + NSMutableDictionary* message = [NSMutableDictionary dictionary]; + + if (data) { + message[@"data"] = data; + } + + if (responseCallback) { + NSString* callbackId = [NSString stringWithFormat:@"objc_cb_%ld", ++_uniqueId]; + self.responseCallbacks[callbackId] = [responseCallback copy]; + message[@"callbackId"] = callbackId; + } + + if (handlerName) { + message[@"handlerName"] = handlerName; + } + [self _dispatchMessage:message]; +} + +- (void)flushMessageQueue:(NSString *)messageQueueString{ + if (messageQueueString == nil || messageQueueString.length == 0) { + NSLog(@"WebViewJavascriptBridge: WARNING: ObjC got nil while fetching the message queue JSON from webview. This can happen if the WebViewJavascriptBridge JS is not currently present in the webview, e.g if the webview just loaded a new page."); + return; + } + + id messages = [self _deserializeMessageJSON:messageQueueString]; + for (WVJBMessage* message in messages) { + if (![message isKindOfClass:[WVJBMessage class]]) { + NSLog(@"WebViewJavascriptBridge: WARNING: Invalid %@ received: %@", [message class], message); + continue; + } + NSString* responseId = message[@"responseId"]; + if (responseId) { + WVJBResponseCallback responseCallback = _responseCallbacks[responseId]; + responseCallback(message[@"responseData"]); + [self.responseCallbacks removeObjectForKey:responseId]; + } else { + WVJBResponseCallback responseCallback = NULL; + NSString* callbackId = message[@"callbackId"]; + if (callbackId) { + responseCallback = ^(id responseData) { + if (responseData == nil) { + responseData = [NSNull null]; + } + + WVJBMessage* msg = @{ @"responseId":callbackId, @"responseData":responseData }; + [self _dispatchMessage:msg]; + }; + } else { + responseCallback = ^(id ignoreResponseData) { + // Do nothing + }; + } + + WVJBHandler handler = self.messageHandlers[message[@"handlerName"]]; + + if (!handler) { + NSLog(@"WVJBNoHandlerException, No handler for message from JS: %@", message); + continue; + } + + handler(message[@"data"], responseCallback); + } + } +} + +- (NSString *)_serializeMessage:(id)message pretty:(BOOL)pretty{ + return [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:message options:(NSJSONWritingOptions)(pretty ? NSJSONWritingPrettyPrinted : 0) error:nil] encoding:NSUTF8StringEncoding]; +} + +- (NSArray*)_deserializeMessageJSON:(NSString *)messageJSON { + return [NSJSONSerialization JSONObjectWithData:[messageJSON dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:nil]; +} + +- (void) _evaluateJavascript:(NSString *)javascriptCommand { + [self.delegate _evaluateJavascript:javascriptCommand]; +} + +- (void)_dispatchMessage:(WVJBMessage*)message { + NSString *messageJSON = [self _serializeMessage:message pretty:NO]; + messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\\" withString:@"\\\\"]; + messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""]; + messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\'" withString:@"\\\'"]; + messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"]; + messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\r" withString:@"\\r"]; + messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\f" withString:@"\\f"]; + messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\u2028" withString:@"\\u2028"]; + messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\u2029" withString:@"\\u2029"]; + + NSString* javascriptCommand = [NSString stringWithFormat:@"WebViewJavascriptBridge._handleMessageFromObjC('%@');", messageJSON]; + if ([[NSThread currentThread] isMainThread]) { + [self _evaluateJavascript:javascriptCommand]; + + } else { + dispatch_sync(dispatch_get_main_queue(), ^{ + [self _evaluateJavascript:javascriptCommand]; + }); + } +} + +@end diff --git a/WebViewJavascriptBridge/WebViewJavascriptLeakAvoider.h b/WebViewJavascriptBridge/WebViewJavascriptLeakAvoider.h new file mode 100644 index 00000000..59b8d9a2 --- /dev/null +++ b/WebViewJavascriptBridge/WebViewJavascriptLeakAvoider.h @@ -0,0 +1,18 @@ +// +// LeakAvoider.h +// ExampleApp-iOS +// +// Created by 侯森魁 on 2020/4/20. +// Copyright © 2020 Marcus Westin. All rights reserved. +// + +#import +#import +NS_ASSUME_NONNULL_BEGIN + +@interface WebViewJavascriptLeakAvoider : NSObject +@property(nonatomic,weak)id delegate; +- (instancetype)initWithDelegate:(id )delegate; +@end + +NS_ASSUME_NONNULL_END diff --git a/WebViewJavascriptBridge/WebViewJavascriptLeakAvoider.m b/WebViewJavascriptBridge/WebViewJavascriptLeakAvoider.m new file mode 100644 index 00000000..6cbcb906 --- /dev/null +++ b/WebViewJavascriptBridge/WebViewJavascriptLeakAvoider.m @@ -0,0 +1,21 @@ +// +// LeakAvoider.m +// ExampleApp-iOS +// +// Created by 侯森魁 on 2020/4/20. +// Copyright © 2020 Marcus Westin. All rights reserved. +// + +#import "WebViewJavascriptLeakAvoider.h" + +@implementation WebViewJavascriptLeakAvoider +- (instancetype)initWithDelegate:(id )delegate { + if (self = [super init]) { + self.delegate = delegate; + } + return self; +} +- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message { + [self.delegate userContentController:userContentController didReceiveScriptMessage:message]; +} +@end From 3c3fff93fb49bab31acecd7870b7deaa02141bd1 Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Sat, 9 May 2020 12:51:20 +0800 Subject: [PATCH 41/44] Update README.md --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d61b913d..03a6a5f5 100644 --- a/README.md +++ b/README.md @@ -81,10 +81,8 @@ setupWebViewJavascriptBridge(function(bridge) { }) ``` ### Any question. -You can watch this chinese video https://www.youtube.com/watch?v=4JUNQkohh5E. -English video https://www.youtube.com/watch?v=POohaYA-ew0. +English video https://www.youtube.com/watch?v=2Z3852I58OQ. Also you can contact me with:housenkui@gmail.com or WeChat :[housenkui](https://github.com/housenkui/) ### 如果你有疑问. -你可以观看中文版的视频介绍:https://www.youtube.com/watch?v=4JUNQkohh5E -或者英文版的:https://www.youtube.com/watch?v=POohaYA-ew0 https://v.youku.com/v_show/id_XNDYwNzUwODgwNA==.html +你可以观看中文版的视频介绍:https://www.youtube.com/watch?v=ocyKSpMT7tM 当然你也可以邮件联系我:housenkui@gmail.com 或者微信:[housenkui](https://github.com/housenkui/) From cba724f895e798ced13e36ed47b4734e461e71bd Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Tue, 12 May 2020 11:28:01 +0800 Subject: [PATCH 42/44] Update README.md --- README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 03a6a5f5..012f9e9b 100644 --- a/README.md +++ b/README.md @@ -40,10 +40,11 @@ Usage ```objc self.webView = [[WKWebView alloc] initWithFrame:self.view.bounds]; [self.view addSubview:self.webView]; - - _bridge = [WebViewJavascriptBridge bridgeForWebView:self.webView - showJSconsole:YES - enableLogging:YES]; + if(!_bridge){ + _bridge = [WebViewJavascriptBridge bridgeForWebView:self.webView + showJSconsole:YES + enableLogging:YES]; + } ``` 2) Register a handler in ObjC, and call a JS handler: @@ -82,7 +83,7 @@ setupWebViewJavascriptBridge(function(bridge) { ``` ### Any question. English video https://www.youtube.com/watch?v=2Z3852I58OQ. -Also you can contact me with:housenkui@gmail.com or WeChat :[housenkui](https://github.com/housenkui/) +Also you can contact me with:housenkui@gmail.com ### 如果你有疑问. 你可以观看中文版的视频介绍:https://www.youtube.com/watch?v=ocyKSpMT7tM -当然你也可以邮件联系我:housenkui@gmail.com 或者微信:[housenkui](https://github.com/housenkui/) +当然你也可以邮件联系我:housenkui@gmail.com From 66d22a391441c714e873583a47d4495b0cf7e112 Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Fri, 13 May 2022 11:29:52 +0800 Subject: [PATCH 43/44] Update README.md --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 012f9e9b..2595aec0 100644 --- a/README.md +++ b/README.md @@ -81,9 +81,7 @@ setupWebViewJavascriptBridge(function(bridge) { }) }) ``` -### Any question. -English video https://www.youtube.com/watch?v=2Z3852I58OQ. -Also you can contact me with:housenkui@gmail.com + ### 如果你有疑问. 你可以观看中文版的视频介绍:https://www.youtube.com/watch?v=ocyKSpMT7tM 当然你也可以邮件联系我:housenkui@gmail.com From 63ef08930ffcf62c3854cc1dd2e8b9fec43202aa Mon Sep 17 00:00:00 2001 From: Daves_Hou Date: Wed, 18 May 2022 20:09:33 +0800 Subject: [PATCH 44/44] Update README.md --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 2595aec0..673bcf5e 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,3 @@ setupWebViewJavascriptBridge(function(bridge) { }) ``` -### 如果你有疑问. -你可以观看中文版的视频介绍:https://www.youtube.com/watch?v=ocyKSpMT7tM -当然你也可以邮件联系我:housenkui@gmail.com