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

Questions on the 'Access Control' lesson for 100DaysOfSwift present contradictory solutions

Forums > 100 Days of Swift

On question 1, we are presented with this INVALID code.

struct FacebookUser {
    private var privatePosts: [String]
    public var publicPosts: [String]
}
let user = FacebookUser()

It is invalid because 'private var privatePosts' cannot be generated by the memberwise initializer.

"This has a private property, so Swift is unable to generate its memberwise initializer for us."

HOWEVER, on question 4, we are presented with this VALID code:


struct Contributor {
    private var name = "Anonymous"
}
let paul = Contributor()

How can this be valid? 'name' is a private var and thus cannot be generated by the memberwise initializer. They are virtually identical in principle yet one is valid and the other is invalid.

What is going on?

3      

Dan thinks the Swift compiler is messing with him!

They are virtually identical in principle yet one is valid and the other is invalid.
What is going on?

// Here you are asking the compiler to create a FacebookUser.
// You are asking the compiler to set aside space in memory
// to store the variables that you'll later use.
let user = FacebookUser()

// Same here. You are asking the compiler to reserve a portion of memory
// to store variables for a Contributor() object.
let paul = Contributor()

In both cases, you are asking the compiler to set aside portions of memory for Swift objects that hold named variables, and possibly methods.

Direct Assignment

By definition, the Contributor()object will hold a var called name. However, because it's marked private, the struct itself must supply the value for this variable. It can supply it via a method call, or direct assignment.

struct Contributor {
    private var name = "Anonymous"  // <-- Directly assign a value to this variable
}
let paul = Contributor()

The compiler can create the paul object because it can reserve the required space in memory, and can populate the internal variable with sensible content.

Incomplete Assignment

By definition, the FacebookUser()object will hold two vars, privatePosts and publicPosts.
How do these get their initial values?

Because it's marked public, the Swift compiler will attempt to create an initializer for you allowing you to pass in an array of Strings to initialize the publicPosts variable.

But how?
How will you initialize the privatePosts variable? Because it is marked private, you can't access this variable outside of this structure's definition. You are not initializing this variable inside the struct directly, or via a method. Consequently, the compiler cannot create a user object, because this variable must have values.

struct FacebookUser {
    private var privatePosts: [String]
    public var publicPosts: [String]
}
// FAIL: Compiler cannot create an initializer for you!
let user = FacebookUser(publicPosts: ["Post 1", "Post 2", "Post 3"] ) 

Keep coding!

3      

@Obelix, thank you for your great explanation. However, I have an issue that seems to contradict your explanation.

This is question 6 of that same quiz:

struct Doctor {
    var name: String
    var location: String
    private var currentPatient = "No one"
}
let drJones = Doctor(name: "Esther Jones", location: "Bristol")

This code is marked as INVALID. The solution states "Oops – that's not correct. This has a private property, so Swift is unable to generate its memberwise initializer for us."

However, we see that "No one" has been assigned to private var currentPatient, which should work per your explanation. Can you please clarify?

4      

Dan gains points for persistence! Nice.

However, we see that "No one" has been assigned to private var currentPatient,
which should work per your explanation. Can you please clarify?

Too many times I've found I'm incomplete, or just inaccurate. Sorry! I reordered my examples above in the Incomplete Assignment section to point out that the compiler, indeed, would complain.

Playgrounds

I hope you are using XCode's Playgrounds? This is a great advertisement to any new developer why Playgrounds is an awesome tool to use in your development journey. I encourage you to fire it up and create a few test structs, both with and without private vars.

Create them. Destroy them. Try out numerous combinations. By building, breaking and fixing your own examples, you'll gain a deeper understanding than @twoStraw's videos and my one forum post.

Here are a few structs to get you started in Playgrounds:

struct Doctor {
    var name: String
    var location: String
    private var currentPatient: String

    // Create your own initializer
    init( newName: String, newLocation: String) {
        name           = newName
        location       = newLocation
        currentPatient = "Short Round" // <- Set a default
    }
    // Create an accessor for private var
    var patient: String {
        currentPatient.capitalized
    }
}

