Skip to content

Instantly share code, notes, and snippets.

@darknoon
Created March 23, 2017 02:52
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save darknoon/60dae68eeddce9a299420b0fb1e114ac to your computer and use it in GitHub Desktop.
Save darknoon/60dae68eeddce9a299420b0fb1e114ac to your computer and use it in GitHub Desktop.
A little bit disgusting, but you can totally create classes with ivars in cocoa script :D
// Copy-paste this into 💎Sketch.app and run it 🔥
// Scroll to bottom for usage
// Use any C function, not just ones with BridgeSupport
function CFunc(name, args, retVal) {
// Due to particularities of the JS bridge, we can't call into MOBridgeSupport objects directly
// But, we can ask key value coding to do the dirty work for us ;)
function setKeys(o, d) {
const funcDict = NSMutableDictionary.dictionary()
funcDict.o = o
Object.keys(d).map( k => funcDict.setValue_forKeyPath(d[k], "o." + k) )
}
function makeArgument(a) {
if (!a) return null;
const arg = MOBridgeSupportArgument.alloc().init();
setKeys(arg, {
type64: a.type,
});
return arg;
}
const func = MOBridgeSupportFunction.alloc().init();
const argArr = args.map( makeArgument );
setKeys(func, {
name: name,
arguments: argArr,
returnValue: makeArgument(retVal),
})
return func;
}
/*
@encode(char*) = "*"
@encode(id) = "@"
@encode(Class) = "#"
@encode(void*) = "^v"
@encode(CGRect) = "{CGRect={CGPoint=dd}{CGSize=dd}}"
*/
// This assumes the ivar is an object type. Return value is pretty useless.
const object_getInstanceVariable = CFunc("object_getInstanceVariable", [{type: "@"}, {type:'*'}, {type: "^@"}], {type: "^{objc_ivar=}"});
// Again, ivar is of object type
const object_setInstanceVariable = CFunc("object_setInstanceVariable", [{type: "@"}, {type:'*'}, {type: "@"}], {type: "^{objc_ivar=}"});
function ObjCClass (handlers, ivars = [], superclass = NSObject){
var className = "GeneratedClass" + NSUUID.UUID().UUIDString()
var cls = MOClassDescription.allocateDescriptionForClassWithName_superclass_(className, superclass)
// Add each handler to the clas description
for(var selectorString in handlers) {
var selector = NSSelectorFromString(selectorString)
cls.addInstanceMethodWithSelector_function_(selector, handlers[selector])
}
ivars.map( name => cls.addInstanceVariableWithName_typeEncoding(name, "@"))
return cls.registerClass();
};
function getIvar(obj, name) {
const retPtr = MOPointer.alloc().init();
object_getInstanceVariable(obj, 'ivar', retPtr);
return retPtr.value();
}
/**************************************
************** Usage ******************
***************************************/
const MyClass = new ObjCClass({
test() {
log("ivar is " + getIvar(this, 'ivar'));
},
}, ["ivar"], NSObject)
const obj = MyClass.new();
obj.test();
// Yes you can do this
obj.setValue_forKey("abcd", "ivar")
obj.test();
// But this is more elegant!
object_setInstanceVariable(obj, "ivar", "efgh");
obj.test();
/* output:
ivar is null
ivar is abcd
ivar is efgh
Script executed in 0.025665s
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment