Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

Mark FX

macrumors regular
Original poster
Nov 18, 2011
159
17
West Sussex, UK
I have a number of small command line tools made over time, and I'm thinking of combining them into one command line tool.
It's not a project as yet, but just a concept I'm playing around with, so there's no code base as yet which I can post.
But some thing like this below for the main.swift file.

Swift:
//main.swift
import Foundation

func run() {
  
    var toolType: Any
  
    let arguments = Array(CommandLine.arguments)
    if arguments.count == 0 {
        print("Error: No tool type parameter entered")
        exit(EXIT_FAILURE)
    } else {
        let argument = arguments[0]
        switch argument {
        case "Tool1": toolType = Tool1() // ERROR: won't allow this
        case "Tool2": toolType = Tool2() // ERROR: won't allow this
        case "Tool3": toolType = Tool3() // ERROR: won't allow this
        default: print("Error: Unknown tool type parameter entered")
            exit(EXIT_FAILURE)
        }
        print(toolType.name()) // ERROR: Value of type Any has no member 'name'
        exit(EXIT_SUCCESS)
    }
}

run()

And a number of swift files in the project for the different tools, like this below.

Swift:
//Tool1.swift
class Tool1 {
  
    func name() -> String {
        return "Tool1"
    }
  
}

//Tool2.swift
class Tool2 {
  
    func name() -> String {
        return "Tool2"
    }
  
}

//Tool3.swift
class Tool3 {
  
    func name() -> String {
        return "Tool3"
    }
  
}

My question is, how would I declare the "toolType" variable ?
And of what class type should it be ?
And what class type should the different tools "Tool1", "Tool2", "Tool3" be declared as, or class inherited from ?

Ive tried changing the different tools 1 to 3 as inherited from "NSObject" and "Any" but neither work with the above code.
As stated above, this is not a project with a name or code base yet, other than the numerous small command line tools already made.
But in trying the toolbox concept above, Ive stumbled across the first problem, and need some guidance on how to implement the above.

The final goal would be to type from the terminal like this.

Bash:
toolbox tool1 <commands>

Thanks for any help

Regards Mark
 
Why would you want to combine them like that anyway? Do you plan on copying in your existing tools into Tool1, 2 and 3?

What you'd probably want there was

Code:
protocol ToolType {
    func name() -> String
}
and then have all your Tools belong to the protocol. So for Tool1 it'd look like

Code:
class Tool1: ToolType {
     //same as before
}

When declaring ToolType set it to the protocol type. - Then just extend the prototype as needed.

Also, you don't need the "else" block. Since your if block exits the program, you can remove else and behaviour will be unaffected


If you want this to essentially act as a wrapper and call your other command line tools, you're going to want to look at the "Process" class. I have an example of using it (with not the prettiest code) in my cronx repository, though be aware that it was written for a version of the API that is not the very latest. If you compile it against a newer API it'll show a warning that some of the API is deprecated and replaced. It still works, but there's a newer way of doing this in the docs
Fairly similar though:
 
Hey casperes1996 Thanks

No I don't plan on calling the other command line tools from the toolkit command line tool.
The plan is to rewrite the function of the various tools into the tool1 tool2 tool3 etc.
I'm very familiar with the "Process" Class, and have used it a lot in the past, but thanks for the posting anyway.

I'll have a try with the protocol pattern that your suggesting, but Im not sure that will help me if all of the different tool's functions are needing to be declared in the protocol, it's late night for me now, so will try your way tomorrow.

Basically, as the number of tools might grow over time, I was trying to avoid this below.

Swift:
let tool1 = Tool1()
let tool2 = Tool2()
let tool3 = Tool3()

// And wanted this

var toolType = Tool1()

// And then

toolType = Tool6()

The different tool's will all have different functions with different function names, so I'm not sure if the protocol method will work, but I'll give it a try, I was thinking I could have the different tool's as inheriting from NSObject or AnyObject, but could not get it to work, as I say, I'm just trying to get the concept right before starting proper.

Yeah the code posted was a quick type for the forum posting, it's not intended production code, just rough concept code with a few errors I'm sure.

Thanks Again

Regards Mark
 
Last edited:
Basically, as the number of tools might grow over time, I was trying to avoid this below.

Yes, what you want is called the State Pattern, and was described back in the original Gang of Four book "Design Patterns". It's a good design for your purposes.


However, you cannot dynamically call functions on objects that do not have a shared hierarchy. I.e. if you want to eventually call a
makeChicken() method there is no way to have a single
toolType.makeChicken()
call unless all tools share a common ancestor or protocol declaration that specifies they all have a makeChicken function. Otherwise you'd be calling functions that don't exist.

