HomeiOS DevelopmentTips on how to write Swift scripts utilizing the brand new Command...

Tips on how to write Swift scripts utilizing the brand new Command API in Vapor 4?


Shell scripts are necessities on the server facet. Discover ways to construct Swift scripts on 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). 😉

However Vapor already had a considerably comparable strategy to construct scripts, however in Vapor 4 the Command API is best 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 so much.

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("Good day (self.title)!")
    }
}
HelloCommand.major()

Now I will present you how you can implement the same command utilizing Vapor:


import Vapor

last 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("Good day (signature.title)!")
    }
}

public func configure(_ app: Utility) throws {
    app.instructions.use(HelloCommand(), as: "good day")
}

As you possibly can see they virtually seem like the identical.


In case you love scripting, it is best to undoubtedly test swift-sh and Brisk


The Swift Argument Parser library is a light-weight answer in case you are solely in search of a easy Swift script. An excellent instance is a instrument that manipulates recordsdata on the system or one thing comparable. It is only one little dependency, nevertheless it removes a lot boilerplate out of your scripts. It lets you give attention to the script itself, as a substitute 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 beneficial if you wish to carry out extra difficult duties together with your scripts. Something that is a part of your Vapor utility will be triggered from a command, so you possibly can simply create a backend instrument that reads (or writes) data from the database utilizing Fluent 4. That is the principle benefit of utilizing a Vapor command, as a substitute a stanadlone Swift script.




Arguments, choices, flags

Let’s lengthen the good day command with a brand new choice and a flag. The primary distinction between an choice and a flag is that an choice 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 one dashed model makes use of a brief title for a similar factor. 🤓

Arguments are person offered values learn so as (eg.: ./good day joe bob john).

Now that you already know the fundamental definitions, right here is the instance:

last class HelloCommand: Command {
        
    struct Signature: CommandSignature {

        @Argument(title: "title", assist: "The title to say good day")
        var title: String

        @Possibility(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 good day to a given title."

    func run(utilizing context: CommandContext, signature: Signature) throws {
        let greeting = signature.greeting ?? "Good day"
        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 (quick and lengthy) for all the things, plus you possibly can customise the assistance message for each part.

swift run Run good day john


swift run Run good day john --greeting Hello


swift run Run good day john --greeting Hello --capitalized


swift run Run good day john -g Szia -c

You may name the command utilizing a number of types. Be at liberty to select 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 by subcommands. Utilities reminiscent of git and the Swift bundle supervisor are capable of present assorted interfaces for every of their sub-functions by implementing subcommands reminiscent of git department or swift bundle init.

Vapor can deal with command teams in a very cool means. I will 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 = "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 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 good day command below the welcome namespace.

swift run Run welcome good day john --greeting "Hello" --capitalize

In case you learn the Swift Argument Parser docs, you possibly can obtain the very same habits by a customized CommandConfiguration. Personally, I want 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 a must to look forward to 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: "Await async duties...")
        strive todo.create(on: app.db).wait()
        
        app.logger.discover("Todo is prepared.")
    }
}

There’s a throwing wait() methodology you could make the most of to “keep within the loop” till all the things is completed. It’s also possible to 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 utilize 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 additionally bundles a lot of different core issues. What if I simply need the goodies for my Swift scripts? No downside. 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 need to do some further work in your major.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 possibly can eliminate a lot of the community associated core packages (which are included by default in case you use Vapor). This strategy solely fetches swift-log as a 3rd social gathering 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 keep up databases by scripts otherwise you carry out a lot of networking or asynchronous operations it is likely to be higher to go along with Vapor, since you possibly can all the time develop by importing a brand new part from the ecosystem.


RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments