<!--
{
  "availability" : [
    "iOS: 18.0.0 -",
    "iPadOS: 18.0.0 -",
    "macCatalyst: 18.0.0 -",
    "macOS: 15.0.0 -",
    "tvOS: 18.0.0 -",
    "visionOS: 1.0.0 -",
    "watchOS: 11.0.0 -"
  ],
  "documentType" : "symbol",
  "framework" : "Foundation",
  "identifier" : "/documentation/Foundation/DiscreteFormatStyle",
  "metadataVersion" : "0.1.0",
  "role" : "Protocol",
  "symbol" : {
    "kind" : "Protocol",
    "modules" : [
      "Foundation"
    ],
    "preciseIdentifier" : "s:10Foundation19DiscreteFormatStyleP"
  },
  "title" : "DiscreteFormatStyle"
}
-->

# DiscreteFormatStyle

A format style that transforms a continuous input into a discrete output and provides
information about its discretization boundaries.

```
protocol DiscreteFormatStyle<FormatInput, FormatOutput> : FormatStyle
```

## Overview

Use this protocol to keep displays up to date if input changes continuously, or to iterate
over all possible outputs of a [`FormatStyle`](/documentation/Foundation/FormatStyle) by obtaining the next discrete input in either direction
from [`discreteInput(before:)`](/documentation/Foundation/DiscreteFormatStyle/discreteInput(before:)) or [`discreteInput(after:)`](/documentation/Foundation/DiscreteFormatStyle/discreteInput(after:)).

## Ordering of Inputs

The ordering over [`FormatInput`](/documentation/Foundation/FormatStyle/FormatInput)
defined by [`discreteInput(before:)`](/documentation/Foundation/DiscreteFormatStyle/discreteInput(before:)) / [`discreteInput(after:)`](/documentation/Foundation/DiscreteFormatStyle/discreteInput(after:)) must be
consistent between the two functions. If [`FormatInput`](/documentation/Foundation/FormatStyle/FormatInput) conforms to the
`Comparable` protocol, the format style’s ordering *should* be consistent with the canonical ordering
defined via the `Comparable` conformance, i.e. it should hold that
`discreteInput(before: x)! < x < discreteInput(after: x)!` where discrete inputs
are not nil.

## Stepping through Discrete Input/Output Pairs

One use case of this protocol is enumerating all discrete inputs of a format style and their respective
outputs.

While the [`discreteInput(before:)`](/documentation/Foundation/DiscreteFormatStyle/discreteInput(before:)) and [`discreteInput(after:)`](/documentation/Foundation/DiscreteFormatStyle/discreteInput(after:))
functions are the right tool for that, they do not give a guarantee that their respective return values
actually produce an output that is different from the output produced by formatting the `input` value
used when calling [`discreteInput(before:)`](/documentation/Foundation/DiscreteFormatStyle/discreteInput(before:)) / [`discreteInput(after:)`](/documentation/Foundation/DiscreteFormatStyle/discreteInput(after:)), they only
provide a value that produces a different output for *most* inputs. E.g. when formatting a floating point
value as an integer, we can get the next discrete input after `x` by calculating `floor(x + 1)`.
However, when rounding toward zero, the whole interval (-1;1) formats as zero. It would be ok for a
discrete format style to ignore that edge case and return `0` for the [`discreteInput(after:)`](/documentation/Foundation/DiscreteFormatStyle/discreteInput(after:)) a
negative value greater than `-1`. Therefore, to enumerate all discrete input/output pairs, adjacent
outputs must be deduplicated in order to guarantee no adjacent outputs are the same.

The following example produces all discrete input/output pairs for inputs in a given `range` making
sure adjacent outputs are unequal:

