Skip to content

Commit 859c823

Browse files
committed
Added JavascriptWebViewBridgeBase to WKWebViewJavascriptBridge
1 parent 95779d0 commit 859c823

File tree

5 files changed

+308
-181
lines changed

5 files changed

+308
-181
lines changed

Example Apps/ExampleApp-iOS.xcodeproj/project.pbxproj

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10+
0E50601C1A01B442000BEEEA /* WebViewJavascriptBridgeBase.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E50601B1A01B442000BEEEA /* WebViewJavascriptBridgeBase.m */; };
1011
0E8082DB19EDC32300479452 /* WKWebViewJavascriptBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E8082DA19EDC32300479452 /* WKWebViewJavascriptBridge.m */; };
1112
2C1562B5176B9F8400B4AE50 /* WebViewJavascriptBridge.js.txt in Resources */ = {isa = PBXBuildFile; fileRef = 2C1562B4176B9F8400B4AE50 /* WebViewJavascriptBridge.js.txt */; };
1213
2C1562C0176BA63500B4AE50 /* WebViewJavascriptBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C1562A9176B9F6200B4AE50 /* WebViewJavascriptBridge.m */; };
@@ -22,6 +23,8 @@
2223
/* End PBXBuildFile section */
2324

2425
/* Begin PBXFileReference section */
26+
0E50601B1A01B442000BEEEA /* WebViewJavascriptBridgeBase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WebViewJavascriptBridgeBase.m; sourceTree = "<group>"; };
27+
0E50601D1A01B44C000BEEEA /* WebViewJavascriptBridgeBase.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WebViewJavascriptBridgeBase.h; sourceTree = "<group>"; };
2528
0E8082D919EDC32300479452 /* WKWebViewJavascriptBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKWebViewJavascriptBridge.h; sourceTree = "<group>"; };
2629
0E8082DA19EDC32300479452 /* WKWebViewJavascriptBridge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WKWebViewJavascriptBridge.m; sourceTree = "<group>"; };
2730
0E8082DC19EDD98700479452 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; };
@@ -64,8 +67,10 @@
6467
2C1562B4176B9F8400B4AE50 /* WebViewJavascriptBridge.js.txt */,
6568
2C1562A8176B9F6200B4AE50 /* WebViewJavascriptBridge.h */,
6669
2C1562A9176B9F6200B4AE50 /* WebViewJavascriptBridge.m */,
67-
0E8082D919EDC32300479452 /* WKWebViewJavascriptBridge.h */,
6870
0E8082DA19EDC32300479452 /* WKWebViewJavascriptBridge.m */,
71+
0E8082D919EDC32300479452 /* WKWebViewJavascriptBridge.h */,
72+
0E50601B1A01B442000BEEEA /* WebViewJavascriptBridgeBase.m */,
73+
0E50601D1A01B44C000BEEEA /* WebViewJavascriptBridgeBase.h */,
6974
);
7075
name = WebViewJavascriptBridge;
7176
path = ../../WebViewJavascriptBridge;
@@ -194,6 +199,7 @@
194199
0E8082DB19EDC32300479452 /* WKWebViewJavascriptBridge.m in Sources */,
195200
2C45CA2C1884AD520002A4E2 /* ExampleAppViewController.m in Sources */,
196201
2CA045C217117439006DEE8B /* ExampleAppDelegate.m in Sources */,
202+
0E50601C1A01B442000BEEEA /* WebViewJavascriptBridgeBase.m in Sources */,
197203
2CA045C317117439006DEE8B /* main.m in Sources */,
198204
);
199205
runOnlyForDeploymentPostprocessing = 0;

WebViewJavascriptBridge/WKWebViewJavascriptBridge.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212
#if defined(supportsWKWebKit )
1313

1414
#import <Foundation/Foundation.h>
15-
#define kCustomProtocolScheme @"wvjbscheme"
16-
#define kQueueHasMessage @"__WVJB_QUEUE_MESSAGE__"
1715

1816
#import <WebKit/WebKit.h>
1917
typedef void (^WVJBResponseCallback)(id responseData);

WebViewJavascriptBridge/WKWebViewJavascriptBridge.m

Lines changed: 23 additions & 178 deletions
Original file line numberDiff line numberDiff line change
@@ -7,28 +7,21 @@
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
307153
didFailNavigation:(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:)]) {
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//
2+
// WebViewJavascriptBridgeBase.h
3+
// ExampleApp-iOS
4+
//
5+
// Created by Loki Meyburg on 2014-10-29.
6+
// Copyright (c) 2014 Marcus Westin. All rights reserved.
7+
//
8+
#import <Foundation/Foundation.h>
9+
10+
#define kCustomProtocolScheme @"wvjbscheme"
11+
#define kQueueHasMessage @"__WVJB_QUEUE_MESSAGE__"
12+
13+
typedef void (^WVJBResponseCallback)(id responseData);
14+
typedef void (^WVJBHandler)(id data, WVJBResponseCallback responseCallback);
15+
typedef NSDictionary WVJBMessage;
16+
17+
@interface WebViewJavascriptBridgeBase : NSObject
18+
19+
@property (strong, nonatomic) NSMutableArray* startupMessageQueue;
20+
@property (strong, nonatomic) NSMutableDictionary* responseCallbacks;
21+
@property (strong, nonatomic) NSMutableDictionary* messageHandlers;
22+
@property (strong, nonatomic) WVJBHandler messageHandler;
23+
@property NSUInteger numRequestsLoading;
24+
25+
26+
+ (void)enableLogging;
27+
-(id)initWithWebViewType:(NSString*)webViewType handler:(WVJBHandler)messageHandler resourceBundle:(NSBundle*)bundle;
28+
-(void)reset;
29+
- (void)_sendData:(id)data responseCallback:(WVJBResponseCallback)responseCallback handlerName:(NSString*)handlerName;
30+
- (void)_queueMessage:(WVJBMessage*)message;
31+
- (void)_dispatchMessage:(WVJBMessage*)message;
32+
- (void)_flushMessageQueue:(NSString *)messageQueueString;
33+
34+
// specific extractions
35+
- (void) injectJavascriptFile:(BOOL)shouldInject;
36+
-(BOOL) correctProcotocolScheme:(NSURL*)url;
37+
-(BOOL) correctHost:(NSURL*)urll;
38+
-(void) logUnkownMessage:(NSURL*)url;
39+
-(NSString *) webViewJavascriptCheckCommand;
40+
-(NSString *) webViewJavascriptFetchQueyCommand;
41+
42+
43+
// probably dont need to be public
44+
- (NSString *)_serializeMessage:(id)message;
45+
- (NSArray*)_deserializeMessageJSON:(NSString *)messageJSON;
46+
- (void)_log:(NSString *)action json:(id)json;
47+
48+
49+
@end

0 commit comments

Comments
 (0)