Skip to content

Commit 5472585

Browse files
author
Andrew Goodale
committed
First working implementation of callbacks from script.
1 parent d39ee31 commit 5472585

7 files changed

Lines changed: 223 additions & 1 deletion

File tree

Classes/GAScriptEngine.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,15 @@
3838
{
3939
@private
4040
UIWebView* m_webView;
41+
42+
NSMutableArray* m_receivers;
4143
}
4244

45+
/**
46+
* An array of objects that take callbacks from JavaScript code.
47+
*/
48+
@property (nonatomic, retain) NSMutableArray* receivers;
49+
4350
/**
4451
* The designated initializer.
4552
*/

Classes/GAScriptEngine.m

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,18 +37,26 @@ @interface GAScriptEngine ()
3737
*/
3838
- (void)loadScriptRuntime;
3939

40+
- (void)makeLotsaCalls;
41+
42+
- (void)callReceiversForSelector:(SEL)theSelector withArguments:(NSArray *)arguments;
43+
4044
@end
4145

4246
#pragma mark -
4347

4448
@implementation GAScriptEngine
4549

50+
@synthesize receivers = m_receivers;
51+
4652
- (id)initWithWebView:(UIWebView *)webView
4753
{
4854
if ((self = [super init]))
4955
{
5056
m_webView = [webView retain];
5157
m_webView.delegate = self;
58+
59+
m_receivers = [[NSMutableArray alloc] initWithCapacity:4];
5260
}
5361

5462
return self;
@@ -57,6 +65,7 @@ - (id)initWithWebView:(UIWebView *)webView
5765
- (void)dealloc
5866
{
5967
[m_webView release];
68+
[m_receivers release];
6069

6170
[super dealloc];
6271
}
@@ -97,6 +106,51 @@ - (void)loadScriptRuntime
97106
[m_webView stringByEvaluatingJavaScriptFromString:scriptData];
98107
}
99108

109+
- (void)makeLotsaCalls
110+
{
111+
id calls = [self scriptObjectWithReference:@"GAJavaScript.calls"];
112+
NSNumber* numCalls = [calls valueForKey:@"length"];
113+
calls = [calls callFunction:@"splice"
114+
withArguments:[NSArray arrayWithObjects:[NSNumber numberWithInt:0], numCalls, nil]];
115+
116+
for (NSInteger i = 0; i < [calls count]; ++i)
117+
{
118+
id call = [calls objectAtIndex:i];
119+
120+
NSString* selName = [call valueForKey:@"sel"];
121+
NSArray* arguments = [call valueForKey:@"args"];
122+
SEL theSelector = NSSelectorFromString(selName);
123+
124+
[self callReceiversForSelector:theSelector withArguments:arguments];
125+
}
126+
}
127+
128+
- (void)callReceiversForSelector:(SEL)theSelector withArguments:(NSArray *)arguments
129+
{
130+
// The first argument is the selector name, so ignore it
131+
//
132+
arguments = [arguments subarrayWithRange:NSMakeRange(1, [arguments count] - 1)];
133+
134+
for (id receiver in self.receivers)
135+
{
136+
if (![receiver respondsToSelector:theSelector])
137+
continue;
138+
139+
NSMethodSignature* methodSig = [receiver methodSignatureForSelector:theSelector];
140+
NSInvocation* inv = [NSInvocation invocationWithMethodSignature:methodSig];
141+
NSInteger argIndex = 2;
142+
143+
for (id arg in arguments)
144+
[inv setArgument:&arg atIndex:argIndex++];
145+
146+
[inv setSelector:theSelector];
147+
[inv invokeWithTarget:receiver];
148+
149+
// Ignore return values...
150+
break;
151+
}
152+
}
153+
100154
#pragma mark UIWebViewDelegate
101155

102156
- (void)webViewDidFinishLoad:(UIWebView *)webView
@@ -108,6 +162,19 @@ - (void)webViewDidFinishLoad:(UIWebView *)webView
108162
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request
109163
navigationType:(UIWebViewNavigationType)navigationType
110164
{
165+
NSURL* theUrl = [request URL];
166+
167+
if ([[theUrl scheme] isEqualToString:@"ga-js"])
168+
{
169+
if ([[theUrl resourceSpecifier] isEqualToString:@"makeLotsaCalls"])
170+
{
171+
[self makeLotsaCalls];
172+
}
173+
174+
return NO;
175+
}
176+
177+
// TODO: Only YES for the inital HTML page
111178
return YES;
112179
}
113180