let drJones = Doctor(newName: "Indiana Jones", newLocation: "Alexandria, Egypt")
print (drJones.patient)

It's all about the initializer. You have a plan for a Doctor struct. But the compiler wants to know that when it creates a new Doctor object that it can fill in all the variables. For simple structs, the compiler will help you and create a memberwise initializer.

Without writing your own initializer code, you usually can create a new Doctor object like this:

// Most of the time Swift compiler creates this for you!
let drJones = Doctor(name: "Indiana Jones", location: "Alexandria, Egypt", currentPatient: "Marion")

In this case however, the currentPatient is marked private, so the compiler has extra rules. The first being, you cannot read or write to currentPatient outside the struct's definition. The second rule is the compiler will not create an initializer for you. This is your new job.

Here are two more examples for your Playgrounds. I encourage you to make additional tests. Build and break.

struct PrivateTest_One {
    var one: Int              = 1    // initial value
    var two: Double           = 2.0  // initial value
    private var three: String = "3️⃣" // initial value, but private
}
// We have a struct with a private variable!
var firstTest = PrivateTest_One()  // <- Why does this work?
firstTest.one
firstTest.two

struct PrivateTest_Two {
    var one: Int              = 1    // Initial value
    var two: Double                  // NO initial value
    private var three: String = "3️⃣" // Initial value
}
// Another struct with a private variable!
var secondTest = PrivateTest_Two()  // <- This doesn't work. Why?
secondTest.one
secondTest.two  // <- Compiler needs a way to initialize this variable.

And to verify the compiler's intentions. Toss this into the Playgrounds:

struct PrivateTest_Three {
    var one: Int        // NO initial value
    var two: Double     // NO initial value
    var three: String   // NO initial value
}

// Initializer provided by the Swift compiler. Neat!
let test3 = PrivateTest_Three(one: 1, two: 2.0, three: "3️⃣")
test3.two
test3.three

let test3_1 = PrivateTest_Three() // This doesn't work. Why?
let test3_2 = PrivateTest_Three(one: 1) // This doesn't work. Why?

4      

I still don't get why quesiton 6 is not ok but question 4 is valid?

4      

Joshua would like additional clarification:

I still don't get why question 6 is not ok but question 4 is valid?

I hope you tried Playgrounds? Have you tried creating several structs in Playgrounds to see what works and what doesn't work? It would be great to know you're trying this out on your own and experimenting.

In short, the struct in Question 4 has just one private element, and it's initialized with a default value. The compiler is able to initialize all the defined variables in this struct each time a new Contributor() object is created.

In Question 6, the struct has three variables: name, location, and currentPatient.
When the compiler creates a new Doctor struct it MUST have values for each variable. Because ONE of these is marked private, the compiler will NOT create a member wise initializer for you. Therefore, you must create your own initializer.

You will not get this for free:

// One of these items is private.
// You do NOT GET THIS initializer for free.
let drJones = Doctor(name: "Indiana Jones", location: "Alexandria, Egypt", currentPatient: "Marion")

It would be nice, because to create a Doctor object, the compiler needs to give each variable a value. But one of those items is marked private, so the compiler does NOT create that initializer for you.

So you either need to initialize each variable inside the struct.
OR
Or you need to create a custom initializer.

This is a custom initializer:

// In Question 6, the user is atteming to use a custom initializer that only fills two values. 
// The struct's code given in Question 6 does not contain a custom initializer.
let drJones = Doctor(name: "Esther Jones", location: "Bristol")

The code in Question 6 does not contain a custom initializer. Therefore the code in Question 6 is invalid.

4      

Thank for the help, @Obelix. I'll play around in the playground to run the code and experiment with the concepts. Much appreciated!

3      

I think I got it.... (after time messing in playgrounds)

A. If you use public variables you can use memberwise init provided by swift (or custom if you choose)

B. If you have private variables in struct you have to do both of the following:

  1. assign them a value in struct (or in struct custom initializer)
  2. create custom init
    since swift will not let you use memberwise init if one variable is private

Does that sound right?

Thanks in advance .

4      

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

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.