Methods to write Swift scripts utilizing the brand new Command API in Vapor 4?

0
11


Shell scripts are necessities on the server facet. Discover ways to construct Swift scripts to your backend apps utilizing property wrappers.

Vapor

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 related strategy to construct scripts, however in Vapor 4 the Command API is healthier than ever. Property Wrappers (obtainable from Swift 5.1) are utilized in each instances to deal with arguments, flags & choices. Personally I like this strategy loads.

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("Hey (self.title)!")
    }
}
HelloCommand.principal()

Now I am going to present you find out how to implement an identical 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("Hey (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, you need to positively verify swift-sh and Brisk


The Swift Argument Parser library is a light-weight answer if you’re solely in search of a easy Swift script. An excellent instance is a instrument that manipulates recordsdata on the system or one thing related. It is only one little dependency, however it removes a lot boilerplate out of your scripts. It lets you give attention to the script itself, as an alternative of parsing the command line inputs. You’ll find 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 together with your scripts. Something that is a part of your Vapor utility may be triggered from a command, so you possibly can simply create a backend instrument 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 stanadlone Swift script.




Arguments, choices, flags

Let’s prolong the whats up command with a brand new possibility and a flag. The primary distinction between an possibility and a flag is that an possibility has an related worth, however a flag is simply one thing that you simply 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 offered values learn so as (eg.: ./whats up joe bob john).

Now that you realize the essential 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 ?? "Hey"
        var title = signature.title
        if signature.capitalize {
            title = title.capitalized
        }
        print("(greeting) (title)!")
    }
}

Arguments are required by default, choices and flags are optionals. You possibly can have a customized title (quick and lengthy) for every part, plus you possibly can customise the assistance message for each element.

swift run Run whats up john


swift run Run whats up john --greeting Hello


swift run Run whats up john --greeting Hello --capitalized


swift run Run whats up john -g Szia -c

You possibly can name the command utilizing a number of types. Be happy to choose a most well-liked model. ⭐️



Subcommands

When command-line applications develop bigger, it may be helpful to divide them into a bunch of smaller applications, offering an interface via subcommands. Utilities resembling git and the Swift package deal supervisor are in a position to present diversified interfaces for every of their sub-functions by implementing subcommands resembling git department or swift package deal init.

Vapor can deal with command teams in a very cool method. 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:

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 = "website positioning 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 habits via a customized CommandConfiguration. Personally, I desire Vapor’s strategy right here… 🤷‍♂️



Ready for async duties

Vapor builds on prime of SwiftNIO together with EventLoops, Futures & Guarantees. Many of the API is asynchronous, however within the CLI world you must 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.utility
        app.logger.discover("Creating todos...")
        
        let todo = Todo(title: "Watch for async duties...")
        attempt 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 finished. You may also get a pointer for the appliance object through the use of 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 speak about overheads. Vapor comes with this neat instructions API, but additionally bundles a lot of different core issues. What if I simply need the goodies for my Swift scripts? No downside. You need to 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 further work in your principal.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.")
    attempt console.run(group, enter: enter)
}
catch {
    console.error("(error)")
    exit(1)
}

This manner you possibly can eliminate a lot of the community associated core packages (which are included by default if you happen to use Vapor). This strategy solely fetches swift-log as a 3rd occasion dependency. 😍




Abstract

ConsoleKit in Vapor is a good way to put in writing CLI instruments and small scripts. The brand new Swift Argument Parser is a extra light-weight answer for a similar downside. In case your plan is to take care of databases via scripts otherwise you carry out a lot of networking or asynchronous operations it is perhaps higher to go along with Vapor, since you possibly can at all times develop by importing a brand new element from the ecosystem.


LEAVE A REPLY

Please enter your comment!
Please enter your name here