GAJavaScript.xcodeproj/project.pbxproj

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

99
/* Begin PBXBuildFile section */
10+
2D5994871382B0F0006B728D /* TScriptEngine.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D5994861382B0F0006B728D /* TScriptEngine.m */; };
1011
2D8500AE1381647F00758EA2 /* GAScriptEngine.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D8500AC1381647F00758EA2 /* GAScriptEngine.h */; };
1112
2D8500AF1381647F00758EA2 /* GAScriptEngine.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D8500AD1381647F00758EA2 /* GAScriptEngine.m */; };
1213
2D8500B213816B2100758EA2 /* TestWebViewContent.html in Resources */ = {isa = PBXBuildFile; fileRef = 2D8500B113816B2100758EA2 /* TestWebViewContent.html */; };
@@ -40,6 +41,8 @@
4041
/* End PBXContainerItemProxy section */
4142

4243
/* Begin PBXFileReference section */
44+
2D5994851382B0F0006B728D /* TScriptEngine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TScriptEngine.h; path = Tests/TScriptEngine.h; sourceTree = "<group>"; };
45+
2D5994861382B0F0006B728D /* TScriptEngine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TScriptEngine.m; path = Tests/TScriptEngine.m; sourceTree = "<group>"; };
4346
2D8500AC1381647F00758EA2 /* GAScriptEngine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GAScriptEngine.h; path = Classes/GAScriptEngine.h; sourceTree = "<group>"; };
4447
2D8500AD1381647F00758EA2 /* GAScriptEngine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GAScriptEngine.m; path = Classes/GAScriptEngine.m; sourceTree = "<group>"; };
4548
2D8500B113816B2100758EA2 /* TestWebViewContent.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; name = TestWebViewContent.html; path = Tests/TestWebViewContent.html; sourceTree = "<group>"; };
@@ -154,6 +157,8 @@
154157
4983385E13782DFC0006B26A /* ApplicationDelegate.m */,
155158
4933848412B4373600FBE23C /* TScriptObject.h */,
156159
4933848512B4373600FBE23C /* TScriptObject.m */,
160+
2D5994851382B0F0006B728D /* TScriptEngine.h */,
161+
2D5994861382B0F0006B728D /* TScriptEngine.m */,
157162
4933870E12B58C5700FBE23C /* TWebView.h */,
158163
4933870F12B58C5700FBE23C /* TWebView.m */,
159164
493383A012B4338200FBE23C /* GAJavaScriptTests-Info.plist */,
@@ -262,6 +267,7 @@
262267
4933848612B4373600FBE23C /* TScriptObject.m in Sources */,
263268
4933871412B58C9E00FBE23C /* TWebView.m in Sources */,
264269
4983385F13782DFC0006B26A /* ApplicationDelegate.m in Sources */,
270+
2D5994871382B0F0006B728D /* TScriptEngine.m in Sources */,
265271
);
266272
runOnlyForDeploymentPostprocessing = 0;
267273
};

Tests/TScriptEngine.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
Copyright (c) 2011 Andrew Goodale. All rights reserved.
3+
4+
Redistribution and use in source and binary forms, with or without modification, are
5+
permitted provided that the following conditions are met:
6+
7+
1. Redistributions of source code must retain the above copyright notice, this list of
8+
conditions and the following disclaimer.
9+
10+
2. Redistributions in binary form must reproduce the above copyright notice, this list
11+
of conditions and the following disclaimer in the documentation and/or other materials
12+
provided with the distribution.
13+
14+
THIS SOFTWARE IS PROVIDED BY ANDREW GOODALE "AS IS" AND ANY EXPRESS OR IMPLIED
15+
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16+
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
17+
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22+
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23+
24+
The views and conclusions contained in the software and documentation are those of the
25+
authors and should not be interpreted as representing official policies, either expressed
26+
or implied, of Andrew Goodale.
27+
*/
28+
29+
#import <GHUnitIOS/GHUnit.h>
30+
31+
@class GAScriptEngine;
32+
33+
@interface TScriptEngine : GHTestCase
34+
{
35+
GAScriptEngine* _engine;
36+
}
37+
38+
@end

