Overwrite Objective-C methods only on the Javascript side of the bridge?

12 views
Skip to first unread message

Ian Beck

unread,
Jul 28, 2010, 6:43:54 PM7/28/10
to JSCocoa
Hey folks,

Is it possible to override methods for Objective-C classes, but only
on the Javascript side of the bridge? I'm working on making
Javascript plugins in Espresso easier, and I've got an Objective-C
class TextActionContext with two related methods:

- (BOOL)applyTextRecipe:(CETextRecipe *)textRecipe;
- (BOOL)applyTextRecipe:(CETextRecipe *)textRecipe options:
(CETextConvertingOptions)options;

I can call these in Javascript plugins just fine. However, in
Javascript it would make better sense to override the applyTextRecipe
method with one that accepts a variable list of arguments. For
instance, it might look like this (if it were just a generic JS
function):

function applyTextRecipe(textRecipe, options) {
if (arguments.length === 1) {
// call original applyTextRecipe:
} else {
// call applyTextRecipe:options:
}
}

I looked into method swizzling, but that doesn't seem quite right
(since it would affect the Objective-C side of the bridge, too,
although maybe that wouldn't matter since Objective-C would just
always end up with one argument). Does anyone have advice on the best
way to accomplish this?

It might be worth noting that the TextActionContext object is already
initialized when I get to it in Javascript, so if there's a way to
override a method on an initialized object (rather than a class) while
still retaining the ability to call the original that would work
equally well.

Ian

Patrick Geiller

unread,
Jul 30, 2010, 6:41:52 PM7/30/10
to jsc...@googlegroups.com
It might be worth noting that the TextActionContext object is already
initialized when I get to it in Javascript, so if there's a way to
override a method on an initialized object (rather than a class) while
still retaining the ability to call the original that would work
equally well.

Normally setting custom properties on objects is disabled as the Javascript object boxing the ObjC object will go away at some point. I've added a switch to enable it.

Set  __jsc__.canSetOnBoxedObjects = true

Then add a function to your object : 

myTextActionContext.applyTextRecipe = function ()
{
if (arguments.length == 0)
return this.applyTextRecipe_(arguments[0]) // Note the trailing underscore to call the ObjC method
if (arguments.length == 1)
return this.applyTextRecipe_options_(arguments[0], arguments[1])
return nil
}


-Patrick

Ian Beck

unread,
Aug 1, 2010, 4:14:26 PM8/1/10
to JSCocoa
Hey Patrick,

Awesome, this looks like exactly what I need! I'm having a little bit
of trouble getting it to work correctly, but I'll have to check back
after I have a chance to debug the issues Monday.

Ian
> Get the latest JSCocoa, and see the samplehttp://github.com/parmanoir/jscocoa/blob/master/Tests/55%20javascript...
>
> -Patrick

Ian Beck

unread,
Aug 2, 2010, 2:06:02 PM8/2/10
to JSCocoa
Hey Patrick,

I'm not sure why, but although test #55 works fine for me, I can't get
canSetOnBoxedObjects to work in my actual use-case scenario (keep
getting argument mismatch errors because it's going straight to the
Objective-C method and bypassing my Javascript method).

canSetOnBoxedObjects works in general; if I use a name that isn't
shared by an Objective-C method, the functionality is exactly what I
expect. For instance:

// Function is defined higher up in the closure
var insertTextSnippet = function() { ... };

__jsc__.canSetOnBoxedObjects = true;
// This works:
context.insertTextSnippetSpecial = insertTextSnippet;
// This doesn't:
context.insertTextSnippet = insertTextSnippet;

I've tried to find where in the JSCocoa source things are going wonky,
but I'm not sure what functions are being called. Originally, I
thought the problem might be because the test uses an object defined
in JSCocoa (which means it will have the selector JSValueForJSName:
while my Objective-C object does not, allowing the logic around line
3341 in JSCocoaController.m to trigger for the test but not my code),
but if the problem were that my object can't lookup items in its JS
hash then it seems like context.insertTextSnippetSpecial shouldn't
work, either.

Do you have any advice on the best way to debug this problem? I'm
quite willing to hack around in the JSCocoa source, but my
understanding of its underlying logic is still a little fuzzy so any
advice on what method is likely causing the issue would be very
helpful.

Ian

Patrick Geiller

unread,
Aug 3, 2010, 11:10:47 AM8/3/10
to jsc...@googlegroups.com

Sorry, my fault. Indeed ObjC objects not derived by JSCocoa don't go through JSValueForJSName and instead store the value in the object itself. JSCocoa did not retrieve that value and therefore went the usual route of trying to call the selector. Fixed.

> --
> JSCocoa: http://inexdo.com/JSCocoa
> Source: http://github.com/parmanoir/jscocoa/tree/master
> Docs: http://code.google.com/p/jscocoa/
> Group: http://groups.google.com/group/jscocoa
> Unsubscribe: jscocoa+u...@googlegroups.com

Ian Beck

unread,
Aug 3, 2010, 1:42:49 PM8/3/10
to JSCocoa
Hey Patrick,

You. Are. Awesome.

This is working exactly how I need! I'll probably revisit it to see if
I can figure out a nice way to switch custom JS method setting on
without leaving it on, but now that I know where in the code the logic
is taking place that shouldn't be too hard for me to do on my own
(I'll fork the project and contribute the changes if I do end up
working on that).

Thanks for your help!

Ian
Reply all
Reply to author
Forward
0 new messages