Swift: Swift Input, Output, and Debugging

Writing code is only half the job. This lesson teaches you how to use print, assertions, and breakpoints to find and fix errors in your code.

1. What You'll Learn


2. A True Story: A Self-Taught Beginner

(1) The Pain Point: Logically Correct Code, Wrong Results

Mike is a self-taught Swift beginner working on a program to calculate an average score:

SWIFT
let scores = [85, 92, 78, 90, 88]
var total = 0
for i in 0...5 {
    total += scores[i]
}
let average = total / 5
print("Average: \(average)")

The program crashed immediately. Mike couldn't figure out why. He spent 30 minutes searching online, trying various fixes. Every modification required re-running, but he still couldn't pinpoint the problem.

(2) The Solution: Print-Based Debugging

A friend suggested using print to output intermediate variables to locate the issue:

SWIFT
let scores = [85, 92, 78, 90, 88]
print("Array length: \(scores.count)")     // Output: 5
var total = 0
for i in 0..<scores.count {               // Use count instead of hardcoding
    print("Processing index \(i): value = \(scores[i])")
    total += scores[i]
}
print("Total: \(total)")
let average = total / 5
print("Average: \(average)")

Output:

TEXT 📖 Display only
Array length: 5
Processing index 0: value = 85
Processing index 1: value = 92
Processing index 2: value = 78
Processing index 3: value = 90
Processing index 4: value = 88
Total: 433
Average: 86

By printing intermediate variables, Mike immediately discovered that the array indices are 0-4, but he had used 0...5 (which includes index 5).

(3) The Result: Debugging Efficiency After Mastering Print

Dimension Before (Guessing) After (Print Debugging)
Time to locate issue 30+ minutes 1-2 minutes
Fix accuracy 50% (often wrong) 95%
Code understanding "No idea why it crashed" "Know the state at every step"
Confidence solving new issues 3/10 8/10

3. Advanced Print Usage

print is Swift's most commonly used debugging tool, but its parameters do more than just output a value.

100%
graph TB
    A[print Function] --> B["items: Values to output"]
    A --> C["separator: Delimiter"]
    A --> D["terminator: End character"]
    A --> E["to: Output target"]
    B --> F[Multiple values separated by commas]
    C --> G["Default: space"]
    C --> H["Custom: | or , etc."]
    D --> I["Default: newline"]
    D --> J["Custom: empty string"]
Parameter Type Default Purpose
items Any... Required Content to output
separator String " " Separator between multiple items
terminator String "\n" Trailing newline character
to TextOutputStream nil Output target (default: console)

(1) separator and terminator

SWIFT
// Default: space-separated, newline-terminated
print("Hello", "Swift", "World")
// Hello Swift World
// Custom separator
print("Hello", "Swift", "World", separator: ", ")
// Hello, Swift, World
// Custom terminator (no newline)
print("Loading", terminator: "...")
print("Done")
// Loading...Done
// Combined usage
print("A", "B", "C", separator: " | ", terminator: ".\n")
// A | B | C.

(2) debugPrint and dump

debugPrint outputs debug information (with quotes and type info); dump outputs detailed structures:

SWIFT
let name = "Alice"
let numbers = [1, 2, 3]
print(name)          // Alice
debugPrint(name)     // "Alice"
dump(name)           // - "Alice"
print(numbers)       // [1, 2, 3]
debugPrint(numbers)  // [1, 2, 3]
dump(numbers)
// ▿ 3 elements
//   - 0 : 1
//   - 1 : 2
//   - 2 : 3
Function Purpose String Output Array Output
print Normal output Alice [1, 2, 3]
debugPrint Debug output (shows type info) "Alice" [1, 2, 3]
dump Detailed structure output - "Alice" One element per line

▶ Example: Formatted Log Output