Tests/TScriptEngine.m

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
Copyright (c) 2011 Andrew Goodale. All rights reserved.
3+
4+
Redistribution and use in source and binary forms, with or without modification, are
5+
permitted provided that the following conditions are met:
6+
7+
1. Redistributions of source code must retain the above copyright notice, this list of
8+
conditions and the following disclaimer.
9+
10+
2. Redistributions in binary form must reproduce the above copyright notice, this list
11+
of conditions and the following disclaimer in the documentation and/or other materials
12+
provided with the distribution.
13+
14+
THIS SOFTWARE IS PROVIDED BY ANDREW GOODALE "AS IS" AND ANY EXPRESS OR IMPLIED
15+
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16+
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
17+
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22+
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23+
24+
The views and conclusions contained in the software and documentation are those of the
25+
authors and should not be interpreted as representing official policies, either expressed
26+
or implied, of Andrew Goodale.
27+
*/
28+
29+
#import "TScriptEngine.h"
30+
#import "GAScriptEngine.h"
31+
32+
@implementation TScriptEngine
33+
34+
- (BOOL)shouldRunOnMainThread
35+
{
36+
// By default NO, but if you have a UI test or test dependent on running on the main thread return YES
37+
return YES;
38+
}
39+
40+
- (void)setUp
41+
{
42+
id appDelegate = [[UIApplication sharedApplication] delegate];
43+
44+
_engine = [appDelegate valueForKey:@"scriptEngine"];
45+
[_engine.receivers addObject:self];
46+
}
47+
48+
- (void)testCallback
49+
{
50+
[_engine callFunction:@"testCallback"];
51+
}
52+
53+
- (void)testCallbackOneArg
54+
{
55+
[_engine callFunction:@"testCallbackOneArg"];
56+
}
57+
58+
- (void)testMultipleCallbacks
59+
{
60+
[_engine callFunction:@"testMultipleCallbacks"];
61+
}
62+
63+
- (void)callbackNoArgs
64+
{
65+
NSLog(@"Callback() from JavaScript");
66+
}
67+
68+
- (void)callbackOneArg:(NSString *)theArgument
69+
{
70+
NSLog(@"Callback(%@) from JavaScript", theArgument);
71+
}
72+
73+
@end

Tests/TestWebViewContent.html

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,32 @@
55

66
function CustomTestObject () {
77
this.prop1 = "prop1";
8+
this.length = 29;
89
this.stuff = [];
910
}
1011

1112
CustomTestObject.prototype.addObject = function (add) {
1213
this.stuff.push(add);
1314
};
1415
</script>
16+
<script type="text/javascript">
17+
18+
// Unit Test functions
19+
20+
// This tests a simple call with no arguments
21+
function testCallback () {
22+
GAJavaScript.performSelector('callbackNoArgs');
23+
}
24+
25+
function testCallbackOneArg () {
26+
GAJavaScript.performSelector('callbackOneArg:', "Hello World");
27+
}
28+
29+
function testMultipleCallbacks () {
30+
GAJavaScript.performSelector('callbackNoArgs');
31+
GAJavaScript.performSelector('callbackOneArg:', "2nd call in testMultipleCallbacks");
32+
}
33+
</script>
1534
</head>
1635
<body>
1736
<p>Hello World</p>

ga-js-runtime.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ GAJavaScript = {
3131
ref: {
3232
index: 0
3333
},
34+
35+
calls: [],
3436

3537
typeOf: function (value) {
3638
var t = typeof value;
@@ -115,5 +117,15 @@ GAJavaScript = {
115117

116118
this.ref[key] = obj;
117119
return "GAJavaScript.ref['" + key + "']";
118-
}
120+
},
121+
122+
performSelector: function (selName) {
123+
var newCall = {
124+
sel: selName,
125+
args: Array.prototype.slice.call(arguments) // Converts it to a true Array
126+
}
127+
this.calls.push(newCall);
128+
129+
location.replace("ga-js:makeLotsaCalls");
130+
}
119131
};

0 commit comments

Comments
 (0)