🏡 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:

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.