Swift Argument Parser vs Vapor Instructions
Apple open-sourced a brand new library that may assist you numerous if you wish to construct scripts that written in Swift. The Swift Argument Parser was beforehand a part of the Swift Package deal Supervisor instruments, however now it’s even highly effective & has it is personal life (I imply repository). 😉
Alternatively Vapor already had a considerably comparable strategy to construct scripts, however in Vapor 4 the Command API is healthier than ever. Property Wrappers (accessible from Swift 5.1) are utilized in each instances to deal with arguments, flags & choices. Personally I like this strategy quite a bit.
Let me present you a easy whats up command:
import ArgumentParser
struct HelloCommand: ParsableCommand {
@Argument(assist: "The title to say whats up")
var title: String
func run() throws {
print("Hi there (self.title)!")
}
}
HelloCommand.important()
Now I am going to present you learn how to implement an analogous command utilizing Vapor:
import Vapor
last class HelloCommand: Command {
let assist = "This command will say whats up to a given title."
struct Signature: CommandSignature {
@Argument(title: "title", assist: "The title to say whats up")
var title: String
}
func run(utilizing context: CommandContext, signature: Signature) throws {
print("Hi there (signature.title)!")
}
}
public func configure(_ app: Utility) throws {
app.instructions.use(HelloCommand(), as: "whats up")
}
As you possibly can see they nearly appear to be the identical.
In case you love scripting, it is best to positively verify swift-sh and Brisk
The Swift Argument Parser library is a light-weight resolution if you’re solely in search of a easy Swift script. An excellent instance is a software that manipulates information on the system or one thing comparable. It is only one little dependency, but it surely removes a lot boilerplate out of your scripts. It lets you deal with the script itself, as an alternative of parsing the command line inputs. You will discover extra detailed examples and an in depth documentation contained in the GitHub repository. 🙏
Vapor’s Command API is helpful if you wish to carry out extra difficult duties along with your scripts. Something that is a part of your Vapor software could be triggered from a command, so you possibly can simply create a backend software that reads (or writes) information from the database utilizing Fluent 4. That is the principle benefit of utilizing a Vapor command, as an alternative a standalone Swift script.
Arguments, choices, flags
Let’s prolong the whats up command with a brand new possibility and a flag. The principle distinction between an possibility and a flag is that an possibility has an related worth, however a flag is simply one thing that you just give to the command or not. Each choices and flags begin with a single -
or a double sprint --
, normally the only dashed model makes use of a brief title for a similar factor. 🤓
Arguments are person supplied values learn so as (e.g. ./whats up joe bob john
).
Now that you understand the fundamental definitions, right here is the instance:
last class HelloCommand: Command {
struct Signature: CommandSignature {
@Argument(title: "title", assist: "The title to say whats up")
var title: String
@Choice(title: "greeting", quick: "g", assist: "Greeting used")
var greeting: String?
@Flag(title: "capitalize", quick: "c", assist: "Capitalizes the title")
var capitalize: Bool
}
let assist = "This command will say whats up to a given title."
func run(utilizing context: CommandContext, signature: Signature) throws {
let greeting = signature.greeting ?? "Hi there"
var title = signature.title
if signature.capitalize {
title = title.capitalized
}
print("(greeting) (title)!")
}
}
Arguments are required by default, choices and flags are optionals. You’ll be able to have a customized title (quick and lengthy) for all the pieces, plus you possibly can customise the assistance message for each part.
swift run Run whats up john
# Hi there john!
swift run Run whats up john --greeting Hello
# Hello john!
swift run Run whats up john --greeting Hello --capitalized
# Hello John!
swift run Run whats up john -g Szia -c
# Szia John!
You’ll be able to name the command utilizing a number of kinds. Be at liberty to select a most well-liked model. ⭐️
Subcommands
When command-line packages develop bigger, it may be helpful to divide them into a bunch of smaller packages, offering an interface by way of subcommands. Utilities equivalent to git and the Swift bundle supervisor are capable of present assorted interfaces for every of their sub-functions by implementing subcommands equivalent to git department or swift bundle init.
Vapor can deal with command teams in a extremely cool approach. I am going to add an additional static property to call our instructions, since I do not wish to repeat myself or bloat the code with pointless strings:
last class HelloCommand: Command {
static var title = "whats up"
}
struct WelcomeCommandGroup: CommandGroup {
static var title = "welcome"
let assist: String
let instructions: [String: AnyCommand]
var defaultCommand: AnyCommand? {
self.instructions[HelloCommand.name]
}
init() {
self.assist = "search engine optimisation command group assist"
self.instructions = [
HelloCommand.name: HelloCommand(),
]
}
}
public func configure(_ app: Utility) throws {
app.instructions.use(WelcomeCommandGroup(), as: WelcomeCommandGroup.title)
}
That is it, we simply moved our whats up
command beneath the welcome
namespace.
swift run Run welcome whats up john --greeting "Hello" --capitalize
In case you learn the Swift Argument Parser docs, you possibly can obtain the very same conduct by way of a customized CommandConfiguration
. Personally, I favor Vapor’s strategy right here… 🤷♂️
Ready for async duties
Vapor builds on high of SwiftNIO together with EventLoops, Futures & Guarantees. Many of the API is asynchronous, however within the CLI world it’s important to anticipate the async operations to complete.
last class TodoCommand: Command {
static let title = "todo"
struct Signature: CommandSignature { }
let assist = "This command will create a dummy Todo merchandise"
func run(utilizing context: CommandContext, signature: Signature) throws {
let app = context.software
app.logger.discover("Creating todos...")
let todo = Todo(title: "Await async duties...")
strive todo.create(on: app.db).wait()
app.logger.discover("Todo is prepared.")
}
}
There’s a throwing wait()
methodology which you can make the most of to “keep within the loop” till all the pieces is completed. You can too get a pointer for the applying object by utilizing the present context. The app has the database connection, so you possibly can inform Fluent to create a brand new mannequin. Additionally you should use the built-in logger to print information to the console whereas the person waits. ⏳
Utilizing ConsoleKit with out Vapor
Let’s discuss overheads. Vapor comes with this neat instructions API, but in addition bundles a lot of different core issues. What if I simply need the goodies for my Swift scripts? No drawback. You should use the underlying ConsoleKit by including it as a dependency.
import PackageDescription
let bundle = Package deal(
title: "myProject",
platforms: [
.macOS(.v10_15)
],
dependencies: [
.package(url: "https://github.com/vapor/console-kit", from: "4.1.0"),
],
targets: [
.target(name: "myProject", dependencies: [
.product(name: "ConsoleKit", package: "console-kit"),
])
]
)
You continue to must do some further work in your important.swift
file, however nothing severe:
import ConsoleKit
import Basis
let console: Console = Terminal()
var enter = CommandInput(arguments: CommandLine.arguments)
var context = CommandContext(console: console, enter: enter)
var instructions = Instructions(enableAutocomplete: true)
instructions.use(HelloCommand(), as: HelloCommand.title, isDefault: false)
do {
let group = instructions.group(assist: "Utilizing ConsoleKit with out Vapor.")
strive console.run(group, enter: enter)
}
catch {
console.error("(error)")
exit(1)
}
This manner you possibly can do away with a lot of the community associated core packages (which can be included by default in case you use Vapor). This strategy solely fetches swift-log as a 3rd celebration dependency. 😍
Abstract
ConsoleKit in Vapor is an effective way to jot down CLI instruments and small scripts. The brand new Swift Argument Parser is a extra light-weight resolution for a similar drawback. In case your plan is to take care of databases by way of scripts otherwise you carry out a lot of networking or asynchronous operations it may be higher to go together with Vapor, since you possibly can all the time develop by importing a brand new part from the ecosystem.