It’s easy to apply the Swift Argument Parser in the last post to a SwiftUI app. This capability can be helpful in testing by allowing you to bootstrap data into your app. In this post, you will take a minimal SwiftUI app and add argument parsing to it. Here is the app greeting that shows a greeting message to the user.

Sample app

The entire code for this app is here:

import SwiftUI

final class GreetingViewModel: ObservableObject {
  @Published var name: String = "World 🌏"
}

struct GreetingView: View {
  @ObservedObject var model: GreetingViewModel
  var body: some View {
    Text("Hello, \(model.name)!").padding()
  }
}

@main
struct GreetingApp: App {
  @StateObject var model = GreetingViewModel()
  var body: some Scene {
    WindowGroup {
      GreetingView(model: model)
    }
  }
}

The GreetingViewModel holds the app’s business logic. A real app would have a more involved view model, obviously. Keep it simple for this example.

Adding in ArgumentParser

Next, add the Swift Argument Parser Swift package with File > Swift Packages > Add Package Dependency… Enter in https://github.com/apple/swift-argument-parser and click through until added and finished.

Back in the source file, add this to the top of the file:

import ArgumentParser

struct GreetingArguments: ParsableArguments {
  @Option(help: "Override the greeting name")
  var name: String?
}

Notice that since this isn’t a command. You create a type conforming to ParsableArguments, so you don’t need to implement a run method.

Create a helper for updating parameters

Now add this:

extension GreetingArguments {
  func update(_ viewModel: GreetingViewModel) {
    if let name = name {
      viewModel.name = name
    }
  }
}

This update isn’t part of the protocol; it is just a convenient method that clarifies the type for updating GreetingViewModel.

Using GreetingArguments

Next change how the source of truth is created with the below code:

@StateObject var model: GreetingViewModel = {
  let model = GreetingViewModel()
  do {
    let args = try GreetingArguments.parse()
    args.update(model)
  }
  catch {
    print("Error: Could not parse arguments")
    print(CommandLine.arguments.dropFirst().joined(separator: " "))
    print(GreetingArguments.helpMessage())
  }
  return model
}()

This code makes a closure and calls it to produce a GreetingViewModel type. You call parse and update the model.

If you run the app now, you won’t see any difference.

Sample app still looks the same

There’s no difference because you haven’t passed an option. To do that, open the scheme with Command < and enter it like so:

Adding an argument with Xcode

When you run the app now you will get this:

Sample app with argument in effect

Error handling

If you enter invalid arguments, the app will still run but will print messages to the console.

Error: Could not parse arguments
Kitty
USAGE: greeting_arguments [--name <name>]

OPTIONS:
  --name <name>           Override the greeting name 
  -h, --help              Show help information.

Here, you didn’t put in --name, so it failed to parse. It is a soft error and the app launches, but you could easily make it trap as well.

Final thoughts

Adding a command line to your app is a great way to improve development speed by generating fake data to test things out. The example here is trivial, but easy to imagine how you could scale it up to a larger project.