Swift
Struct Initialization
Optional Properties
Swift Programming
Software Development

Swift Initialize Struct with optional stored properties

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

In Swift, structs automatically receive a memberwise initializer that includes all stored properties as parameters. When a stored property is declared as optional (e.g., String?), its default value is nil, which means the memberwise initializer makes that parameter optional — you can omit it during initialization. This allows you to create struct instances without providing every property, while still requiring non-optional properties. Understanding how optional properties interact with the memberwise initializer, default values, and custom initializers is essential for writing clean Swift data models.

Memberwise Initializer with Optionals

swift
1struct User {
2    let id: Int
3    var name: String
4    var email: String?       // Optional — defaults to nil
5    var avatarURL: String?   // Optional — defaults to nil
6}
7
8// Full initialization — providing all properties
9let user1 = User(id: 1, name: "Alice", email: "[email protected]", avatarURL: nil)
10
11// Omitting optional properties — they default to nil
12let user2 = User(id: 2, name: "Bob")
13
14// Providing some optionals
15let user3 = User(id: 3, name: "Charlie", email: "[email protected]")
16
17print(user2.email)  // nil
18print(user3.email)  // Optional("[email protected]")

Swift's auto-generated memberwise initializer gives optional properties a default value of nil, so callers can omit them. Non-optional properties (id, name) must always be provided.

Default Values and Optionals

swift
1struct Configuration {
2    var host: String = "localhost"
3    var port: Int = 8080
4    var timeout: Double? = nil
5    var maxRetries: Int? = 3       // Optional with a non-nil default
6    var apiKey: String?            // Optional, defaults to nil
7}
8
9// Use all defaults
10let config1 = Configuration()
11
12// Override specific values
13let config2 = Configuration(host: "api.example.com", port: 443)
14
15// Override an optional
16let config3 = Configuration(timeout: 30.0, maxRetries: 5)
17
18print(config1.host)        // "localhost"
19print(config1.maxRetries)  // Optional(3)
20print(config1.apiKey)      // nil

Properties with default values (both optional and non-optional) can be omitted from the memberwise initializer. The compiler generates an initializer where every property with a default is an optional parameter.

Custom Initializers

swift
1struct Rectangle {
2    var width: Double
3    var height: Double
4    var color: String?
5    var label: String?
6
7    // Custom initializer for square
8    init(side: Double, color: String? = nil) {
9        self.width = side
10        self.height = side
11        self.color = color
12        self.label = nil
13    }
14
15    // Custom initializer with validation
16    init?(width: Double, height: Double) {
17        guard width > 0, height > 0 else { return nil }
18        self.width = width
19        self.height = height
20        self.color = nil
21        self.label = nil
22    }
23}
24
25let square = Rectangle(side: 5.0, color: "red")
26let rect = Rectangle(width: 10, height: 20)  // Returns Optional

When you define any custom initializer, Swift no longer generates the default memberwise initializer. You must initialize all stored properties (including optionals) in every custom initializer.

Preserving the Memberwise Initializer

swift
1struct Profile {
2    var username: String
3    var bio: String?
4    var website: String?
5}
6
7// Define custom initializers in an extension to keep the memberwise init
8extension Profile {
9    init(username: String) {
10        self.username = username
11        self.bio = nil
12        self.website = nil
13    }
14
15    init(from dictionary: [String: String]) {
16        self.username = dictionary["username"] ?? "unknown"
17        self.bio = dictionary["bio"]
18        self.website = dictionary["website"]
19    }
20}
21
22// Memberwise init still works
23let p1 = Profile(username: "alice", bio: "Developer", website: "https://alice.dev")
24
25// Custom inits also work
26let p2 = Profile(username: "bob")
27let p3 = Profile(from: ["username": "charlie", "bio": "Designer"])

Placing custom initializers in an extension preserves the auto-generated memberwise initializer. This is a common Swift pattern for structs that need both convenience and memberwise initialization.

Codable Structs with Optionals

swift
1struct APIResponse: Codable {
2    let id: Int
3    let title: String
4    var subtitle: String?    // Missing keys decode as nil
5    var imageURL: String?    // Missing keys decode as nil
6    var tags: [String]?      // Missing keys decode as nil
7}
8
9let json = """
10{"id": 1, "title": "Hello World"}
11""".data(using: .utf8)!
12
13let response = try JSONDecoder().decode(APIResponse.self, from: json)
14print(response.title)     // "Hello World"
15print(response.subtitle)  // nil — key was missing in JSON
16print(response.tags)      // nil

When decoding JSON, optional properties automatically handle missing keys by defaulting to nil. Non-optional properties cause a decoding error if their key is missing.

Mutating Optional Properties

swift
1struct Task {
2    let id: Int
3    var title: String
4    var completedAt: Date?
5    var assignee: String?
6
7    mutating func complete() {
8        completedAt = Date()
9    }
10
11    mutating func assign(to person: String) {
12        assignee = person
13    }
14
15    mutating func unassign() {
16        assignee = nil  // Set optional back to nil
17    }
18}
19
20var task = Task(id: 1, title: "Fix bug")
21print(task.completedAt)  // nil
22
23task.assign(to: "Alice")
24print(task.assignee)     // Optional("Alice")
25
26task.complete()
27print(task.completedAt)  // Optional(2025-03-15 ...)

Common Pitfalls

  • Losing the memberwise initializer with custom inits: Defining any init inside the struct body removes the auto-generated memberwise initializer. Move custom initializers to an extension to keep both. This is a frequent surprise for developers coming from other languages.
  • Assuming optional means the parameter is optional in custom inits: In custom initializers, you must assign a value to every stored property — including optionals. The compiler does not auto-default optionals to nil in custom initializers. Add = nil to the parameter explicitly: init(name: String, email: String? = nil).
  • Force-unwrapping optionals without checking: Accessing user.email! crashes at runtime if email is nil. Use optional binding (if let), nil coalescing (??), or optional chaining (user.email?.count) instead.
  • Confusing String? with String: An optional String? and a non-optional String are different types. You cannot pass a String? where a String is expected without unwrapping. Use email ?? "default" or guard let email = email else { return }.
  • Not handling nil in Codable decoding: Optional properties silently become nil when their JSON key is missing. This can hide data issues. If a field should always be present, declare it as non-optional so decoding fails loudly when the key is absent.

Summary

  • Optional stored properties (String?) default to nil in the auto-generated memberwise initializer
  • Non-optional properties must always be provided during initialization
  • Define custom initializers in an extension to preserve the memberwise initializer
  • In custom initializers, explicitly assign all stored properties including optionals
  • Use Codable with optional properties to gracefully handle missing JSON keys

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.