Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions Examples/default-as-flag/DefaultAsFlag.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Argument Parser open source project
//
// Copyright (c) 2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
//
//===----------------------------------------------------------------------===//

import ArgumentParser

@main
struct DefaultAsFlag: ParsableCommand {
static let configuration = CommandConfiguration(
abstract: "A utility demonstrating defaultAsFlag options.",
discussion: """
This command shows how defaultAsFlag options can work both as flags
and as options with values.
"""
)

@Option(defaultAsFlag: "default", help: "A string option with defaultAsFlag.")
var stringFlag: String?

@Option(defaultAsFlag: 42, help: "An integer option with defaultAsFlag.")
var numberFlag: Int?

@Option(defaultAsFlag: true, help: "A boolean option with defaultAsFlag.")
var boolFlag: Bool?

@Option(
defaultAsFlag: "transformed",
help: "A string option with transform and defaultAsFlag.",
transform: { $0.uppercased() }
)
var transformFlag: String?

@Option(name: .shortAndLong, help: "A regular option for comparison.")
var regular: String?

@Argument
var additionalArgs: [String] = []

func run() {
print("String flag: \(stringFlag?.description ?? "nil")")
print("Number flag: \(numberFlag?.description ?? "nil")")
print("Bool flag: \(boolFlag?.description ?? "nil")")
print("Transform flag: \(transformFlag?.description ?? "nil")")
print("Regular option: \(regular?.description ?? "nil")")
print("Additional args: \(additionalArgs)")
}
}
5 changes: 5 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ var package = Package(
name: "color",
dependencies: ["ArgumentParser"],
path: "Examples/color"),
.executableTarget(
name: "default-as-flag",
dependencies: ["ArgumentParser"],
path: "Examples/default-as-flag"
),

