Free NSSpain ticket up for grabs, enter in 10 seconds. Go to the giveaway →
Giveaway: Free NSSpain ticket giveaway.
Give your simulator superpowers

RocketSim: An Essential Developer Tool
as recommended by Apple

Subscribe to my YouTube Channel

Memberwise Initializer in Swift explained with Code Examples

Memberwise initializers in Swift allow you to create struct instances without manually writing an initializer. The compiler examines the stored properties of your struct and automatically generates an initializer that accepts values for them.

This feature is one of the reasons why structs are so lightweight to use in Swift. However, there are a few details around access control and private properties that can surprise you. Swift 6.4 improves the default behavior, making memberwise initializers more useful when your struct contains private implementation details.

What is a memberwise initializer?

A memberwise initializer is an initializer that Swift automatically generates for structs. It allows you to initialize each stored property directly:

struct Article {
    let title: String
    let url: URL
    var readCount: Int
}

let article = Article(
    title: "Memberwise Initializers in Swift",
    url: URL(string: "https://www.avanderlee.com")!,
    readCount: 0
)

You did not have to write this initializer yourself:

init(title: String, url: URL, readCount: Int) {
    self.title = title
    self.url = url
    self.readCount = readCount
}

The compiler generates it for you. This keeps simple model types short and readable, which is one of the reasons I often start with a struct by default.

Default property values

Memberwise initializers also support default property values. If a stored property has a default value, Swift uses that value as the default argument in the generated initializer:

struct Article {
    let title: String
    let url: URL
    var readCount: Int = 0
}

let article = Article(
    title: "Memberwise Initializers in Swift",
    url: URL(string: "https://www.avanderlee.com")!
)

In this case, Swift still generates a parameter for readCount, but it gets a default value:

// Generated automatically:
init(title: String, url: URL, readCount: Int = 0)

This behavior was improved in Swift 5.1 through SE-0242: Synthesize default values for the memberwise initializer. I already touched on this briefly in my article about structs vs classes in Swift, but Swift 6.4 adds another useful improvement.

FREE 5-day email course: The Swift Concurrency Playbook by Antoine van der Lee

FREE 5-Day Email Course: The Swift Concurrency Playbook

