Polyglot
Languages Swift strings
Swift § strings

Strings

Swift’s String type is Unicode-correct by default — it stores text as a sequence of Extended Grapheme Clusters (user-perceived characters), not bytes or UTF-16 code units. The principal forms are double-quoted strings (admit interpolation and escapes) and multi-line strings (delimited by """). Strings admit substantial methods through the StringProtocol and the Sequence/Collection conformances. String interpolation via \(expression) admits embedded expressions; the mechanism is extensible via ExpressibleByStringInterpolation. Indexing uses String.Index rather than Int — admitting substantial Unicode correctness at the cost of some convenience. The combination — Unicode-correct grapheme clusters, the substantial method surface, multi-line literals, the extensible interpolation — is the substance of Swift’s text surface.

String literals

let single = "hello"
let multi = """
            line one
            line two
            """

let escaped = "with \"quotes\" and \n newlines"
let raw = #"raw \n string with no escapes"#       // raw string (Swift 5+)
let rawWithInterp = #"hello \#(name)"#             // raw with explicit interpolation

The """ form admits multi-line content; the indentation of the closing """ is stripped from each line:

let html = """
    <html>
        <body>
            <p>Hello</p>
        </body>
    </html>
    """                                            // 4-space indent stripped from each line

The conventional discipline:

  • Double quotes for single-line strings.
  • """ for multi-line strings.
  • #"..."# for strings with substantial backslashes (regex, paths).

String interpolation

let name = "Alice"
let age = 30
let greeting = "Hello, \(name)! You are \(age) years old."

let pi = "Pi: \(Double.pi.formatted(.number.precision(.fractionLength(2))))"

The \(expression) admits any expression. The compiler builds a String by calling each component’s description (via CustomStringConvertible).

For substantial format control, Swift 5.5+ admits formatted output:

import Foundation

let n = 1234567.89
n.formatted()                                      // "1,234,567.89"
n.formatted(.number.precision(.fractionLength(2)))
n.formatted(.percent)
n.formatted(.currency(code: "USD"))

Date.now.formatted(date: .complete, time: .complete)

Characters and graphemes

A Character is a single extended grapheme cluster:

let s = "héllo"

s.count                                            // 5 (graphemes)
                                                   // not 6 (bytes) or 5 (UTF-16 code units)

for c in s {
    print(c)                                       // h, é, l, l, o
}

let first: Character = "é"                         // single character
let manyCodePoints: Character = "👨‍👩‍👧"           // also one Character (combining family)

The mechanism admits substantial Unicode correctness — what users perceive as one character is one Character, regardless of how many UTF code units it occupies.

Indexing

String indexing uses String.Index rather than Int:

let s = "hello"

let first = s[s.startIndex]                        // "h"
let second = s[s.index(after: s.startIndex)]
let last = s[s.index(before: s.endIndex)]

let i = s.index(s.startIndex, offsetBy: 2)
let third = s[i]                                   // "l"

// Slicing:
let substring = s[s.startIndex..<i]                // "he" (Substring)
let slice = s[s.index(s.startIndex, offsetBy: 1)...]

The index-by-Int form is not admitted directly — the underlying string is variable-width Unicode, so position-by-integer is not constant-time:

let third = s[2]                                   // ERROR: Int not admitted as String.Index

For Int-style indexing, conversion via index(_:offsetBy:) admits substantial flexibility but with O(n) cost.

Substring

A Substring shares storage with its parent String:

let s = "hello world"
let prefix = s.prefix(5)                           // "hello" — Substring
let suffix = s.suffix(5)
let dropped = s.dropFirst(6)                       // "world"

// Convert to String:
let independent = String(prefix)                   // independent copy

The conventional discipline converts Substring to String for storage; substrings reference the parent and may keep substantial memory alive.

String methods

The substantial method surface:

let s = "hello world"

s.count
s.isEmpty
s.contains("ll")                                   // true
s.hasPrefix("hello")                               // true
s.hasSuffix("world")                               // true

s.uppercased()                                     // "HELLO WORLD"
s.lowercased()
s.capitalized                                      // "Hello World"

s.trimmingCharacters(in: .whitespaces)             // requires Foundation
s.replacingOccurrences(of: "world", with: "Swift") // requires Foundation
s.components(separatedBy: " ")                     // requires Foundation

// Splitting:
s.split(separator: " ")                            // [Substring]
s.split(separator: " ", maxSplits: 2)
s.split(separator: " ", omittingEmptySubsequences: false)

// Joining:
["a", "b", "c"].joined()                           // "abc"
["a", "b", "c"].joined(separator: ", ")            // "a, b, c"

The Foundation framework adds substantial additional methods (regex, formatting, etc.).

Iteration

let s = "hello"

for char in s {
    print(char)                                    // h, e, l, l, o (Character each)
}

// As bytes (UTF-8):
for byte in s.utf8 {
    print(byte)
}

// As UTF-16:
for unit in s.utf16 {
    print(unit)
}

// As Unicode scalars:
for scalar in s.unicodeScalars {
    print(scalar)                                   // Unicode.Scalar
}

The principal views: String, String.UTF8View, String.UTF16View, String.UnicodeScalarView.

Conversion

// Number to String:
let s = String(42)                                 // "42"
let s = String(3.14)                               // "3.14"
let s = "\(42)"                                    // interpolation
let s = String(format: "%.2f", 3.14)               // "3.14" (requires Foundation)

// String to number:
let n = Int("42")                                  // Int? (returns nil on failure)
let d = Double("3.14")                             // Double?
let n = Int("abc")                                 // nil

// Boolean:
String(true)                                       // "true"
Bool("true")                                       // Optional(true)

// Character to String:
let c: Character = "A"
let s = String(c)

The conventional defence for parsing is Optional returns; the conventional defence for formatting is interpolation or formatted().

Regular expressions (Swift 5.7+)

Since Swift 5.7, regular expressions are built in:

let s = "Date: 2026-01-15"

if let match = s.firstMatch(of: /(\d{4})-(\d{2})-(\d{2})/) {
    let (full, year, month, day) = match.output
    print("year: \(year), month: \(month), day: \(day)")
}

// Replace:
let cleaned = s.replacing(/\d+/, with: "X")

// Split:
let parts = s.split(separator: /\s+/)

The regex literal /.../is admitted directly; the RegexBuilder DSL (Swift 5.7+) admits substantial declarative regex construction:

import RegexBuilder

let dateRegex = Regex {
    Capture { OneOrMore(.digit) }
    "-"
    Capture { OneOrMore(.digit) }
    "-"
    Capture { OneOrMore(.digit) }
}

For older Swift, the NSRegularExpression (from Foundation) is the conventional alternative.

Common patterns

Formatted strings

let total = 42
let price = 9.99
let report = """
    Items: \(total)
    Total: $\(String(format: "%.2f", price * Double(total)))
    """

Building a string

// Inefficient (each += allocates):
var result = ""
for item in items {
    result += "\(item)\n"
}

// Efficient with += and explicit String(...):
var result = ""
for item in items {
    result.append("\(item)\n")
}

// Or with reduce:
let result = items.map(String.init).joined(separator: "\n")

// Or with reserveCapacity for known sizes:
var s = String()
s.reserveCapacity(items.count * 10)

Multi-line for SQL/HTML

let sql = """
    SELECT id, name, email
    FROM users
    WHERE active = TRUE
    ORDER BY created_at DESC
    """

let html = """
    <html>
        <body>
            <h1>\(title)</h1>
            <p>\(content)</p>
        </body>
    </html>
    """

Parsing

import Foundation

let input = "42"
guard let n = Int(input) else {
    throw ParseError.invalidNumber(input)
}

// With trimming:
let cleaned = input.trimmingCharacters(in: .whitespacesAndNewlines)

String slicing

let s = "hello world"

// First 5:
let first = s.prefix(5)                            // "hello"

// Last 5:
let last = s.suffix(5)                             // "world"

// Drop first/last:
let dropped = s.dropFirst()                        // "ello world"
let dropFirst3 = s.dropFirst(3)                    // "lo world"
let dropLast = s.dropLast(6)                       // "hello"

// Custom range:
let start = s.index(s.startIndex, offsetBy: 2)
let end = s.index(s.startIndex, offsetBy: 5)
let sub = String(s[start..<end])                   // "llo"

Case-insensitive comparison

let a = "Hello"
let b = "hello"

a.lowercased() == b.lowercased()                   // true
a.caseInsensitiveCompare(b) == .orderedSame        // requires Foundation

Word splitting

let words = "the quick brown fox".split(separator: " ")
// [Substring]: ["the", "quick", "brown", "fox"]

// With Foundation:
import Foundation
let words = "the quick brown fox".components(separatedBy: " ")
// [String]

Padding

// Left-pad:
String(repeating: "0", count: 5 - "42".count) + "42"  // "00042"

// Or with format (Foundation):
String(format: "%05d", 42)                         // "00042"

Validation

import Foundation

let email = "alice@example.com"

let isValid = email.range(of: #"^[^\s@]+@[^\s@]+\.[^\s@]+$"#, options: .regularExpression) != nil

// Swift 5.7+:
let isValid = email.contains(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)

Custom string interpolation

extension String.StringInterpolation {
    mutating func appendInterpolation(_ value: Date, format: String) {
        let formatter = DateFormatter()
        formatter.dateFormat = format
        appendLiteral(formatter.string(from: value))
    }
}

let formatted = "Today is \(Date.now, format: "yyyy-MM-dd")"

The mechanism admits substantial extensible interpolation — conventional in the Swift ecosystem.

A note on Unicode correctness

The principal Unicode features:

  • Extended Grapheme Clusters — what users perceive as one character.
  • Canonical equivalence — different code-point sequences may produce the same character.
  • Comparison: == compares by canonical equivalence, not by code units.
let a = "é"                                        // single Unicode code point
let b = "e\u{0301}"                                // e + combining acute accent (two code points)

a.count                                            // 1
b.count                                            // 1 (one grapheme)
a == b                                             // true (canonical equivalence)
a.unicodeScalars.count                             // 1
b.unicodeScalars.count                             // 2

The mechanism admits substantial correctness for international text; the cost is some performance (string operations are not constant-time).

A note on the conventional discipline

The contemporary Swift strings advice:

  • Use double quotes for single-line strings.
  • Use """ for multi-line strings.
  • Use #"..."# for raw strings (regex, paths).
  • Use string interpolation for formatting.
  • Use formatted() (Swift 5.5+) for substantial number/date formatting.
  • Use prefix(_:), suffix(_:), dropFirst(_:), dropLast(_:) over manual indexing.
  • Use the regex literal /.../ (Swift 5.7+) for substantial regex.
  • Convert Substring to String for storage.
  • Use .utf8 for byte-level operations.
  • Trust the Unicode correctnesscount returns graphemes, not bytes.

The combination — Unicode-correct strings, the multi-line literal form, the substantial interpolation, the regex integration, the substantial Foundation extensions — is the substance of Swift’s text surface. The discipline produces clear, internationally-correct text handling with substantial built-in functionality.