Swift & Objective-C

Swift Optional: Distinguish nil, Empty Text, and Zero

Test six inputs, preserve missing and invalid states, and verify when a nil-coalescing fallback runs instead of treating zero as absence.

2 min read
Cover image for Swift Optional: Distinguish nil, Empty Text, and Zero

nil, an empty string, and numeric zero are different states. Replacing every optional with a default can erase the distinction between no input and an explicit zero. Decide whether absence is a normal default or a separate state first.

Mechanism and design choice

Optional represents presence with some and none. Use if let for local conditional use, guard let for early exit, and ?? when a meaningful default exists. Optional chaining yields nil for a missing link; force unwrapping traps on nil and is unsuitable for expected input failures. Primary source

Reproduce the experiment

quantity returns separate enum cases for missing, invalid, and valid quantities. Six checks cover nil, empty text, cat, -1, 0, and 7. A fallback function records how often ?? invokes it, and separate checks compare counts for empty and missing strings.

This standalone example was run on 2026-10-23 with Apple Swift 6.4 targeting arm64 macOS, in Swift 6 language mode. It needs no network or additional packages.

Download the complete Swift file

Run the command in the download directory. Preconditions stop execution if a check fails. Do not use -Ounchecked: it removes these checks.

swift -swift-version 6 swift-optional-enum-unwrapping.swift
enum Quantity: Equatable { case missing, invalid, value(Int) }
func quantity(_ raw: String?) -> Quantity {
    guard let raw else { return .missing }
    guard let value = Int(raw), value >= 0 else { return .invalid }
    return .value(value)
}

let cases: [(String?, Quantity)] = [
    (nil, .missing), ("", .invalid), ("cat", .invalid),
    ("-1", .invalid), ("0", .value(0)), ("7", .value(7))
]
for (raw, expected) in cases { precondition(quantity(raw) == expected) }

var fallbackCalls = 0
@MainActor func fallback() -> Int { fallbackCalls += 1; return 99 }
let present: Int? = 0
let absent: Int? = nil
precondition((present ?? fallback()) == 0 && fallbackCalls == 0)
precondition((absent ?? fallback()) == 99 && fallbackCalls == 1)

let empty: String? = ""
let missing: String? = nil
precondition(empty?.count == 0 && missing?.count == nil)
print("inputs=6 missing=1 invalid=3 valid=2 fallback-calls=1 empty-count=0 missing-count=nil")

Recorded output and interpretation

inputs=6 missing=1 invalid=3 valid=2 fallback-calls=1 empty-count=0 missing-count=nil

The results distinguish one missing input, three invalid inputs, and two valid values. some(0) does not call the fallback; none calls it once. Empty text has count 0, while chaining through a missing string yields nil. A zero-like value is not absence.

Scope and limitations

This quantity validator rejects negatives; it is not a universal number-input policy. Add whitespace handling, decimals, locale rules, and upper bounds as requirements demand. String! is also an optional declaration that may unwrap implicitly where needed, not an unconditional unwrap on every use.

What to check next

Decide whether missing and invalid inputs need different UI messages and match the server contract. If callers need more detailed failure reasons, preserve error information using throws or Result rather than silently replacing it with a default.

Related experiment

Sources and verification

  • The Swift Programming Language: The BasicsSwift.org · Official documentation · Checked October 23, 2026Supports: Source for the implementation contract and explanation. The output was checked separately using the experiment included here.