How to provide default values for parameters How to handle errors in functions
  • July 29, 2025

1. Providing Default Values for Function Parameters (Deep Dive)

1.1 Syntax and Ordering

  • Declaration
    You assign a default right in the parameter list:

    func foo(a: Int = 1, b: String = "hi") { … }
    
  • Ordering rules
    • All parameters with defaults can be mixed with non-defaulted parameters—but any non-defaulted parameter must come before any call site that omits it.
    • In practice, it’s clearest to group required parameters first, then defaulted ones:
      func connect(to host: String, port: Int = 80, useSSL: Bool = false) { … }
      

1.2 How It Works at the Call Site

  • When you omit an argument, the compiler rewrites your call to insert the default expression.
    connect(to: "example.com")  
    // is treated as:
    connect(to: "example.com", port: 80, useSSL: false)
    
  • Because defaults are compile-time constants or expressions, you can’t change them at runtime—clients never see a “nil” placeholder.

1.3 Default Values as Expressions

Defaults aren’t limited to literals; any expression that’s valid in that scope is allowed:

let globalTimeout = 30

func fetchData(endpoint: String,
               timeout: TimeInterval = TimeInterval(globalTimeout),
               cache: Bool = true)
{
    // …
}

or even calling other functions:

func now() -> Date { Date() }

func log(message: String, at timestamp: Date = now()) {
    print("[\(timestamp)]", message)
}

1.4 Interplay with Parameter Labels

Swift distinguishes between the external label (used by callers) and the internal name (used in the function body):

func move(from start: Point, to end: Point = .zero) { … }

// Callers write:
move(from: p1)            // second uses default .zero
move(from: p1, to: p2)

You can even omit the external label (_) while still providing a default:

func increment(_ value: Int = 1) -> Int {
    return base + value
}
increment()   // adds 1
increment(5)  // adds 5

1.5 Variadic Parameters with Defaults

You can combine variadics and defaults—though variadics always subsume any values provided:

func sum(_ values: Int..., initial: Int = 0) -> Int {
    return values.reduce(initial, +)
}

sum()             // returns 0
sum(1, 2, 3)      // returns 6
sum(4, 5, 6, initial: 10)  // returns 25

2. Handling Errors in Functions (Deep Dive)

Swift’s rich error‐handling model gives you fine-grained control over failure paths, cleanup, and propagation.

2.1 Defining Error Types

  • enums conforming to Error are the standard way:
    enum DataError: Error {
      case notFound(path: String)
      case invalidFormat(reason: String)
      case permissionDenied
    }
    
  • You can attach associated values to carry context.

2.2 Writing Throwing Functions

  • Mark with throws and use throw anywhere inside:
    func loadJSON(from path: String) throws -> [String: Any] {
      guard FileManager.default.fileExists(atPath: path) else {
        throw DataError.notFound(path: path)
      }
      // … attempt to read & parse …
      guard let data = try? Data(contentsOf: URL(fileURLWithPath: path)) else {
        throw DataError.invalidFormat(reason: "Unreadable data")
      }
      // parse JSON…
      return jsonObject
    }
    
  • rethrows:
    Use when your function only throws if one of its function-typed parameters throws:

    func perform<T>(_ work: () throws -> T) rethrows -> T {
      // If `work` throws, this function will too.
      return try work()
    }
    

2.3 Propagating Errors

  • Within a throwing function, prefix calls to other throwing functions with try. Omitting try is a compile-error.
  • You can mix try? or try! to convert or force-unwrap if needed, but most of the time you’ll use plain try to let errors bubble up.

2.4 Catching Errors: do-catch

do {
  let json = try loadJSON(from: "config.json")
  print("Loaded config:", json)
} catch DataError.notFound(let path) {
  print("File not found at \(path)")
} catch DataError.invalidFormat(let reason) {
  print("Bad format:", reason)
} catch {
  print("Unexpected error:", error)
}
  • Pattern matching in catch lets you extract associated values.
  • A final catch without a pattern catches any error.

2.5 Cleanup with defer

If you need guaranteed cleanup—regardless of success or failure—use defer:

func processFile(_ path: String) throws {
  let handle = try FileHandle(forReadingFrom: URL(fileURLWithPath: path))
  defer {
    handle.closeFile()
    print("File closed")
  }

  // If any throw happens below, the defer block still runs
  let data = try handle.readToEnd() ?? Data()
  // …
}
  • You can have multiple defer blocks; they execute in reverse order of appearance.

2.6 Shorthand Forms: try? and try!

  • try?
    Wraps the call in an optional, returning nil on error:

    let maybeJSON = try? loadJSON(from: "config.json")
    // maybeJSON is [String: Any]?; error is swallowed
    
  • try!
    Force‐unwrap: if an error is thrown, your program crashes:

    let guaranteedJSON = try! loadJSON(from: "default.json")
    // Only use if you're 100% sure it can't fail
    

2.7 Bridging to NSError (Objective-C Interop)

When calling Swift throwing functions from Obj-C (or Cocoa APIs expecting NSError**):

@objc func objcLoadJSON(from path: String, error: NSErrorPointer) -> [String: Any]? {
  do {
    return try loadJSON(from: path)
  } catch let err as NSError {
    error?.pointee = err
    return nil
  }
}

2.8 Rethrowing with Context

You can catch an error, wrap or annotate it, then rethrow:

func enhancedLoad(path: String) throws -> [String: Any] {
  do {
    return try loadJSON(from: path)
  } catch {
    throw DataError.invalidFormat(reason: "Failed at path \(path): \(error)")
  }
}

This lets you add context while preserving the error-handling flow.


 

YOU MIGHT ALSO LIKE...
PermissionsSwiftUI: A SwiftUI package to handle permissions

PermissionsSwiftUI displays and handles permissions in SwiftUI. It is largely inspired by SPPermissions. The UI is highly customizable and resembles an Apple style. ...

Pager tab strip view

Introduction PagerTabStripView is the first pager view built in pure SwiftUI. It provides a component to create interactive pager views ...

PageView

SwiftUI view enabling page-based navigation, imitating the behaviour of UIPageViewController in iOS.

Pages

    

How to take action when a property changes

1. Taking Action When a Property Changes: Property Observers Swift lets you observe and respond to changes in a property’s ...