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


Shell scripts are necessities on the server aspect. Learn 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 a large number 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 method 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 method so much.

Let me present you a easy howdy command:


import ArgumentParser

struct HelloCommand: ParsableCommand {
    @Argument(assist: "The identify to say howdy")
    var identify: String

    func run() throws {
        print("Whats up (self.identify)!")
    }
}
HelloCommand.principal()

Now I will present you tips on how to implement an identical command utilizing Vapor:


import Vapor

remaining class HelloCommand: Command {
    
    let assist = "This command will say howdy to a given identify."

    struct Signature: CommandSignature {
        @Argument(identify: "identify", assist: "The identify to say howdy")
        var identify: String
    }

    func run(utilizing context: CommandContext, signature: Signature) throws {
        print("Whats up (signature.identify)!")
    }
}

public func configure(_ app: Software) throws {
    app.instructions.use(HelloCommand(), as: "howdy")
}

As you’ll be able to see they nearly seem like the identical.


Should 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. A very good instance is a software that manipulates recordsdata on the system or one thing related. It is only one little dependency, nevertheless it removes a lot boilerplate out of your scripts. It permits you to concentrate on the script itself, as an alternative of parsing the command line inputs. Yow will discover 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 difficult duties along with your scripts. Something that is a part of your Vapor software will be triggered from a command, so you’ll be able to simply create a backend software that reads (or writes) information from the database utilizing Fluent 4. That is the primary benefit of utilizing a Vapor command, as an alternative a stanadlone Swift script.




Arguments, choices, flags

Let’s lengthen the howdy 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 identify for a similar factor. πŸ€“

Arguments are consumer supplied values learn so as (eg.: ./howdy joe bob john).

Now that the essential definitions, right here is the instance:

remaining class HelloCommand: Command {
        
    struct Signature: CommandSignature {

        @Argument(identify: "identify", assist: "The identify to say howdy")
        var identify: String

        @Possibility(identify: "greeting", quick: "g", assist: "Greeting used")
        var greeting: String?

        @Flag(identify: "capitalize", quick: "c", assist: "Capitalizes the identify")
        var capitalize: Bool
    }

    let assist = "This command will say howdy to a given identify."

    func run(utilizing context: CommandContext, signature: Signature) throws {
        let greeting = signature.greeting ?? "Whats up"
        var identify = signature.identify
        if signature.capitalize {
            identify = identify.capitalized
        }
        print("(greeting) (identify)!")
    }
}

Arguments are required by default, choices and flags are optionals. You possibly can have a customized identify (quick and lengthy) for every little thing, plus you’ll be able to customise the assistance message for each element.

swift run Run howdy john


swift run Run howdy john --greeting Hello


swift run Run howdy john --greeting Hello --capitalized


swift run Run howdy john -g Szia -c

You possibly can name the command utilizing a number of types. Be happy to select a most popular model. ⭐️



Subcommands

When command-line packages develop bigger, it may be helpful to divide them into a gaggle of smaller packages, offering an interface via subcommands. Utilities resembling git and the Swift bundle supervisor are capable of present assorted interfaces for every of their sub-functions by implementing subcommands resembling git department or swift bundle init.

Vapor can deal with command teams in a extremely cool manner. I will add an additional static property to call our instructions, since I do not prefer to repeat myself or bloat the code with pointless strings:

remaining class HelloCommand: Command {
    
    static var identify = "howdy"
        
    
}

struct WelcomeCommandGroup: CommandGroup {
    
    static var identify = "welcome"

    let assist: String
    let instructions: [String: AnyCommand]
    
    var defaultCommand: AnyCommand? {
        self.instructions[HelloCommand.name]
    }

    init() {
        self.assist = "web optimization command group assist"

        self.instructions = [
            HelloCommand.name: HelloCommand(),
        ]
    }
}

public func configure(_ app: Software) throws {

    app.instructions.use(WelcomeCommandGroup(), as: WelcomeCommandGroup.identify)
}


That is it, we simply moved our howdy command beneath the welcome namespace.

swift run Run welcome howdy john --greeting "Hello" --capitalize

Should you learn the Swift Argument Parser docs, you’ll be able to obtain the very same conduct via a customized CommandConfiguration. Personally, I favor Vapor’s method 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 you need to await the async operations to complete.

remaining class TodoCommand: Command {
    
    static let identify = "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: "Watch for async duties...")
        strive todo.create(on: app.db).wait()
        
        app.logger.discover("Todo is prepared.")
    }
}

There’s a throwing wait() technique that you would be able to make the most of to “keep within the loop” till every little thing is finished. You may as well get a pointer for the applying 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 bundle = Package deal(
    identify: "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 critical:

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.identify, 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’ll be able to do away with a lot 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 social gathering dependency. 😍




Abstract

ConsoleKit in Vapor is a good way to write down CLI instruments and small scripts. The brand new Swift Argument Parser is a extra light-weight answer for a similar drawback. In case your plan is to take care of databases via scripts otherwise you carry out plenty of networking or asynchronous operations it is perhaps higher to go together with Vapor, since you’ll be able to at all times develop by importing a brand new element from the ecosystem.


See also  ios - Do I nonetheless want CocoaPod set up?

Leave a Reply