Created
March 23, 2017 02:52
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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