Skip to content

Instantly share code, notes, and snippets.

@Frityet
Created March 26, 2026 23:50
Show Gist options
  • Select an option

  • Save Frityet/fd1d3f7056506bd2015e84396c1bf078 to your computer and use it in GitHub Desktop.

Select an option

Save Frityet/fd1d3f7056506bd2015e84396c1bf078 to your computer and use it in GitHub Desktop.
#import "jsobjc.h"
#import "dom.generated.h"
#include <emscripten/emscripten.h>
EM_JS(void, install_demo_interop, (), {
class BaseGreeter {
constructor(name) {
this.name = name;
}
print() {
globalThis.console.log("BaseGreeter:", this.name);
}
}
globalThis.BaseGreeter = BaseGreeter;
globalThis.doSomething = function(cl) {
const value = new cl("bob");
value.print();
return value.name;
};
globalThis.makeJSSubclassInstance = function(cl) {
class JSGreeter extends cl {
print() {
super.print();
globalThis.console.log("JS subclass:", this.name);
}
}
return new JSGreeter("jane");
};
globalThis.useObjCSubclassOfJS = function(cl) {
const value = new cl("erin");
value.print();
return value.name;
};
});
@class MyClass;
@class ObjCGreeter;
@interface Window (DemoInterop)
- (String *)doSomething:(Class)classValue;
- (MyClass *)makeJSSubclassInstance:(Class)classValue;
- (String *)useObjCSubclassOfJS:(Class)classValue;
@end
@interface Window (DemoCompat)
@property (nonatomic, readonly, strong) XMLHttpRequest *XMLHTTPRequest;
@end
@interface MyClass : JSObject {
String *_name;
}
@property (nonatomic, readonly, strong) String *name;
+ (instancetype)newWithName:(String *)nm;
- (void)print;
@end
@interface BaseGreeter : JSObject
+ (instancetype)new:(String *)name;
- (String *)name;
- (void)print;
@end
@interface ObjCGreeter : BaseGreeter
- (void)print;
@end
@implementation MyClass
@synthesize name = _name;
+ (instancetype)newWithName:(String *)nm {
auto instance = [super new];
instance->_name = nm;
return instance;
}
- (void)print {
[js.window.console log:@"Hello, " :_name];
}
@end
@implementation BaseGreeter
@end
@implementation ObjCGreeter
- (void)print {
[super print];
[js.window.console log:@"ObjC subclass:" :self.name];
}
@end
@implementation Window (DemoCompat)
- (XMLHttpRequest *)XMLHTTPRequest {
return self.XMLHttpRequest;
}
@end
int main() {
install_demo_interop();
auto window = js.window;
auto console = window.console;
[console log:@"hello, world!"];
auto response = [window fetch: @"./demo/data.json"].await;
auto data = response.json.await;
window.document.title = data[@"title"];
auto request = [js.window.XMLHTTPRequest new];
[request open:@"GET" url:@"./demo/data.json" async:false];
[request send];
[console log:@"xhr response:" :request.responseText];
auto createdName = [window doSomething:MyClass.class];
[console log:@"objc class result:" :createdName];
auto jsSubclassInstance = [window makeJSSubclassInstance:MyClass.class];
[jsSubclassInstance print];
[console log:@"js subclass result:" :jsSubclassInstance.name];
auto objcSubclassClass = ObjCGreeter.class;
auto objcSubclassName = [window useObjCSubclassOfJS:objcSubclassClass];
[console log:@"objc subclass of js result:" :objcSubclassName];
window.document.body.textContent = objcSubclassName;
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment