Group Group Group Group Group Group Group Group Group

raywenderlich.com Forums

Command Line Programs on macOS Tutorial

Discover how easy it is to make your own terminal-based apps with this command line programs on macOS tutorial. Updated for Xcode 9 and Swift 4!


This is a companion discussion topic for the original entry at https://www.raywenderlich.com/511-command-line-programs-on-macos-tutorial

Thank you for a nice tutorial, however I made an improvent by adding the following func to the String extension:

private func alphanumericsOnly() -> String {
	return String(characters.filter {
		String($0).rangeOfCharacter(from: .alphanumerics) != nil
	})
}

And then using this instead of replacingOccurrences(of: " ", with: "").
This way it ignores punctuation.

Thank you for this improvement. Nicely done.

Thanks for this detailed tutorial, I’ve got a question that didn’t mention in it:
How to call other command line tool from a Command Line Tool program?

In my situation, I wrote a Cocoa App which use some command line tools with NSTask. Now I try to implement a command line interface for this app, and it crashed every time it launched a NSTask, which make sense because you shouldn’t suppose to use NSTask within a command line tool.

NSTask *task = [NSTask new];
//prepare task

[task launch]; //crashed

I would be highly appreciatied if you could shed in some light on this matter.

Thanks again for your hard working on this tutorial. :blush:

Hi,

Thanks for your question.

I don’t have a lot of experience with NSTask, but perhaps if you can share what you’re doing in “//prepare task”, I may spot something that can help. My suspicion is that the other steps to prepare the task may have some issue.

Let me know.

Thanks,

Eric.

Hi Eric,
Thanks for your quick reply.

As you mentioned, I just reviewed the “prepare task” part of my code, and it was crashed because I was setting a working directory that wasn’t exist…

Sorry to disturb you and thanks again. :blush:

Hello. Not a problem at all. I’m glad I was able to help you get to a solution. We’re here to help!

So, for others wondering what happened:

NSTask *task = [NSTask new];
//prepare task

// One or more settings here in preparing the task 
// had an issue. In this case, a directory being 
// referenced did not actually exist.

[task launch]; //crashed

And though you probably know all of this, for the benefit of those learning, let me cover some other basics about NSTask() (now Process() in Swift 4).

Note in Swift 4, you now use “Process()”
https://developer.apple.com/documentation/foundation/process

It’s interesting to note for others that after you create an instance of NSTask/Process you have to set other properties (updated for Swift 4):

  • directoryPath <- must exist in order to avoid a crash
  • lauchPath <- must exist in order to avoid a crash
  • arguments <- applicable to whatever the application you’re launching requires

Below is a very simple example that calls up a directory list (“ls -la”) on your root folder.

Drop this into a playground to see your ROOT directory content in the xcode console.

import Foundation

// 1
let task = Process()
// 2
task.launchPath = "/bin/ls"
// 3
task.arguments = ["-la"]
// 4
task.launch()
task.waitUntilExit()
  1. Setup a new instance of the Process class (used to be NSTask).
  2. Set the path of the executable. In this case, we’re calling up “ls” (directory list), but this could be anything as long as the path is correct.
  3. Pass arguments to the command, in this case “-la” to get a column formatted list.
  4. Launch and wait.

Note, I saw examples of folks calling “/bin/bash” (a shell) and then using the “-c” argument to pass the command as an argument. For example, the above cold be rewritten as follows.

Drop this into a playground to see your ROOT directory content in the xcode console.

import Foundation

let task2 = Process()
task2.launchPath = "/bin/bash"
task2.arguments = ["-c", "ls -la"]
task2.launch()
task2.waitUntilExit()

Once again, thanks for your question and for fostering this discussion.

Eric.

I’m getting a “Value of type ‘String’ has no member ‘sorted’” error in the return line of isAnagramOf(). Are strings supposed to have a sorted() method?

EDIT:
I could get rid of the error like this:

return lowerSelf.characters.sorted() == lowerOther.characters.sorted()

Right. the tutorial was updated to support Xcode 9 Beta and Swift 4. In Swift 4, Apple introduced the ability to be able to drop the characters property from strings.

The workaround you identify is the correct pre-Swift 4 syntax.

To summarize, in Swift 4, you can do:
return lowerSelf.sorted() == lowerOther.sorted()

For versions of Swift prior to 4:
return lowerSelf.characters.sorted() == lowerOther.characters.sorted()

Thanks for your comment! Great question.

Eric.

Ah, that makes sense. How can I know which version of Swift I’m using? I think I’m using the latets XCode.

If you’re using Xcode latest released version, as of toady, that is Xcode 8.3 (and Swift 3.2 for new projects if you keep the default).

Assuming you just have the one version of xcode installed, you can type the following in Terminal:

swift -version

You can download Xcode 9 BETA from https://developer.apple.com/download/. You need an Apple Developer Account in order to download, but the account is free if you don’t plan to publish apps to the AppStore.

Note, Xcode 9 should almost be ready for release (especially considering Apple’s announcements scheduled for tomorrow 9/12/17.) So, perhaps stay tuned to the announcements tomorrow and check the download website after that.

1 Like

Got it, thank you very much! Looking forward for XCode 9.

Hello, Eric.

I’m building a Mac software that write files into system(which blocks by System Integrity Protection function).

It’s not an application like usual. Is there any way to put this software to Mac App Store?

Thanks in advance.
Vern.

This tutorial is more than six months old so questions are no longer supported at the moment for it. We will update it as soon as possible. Thank you! :]