Skip to content

Commit 83448f4

Browse files
committed
Restructure of WebViewJavascriptBridge, now the bridges delegate doesn't lose the delegation of the webview, it gets, well, bridged. Modified naming to be more conventional
1 parent 4dbb54e commit 83448f4

File tree

4 files changed

+182
-125
lines changed

4 files changed

+182
-125
lines changed
Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,33 @@
1-
#import <Foundation/Foundation.h>
1+
#import <UIKit/UIKit.h>
22

3-
@protocol WebViewJavascriptBridgeDelegate <NSObject>
3+
@class WebViewJavascriptBridge;
44

5-
- (void) handleMessage:(NSString*) message fromWebView: (UIWebView *)theWebView;
5+
@protocol WebViewJavascriptBridgeDelegate <UIWebViewDelegate>
6+
7+
- (void)javascriptBridge:(WebViewJavascriptBridge *)bridge receivedMessage:(NSString *)message fromWebView:(UIWebView *)webView;
68

79
@end
810

9-
@interface WebViewJavascriptBridge : NSObject <UIWebViewDelegate> {
10-
id <WebViewJavascriptBridgeDelegate> _delegate;
11-
NSMutableArray *_startupMessageQueue;
12-
}
11+
@interface WebViewJavascriptBridge : NSObject <UIWebViewDelegate>
1312

1413
/** Delegate to receive messages from javascript. */
15-
@property (readwrite, assign) id <WebViewJavascriptBridgeDelegate> delegate;
14+
/** Defined as IBOutlet for Interface Builder assignment */
15+
@property (nonatomic, assign) IBOutlet id <WebViewJavascriptBridgeDelegate> delegate;
16+
17+
/** Init with a predefined delegate */
18+
- (id)initWithDelegate:(id <WebViewJavascriptBridgeDelegate>)delegate;
1619

17-
/** Creates & returns new autoreleased javascript Bridge with no delegate set. */
18-
+ (id) javascriptBridge;
20+
/** Convenience methods for obtaining a bridge */
21+
+ (id)javascriptBridge;
22+
+ (id)javascriptBridgeWithDelegate:(id <WebViewJavascriptBridgeDelegate>)delegate;
1923

2024
/** Sends message to given webView. You need to integrate javascript bridge into
2125
* this view before by calling WebViewJavascriptBridge#webViewDidFinishLoad: with that view.
2226
*
23-
* You can call this method before calling webViewDidFinishLoad: , than all messages
27+
* You can call this method before calling webViewDidFinishLoad: , then all messages
2428
* will be accumulated in _startupMessageQueue & sended to webView, provided by first
2529
* webViewDidFinishLoad: call.
2630
*/
27-
- (void) sendMessage:(NSString*) message toWebView:(UIWebView *) theWebView;
31+
- (void)sendMessage:(NSString *)message toWebView:(UIWebView *)webView;
2832

2933
@end

WebViewJavascriptBridge/Classes/WebViewJavascriptBridge.m

Lines changed: 137 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
@interface WebViewJavascriptBridge ()
44

5-
@property (readwrite,retain) NSMutableArray* startupMessageQueue;
5+
@property (nonatomic,strong) NSMutableArray *startupMessageQueue;
66

7-
- (void) _flushMessageQueueFromWebView: (UIWebView *) theWebView;
8-
- (void) _doSendMessage:(NSString*)message toWebView:(UIWebView *) theWebView;
7+
- (void)_flushMessageQueueFromWebView:(UIWebView *)webView;
8+
- (void)_doSendMessage:(NSString*)message toWebView:(UIWebView *)webView;
99

1010
@end
1111

@@ -14,135 +14,176 @@ @implementation WebViewJavascriptBridge
1414
@synthesize delegate = _delegate;
1515
@synthesize startupMessageQueue = _startupMessageQueue;
1616

17-
static NSString* MESSAGE_SEPERATOR = @"__wvjb_sep__";
18-
static NSString* CUSTOM_PROTOCOL_SCHEME = @"webviewjavascriptbridge";
19-
static NSString* QUEUE_HAS_MESSAGE = @"queuehasmessage";
17+
static NSString *MESSAGE_SEPARATOR = @"__wvjb_sep__";
18+
static NSString *CUSTOM_PROTOCOL_SCHEME = @"webviewjavascriptbridge";
19+
static NSString *QUEUE_HAS_MESSAGE = @"queuehasmessage";
2020

