UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

Why do memberwise initializers change to a private access level when a property of its struct changes to private?

Forums > 100 Days of SwiftUI

Hi there! I have a quick question about how memberwise initializers work when it comes to private properties. This stems off of another post of mine, and it's really just a question about how Swift is designed.

Apple's documentation states:

Default Memberwise Initializers for Structure Types

The default memberwise initializer for a structure type is considered private if any of the structure’s stored properties are private. Likewise, if any of the structure’s stored properties are file private, the initializer is file private. Otherwise, the initializer has an access level of internal.

This is great and all, but I am just wondering exactly why Swift is behaved to work like this.

If you consider this struct, you would get an error because of what was just described in the documentation:

struct Pear {
    private var color: String
    private var price: Double
}

let pear = Pear(color: "Green", price: 3.50) // 'Pear' initializer is inaccessible due to 'private' protection level

You cannot use the memberwise initializer here because the access level of the memberwise initializer was changed from internal to private, due to color and price both being marked as private.

However, if I were to write another struct, but with a custom initializer (that works exactly the same as a memberwise initializer with the exception of its access level) this time:

struct Apple {
    private var color: String
    private var price: Double

    init(color: String, price: Double) {
        self.color = color
        self.price = price
    }
}

let apple = Apple(color: "Red", price: 4.25)

I wouldn’t get an error. This is because I’m overriding the memberwise initializer (along with its behavior just described in the documentation) and using the default access level of internal on that custom initializer. Therefore, I can call that custom initializer externally and not have any issues.

This is where my question comes in. What’s the actual benefit of Swift changing the memberwise initializer’s access level from internal to private after you privately mark any property? Why do memberwise initializers behave in the way described in the documentation?

I’m asking this because wouldn’t you design memberwise initializers in a way that could allow people to use them just like I used my custom initializer? If I could have a memberwise initializer with private properties and still be able to allow that memberwise initializer to have an access level of internal, I wouldn’t have to do the extra work of making my own custom initializer that would really just do the exact same thing as a memberwise initializer, except it would obviously have the ability to be called outside of its struct.

It’s a somewhat philosophical question (and perhaps the answer could be very simple and I am just overthinking this), but I just feel like understanding why Swift was designed in certain ways is going to help me understand what I am doing. I am confident that I understand everything surrounding these ideas very well, but I would just really like to dig a bit deeper.

1      

Joe ponders initializers:

What’s the actual benefit of Swift changing the memberwise initializer’s access level from internal to private after you privately mark any property?

You might see this in a different light if your initializer didn't use the same names as your private vars.

Consider:

struct Apple {
    private var color: String  // do not let developers set or read
    private var price: Double  // these values directly

    init(shade: String, presalesMarkup: Double) {
        // .... lots of validation code here .....
        self.color = tastyVariation(suggestedShade: shade) // returns a market saavy substitution
        // .... market pricing shenanigans here ....
        self.price = presalesMarkup * priceMagic // returns a supply & demand based price
    }
}

let apple = Apple(shade: "Red", presalesMarkup: 4.25)

In this example, you eventually get an initialized Apple object. However, the developer using your Apple struct only provides suggested starting values that you might use. More likely, you'll apply some additional business rules before you populate the struct's color and price variables.

If you allowed direct access via a memberwise initializer, any developer could bypass your customer's precise and stringent business rules. You marked them private indicating you do NOT want external processes to directly modify these values. So you probably have good business reasons to make developers use your internal methods.

@rooster gives a nice explanation in a previous post. See -> Memberwise Initializers

1      

This might be a good question to ask on the Swift language forums.

1      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

Thank you for the help and suggestion. I get everything you are saying, but I just want to make sure you're having the same train of thought as I am. Just to clarify, when you say directly access properties, you don't mean that they are directly accessed through the memberwise init(), right?

If I were to have a struct:

struct Apple {
  private var color: String
  private var price: Double
}

let apple = Apple(color: "Red", price: 4.25)

The color and price properties are not "directly" being accessed at the init() callsite, right? As in, the color param and the price param are the actual properties themselves within the struct. That idea doesn't even seem possible... or make sense.

To specify, they're being accessed through parameters "indirectly" like how you would explicitly write a custom initializer:

init(color: String, price: Double) {
  self.color = color
  self.price = price
}

Right?

1      

The more I think of it, I think I am actually understanding @Obelix's point... a little bit. I am going to look over it tomorrow.

1      

Oh! I think I am getting it a bit more. @roosterboy, just to confirm, when you (somewhat incorrectly) said that memberwise initializers "directly" set values from structs properties, and later corrected yourself, you actually meant that properties can be directly accessed outside of structs when using memberwise initializers because actually calling memberwise initializers requires them to not be private and have no private properties, thus we can directly access properties outside of structs. I think that's what you meant, right?

1      

@Obelix: Oh! I think I get it now! So, you're saying that if the memberwise initializer didn't change to a private access level due to having privately marked properties, then you could still access those private properties and totally bypass the important logic inside of your custom initializer. I am aware that memberwise initializers are replaced when you write a custom initializer for that reason, but I don't know if that's exactly adressing my question. Is it? I feel like I am really close to having an answer, but I don't know. I will go to bed and refresh myself tomorrow!

1      

@roosterboy Thank you for suggesting the Swift Forums, as I have found someone asking the exact same question as me. They even added in virtually the same code as I did in my initial post.

The first answer makes me think that this behavior could certainly have the potential to change. I guess it's just a default behavior. Any thoughts?

1      

Given that the proposal mentioned in that thread (SE-0018) has been in deferred status since 2016, I wouldn't get my hopes up for it being implemented any time soon.

1      

Haha, yeah. That's what I was talking about though. Glad to see that others were also thinking the same thing! I actually contacted the person today who proposed this and he said nothing happened! :)

1      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.