```swift
extension DiscreteFormatStyle
    where FormatInput : Comparable, FormatOutput : Equatable
{
        func enumerated(
        in range: ClosedRange<FormatInput>
    ) -> [(input: FormatInput, output: FormatOutput)] {
        var input = range.lowerBound
        var output = format(input)

        var pairs = [(input: FormatInput, output: FormatOutput)]()
        pairs.append((input, output))

        // get the next discretization bound
        while let nextInput = discreteInput(after: input),
              // check that it is still in the requested `range`
              nextInput <= range.upperBound {
            // get the respective formatted output
            let nextOutput = format(nextInput)
            // deduplicate based on the formatted output
            if nextOutput != output {
                pairs.append((nextInput, nextOutput))
            }
                input = nextInput
            output = nextOutput
        }

        return pairs
    }
}
```

## Imperfect Discretization Boundaries

In some scenarios, a format style cannot provide precise discretization boundaries in
a performant manner. In those cases it must override [`input(before:)`](/documentation/Foundation/DiscreteFormatStyle/input(before:)) and
[`input(after:)`](/documentation/Foundation/DiscreteFormatStyle/input(after:)) to reflect that. For any discretization boundary `x` returned by either
[`discreteInput(before:)`](/documentation/Foundation/DiscreteFormatStyle/discreteInput(before:)) or [`discreteInput(after:)`](/documentation/Foundation/DiscreteFormatStyle/discreteInput(after:)) based on the
original input `y`, all values representable in the [`FormatInput`](/documentation/Foundation/FormatStyle/FormatInput)strictly  between
`x` and the return value of `input(after: x)` or `input(before: x)`, respectively, are not
guaranteed to produce the same formatted output as `y`.

The following schematic shows an overview of the guarantees given by the protocol:

```
xB = discreteInput(before: y)       y      xA = discreteInput(after: y)
      |                             |                             |
<-----+---+-------------------------+-------------------------+---+--->
          |                                                   |
 zB = input(after: xB)                          zA = input(before: xA)
```

- the formatted output for everything in `zB...zA` (including bounds) is **guaranteed** to be equal
  to `format(y)`
- the formatted output for `xB` and lower is **most likely** different from `format(y)`
- the formatted output for `xA` and higher is **most likely** different from `format(y)`
- the  formatted output between `xB` and `zB`, as well as `zA` and `xA` (excluding bounds) cannot
  be predicted

## Topics

### Instance Methods

[`func discreteInput(after: Self.FormatInput) -> Self.FormatInput?`](/documentation/Foundation/DiscreteFormatStyle/discreteInput(after:))

The next discretization boundary after the given input.

[`func discreteInput(before: Self.FormatInput) -> Self.FormatInput?`](/documentation/Foundation/DiscreteFormatStyle/discreteInput(before:))

The next discretization boundary before the given input.

[`func input(after: Self.FormatInput) -> Self.FormatInput?`](/documentation/Foundation/DiscreteFormatStyle/input(after:))

The next input value after the given input.

[`func input(before: Self.FormatInput) -> Self.FormatInput?`](/documentation/Foundation/DiscreteFormatStyle/input(before:))

The next input value before the given input.

## Relationships

### Conforming Types

[`AnchoredRelativeFormatStyle`](/documentation/Foundation/Date/AnchoredRelativeFormatStyle)

[`FormatStyle`](/documentation/Foundation/Date/FormatStyle)

[`Attributed-swift.struct`](/documentation/Foundation/Date/FormatStyle/Attributed-swift.struct)

[`Attributed-swift.struct`](/documentation/Foundation/Date/VerbatimFormatStyle/Attributed-swift.struct)

[`VerbatimFormatStyle`](/documentation/Foundation/Date/VerbatimFormatStyle)

[`ComponentsFormatStyle`](/documentation/Foundation/Date/ComponentsFormatStyle)

### Inherits From

[`FormatStyle`](/documentation/Foundation/FormatStyle)

[`Encodable`](/documentation/Swift/Encodable)

[`Equatable`](/documentation/Swift/Equatable)

[`Decodable`](/documentation/Swift/Decodable)

[`Hashable`](/documentation/Swift/Hashable)

---

Copyright &copy; 2026 Apple Inc. All rights reserved. | [Terms of Use](https://www.apple.com/legal/internet-services/terms/site.html) | [Privacy Policy](https://www.apple.com/privacy/privacy-policy)