SWIFT
// ============================================
// Simulate system log output
// Demonstrates advanced print parameters and debugPrint
// ============================================
let event = "USER_LOGIN"
let user = "Alice"
let statusCode = 200
let duration = 0.045
// 1. Use separator to format the log
print("[\(event)]", user, "Status: \(statusCode)", separator: " | ", terminator: "")
print(" (\(duration)s)")
// [USER_LOGIN] | Alice | Status: 200 (0.045s)
// 2. Output tabular data
print()
print("=== Report ===")
print("Item", "Price", "Qty", separator: " | ")
print("-----", "-----", "---", separator: " | ")
print("Book", "12.99", "3", separator: " | ")
print("Pen", "1.50", "10", separator: " | ")
print("Bag", "49.99", "1", separator: " | ")
// 3. debugPrint for development debugging
let input: String? = "test"
debugPrint("Debug: input = \(input)")
// "Debug: input = Optional(\"test\")"

Output:

TEXT 📖 Display only
[USER_LOGIN] | Alice | Status: 200 (0.045s)

=== Report ===
Item | Price | Qty
----- | ----- | ---
Book | 12.99 | 3
Pen | 1.50 | 10
Bag | 49.99 | 1
Debug: input = Optional("test")

4. Assertions and Preconditions

Assertions and preconditions are Swift's built-in defensive programming tools, catching logic errors early during development.

100%
graph LR
    A[Runtime Checks] --> B[assert]
    A --> C[precondition]
    B --> D[Debug-only mode]
    B --> E[Catch issues during development]
    C --> F[Debug + Release]
    C --> G[Unrecoverable errors]
Function Active In Purpose Example
assert Debug only Internal consistency checks during development assert(age > 0)
assertionFailure Debug only Unconditional assertion trigger assertionFailure("Should not reach here")
precondition All modes Precondition checks precondition(!name.isEmpty)
preconditionFailure All modes Unconditional termination preconditionFailure("Fatal error")

(1) assert Debug Assertions

assert only takes effect in Debug mode; it is removed in Release mode, so there is no performance impact:

SWIFT
func calculateDiscount(price: Double, percent: Double) -> Double {
    assert(price > 0, "Price must be greater than 0")
    assert(percent >= 0 && percent <= 100, "Discount must be between 0-100")
    let discount = price * percent / 100.0
    return price - discount
}
let finalPrice = calculateDiscount(price: 100.0, percent: 20)
print(finalPrice)  // 80.0
// The following would trigger assertion failures in Debug mode:
// calculateDiscount(price: -10, percent: 20)  // ❌ assert fails
// calculateDiscount(price: 100, percent: 150) // ❌ assert fails

(2) precondition Checks

precondition takes effect in both Debug and Release modes—it terminates the program immediately when a condition cannot be satisfied:

SWIFT
func sendEmail(to address: String, message: String) {
    precondition(address.contains("@"), "Invalid email address: \(address)")
    precondition(!message.isEmpty, "Message cannot be empty")
    print("Sending email to \(address): \(message)")
}
sendEmail(to: "alice@example.com", message: "Hello!")
// Sending email to alice@example.com: Hello!
// The following would trigger precondition failures (all modes):
// sendEmail(to: "invalid", message: "Hi")
💡 Tip: assert is for "internal checks" (ensuring your own code is correct); precondition is for "external contracts" (ensuring callers meet requirements). Use assert heavily during debug; use precondition for public API boundaries.

▶ Example: Parameter Validation

SWIFT
// ============================================
// User registration parameter checks
// Demonstrates assert and precondition usage
// ============================================
import Foundation
func registerUser(name: String, age: Int, email: String) {
    // precondition: Public API contract (active in all modes)
    precondition(name.count >= 2, "Username must be at least 2 characters")
    precondition(age >= 18, "User must be at least 18 years old")
    precondition(email.contains("@"), "Invalid email format")
    // assert: Internal logic check (Debug only)
    let emailParts = email.split(separator: "@")
    assert(emailParts.count == 2, "Email should contain exactly one @ symbol")
    let domain = String(emailParts[1])
    assert(domain.contains("."), "Email domain is invalid")
    // Actual registration logic
    print("Registration successful: \(name), age \(age)")
    print("Confirmation email sent to: \(email)")
}
// Valid call
registerUser(name: "Alice", age: 28, email: "alice@example.com")
print("---")
// Calls that would trigger precondition failures (commented out to avoid crashing)
// registerUser(name: "A", age: 20, email: "test@test.com")

