Mike Lischke
Mike Lischke

Reputation: 53337

Create and run a Selector with several parameters for class functions

I have a situation where I use a class to do a number of conversions with a large number of rules which have the general form of:

  private class func rule0(inout account: String, _ version: Int) -> String? {
    return nil; // Use default rule.
  }

The function names just increase the rule number: rule1, rule2 etc...

I get the rule number from an external source and now want to find a generic way to call the right rule function depending on the given rule number. I could use a big switch statement, but I'm more interested in generating a selector from a string and call that.

My attempts led me to:

let ruleFunction = Selector("rule\(rule):_:");
let result = ruleFunction(&account, version);

but that only causes this error on the second line:

'(inout String, @lvalue Int) -> $T4' is not identical to 'Selector'

What is the right way to accomplish that? And btw, what means "$T4"? That error message is hard to understand, to say it friendly.

Additional note: I'm aware of approaches using a timer or detouring to Obj-C, but am rather interested in a direct and pure Swift solution. How is the Selector class to be used? Can it be used to directly call what it stands for? How do classes like NSTimer use it (detour to Obj-C?)?

Upvotes: 1

Views: 318

Answers (1)

Aaron Rasmussen
Aaron Rasmussen

Reputation: 13316

A couple of approaches that don't use Selector. If all of the "rules" have the same signature, then you could either put them in an array, with the index of the function in the array indicating the rule number, like this:

let functionArray = [rule0, rule1, rule2, rule3]  //  etc...

Then, when it is time to call the function, you just access the one you want with a subscript:

let result = functionArray[ruleNumber](account, version)

You could do the same thing with a dictionary that looked something like this:

let functionDict = ["rule0": rule0, "rule1": rule1]  //  etc...

And then get the function using the string value that you create when you get the rule number from your external source:

let result = functionDict["rule\(ruleNumber)"]?(account, version)

The dictionary approach would work better if you weren't able to guarantee that rule0 would always be at index 0 in the array.

A caution, though: I have tried this and I've been able to get it to work without the inout keyword, but not with it. I don't know if it is a bug, or what, but Swift doesn't seem to want to let me create arrays or dictionaries of functions that take inout parameters.

If you absolutely require the inout parameter for your function to work, then my suggestion won't help. If there is a way to do what you want without the inout, then this might be the solution for you.

Upvotes: 1

Related Questions