// Tools
.executableTarget(
Expand Down
5 changes: 5 additions & 0 deletions [email protected]
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ var package = Package(
name: "color",
dependencies: ["ArgumentParser"],
path: "Examples/color"),
.executableTarget(
name: "default-as-flag",
dependencies: ["ArgumentParser"],
path: "Examples/default-as-flag"
),

// Tools
.executableTarget(
Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ that you need to collect from the command line.
Decorate each stored property with one of `ArgumentParser`'s property wrappers,
and then declare conformance to `ParsableCommand` and add the `@main` attribute.
(Note, for `async` renditions of `run`, conform to `AsyncParsableCommand` rather
than `ParsableCommand`.)
than `ParsableCommand`.)
Finally, implement your command's logic in the `run()` method.

```swift
Expand Down Expand Up @@ -70,7 +70,7 @@ OPTIONS:

## Documentation

For guides, articles, and API documentation see the
For guides, articles, and API documentation see the
[library's documentation on the Web][docs] or in Xcode.

- [ArgumentParser documentation][docs]
Expand All @@ -88,6 +88,7 @@ This repository includes a few examples of using the library:
- [`roll`](Examples/roll/main.swift) is a simple utility implemented as a straight-line script.
- [`math`](Examples/math/Math.swift) is an annotated example of using nested commands and subcommands.
- [`count-lines`](Examples/count-lines/CountLines.swift) uses `async`/`await` code in its implementation.
- [`default-as-flag`](Examples/default-as-flag/DefaultAsFlag.swift) demonstrates hybrid options that can work both as flags and as options with values.

You can also see examples of `ArgumentParser` adoption among Swift project tools:

Expand All @@ -104,7 +105,7 @@ The public API of version 1.0.0 of the `swift-argument-parser` package
consists of non-underscored declarations that are marked public in the `ArgumentParser` module.
Interfaces that aren't part of the public API may continue to change in any release,
including the exact wording and formatting of the autogenerated help and error messages,
as well as the package’s examples, tests, utilities, and documentation.
as well as the package’s examples, tests, utilities, and documentation.

Future minor versions of the package may introduce changes to these rules as needed.

Expand All @@ -115,7 +116,7 @@ Requiring a new Swift release will only require a minor version bump.

## Adding `ArgumentParser` as a Dependency

To use the `ArgumentParser` library in a SwiftPM project,
To use the `ArgumentParser` library in a SwiftPM project,
add it to the dependencies for your package and your command-line executable target:

```swift
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ struct Lucky: ParsableCommand {
```

```
% lucky
% lucky
Your lucky numbers are:
7 14 21
% lucky 1 2 3
Expand Down Expand Up @@ -327,6 +327,89 @@ If a default is not specified, the user must provide a value for that argument/o
You must also always specify a default of `false` for a non-optional `Bool` flag, as in the example above. This makes the behavior consistent with both normal Swift properties (which either must be explicitly initialized or optional to initialize a `struct`/`class` containing them) and the other property types.


### Creating hybrid flag/option behavior with defaultAsFlag

The `defaultAsFlag` parameter allows you to create options that can work both as flags (without values) and as options (with values). This provides flexible command-line interfaces where users can choose between concise flag usage or explicit value specification.

```swift
struct Example: ParsableCommand {
@Option(defaultAsFlag: "default", help: "Set output format.")
var format: String?

@Option(defaultAsFlag: 8080, help: "Server port.")
var port: Int?

func run() {
print("Format: \(format ?? "none")")
print("Port: \(port ?? 3000)")
}
}
```

**Command-line behavior:**
```
% example # format = nil, port = nil
% example --format # format = "default", port = nil
% example --format json # format = "json", port = nil
% example --port # format = nil, port = 8080
% example --port 9000 # format = nil, port = 9000
```

The `defaultAsFlag` parameter creates a hybrid that supports both patterns:
- **Flag behavior**: `--format` (sets format to "default")
- **Option behavior**: `--format json` (sets format to "json")
- **No usage**: format remains `nil`

#### Type requirements

- The property **must** be optional (`T?`)
- The `defaultAsFlag` value must be of the unwrapped type (`T`)
- All standard `ExpressibleByArgument` types are supported (String, Int, Bool, Double, etc.)

#### Advanced usage

You can combine `defaultAsFlag` with transform functions:

```swift
@Option(
defaultAsFlag: "info",
help: "Set log level.",
transform: { $0.uppercased() }
)
var logLevel: String?
```

**Behavior:**
```
% app --log-level # logLevel = "INFO" (transformed default)
% app --log-level debug # logLevel = "DEBUG" (transformed input)
```

#### Help display

DefaultAsFlag options show special help formatting to distinguish them from regular defaults:

```
OPTIONS:
--format [<format>] Set output format. (default as flag: json)
--port [<port>] Server port. (default as flag: 8080)
```

Note the `(default as flag: ...)` text instead of regular `(default: ...)`, and the optional value syntax `[<value>]` instead of required `<value>`.

#### Value detection

The parser determines whether a value follows the option:

1. **Next argument is a value** if it doesn't start with `-` and isn't another known option
2. **No value available**: Use the `defaultAsFlag` value
3. **Explicit value provided**: Parse and use that value

This works with all parsing strategies (`.next`, `.scanningForValue`, `.unconditional`), though `.unconditional` defeats the purpose by always requiring a value.

For complete examples and API reference, see the [`default-as-flag`](https://github.com/apple/swift-argument-parser/tree/main/Examples/default-as-flag) example.


### Specifying a parsing strategy

When parsing a list of command-line inputs, `ArgumentParser` distinguishes between dash-prefixed keys and un-prefixed values. When looking for the value for a key, only an un-prefixed value will be selected by default.
Expand Down Expand Up @@ -479,7 +562,7 @@ When appropriate, you can process supported arguments and ignore unknown ones by
```swift
struct Example: ParsableCommand {
@Flag var verbose = false

@Argument(parsing: .allUnrecognized)
var unknowns: [String] = []

Expand Down
Loading
Loading