While working on an iOS app for a client I came across an interesting language-level bug (or missing feature?) in RubyMotion.
The app has a fancy animation which needed a method on CAMediaTimingFunction called functionWithControlPoints. It turns out that this is one of the places you’ll see a seldom used feature of Objective-C: Nameless Parameters.
A function with Nameless Parameters can be defined like so:
+ (void) sayHello:(NSString *)firstName:(NSString *)lastName;
… and instead of the usual key: value style invocation you can simply omit the keys:
[Greeter sayHello:"Daniel" :"Upton"];
It’s a neat trick, but one that Apple actively discourages and it seems the RubyMotion team aren’t too keen either:
Nameless Objective-C parameters are the most hideous thing I have ever had the displeasure to work with. Also completely unnecessary.— Eloy Durán (@alloy) March 28, 2014
Fortunately they crop up in few places, but unfortunately for us CAMediaTimingFunction is one of them and RubyMotion doesn’t support the syntax (check out the discussion in RM-123).
With a little
magic meta-programming you can poke still poke at the method, here’s
what I ended up doing:
# [CAMediaTimingFunction functionWithControlPoints:0.4, :0.0, :0.2, :1.0]; def media_timing_function signature = NSMethodSignature.signatureWithObjCTypes('@@:ffff') invocation = NSInvocation.invocationWithMethodSignature(signature) invocation.target = CAMediaTimingFunction invocation.selector = 'functionWithControlPoints::::' [0.4, 0.0, 0.2, 1.0].each_with_index do |point, index| pointer = Pointer.new('f') pointer = point invocation.setArgument(pointer, atIndex: index + 2) end invocation.invoke pointer = Pointer.new('@') invocation.getReturnValue(pointer) pointer end
Eloy suggested a more general purpose workaround in the mentioned ticket.