You need to get the distant app's NSDocumentController for this to work. Distant objects don't work with classes - they do send back the class, but serve it in JSTalk's space, producing your bug : creating documents in JSTalk rather than in your app. There's a simple fix : box the distant class in a custom object, call your class methods (sharedDocumentController) on this distant object, and let this distant object forward calls to the distant class.
This will require a box class, some additions to your app's NSApplication, and will let you use distant classes like this :
// Get distant NSDocumentController class
[[[sketch NSDocumentController] sharedDocumentController] newDocument:nil]
// Same, but using a standard method call
[[[sketch classNamed:'NSDocumentController'] sharedDocumentController] newDocument:nil]
First, this object boxes a class and forwards its calls to it :
@implementation ClassBox
+ (id)withClass:(Class)c
{
id o = [[self new] autorelease];
[o setValue:c forKey:@"class"];
return o;
}
// Check boxed class for selector response
- (BOOL)respondsToSelector:(SEL)sel
{
BOOL b = [super respondsToSelector:sel];
if (!b)
b = [class respondsToSelector:sel];
return b;
}
// Query boxed class for method signature
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
id sig = [super methodSignatureForSelector:sel];
if (!sig)
{
Method m = class_getClassMethod(class, sel);
if (!m) return nil;
sig = [NSMethodSignature signatureWithObjCTypes:method_getTypeEncoding(m)];
}
return sig;
}
// Invoke boxed class
- (void)forwardInvocation:(NSInvocation *)invocation
{
[invocation setTarget:class];
[invocation invoke];
}
@end
Then, add these methods to NSApplication to get any distant class (boxed). The first method is enough to work by using [sketch classNamed:'....']. The last three treat an unknown selector as a class request and send the class back (boxed) if they find it, letting you call [sketch NSDocumentController].
@implementation NSApplication (ServeClassesToDistantObjects)
// [sketch classNamed:@"NSDocumentController"]
- (id)classNamed:(NSString*)name
{
id class = objc_getClass([name UTF8String]);
if (!class) return nil;
return [ClassBox withClass:class];
}
// The next three methods handle getting a distant class by using its name as selector, eg [sketch NSDocumentController]
// These might need to be swizzled
- (BOOL)respondsToSelector:(SEL)sel
{
BOOL b = [super respondsToSelector:sel];
if (!b)
{
if (objc_getClass([NSStringFromSelector(sel) UTF8String]))
return YES;
}
return b;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
id sig = [super methodSignatureForSelector:sel];
if (!sig)
{
// We're calling a method that doesn't exist, takes no parameters, and returns an object (the class)
// use the signature of -(id)self which does the same thing
if (objc_getClass([NSStringFromSelector(sel) UTF8String]))
return [NSMethodSignature signatureWithObjCTypes:method_getTypeEncoding(class_getInstanceMethod([self class], @selector(self)))];
}
return sig;
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
id class = [self classNamed:NSStringFromSelector([invocation selector])];
[invocation setReturnValue:&class];
}
@end
You might need to swizzle the last three methods as they could interfere with NSApplication's implementations (if they exist).
-Patrick