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). 😉
Then again Vapor already had a considerably comparable method to construct scripts, however in Vapor 4 the Command API is healthier than ever. Property Wrappers (out there from Swift 5.1) are utilized in each circumstances to deal with arguments, flags & choices. Personally I like this method rather a lot.
Let me present you a easy good day command:
import ArgumentParser
struct HelloCommand: ParsableCommand {
@Argument(assist: "The title to say good day")
var title: String
func run() throws {
print("Howdy (self.title)!")
}
}
HelloCommand.predominant()
Now I am going to present you tips on how to implement the same command utilizing Vapor:
import Vapor
ultimate class HelloCommand: Command {
let assist = "This command will say good day to a given title."
struct Signature: CommandSignature {
@Argument(title: "title", assist: "The title to say good day")
var title: String
}
func run(utilizing context: CommandContext, signature: Signature) throws {
print("Howdy (signature.title)!")
}
}
public func configure(_ app: Utility) throws {
app.instructions.use(HelloCommand(), as: "good day")
}
As you’ll be able to see they virtually appear to be the identical.
When you love scripting, you need 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. A great instance is a software that manipulates information on the system or one thing comparable. It is only one little dependency, however it removes a lot boilerplate out of your scripts. It permits you to deal with the script itself, as an alternative of parsing the command line inputs. You could find extra detailed examples and an in depth documentation contained in the GitHub repository. 🙏
Vapor’s Command API is beneficial if you wish to carry out extra sophisticated duties together with your scripts. Something that is a part of your Vapor software could be triggered from a command, so you’ll be able to simply create a backend software that reads (or writes) data 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 lengthen the good day 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 one dashed model makes use of a brief title for a similar factor. 🤓
Arguments are consumer supplied values learn so as (e.g. ./good day joe bob john
).
Now that you realize the fundamental definitions, right here is the instance:
ultimate class HelloCommand: Command {
struct Signature: CommandSignature {
@Argument(title: "title", assist: "The title to say good day")
var title: String
@Possibility(title: "greeting", brief: "g", assist: "Greeting used")
var greeting: String?
@Flag(title: "capitalize", brief: "c", assist: "Capitalizes the title")
var capitalize: Bool
}
let assist = "This command will say good day to a given title."
func run(utilizing context: CommandContext, signature: Signature) throws {
let greeting = signature.greeting ?? "Howdy"
var title = signature.title
if signature.capitalize {
title = title.capitalized
}
print("(greeting) (title)!")
}
}
Arguments are required by default, choices and flags are optionals. You may have a customized title (brief and lengthy) for every part, plus you’ll be able to customise the assistance message for each element.
swift run Run good day john
# Howdy john!
swift run Run good day john --greeting Hello
# Hello john!
swift run Run good day john --greeting Hello --capitalized
# Hello John!
swift run Run good day john -g Szia -c
# Szia John!
You may name the command utilizing a number of kinds. Be at liberty to choose a most well-liked model. ⭐️
Subcommands
When command-line applications develop bigger, it may be helpful to divide them into a gaggle of smaller applications, offering an interface by subcommands. Utilities comparable to git and the Swift package deal supervisor are capable of present diversified interfaces for every of their sub-functions by implementing subcommands comparable to git department or swift package deal init.
Vapor can deal with command teams in a very cool approach. I am going to add an additional static property to call our instructions, since I do not prefer to repeat myself or bloat the code with pointless strings:
ultimate class HelloCommand: Command {
static var title = "good day"
}
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 optimization 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 good day
command below the welcome
namespace.
swift run Run welcome good day john --greeting "Hello" --capitalize
When you learn the Swift Argument Parser docs, you’ll be able to obtain the very same habits by a customized CommandConfiguration
. Personally, I desire Vapor’s method right here… 🤷♂️
Ready for async duties
Vapor builds on prime of SwiftNIO together with EventLoops, Futures & Guarantees. A lot of the API is asynchronous, however within the CLI world it’s important to look ahead to the async operations to complete.
ultimate 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: "Anticipate async duties...")
strive todo.create(on: app.db).wait()
app.logger.discover("Todo is prepared.")
}
}
There’s a throwing wait()
methodology that you may make the most of to “keep within the loop” till every part is completed. You can even get a pointer for the appliance object by utilizing the present context. The app has the database connection, so you’ll be able to inform Fluent to create a brand new mannequin. Additionally you should utilize the built-in logger to print information to the console whereas the consumer waits. ⏳
Utilizing ConsoleKit with out Vapor
Let’s discuss overheads. Vapor comes with this neat instructions API, but additionally bundles plenty 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 package deal = 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 extra work in your predominant.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 fashion you’ll be able to do away with many of the community associated core packages (which are included by default in case you use Vapor). This method solely fetches swift-log as a 3rd occasion 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 keep up databases by scripts otherwise you carry out plenty of networking or asynchronous operations it could be higher to go along with Vapor, since you’ll be able to all the time develop by importing a brand new element from the ecosystem.