77
88
99#import " WKWebViewJavascriptBridge.h"
10+ #import " WebViewJavascriptBridgeBase.h"
1011
1112#if defined(supportsWKWebKit)
1213
13- typedef NSDictionary WVJBMessage;
14-
1514@implementation WKWebViewJavascriptBridge {
1615 WKWebView * _webView;
1716 id _webViewDelegate;
18- NSMutableArray * _startupMessageQueue;
19- NSMutableDictionary * _responseCallbacks;
20- NSMutableDictionary * _messageHandlers;
2117 long _uniqueId;
22- WVJBHandler _messageHandler;
23- NSBundle *_resourceBundle;
24- NSUInteger _numRequestsLoading;
18+ WebViewJavascriptBridgeBase *_base;
2519}
2620
2721/* API
2822 *****/
2923
30- static bool logging = false ;
31- + (void )enableLogging { logging = true ; }
24+ + (void )enableLogging { [WebViewJavascriptBridgeBase enableLogging ]; }
3225
3326+ (instancetype )bridgeForWebView : (WKWebView *)webView handler : (WVJBHandler)handler {
3427 return [self bridgeForWebView: webView webViewDelegate: nil handler: handler];
@@ -51,7 +44,7 @@ - (void)send:(id)data {
5144}
5245
5346- (void )send : (id )data responseCallback : (WVJBResponseCallback)responseCallback {
54- [self _sendData: data responseCallback: responseCallback handlerName: nil ];
47+ [_base _sendData: data responseCallback: responseCallback handlerName: nil ];
5548}
5649
5750- (void )callHandler : (NSString *)handlerName {
@@ -63,205 +56,57 @@ - (void)callHandler:(NSString *)handlerName data:(id)data {
6356}
6457
6558- (void )callHandler : (NSString *)handlerName data : (id )data responseCallback : (WVJBResponseCallback)responseCallback {
66- [self _sendData: data responseCallback: responseCallback handlerName: handlerName];
59+ [_base _sendData: data responseCallback: responseCallback handlerName: handlerName];
6760}
6861
6962- (void )registerHandler : (NSString *)handlerName handler : (WVJBHandler)handler {
70- _messageHandlers [handlerName] = [handler copy ];
63+ _base. messageHandlers [handlerName] = [handler copy ];
7164}
7265
7366- (void )reset {
74- _startupMessageQueue = [NSMutableArray array ];
75- _responseCallbacks = [NSMutableDictionary dictionary ];
76- _uniqueId = 0 ;
67+ [_base reset ];
7768}
7869
7970/* Internals
8071 ***********/
8172
8273- (void )dealloc {
83- [self _platformSpecificDealloc ];
84-
74+ _base = nil ;
8575 _webView = nil ;
8676 _webViewDelegate = nil ;
87- _startupMessageQueue = nil ;
88- _responseCallbacks = nil ;
89- _messageHandlers = nil ;
90- _messageHandler = nil ;
91- }
92-
93- - (void )_sendData : (id )data responseCallback : (WVJBResponseCallback)responseCallback handlerName : (NSString *)handlerName {
94- NSMutableDictionary * message = [NSMutableDictionary dictionary ];
95-
96- if (data) {
97- message[@" data" ] = data;
98- }
99-
100- if (responseCallback) {
101- NSString * callbackId = [NSString stringWithFormat: @" objc_cb_%ld " , ++_uniqueId];
102- _responseCallbacks[callbackId] = [responseCallback copy ];
103- message[@" callbackId" ] = callbackId;
104- }
105-
106- if (handlerName) {
107- message[@" handlerName" ] = handlerName;
108- }
109- [self _queueMessage: message];
110- }
111-
112- - (void )_queueMessage : (WVJBMessage*)message {
113- if (_startupMessageQueue) {
114- [_startupMessageQueue addObject: message];
115- } else {
116- [self _dispatchMessage: message];
117- }
118- }
119-
120- - (void )_dispatchMessage : (WVJBMessage*)message {
121- NSString *messageJSON = [self _serializeMessage: message];
122- [self _log: @" SEND" json: messageJSON];
123- messageJSON = [messageJSON stringByReplacingOccurrencesOfString: @" \\ " withString: @" \\\\ " ];
124- messageJSON = [messageJSON stringByReplacingOccurrencesOfString: @" \" " withString: @" \\\" " ];
125- messageJSON = [messageJSON stringByReplacingOccurrencesOfString: @" \' " withString: @" \\\' " ];
126- messageJSON = [messageJSON stringByReplacingOccurrencesOfString: @" \n " withString: @" \\ n" ];
127- messageJSON = [messageJSON stringByReplacingOccurrencesOfString: @" \r " withString: @" \\ r" ];
128- messageJSON = [messageJSON stringByReplacingOccurrencesOfString: @" \f " withString: @" \\ f" ];
129- messageJSON = [messageJSON stringByReplacingOccurrencesOfString: @" \u2028 " withString: @" \\ u2028" ];
130- messageJSON = [messageJSON stringByReplacingOccurrencesOfString: @" \u2029 " withString: @" \\ u2029" ];
131-
132- NSString * javascriptCommand = [NSString stringWithFormat: @" WebViewJavascriptBridge._handleMessageFromObjC('%@ ');" , messageJSON];
133- if ([[NSThread currentThread ] isMainThread ]) {
134- [_webView evaluateJavaScript: javascriptCommand completionHandler: nil ];
135- } else {
136- dispatch_sync (dispatch_get_main_queue (), ^{
137- [_webView evaluateJavaScript: javascriptCommand completionHandler: nil ];
138- });
139- }
140- }
141-
142- - (void )_flushMessageQueue : (NSString *)messageQueueString {
143- id messages = [self _deserializeMessageJSON: messageQueueString];
144- if (![messages isKindOfClass: [NSArray class ]]) {
145- NSLog (@" WKWebViewJavascriptBridge: WARNING: Invalid %@ received: %@ " , [messages class ], messages);
146- return ;
147- }
148- for (WVJBMessage* message in messages) {
149- if (![message isKindOfClass: [WVJBMessage class ]]) {
150- NSLog (@" WKWebViewJavascriptBridge: WARNING: Invalid %@ received: %@ " , [message class ], message);
151- continue ;
152- }
153- [self _log: @" RCVD" json: message];
154-
155- NSString * responseId = message[@" responseId" ];
156- if (responseId) {
157- WVJBResponseCallback responseCallback = _responseCallbacks[responseId];
158- responseCallback (message[@" responseData" ]);
159- [_responseCallbacks removeObjectForKey: responseId];
160- } else {
161- WVJBResponseCallback responseCallback = NULL ;
162- NSString * callbackId = message[@" callbackId" ];
163- if (callbackId) {
164- responseCallback = ^(id responseData) {
165- if (responseData == nil ) {
166- responseData = [NSNull null ];
167- }
168-
169- WVJBMessage* msg = @{ @" responseId" :callbackId, @" responseData" :responseData };
170- [self _queueMessage: msg];
171- };
172- } else {
173- responseCallback = ^(id ignoreResponseData) {
174- // Do nothing
175- };
176- }
177-
178- WVJBHandler handler;
179- if (message[@" handlerName" ]) {
180- handler = _messageHandlers[message[@" handlerName" ]];
181- } else {
182- handler = _messageHandler;
183- }
184-
185- if (!handler) {
186- [NSException raise: @" WVJBNoHandlerException" format: @" No handler for message from JS: %@ " , message];
187- }
188-
189- handler (message[@" data" ], responseCallback);
190- }
191- }
192- }
193-
194- - (NSString *)_serializeMessage : (id )message {
195- return [[NSString alloc ] initWithData: [NSJSONSerialization dataWithJSONObject: message options: 0 error: nil ] encoding: NSUTF8StringEncoding];
196- }
197-
198- - (NSArray *)_deserializeMessageJSON : (NSString *)messageJSON {
199- return [NSJSONSerialization JSONObjectWithData: [messageJSON dataUsingEncoding: NSUTF8StringEncoding] options: NSJSONReadingAllowFragments error: nil ];
200- }
201-
202- - (void )_log : (NSString *)action json : (id )json {
203- if (!logging) { return ; }
204- if (![json isKindOfClass: [NSString class ]]) {
205- json = [self _serializeMessage: json];
206- }
207- if ([json length ] > 500 ) {
208- NSLog (@" WVJB %@ : %@ [...]" , action, [json substringToIndex: 500 ]);
209- } else {
210- NSLog (@" WVJB %@ : %@ " , action, json);
211- }
77+ _webView.navigationDelegate = nil ;
21278}
21379
21480
215-
216-
21781/* WKWebView Specific Internals
21882 ******************************/
21983
22084- (void ) _platformSpecificSetup : (WKWebView *)webView webViewDelegate : (id <WKNavigationDelegate >)webViewDelegate handler : (WVJBHandler)messageHandler resourceBundle : (NSBundle *)bundle {
221- _messageHandler = messageHandler;
22285 _webView = webView;
22386 _webViewDelegate = webViewDelegate;
224- _messageHandlers = [NSMutableDictionary dictionary ];
22587 _webView.navigationDelegate = self;
226- _resourceBundle = bundle;
227- }
228-
229- - (void ) _platformSpecificDealloc {
230- _webView.navigationDelegate = nil ;
88+ _base = [[WebViewJavascriptBridgeBase alloc ] initWithWebViewType: @" WKWebView" handler: (WVJBHandler)messageHandler resourceBundle: (NSBundle *)bundle];
23189}
23290
23391
23492- (void )WKFlushMessageQueue {
235- [_webView evaluateJavaScript: @" WebViewJavascriptBridge._fetchQueue(); " completionHandler: ^(NSString * result, NSError * error) {
236- [self _flushMessageQueue: result];
93+ [_webView evaluateJavaScript: [_base webViewJavascriptFetchQueyCommand ] completionHandler: ^(NSString * result, NSError * error) {
94+ [_base _flushMessageQueue: result];
23795 }];
23896}
23997
24098- (void )webView : (WKWebView *)webView didFinishNavigation : (WKNavigation *)navigation
24199{
242100 if (webView != _webView) { return ; }
101+
102+ _base.numRequestsLoading --;
243103
244- _numRequestsLoading--;
245-
246- if (_numRequestsLoading == 0 ) {
247- [webView evaluateJavaScript: @" typeof WebViewJavascriptBridge == \' object\' ;" completionHandler: ^(NSString *result, NSError *error) {
248- if (![result boolValue ]){
249- NSBundle *bundle = _resourceBundle ? _resourceBundle : [NSBundle mainBundle ];
250- NSString *filePath = [bundle pathForResource: @" WebViewJavascriptBridge.js" ofType: @" txt" ];
251- NSString *js = [NSString stringWithContentsOfFile: filePath encoding: NSUTF8StringEncoding error: nil ];
252- [webView evaluateJavaScript: js completionHandler: nil ];
253-
254- if (_startupMessageQueue) {
255- for (id queuedMessage in _startupMessageQueue) {
256- [self _dispatchMessage: queuedMessage];
257- }
258- _startupMessageQueue = nil ;
259- }
260- }
104+ if (_base.numRequestsLoading == 0 ) {
105+ [webView evaluateJavaScript: [_base webViewJavascriptCheckCommand ] completionHandler: ^(NSString *result, NSError *error) {
106+ [_base injectJavascriptFile: [result boolValue ]];
261107 }];
262108 }
263109
264-
265110 __strong typeof (_webViewDelegate) strongDelegate = _webViewDelegate;
266111 if (strongDelegate && [strongDelegate respondsToSelector: @selector (webView:didFinishNavigation: )]) {
267112 [strongDelegate webView: webView didFinishNavigation: navigation];
@@ -275,11 +120,12 @@ - (void)webView:(WKWebView *)webView
275120 if (webView != _webView) { return ; }
276121 NSURL *url = navigationAction.request .URL ;
277122 __strong typeof (_webViewDelegate) strongDelegate = _webViewDelegate;
278- if ([[url scheme ] isEqualToString: kCustomProtocolScheme ]) {
279- if ([[url host ] isEqualToString: kQueueHasMessage ]) {
123+
124+ if ([_base correctProcotocolScheme: url]) {
125+ if ([_base correctHost: url]) {
280126 [self WKFlushMessageQueue ];
281127 } else {
282- NSLog ( @" WKWebViewJavascriptBridge: WARNING: Received unknown WKWebViewJavascriptBridge command %@ :// %@ " , kCustomProtocolScheme , [ url path ]) ;
128+ [_base logUnkownMessage: url] ;
283129 }
284130 [webView stopLoading ];
285131 }
@@ -294,7 +140,7 @@ - (void)webView:(WKWebView *)webView
294140- (void )webView : (WKWebView *)webView didStartProvisionalNavigation : (WKNavigation *)navigation {
295141 if (webView != _webView) { return ; }
296142
297- _numRequestsLoading ++;
143+ _base. numRequestsLoading ++;
298144
299145 __strong typeof (_webViewDelegate) strongDelegate = _webViewDelegate;
300146 if (strongDelegate && [strongDelegate respondsToSelector: @selector (webView:didStartProvisionalNavigation: )]) {
@@ -306,10 +152,9 @@ - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation
306152- (void )webView : (WKWebView *)webView
307153didFailNavigation : (WKNavigation *)navigation
308154 withError : (NSError *)error {
309-
310155 if (webView != _webView) { return ; }
311156
312- _numRequestsLoading --;
157+ _base. numRequestsLoading --;
313158
314159 __strong typeof (_webViewDelegate) strongDelegate = _webViewDelegate;
315160 if (strongDelegate && [strongDelegate respondsToSelector: @selector (webView:didFailNavigation:withError: )]) {
0 commit comments