21-
+ (id) javascriptBridge
21+
- (id)initWithDelegate:(id <WebViewJavascriptBridgeDelegate>)delegate
2222
{
23-
return [[[self alloc] init] autorelease];
24-
}
25-
26-
- (id) init
27-
{
28-
if ( (self = [super init]) )
23+
if ((self = [super init]) )
2924
{
30-
self.startupMessageQueue = [[NSMutableArray new] autorelease];
25+
self.delegate = delegate;
26+
self.startupMessageQueue = [[[NSMutableArray alloc] init] autorelease];
3127
}
3228

3329
return self;
3430
}
3531

36-
- (void) dealloc
32+
- (id)init
3733
{
38-
self.delegate = nil;
39-
self.startupMessageQueue = nil;
34+
return [[[WebViewJavascriptBridge alloc] initWithDelegate:nil] autorelease];
35+
}
36+
37+
+ (id)javascriptBridgeWithDelegate:(id <WebViewJavascriptBridgeDelegate>)delegate
38+
{
39+
return [[[self alloc] initWithDelegate:delegate] autorelease];
40+
}
41+
42+
+ (id)javascriptBridge
43+
{
44+
return [[[self alloc] init] autorelease];
45+
}
46+
47+
- (void)dealloc
48+
{
49+
_delegate = nil;
50+
[_startupMessageQueue release];
4051

4152
[super dealloc];
4253
}
4354

44-
- (void)sendMessage:(NSString *)message toWebView: (UIWebView *) theWebView {
55+
- (void)sendMessage:(NSString *)message toWebView:(UIWebView *)webView {
4556
if (self.startupMessageQueue) { [self.startupMessageQueue addObject:message]; }
46-
else { [self _doSendMessage:message toWebView: theWebView]; }
57+
else { [self _doSendMessage:message toWebView: webView]; }
4758
}
4859

49-
- (void)_doSendMessage:(NSString *)message toWebView: (UIWebView *) aWebView {
60+
- (void)_doSendMessage:(NSString *)message toWebView:(UIWebView *)webView {
5061
message = [message stringByReplacingOccurrencesOfString:@"\\n" withString:@"\\\\n"];
5162
message = [message stringByReplacingOccurrencesOfString:@"'" withString:@"\\'"];
5263
message = [message stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""];
53-
[aWebView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"WebViewJavascriptBridge._handleMessageFromObjC('%@');", message]];
64+
[webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"WebViewJavascriptBridge._handleMessageFromObjC('%@');", message]];
65+
}
66+
67+
- (void)_flushMessageQueueFromWebView:(UIWebView *)webView {
68+
NSString *messageQueueString = [webView stringByEvaluatingJavaScriptFromString:@"WebViewJavascriptBridge._fetchQueue();"];
69+
NSArray* messages = [messageQueueString componentsSeparatedByString:MESSAGE_SEPARATOR];
70+
for (id message in messages) {
71+
[self.delegate javascriptBridge:self receivedMessage:message fromWebView:webView];
72+
}
5473
}
5574

