CLI фреймворк для Swift: создание консольных утилит на iOS/macOS

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

CLI фреймворк для Swift: создание консольных утилит на iOS и macOS

Разработка консольных приложений (CLI — Command Line Interface) для экосистемы Apple долгое время оставалась прерогативой Python, Ruby или Bash. Однако с появлением Swift и его мощного инструментария, включая Swift Package Manager (SPM), появилась возможность создавать быстрые, типизированные и легко поддерживаемые консольные утилиты на родном для iOS/macOS языке. В этой статье мы подробно рассмотрим основной фреймворк для построения CLI на Swift — Apple's ArgumentParser, а также альтернативные решения.

Что такое CLI фреймворк для Swift?

CLI-фреймворк — это библиотека, которая берёт на себя рутинные задачи по парсингу аргументов командной строки, генерации справки, обработке ошибок и организации команд. В мире Swift стандартом де-факто является ArgumentParser, разработанный Apple и входящий в состав Swift Open Source. Он позволяет декларативно описывать интерфейс вашей утилиты, а всё остальное делает автоматически.

Основные возможности ArgumentParser:

  • Декларативное определение команд — вы описываете структуру, а фреймворк сам парсит аргументы.
  • Автоматическая справка — флаги --help и -h генерируются автоматически.
  • Поддержка вложенных команд (как в git или swift).
  • Валидация типов — если ожидается число, а передана строка, будет выведена понятная ошибка.
  • Интеграция с SPM — достаточно добавить зависимость в Package.swift.

Установка и настройка

Для работы вам потребуется Swift 5.5+ (рекомендуется 5.7 или новее). Создайте новый консольный проект через SPM:

mkdir MyCLI
cd MyCLI
swift package init --type executable

Затем отредактируйте Package.swift, добавив зависимость от ArgumentParser:

// swift-tools-version:5.7
import PackageDescription

let package = Package(
    name: "MyCLI",
    dependencies: [
        .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.2.0")
    ],
    targets: [
        .executableTarget(
            name: "MyCLI",
            dependencies: [
                .product(name: "ArgumentParser", package: "swift-argument-parser")
            ]
        )
    ]
)

Теперь обновите зависимости и откройте проект:

swift package update
open Package.swift   # откроет в Xcode

Создание первой команды

Создайте файл main.swift (или Sources/MyCLI/main.swift) со следующим содержимым:

import ArgumentParser

struct Greet: ParsableCommand {
    static let configuration = CommandConfiguration(
        abstract: "Приветствие пользователя",
        discussion: "Эта команда выводит персонализированное приветствие."
    )

    @Argument(help: "Имя пользователя")
    var name: String

    @Option(name: .shortAndLong, help: "Количество повторов")
    var count: Int = 1

    @Flag(name: .shortAndLong, help: "Показать версию")
    var verbose: Bool = false

    mutating func run() throws {
        if verbose {
            print("Версия 1.0.0")
        }
        for _ in 0..<count {
            print("Привет, \(name)!")
        }
    }
}

Greet.main()

Теперь соберите и запустите:

swift run MyCLI Swift --count 3
# Вывод:
# Привет, Swift!
# Привет, Swift!
# Привет, Swift!

swift run MyCLI --help
# Usage: MyCLI <name> [--count <count>] [--verbose]
# Приветствие пользователя
# ...

Вложенные команды (подкоманды)

Для создания сложных утилит (как swift package или git) используйте вложенные команды. Пример структуры для работы с файлами:

struct FileTool: ParsableCommand {
    static let configuration = CommandConfiguration(
        commandName: "filetool",
        subcommands: [Read.self, Write.self, Delete.self],
        defaultSubcommand: Read.self
    )
}

struct Read: ParsableCommand {
    @Argument(help: "Путь к файлу")
    var path: String

    func run() throws {
        let content = try String(contentsOfFile                    

Рекомендаций