🏡 Dan Upton

Nameless parameters in RubyMotion

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[0] = point
    invocation.setArgument(pointer, atIndex: index + 2)
  end

  invocation.invoke

  pointer = Pointer.new('@')
  invocation.getReturnValue(pointer)
  pointer[0]
end

Eloy suggested a more general purpose workaround in the mentioned ticket.