Output:

TEXT 📖 Display only
Registration successful: Alice, age 28
Confirmation email sent to: alice@example.com
---

5. Playground Debugging

Playgrounds offer more powerful debugging capabilities than print, including live previews and breakpoints.

(1) Playground Live Preview

The sidebar in Playgrounds shows the result of each line in real time:

SWIFT
// Run in Playground — the sidebar shows each step's result
let name = "Alice"    // "Alice"
var score = 0         // 0
score += 85           // 85
score += 92           // 177
let average = score / 2  // 88
100%
graph TB
    A[Playground Debugging] --> B[Live Results Panel]
    A --> C[Breakpoint Debugging]
    A --> D[Value History]
    B --> E[Results displayed per line]
    C --> F[Pause / Step / Continue]
    D --> G[Variable value change curves]
Feature How To Use Purpose
Live preview Code editing auto-displays results on the right Quickly see each step's result
Breakpoints Click the line number to add a breakpoint Pause execution, trace step by step
Value history Hover over a variable See how a variable changes over time
Expression preview Select code Quickly evaluate a selected expression

(2) Breakpoint Debugging

Click on the left side of a line number to set a breakpoint. When the program reaches that line, it pauses so you can inspect all current variable values:

SWIFT
func calculateTotal(items: [Double], tax: Double) -> Double {
    var subtotal = 0.0
    // Set a breakpoint on this line
    for item in items {
        subtotal += item
    }
    let taxAmount = subtotal * tax
    let total = subtotal + taxAmount
    return total
}
let cart = [29.99, 49.99, 15.00]
let final = calculateTotal(items: cart, tax: 0.08)
print("Total: $\(final)")
💡 Tip: In Xcode or Playgrounds, click a line number to add a breakpoint. When triggered, the debug area at the bottom shows all current variable values. You can choose "Continue" or "Step Over" to proceed.

▶ Example: Playground Debugging in Practice

SWIFT
// ============================================
// Practice Playground debugging techniques
// Paste this code into a Playground and run it
// ============================================
import Foundation
// 1. Set a breakpoint on the line below to observe variable values
let data: [String: Any] = [
    "product": "Swift Book",
    "price": 39.99,
    "quantity": 3,
    "inStock": true
]
// 2. Step through the following code
let productName = data["product"] as? String ?? "Unknown"
let price = data["price"] as? Double ?? 0.0
let quantity = data["quantity"] as? Int ?? 0
let inStock = data["inStock"] as? Bool ?? false
print("Product: \(productName)")
print("Price: $\(price)")
print("Quantity: \(quantity)")
print("In Stock: \(inStock)")
// 3. Observe the conditional check results
if inStock {
    let totalCost = price * Double(quantity)
    print("Total Cost: $\(totalCost)")
} else {
    print("Item is out of stock")
}
// 4. Use dump to inspect complex data
print("\n=== Debug Info ===")
dump(data)

Output:

TEXT 📖 Display only
Product: Swift Book
Price: $39.99
Quantity: 3
In Stock: true
Total Cost: $119.97

=== Debug Info ===
▿ 4 key/value pairs
  ▿ (2 elements)
    - key: "product"
    - value: "Swift Book"
  ▿ (2 elements)
    - key: "price"
    - value: 39.99
  ▿ (2 elements)
    - key: "quantity"
    - value: 3
  ▿ (2 elements)
    - key: "inStock"
    - value: true

6. Full Example: Grade Analysis Debugging Tool

