LAST CHANCE: Save 50% on all my Swift books and bundles! >>

SOLVED: Need Clarification on Moonshot Code

Forums > 100 Days of SwiftUI

I'm reviewing Moonshot as part of 100 days of SwiftUI and have a bit of trouble connecting the dots on a few things. I'm hoping someone can explain these few things to me:

Question 1: In ContentView, not fully understanding how we are defining missions and astronaut, referring this code:

let astronauts: [String: Astronaut] = Bundle.main.decode("astronauts.json")
let missions: [Mission] = Bundle.main.decode("missions.json")

Question 2: In ContentView, why are we passing in mission: mission, and not missions: missions? Referring this code:

ForEach(missions) { mission in
                        NavigationLink {
                            MissionView(mission: mission, astronauts: astronauts)
                        } label: {
                            VStack {

Question 3: How is it being decided which objects to bring in and what to pass in? I think majority of my questions are me trying to understand how missions and astronaut are being passed in, since in various parts of the code in this project, they have slightly different names/ways they are being passed in?

Question 4: Can someone explain the custom initializer in MissionView, referring to this code:

 init(mission: Mission, astronauts: [String: Astronaut]) {
        self.mission = mission
        self.crew = mission.crew.map { member in
            if let astronaut = astronauts[member.name] {
                return CrewMember(role: member.role, astronaut: astronaut)
            } else {
                fatalError("Missing \(member.name)")
            }
        }
    }

Thank you!

3      

Question 2: ForEach parameter

A new programmer asks:

Why are we passing in mission: mission, and not missions: missions? Referring this code:

ForEach(missions) { mission in
NavigationLink {
MissionView(mission: mission, astronauts: astronauts)
} label: {
VStack {   //.... snip! .... 

I like to think of ForEach as a view factory!
See -> View Factory
or See -> View Factory
or See -> View Factory

So, ask yourself: In the code snip above what is the ForEach view factory trying to make?

It appears you're trying to build several NavigationLink views, one NavigationLink for each of the moon missions. Convince yourself this is what you are building with the ForEach factory.

Now that you understand that the ForEach builds separate, independent views, your next questions is: What are the raw materials the ForEach factory needs to build a NavigationLink view?

Raw Materials

  1. The ForEach view factory needs a label. This label probably has the title of the mission, maybe a small graphic. It's something that your user will recognize and eventually tap on.
  2. Because you are building a navigation destination, the ForEach view factory needs to know what view to show when the user taps the NavigationLink.

In the Moonshot application, when you tap a mission label, you'll want to send the user to a fully defined MissionView(). So what does SwiftUI need to create a single MissionView() view? It needs one, and only one, mission object.

Clarify Intent

I think it confuses new developers when the parameter labels match the incoming variable labels. I am often overruled in this opinion. But here's a great example of how this can be confusing.

// Confusing?
MissionView(mission: mission, astronauts: astronauts)

The MissionView view needs a single mission, and an array of astronauts. The programmer used the same name for the paramater and variable.

// Less confusing?
// This builds ONE and ONLY ONE mission view.
MissionView(showing: mission, withAstronauts: allApolloAstronauts)

This is more verbose, but maybe we can agree this is somewhat more understandable?

3      

Question 4: Custom View Initializer

New Swift programmer also asks:

Can someone explain the custom initializer in MissionView, referring to this code:

 init(mission: Mission, astronauts: [String: Astronaut]) {
        self.mission = mission
        self.crew = mission.crew.map { member in
            if let astronaut = astronauts[member.name] {
                return CrewMember(role: member.role, astronaut: astronaut)
            } else {
                fatalError("Missing \(member.name)")
            }
        }
    }

I'm not sure how I can top @twoStraws explanation here:
See -> How to Link Astronauts to their missions

His explanation is laid out in detail. Perhaps we can help, if you point out sections of his explanations that you don't grasp?

Another attempt

But, I'll give it a try.

Imagine I handed you a fact sheet for Mission X and asked you to build the detail view with Logo, Description, Mission Dates, etc. But I also told you the crew were potter, grainger, and lovegood. In the data I give you, I ony tell you that lovegood was the commander, the other two were redshirt scientists.

I ask you to display each crewmember's photo, and short biography.

You also have a separate list of all the astronauts: ginny weasley, harry potter, ron weasley, draco malfoy, neville longbottom, etc. This list has their photos, biographies, and favorite spells.

Match Astronaut to the Mission Role

What you'll end up doing is looking at a mission (in this case Mission X) and you'll see that the mission commander was lovegood. Next, you'll look throught the entire list of astronauts until you find a match and you'll copy lovegood's data into the commander crew slot of your mission.

In short, this is what the initializer above is doing.

The initializer is looking at the name lovegood in the mission data. Then it looks (maps) through the list of astronauts for a match. When it finds a match, it copies that astronaut's information, AND the role that astronaut had in the mission to a NEW object called CrewMember. Please note, this new object (CrewMember) is only used in the MissionView and is discarded when the view is dismissed.

Initializer Signature

Reading Signatures is a skill all SwiftUI developers need to build.

//------ Initializer Signature ----------
 init(mission: Mission, astronauts: [String: Astronaut]) {

This initializer tells you that to build a single MissionView(), it needs a single Mission object.
It also needs a dictionary object. More specifically it needs a dictionary where the index into the dictionary is a String, and the object referred to by the index is a complete Astronaut object.

This dictionary works like a Websters dictionary. You use the key word in a dictionary to find its definition, yes? If you look up the word cromulent, you won't find the definition of embiggen.

In the astronaut dictionary, if you need a biography for astronaut Draco Malfoy, you look him up using the (String) malfoy. If the dictionary contains malfoy, you'll get a complete astronaut object in return.

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 July 28th.

Click to save your free spot now

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

Reply to this topic…

You need to create an account or log in to reply.

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.