Instead, figure out the tool you are to use like in your original mock code with my protocol suggestion and give all the tools a constructor that takes in the remaining arguments after the tool was picked. Then all they need to share is a "runTool()" method that would act as "main" for each tool independently with all their arguments passed through the constructor; Optionally pass them to runTool method instead.

That's one way of doing it anyway, and probably how I would structure it. You'd get your nice state pattern in the top level toolbox where you only need one tool variable that just gets set to the correct thing and can invoke the other tool. But of course it depends on the goals of combining things; If it's just to invoke the tools then this should be a good solution I think. It would also still allow flexibility to do something like multiple tools executing through one toolbox invocation with few code additions.

But there's no way in a statically typed language to say have
Code:
class A {
    func doA() {}
}

class B {
    func doB() {}
}

var x: Any = A()

x.doA()

The specified type for x is Any, so x will only have access to anything Any object can do. doA is specific behaviour only available to A, so one would have to cast it by checking x is of instance A and using as! A. And then it would have the functionality of an A object.

Some languages have union types but it gets a bit hairy.

But if we think of a situation where we try calling
x.doA()
and
x.doB()

on a type Any, and just imagine it was allowed, well, it might doA fine because the dynamic type of x is actually A even if its static type is Any, but what when it gets to x.doB()? Now it would crash, or might even give corrupt output depending on how this fictional compiler were to interpret it. This breaks type safety

Unfortunately, and especially with Objective C, Apple did have some "dynamic dispatch" functionality, and it's still in part in place in Swift, though only though Obj-C runtime Interop to my knowledge. This I would however avoid like the plague; It is not compile time checked and will just give crashes and bugs
 
Sorry for the slow reply.
I finally got around to trying your suggested protocol pattern, which does work as described.
I've listed the rough concept code below using that technique.

Swift:
//main.swift
import Foundation

protocol ToolType {
    func runToolFunction(functionName: String) -> String
}

func run() {
 
    let toolTypes: [String] = ["Tool1", "Tool2", "Tool3"]
 
    var toolType: ToolType
 
    let arguments = Array(CommandLine.arguments)
    if arguments.count == 0 {
        print("Error: No tool type parameter entered.")
        exit(EXIT_FAILURE)
    }
 
    let tool = arguments[0]
    switch tool {
    case "Tool1": toolType = Tool1()
    case "Tool2": toolType = Tool2()
    case "Tool3": toolType = Tool3()
    default: print("Error: Unknown tool type entered.")
        exit(EXIT_FAILURE)
    }
 
    if arguments.count != 2 {
        print("Error: No tool function name entered.")
        exit(EXIT_FAILURE)
    }
 
    let function = arguments[1]
    if toolType.functionNames.contains(function) {
        print(toolType.runToolFunction(functionName: function))
    } else {
        print("Error: Unknown \(tool) function name entered.")
        exit(EXIT_FAILURE)
    }
 
    exit(EXIT_SUCCESS)
 
}

run()

//Tool1.swift
class Tool1: ToolType {
 
    let functionNames: [String] = ["doStuff", "doMoreStuff"]
 
    func runToolFunction(functionName: String) -> String {
        // Figure out how to call the functionName
        return "Function Result"
    }
 
    func doStuff() -> String {
        return "Tool1.doStuff() called"
    }
 
    func doMoreStuff() -> String {
        return "Tool1.doMoreStuff() called"
    }
 
}

//Tool2.swift
class Tool2: ToolType {
 
    let functionNames: [String] = ["doThis", "doThat"]
 
    func runToolFunction(functionName: String) -> String {
        // Figure out how to call the functionName
        return "Function Result"
    }
 
    func doThis() -> String {
        return "Tool2.doThis() called"
    }
 
    func doThat() -> String {
        return "Tool2.doThat() called"
    }
 
}

//Tool3.swift
class Tool3: ToolType {
 
    let functionNames: [String] = ["doSomething", "doSomethingElse"]
 
    func runToolFunction(functionName: String) -> String {
        // Figure out how to call the functionName
        return "Function Result"
    }
 
    func doSomething() -> String {
        return "Tool3.doSomething() called"
    }
 
    func doSomethingElse() -> String {
        return "Tool3.doSomethingElse() called"
    }
 
}

The only thing left to figure out, is how to call the class's functions, based on the function name string.
In the past when using ObjectiveC code, and most class's where inheriting from the NSObject class, you could call the performSelector method, but Swift does not do reflection very well.

