Build your own command extension for OpenTerm

Open Source projects are fun because there are a lot of amazing developers that spend time and effort and create something that all can use. What is more fun is the fact that other amazing talented developers add functionality and features to these open source projects. There is one such project called OpenTerm (was called terminal for iOS) by Louis who has some other amazing open source projects. For this article we shall focus on the terminal project and create a new command.

So, there were a couple of ideas on what kind of extensions can be created. I tried a couple and realised how things did not work out either due to background and foreground queues or other issues. Something that could work and would not be very difficult but at the same time useful was Notifications. Remote notifications are not easy to manage and would not make a lot of sense in this scenario. However local notifications would. Say you wanted to write a shell script that popped up a notification after a while.

Structure of a Command Extension

A command in OpenTerm is basically a function that is defined as

public func notify(argc: Int32, argv: UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>?) -> Int32 {
    return 0
}

The function returns 0 if everything is OK and a non-zero value if there was an error like most commands do. The next thing is to add this function to the list of commands available in the terminal found in the TerminalViewController.swift

Getting the passed arguments

When you type commands on the terminal, you pass it parameters also called arguments which help make the command more interactive. if you look at the structure of the notify function, it has two parameters, argc and argv (which if you have any experience with C programming) the argc is an Int signifying the number of arguments passed to the function and argv is an array of argc number of parameters passed.

	guard let args = convertCArguments(argc: argc, argv: argv) else {
		return 1
	}

This converts the argv into an array of strings, each containing the parameter. If you have worked with shell scripting then each of the elements in the args is the equivalent of $n . Where the name of the command is the first argument as in args[0] or $0 for shell scripts.

Parsing the flags in the arguments

Once we have the arguments in an array, it still requires parsing the flags to get the values. In our example the command line would look like

notify "This is a test notification" --title "Notification Title"

The args would look like

 0     notify
 1     This is a test notification
 2     --title
 3     Notification Title

We can discard the first as that is just the name of the command. Then we iterate through the array checking if we have any that start with the prefix “–” and if they do, then we get the next item for the value. Right now, the assumption made is that every flag has a value, unlike cases where you might have the need to use single flags like –help , –ignore-case etc.

	var currFlag: String?
	var message: String?
	var flags: [String: String] = [:]
	
	for arg in args.dropFirst() {
		if arg.hasPrefix("--") {
			currFlag = String(arg.dropFirst(2))
			continue
		}
			
		if let cFlag = currFlag {
			flags[cFlag] = arg
			currFlag = nil
			continue
		}
		
		if message == nil {
			message = arg
		}
	}

At the end of this, we have the message set to the variable message and the dictionary flags has the flags settings.

Displaying the notification

This is perhaps the easiest part of them all. Now that we can parse the message and the bits we want to get via flags, like in our case we get the –title and –after for the duration in seconds to display the notification by setting the details on the content.

What does it look like?

http://files.oz-apps.com/Notify_01.mov

Burning your fingers with Open Source

I had fun creating this however I also faced a couple of issues that made this process not fun. At the end of the day, if what we do is not fun then the whole point of enjoying your work is lost.

Badly written tests before merge

Tests are a good measure for code, but if the tests are badly written or the expectations are contrary to what you want to achieve, then it kind of sucks to have written that test. In this case, one of the test checks for a list of commands and that too in an alphabetically sorted manner. When you add a new command, you need to add your command to the array of commands, this is not really a test unless you do not want anyone to add more commands.

Changes to the base system

I had a bit of a struggle trying to write the code for a new command because at first I could not run the project in the simulator and could not run it on a device either because the application runs only on iOS 11.0 and above and XCode 9.2 does not support the latest iOS version 11.3. This is the biggest issue that Apple has not addressed, It is understandable that unless Xcode does not have the system images of that new SDK then it cannot debug. However the only way to get to that is upgrade Xcode, there is no way of simply downloading a new version Debug details. To add to this, currently what Apple has done is even worse, you cannot upgrade to Xcode 9.3 unless you upgrade to Mac OS X 10.13 and then upgrade Xcode to 9.3. In my case I have stayed at 10.12 so I cannot upgrade to Xcode 9.3

This is part of the problem, as the real problem is that the author has upgraded the project to Swift 4.1, where his pet project Cub stops compiling, so if one refreshed the project, everything stops working till you realise that the problem is the upgrade.

With a bit of experimentation and checking out a slightly older version, I have been able to get the app to run in the simulator HOWEVER it won’t install on the device and keeps throwing errors.

Other vulnerabilities in the Project

Here vulnerabilities does not mean trojans or backdoors, it simply means that the architecture is a bit weak, the threading and DispatchQueue methods are a bit weakly implemented and crash the moment you try to do something.

In Closing

It was fun working with OpenTerm, but given the issues I think I might be away from OpenSource projects for a while. There were a couple of good ideas that I wanted to add to OpenTerm that I would definitely use Open Term for, but given the issues and the difficulties in getting it to work and install, I could spend my time on something else. Till then, maybe you can get inspired and create a few commands or extension.

Views All Time
Views All Time
1814
Views Today
Views Today
1
Posted in Article, Tip, Tutorial.