SWIFT
// ============================================
// Grade analysis tool
// Combines print debugging + assertion checks + formatted output
// ============================================
import Foundation
// 1. Student grade data
let studentName = "Alice"
let scores = [85.0, 92.0, 78.0, 90.0, 88.0]
// 2. Debug assertions to validate data
assert(scores.count > 0, "Score list cannot be empty")
for score in scores {
    assert(score >= 0 && score <= 100, "Score must be between 0-100: \(score)")
}
// 3. Calculate statistics
var total = 0.0
var highest = scores[0]
var lowest = scores[0]
// Set a breakpoint here to observe the loop process
for (index, score) in scores.enumerated() {
    print("[DEBUG] Index \(index): \(score)")
    total += score
    if score > highest { highest = score }
    if score < lowest { lowest = score }
}
let average = total / Double(scores.count)
// 4. Formatted output
print(String(repeating: "=", count: 35))
print("Student: \(studentName)")
print(String(repeating: "-", count: 35))
print("Subject", "Score", "Grade", separator: " | ")
print(String(repeating: "-", count: 35))
for (index, score) in scores.enumerated() {
    let grade = score >= 90 ? "A" : score >= 80 ? "B" : score >= 70 ? "C" : "D"
    print("Subject \(index + 1)", score, grade, separator: " | ")
}
print(String(repeating: "-", count: 35))
print("Total: \(total)")
print("Average: \(String(format: "%.1f", average))")
print("Highest: \(highest)")
print("Lowest: \(lowest)")
print(String(repeating: "=", count: 35))
// 5. Precondition to ensure reasonable results
precondition(average >= 0 && average <= 100, "Average is outside expected range")
precondition(highest >= lowest, "Highest score should not be lower than lowest")

Output:

TEXT 📖 Display only
[DEBUG] Index 0: 85.0
[DEBUG] Index 1: 92.0
[DEBUG] Index 2: 78.0
[DEBUG] Index 3: 90.0
[DEBUG] Index 4: 88.0
===================================
Student: Alice
-----------------------------------
Subject | Score | Grade
-----------------------------------
Subject 1 | 85.0 | B
Subject 2 | 92.0 | A
Subject 3 | 78.0 | C
Subject 4 | 90.0 | A
Subject 5 | 88.0 | B
-----------------------------------
Total: 433.0
Average: 86.6
Highest: 92.0
Lowest: 78.0
===================================

❓ FAQ

Q What's the actual difference between print and debugPrint?
A print outputs in a human-readable format (e.g. Alice), while debugPrint outputs in a debugging-oriented format (e.g. "Alice" with quotes). Custom types can implement CustomStringConvertible and CustomDebugStringConvertible protocols to control both outputs.
Q Is assert removed in Release mode?
A Yes. assert only takes effect in Debug mode (-Onone). In Release mode, the compiler skips evaluation and execution of asserts. Therefore, never put side-effect logic inside an assert.
Q When should I use precondition instead of assert?
A precondition takes effect in all modes and is for unrecoverable fatal errors. Examples: pre-array-bounds check, required parameter being nil, a branch that should logically never be reached. Use precondition for public API parameter validation.
Q How do I use breakpoints in Playgrounds?
A Click on the left side of a line number to set a breakpoint. When the program hits a breakpoint, it pauses. Hover over any variable to see its value. Press the continue button or F6 to step to the next line.
Q Why can't I see output in the console even though I used print?
A Check whether your Playground has ".enableResults" mode enabled. In Xcode, make sure the Debug Area is open (Shift+Cmd+Y). In the Swift Playgrounds app, output appears in the bottom panel.

📖 Summary


📝 Exercises

  1. Beginner: Use print to generate a simple multiplication table (1-3), controlling the format with separator and terminator to output a table style.
  2. Intermediate: Write a function divide(_ a: Double, by b: Double) -> Double, using precondition to check the divisor is not 0 and assert to verify the result is within a reasonable range. Then use print with formatting to output the result.
  3. Challenge: Simulate an ATM withdrawal program. Use assert to verify the withdrawal amount is a multiple of 100, and precondition to check sufficient balance. Use print with separator and terminator to format transaction details including time, amount, and balance.
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