In conclusion, I'm trying to decide if the protocol pattern above is the correct way to go.
I have to be honest and say I don't like it, and I don't know why I don't like it, there's a lot of abstraction going on.
And not being able to access the different tool class's functions directly doesn't feel right.
But it is what it is!

The choice is this.

Swift:
protocol ToolType {
    func runToolFunction(functionName: String) -> String
}

var toolType: ToolType

toolType = Tool3()
let result3 = toolType.runToolFunction(functionName: "doSomething")

toolType = Tool1()
let result1 toolType.runToolFunction(functionName: "doStuff")


// OR

let tool1 = Tool1()
let tool2 = Tool2()
let tool3 = Tool3()

let result3 = tool3.doSomething()
let result1 = tool1.doStuff()

I like the idea in the first case of using the same tool variable for the different tools, but don't like the added class abstraction that you have to deal with.
The second case where each tool has it's own variable, and you can access the tool's functions directly feels more natural, but I don't like the idea of wasted system memory and resources, having numerous variables initialised when only one of them might be needed.
In the scenario when the toolbox grows over time, you could end up with dozens of initialised tool variables.

Any further help with calling the tool class's functions based on the function's name string would be appreciated.

But at this point, I need to decide which way to go with this toolbox concept.

Regards Mark
 
Last edited:
What's the point of that toolTypes array that just contains the Tool1, 2, 3 names?

You can still use selectors but I don't advise it. It carries all the problems I mentioned before where a typo will crash your program at runtime and you'll get no compile time warnings. Instead I would just switch through the cases inside the Tools.

case "function1":
function1(args)
default:
print("This tool does not implement specified function")


Then you can also remove the arrays with what functions each tool implements and the .contains comparison since the handling of tools not allowed goes inside the tools.

But I mean, I will also say that coding style, especially for personal projects, is a matter of preference. I'm writing my posts based on what I find clean and nice, but if you feel it clashes with your style there's several other ways of doing things and my solution isn't necessarily the optimal one for you :)
 
casperes1996 said:
What's the point of that toolTypes array that just contains the Tool1, 2, 3 names?

It was something else I was messing about with, but forgot to remove it, so once again please look upon the code as rough draft code, with issues and possible errors.
I'm working on a proof of concept here, not a finished article.

I think I've got the prototype pattern you suggested working in full now, but it does not look pretty.
the tool class's have to inherit from NSObject, and conform to the ToolType protocol.
And the contained functions have to be Objc functions, in order to be called from a string representation.

I missed your point on wether the tool has a callable function, where you suggesting that the tool does that check, or the calling main.swift file does the checking.

So still trying to decide wether the suggested protocol pattern is the way to go.

Regards Mark
 
It was something else I was messing about with, but forgot to remove it, so once again please look upon the code as rough draft code, with issues and possible errors.
I'm working on a proof of concept here, not a finished article.

Right right, I do look at it that way, and there's no judgment or anything. It was more just the case that I wanted to understand the idea you were going for properly, so if I had missed something I didn't want to just ignore it :)

I missed your point on wether the tool has a callable function, where you suggesting that the tool does that check, or the calling main.swift file does the checking.
So that was not really relevant to the reflection based approach where you do the call based on the string representation.
 
Well I've got a working version using your idea with the protocol pattern, and it works pretty well.
But is a lot more complicated than I was hoping, and it requires the tool classes to inherit from NSObject.
And also to comply with the ToolType protocol, and also have the functions marked as objc functions.
The reason for this is that I could not find a way to call the functions from just a string representation.
But the NSObject class does have a way to call an ObjectiveC function from it's string name.
But as stated previously, it's not a very pretty sight, and having to use out of date techniques.

Swift:
//Tool1.swift
class Tool1: NSObject, ToolType {
 
    let functionNames: [String] = ["doStuff", "doMoreStuff"]
 
    override init() { // Have To Include An Init Function
        super.init()
    }
 
    func runToolFunction(functionName: String) -> String {
        let functionReturnObject: Unmanaged<AnyObject> = self.perform(Selector(functionName))
        return (functionReturnObject.takeRetainedValue() as! String)
    }
 
    @objc func doStuff() -> String { // Change to objc Function
        return "Tool1.doStuff() function called"
    }
 
    @objc func doMoreStuff() -> String { // Change to objc Function
        return "Tool1.doMoreStuff() function called"
    }
 
}

The original Swift only way with each tool having it's own variable is a lot cleaner and more concise.
But does have the problem of multiple unused initialised tool variables for each tool.

So I'm still undecided as to the way I will tackle this project, but it's been interesting experimenting.
And I'm grateful for all of your input with this idea, and appreciate your thoughts for this concept.

Kind Regards

Mark
 
Last edited:
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.