Skip to main content
A throwing initializer is an initialization method marked with the throws keyword that can propagate an error to its caller if the instantiation process fails. Unlike failable initializers (init? or init!) that return an optional upon failure, throwing initializers abort the initialization sequence and yield a specific type conforming to the Error protocol, providing precise diagnostic information regarding the failure.

Syntax and Declaration

The throws keyword is placed immediately after the initializer’s parameter list and before the opening brace.

Invocation Mechanics

Because the initializer can throw an error, any call to it must be evaluated within an error-handling context using try, try?, or try!.

Memory and Two-Phase Initialization

Swift enforces strict two-phase initialization. If a throwing initializer throws an error before Phase 1 is complete (i.e., before all stored properties are assigned a value), the compiler guarantees that the allocation is aborted. The partially constructed instance is immediately destroyed, and no memory leaks occur. Any properties that were initialized prior to the throw statement are destroyed and their memory is freed (which includes decrementing reference counts for class properties), rather than explicitly invoking a deinit method.

Delegation Rules

Throwing initializers follow specific rules regarding initializer delegation (self.init for value types or convenience initializers, and super.init for class inheritance):
  1. Throwing to Non-Throwing: A throwing initializer can freely delegate to a non-throwing initializer.
  2. Non-Throwing to Throwing: A non-throwing initializer can delegate to a throwing initializer by either wrapping the delegation call (try self.init(...) or try super.init(...)) in a do-catch block, or by using try! to force the initialization.
    • If using a do-catch block, Swift’s definite initialization analysis strictly prevents returning an uninitialized instance. Because a non-throwing initializer cannot propagate the error and cannot recover by re-initializing self inside the catch block, the catch block must unconditionally abort execution by calling a function that returns Never (such as fatalError()). If you attempt to exit the catch block with a standard return, the compiler will emit an error stating that the initializer returns without initializing all stored properties.
    • If failure is not expected, using try! is the idiomatic way to force initialization, which will automatically trap and abort execution if an error is thrown without requiring a do-catch block.

Inheritance and Overriding Rules

When subclassing, the throws signature dictates how initializers can be overridden:
  • Overriding a Throwing Initializer: A subclass can override a superclass’s throwing designated initializer with either a throwing initializer or a non-throwing initializer. (Downgrading from throwing to non-throwing is permitted).
  • Overriding a Non-Throwing Initializer: A subclass cannot override a superclass’s non-throwing designated initializer with a throwing initializer. (Upgrading from non-throwing to throwing is prohibited, as it would violate the superclass’s contract).
Tired of Poor Swift Skills? Fix That With Deep Grasping!Learn More