A FREE 5-day email course revealing the 5 biggest mistakes iOS developers make with with async/await that lead to App Store rejections And migration projects taking months instead of days (even if you've been writing Swift for years)

Private properties before Swift 6.4

Before Swift 6.4, private stored properties with default values could make the generated memberwise initializer much less useful:

struct Article {
    let title: String
    let url: URL
    private var readCount: Int = 0
}

// You would expect this to work, but it does not before Swift 6.4:
let article = Article(
    title: "Memberwise Initializers in Swift",
    url: URL(string: "https://www.avanderlee.com")!
)

You might expect this to work. After all, readCount is private and already has a default value, so callers should not have to know about it.

However, before Swift 6.4, the compiler still included readCount in the memberwise initializer:

// Before Swift 6.4, readCount is still included although private:
private init(title: String, url: URL, readCount: Int = 0)

Since the initializer included a private property, the initializer itself became private. As a result, creating the Article outside the struct would fail:

Memberwise initializers before Swift 6.4 could result in an error: "initializer is inaccessible due to 'private' protection level"
Memberwise initializers before Swift 6.4 could result in an error: “initializer is inaccessible due to ‘private’ protection level”

This often forced you to write a manual initializer just to hide a private implementation detail:

struct Article {
    let title: String
    let url: URL
    private var readCount: Int = 0

    init(title: String, url: URL) {
        self.title = title
        self.url = url
    }
}

The manual initializer works, but it removes part of the convenience that made the struct nice to use in the first place.

Private properties in Swift 6.4

Swift 6.4 implements SE-0502: Exclude private initialized properties from memberwise initializer. The generated memberwise initializer now excludes less accessible properties that already have an initial value.

In practice, the earlier example becomes valid:

struct Article {
    let title: String
    let url: URL
    private var readCount: Int = 0
}

let article = Article(
    title: "Memberwise Initializers in Swift",
    url: URL(string: "https://www.avanderlee.com")!
)

Swift now generates the initializer you probably expected:

// Starting in Swift 6.4, readCount is no longer needed:
init(title: String, url: URL)

The private readCount property still exists and still starts at 0, but it no longer forces the memberwise initializer to become private. This is a small language improvement that removes a common source of boilerplate.

I like this change because it better matches how I think about private stored properties. If a property is private and already initialized, it usually feels like an implementation detail. Callers should not have to pass it into the initializer.

Which properties are excluded?

Swift 6.4 does not simply remove every private property from memberwise initializers. The rule is more precise: a property is excluded when it is less accessible than the generated initializer and already has an initial value.

An initial value can be explicit:

struct Article {
    let title: String
    private var readCount = 0
}

It can also be a default-initialized value, like an optional that starts as nil:

struct Article {
    let title: String
    private var cachedSummary: String?
}

In Swift 6.4, both readCount and cachedSummary can be excluded from the memberwise initializer when there are more accessible properties like title.

However, a private property without an initial value still has to be initialized somehow:

struct Article {
    let title: String
    private let identifier: UUID
}

In this case, Swift still includes identifier in the memberwise initializer. Since identifier is private, the initializer remains limited by that private access level:

// Still private, since `identifier` has no default value:
private init(title: String, identifier: UUID)

This makes sense. Swift cannot create an Article without knowing the identifier, so you still need to write a custom initializer if you want to hide that property from callers:

struct Article {
    let title: String
    private let identifier: UUID

    init(title: String) {
        self.title = title
        self.identifier = UUID()
    }
}

Public structs still need explicit initializers

There is one important detail to remember: memberwise initializers are only synthesized up to internal access. Even if your struct is public, Swift will not generate a public memberwise initializer for you.

For example:

public struct Article {
    public let title: String
    public let url: URL
}

Swift can synthesize an internal memberwise initializer for use inside the module, but clients of your module still cannot create Article unless you define a public initializer:

public struct Article {
    public let title: String
    public let url: URL

    public init(title: String, url: URL) {
        self.title = title
        self.url = url
    }
}

This is not new in Swift 6.4, but it is easy to forget when working with Swift packages. If you are exposing models from a package, you still need to decide which initializers are part of your public API.

Source compatibility

Swift 6.4 includes a compatibility overload for the old memberwise initializer shape. This means existing code that initializes private properties through the synthesized initializer can continue to work for now.

For example, Swift can synthesize both forms internally:

struct Article {
    let title: String
    private var readCount: Int = 0

    func resettingReadCount() -> Article {
        Article(title: title, readCount: 0)
    }
}

A future language mode could remove this compatibility overload. If your code relies on initializing private properties through the generated memberwise initializer, I would make that initializer explicit instead:

struct Article {
    let title: String
    private var readCount: Int = 0

    private init(title: String, readCount: Int) {
        self.title = title
        self.readCount = readCount
    }
}

This makes your intent clear and avoids depending on a compatibility path that might go away later.

Why this change matters

This change makes memberwise initializers better align with access control. A private property with a default value is usually an implementation detail, so it should not reduce the usefulness of the initializer that callers actually use.

It also helps macro authors. Attached macros often need to add private backing storage, similar to property wrappers. Before Swift 6.4, that private storage could accidentally make the generated initializer private, forcing you to write boilerplate.

You do not need to know all macro details to benefit from the change. The simple takeaway is this: you can add private initialized storage to a struct without losing the convenient memberwise initializer for the properties that matter to callers.

Conclusion

Memberwise initializers in Swift remove boilerplate by letting the compiler generate struct initializers for stored properties. Default values make them even more convenient, and Swift 6.4 improves the behavior further by excluding private initialized properties when they should behave like implementation details.

If you are using private stored properties with default values, you might be able to remove manual initializers after adopting Swift 6.4. Just remember that private properties without default values still need initialization, and public structs still require explicit public initializers.

If you want to improve your Swift knowledge even more, check out the Swift category page. Feel free to contact me or tweet me on Twitter if you have any additional tips or feedback.

Thanks!

 
Antoine van der Lee

Written by

Antoine van der Lee

iOS Developer since 2010, former Staff iOS Engineer at WeTransfer and currently full-time Indie Developer & Founder at SwiftLee. Writing a new blog post every week related to Swift, iOS and Xcode. Regular speaker and workshop host.

Are you ready to

Turn your side projects into independence?

Learn my proven steps to transform your passion into profit.