56-
- (void)webViewDidFinishLoad:(UIWebView *)theWebView {
57-
NSString* js;
58-
js = [NSString stringWithFormat:@";(function() {"
59-
"if (window.WebViewJavascriptBridge) { return; };"
60-
"var _readyMessageIframe,"
61-
" _sendMessageQueue = [],"
62-
" _receiveMessageQueue = [],"
63-
" _MESSAGE_SEPERATOR = '%@',"
64-
" _CUSTOM_PROTOCOL_SCHEME = '%@',"
65-
" _QUEUE_HAS_MESSAGE = '%@';"
66-
""
67-
"function _createQueueReadyIframe(doc) {"
68-
" _readyMessageIframe = doc.createElement('iframe');"
69-
" _readyMessageIframe.style.display = 'none';"
70-
" doc.documentElement.appendChild(_readyMessageIframe);"
71-
"}"
72-
""
73-
"function _sendMessage(message) {"
74-
" _sendMessageQueue.push(message);"
75-
" _readyMessageIframe.src = _CUSTOM_PROTOCOL_SCHEME + '://' + _QUEUE_HAS_MESSAGE;"
76-
"};"
77-
""
78-
"function _fetchQueue() {"
79-
" var messageQueueString = _sendMessageQueue.join(_MESSAGE_SEPERATOR);"
80-
" _sendMessageQueue = [];"
81-
" return messageQueueString;"
82-
"};"
83-
""
84-
"function _setMessageHandler(messageHandler) {"
85-
" if (WebViewJavascriptBridge._messageHandler) { return alert('WebViewJavascriptBridge.setMessageHandler called twice'); }"
86-
" WebViewJavascriptBridge._messageHandler = messageHandler;"
87-
" var receivedMessages = _receiveMessageQueue;"
88-
" _receiveMessageQueue = null;"
89-
" for (var i=0; i<receivedMessages.length; i++) {"
90-
" messageHandler(receivedMessages[i]);"
91-
" }"
92-
"};"
93-
""
94-
"function _handleMessageFromObjC(message) {"
95-
" if (_receiveMessageQueue) { _receiveMessageQueue.push(message); }"
96-
" else { WebViewJavascriptBridge._messageHandler(message); }"
97-
"};"
98-
""
99-
"window.WebViewJavascriptBridge = {"
100-
" setMessageHandler: _setMessageHandler,"
101-
" sendMessage: _sendMessage,"
102-
" _fetchQueue: _fetchQueue,"
103-
" _handleMessageFromObjC: _handleMessageFromObjC"
104-
"};"
105-
""
106-
"setTimeout(function() {"
107-
" var doc = document;"
108-
" _createQueueReadyIframe(doc);"
109-
" var readyEvent = doc.createEvent('Events');"
110-
" readyEvent.initEvent('WebViewJavascriptBridgeReady');"
111-
" doc.dispatchEvent(readyEvent);"
112-
"}, 0);"
113-
"})();",
114-
MESSAGE_SEPERATOR,
115-
CUSTOM_PROTOCOL_SCHEME,
116-
QUEUE_HAS_MESSAGE];
75+
#pragma mark UIWebViewDelegate
76+
77+
- (void)webViewDidFinishLoad:(UIWebView *)webView {
78+
NSString *js = [NSString stringWithFormat:@";(function() {"
79+
"if (window.WebViewJavascriptBridge) { return; };"
80+
"var _readyMessageIframe,"
81+
" _sendMessageQueue = [],"
82+
" _receiveMessageQueue = [],"
83+
" _MESSAGE_SEPERATOR = '%@',"
84+
" _CUSTOM_PROTOCOL_SCHEME = '%@',"
85+
" _QUEUE_HAS_MESSAGE = '%@';"
86+
""
87+
"function _createQueueReadyIframe(doc) {"
88+
" _readyMessageIframe = doc.createElement('iframe');"
89+
" _readyMessageIframe.style.display = 'none';"
90+
" doc.documentElement.appendChild(_readyMessageIframe);"
91+
"}"
92+
""
93+
"function _sendMessage(message) {"
94+
" _sendMessageQueue.push(message);"
95+
" _readyMessageIframe.src = _CUSTOM_PROTOCOL_SCHEME + '://' + _QUEUE_HAS_MESSAGE;"
96+
"};"
97+
""
98+
"function _fetchQueue() {"
99+
" var messageQueueString = _sendMessageQueue.join(_MESSAGE_SEPERATOR);"
100+
" _sendMessageQueue = [];"
101+
" return messageQueueString;"
102+
"};"
103+
""
104+
"function _setMessageHandler(messageHandler) {"
105+
" if (WebViewJavascriptBridge._messageHandler) { return alert('WebViewJavascriptBridge.setMessageHandler called twice'); }"
106+
" WebViewJavascriptBridge._messageHandler = messageHandler;"
107+
" var receivedMessages = _receiveMessageQueue;"
108+
" _receiveMessageQueue = null;"
109+
" for (var i=0; i<receivedMessages.length; i++) {"
110+
" messageHandler(receivedMessages[i]);"
111+
" }"
112+
"};"
113+
""
114+
"function _handleMessageFromObjC(message) {"
115+
" if (_receiveMessageQueue) { _receiveMessageQueue.push(message); }"
116+
" else { WebViewJavascriptBridge._messageHandler(message); }"
117+
"};"
118+
""
119+
"window.WebViewJavascriptBridge = {"
120+
" setMessageHandler: _setMessageHandler,"
121+
" sendMessage: _sendMessage,"
122+
" _fetchQueue: _fetchQueue,"
123+
" _handleMessageFromObjC: _handleMessageFromObjC"
124+
"};"
125+
""
126+
"setTimeout(function() {"
127+
" var doc = document;"
128+
" _createQueueReadyIframe(doc);"
129+
" var readyEvent = doc.createEvent('Events');"
130+
" readyEvent.initEvent('WebViewJavascriptBridgeReady');"
131+
" doc.dispatchEvent(readyEvent);"
132+
"}, 0);"
133+
"})();",
134+
MESSAGE_SEPARATOR,
135+
CUSTOM_PROTOCOL_SCHEME,
136+
QUEUE_HAS_MESSAGE];
117137

118-
[theWebView stringByEvaluatingJavaScriptFromString:js];
138+
[webView stringByEvaluatingJavaScriptFromString:js];
119139

120140
for (id message in self.startupMessageQueue) {
121-
[self _doSendMessage:message toWebView: theWebView];
141+
[self _doSendMessage:message toWebView: webView];
122142
}
143+
123144
self.startupMessageQueue = nil;
145+
146+
if(self.delegate != nil && [self.delegate respondsToSelector:@selector(webViewDidFinishLoad:)])
147+
{
148+
[self.delegate webViewDidFinishLoad:webView];
149+
}
124150
}
125151

152+
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
153+
{
154+
if(self.delegate != nil && [self.delegate respondsToSelector:@selector(webView:didFailLoadWithError:)])
155+
{
156+
[self.delegate webView:webView didFailLoadWithError:error];
157+
}
158+
}
126159

127-
- (BOOL)webView:(UIWebView *)theWebView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
160+
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
128161
NSURL *url = [request URL];
129-
if (![[url scheme] isEqualToString:CUSTOM_PROTOCOL_SCHEME]) { return YES; }
130-
162+
if (![[url scheme] isEqualToString:CUSTOM_PROTOCOL_SCHEME])
163+
{
164+
if(self.delegate != nil && [self.delegate respondsToSelector:@selector(webView:shouldStartLoadWithRequest:navigationType:)])
165+
{
166+
[self.delegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType];
167+
}
168+
return YES;
169+
}
170+
131171
if ([[url host] isEqualToString:QUEUE_HAS_MESSAGE]) {
132-
[self _flushMessageQueueFromWebView: theWebView];
133-
} else {
134-
NSLog(@"WARNING: Received unknown WebViewJavascriptBridge command %@://%@", CUSTOM_PROTOCOL_SCHEME, [url path]);
172+
[self _flushMessageQueueFromWebView: webView];
173+
}
174+
else {
175+
NSLog(@"WebViewJavascriptBridge: WARNING: Received unknown WebViewJavascriptBridge command %@://%@", CUSTOM_PROTOCOL_SCHEME, [url path]);
135176
}
136177

137178
return NO;
138179
}
139180

140-
- (void) _flushMessageQueueFromWebView: (UIWebView *) theWebView {
141-
NSString* messageQueueString = [theWebView stringByEvaluatingJavaScriptFromString:@"WebViewJavascriptBridge._fetchQueue();"];
142-
NSArray* messages = [messageQueueString componentsSeparatedByString:MESSAGE_SEPERATOR];
143-
for (id message in messages) {
144-
[self.delegate handleMessage:message fromWebView: theWebView];
145-
}
181+
- (void)webViewDidStartLoad:(UIWebView *)webView
182+
{
183+
if(self.delegate != nil && [self.delegate respondsToSelector:@selector(webViewDidStartLoad:)])
184+
{
185+
[self.delegate webViewDidStartLoad:webView];
186+
}
146187
}
147188

148189
@end

WebViewJavascriptBridge/ExampleAppDelegate.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@
77
@property (strong, nonatomic) UIWebView *webView;
88
@property (strong, nonatomic) WebViewJavascriptBridge *javascriptBridge;
99

10-
- (void) loadExamplePage;
10+
- (void)loadExamplePage;
1111

1212
@end

0 commit comments

Comments
 (0)