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

Moonshot Wrapup Challenge Number 3

Forums > SwiftUI

Make a bar button in ContentView that toggles between showing launch dates and showing crew names.

I created the Button to toggle between Date and Crew Member.

{
    let astronauts: [Astronaut] = Bundle.main.decode("astronauts.json")
    let missions: [Mission] = Bundle.main.decode("missions.json")
    @State private var showCrewMember = false

    var body: some View {
        NavigationView{
            List(missions){ mission in
                NavigationLink(
                    destination: MissionView(mission: mission, astronauts: self.astronauts)){
                        Image(mission.image)
                            .resizable()
                            .scaledToFit()
                            .frame(width: 44, height: 44)

                        VStack(alignment: .leading) {
                            Text(mission.displayName)
                                .font(.headline)
                            if showCrewMember{
                                ForEach(mission.crew, id: \.self){ crew in
                                    Text(crew.name)
                                }

                            }
                            else{
                                Text(mission.formattedLaunchDate)
                            }

                        }
                    }
            }
            .navigationBarTitle("Moonshot")
            .toolbar{
                ToolbarItem(){
                    Button(action: {
                        showCrewMember.toggle()
                    }, label: {
                        showCrewMember ? Text("Show Dates") : Text("Show Crew Members")
                    })
                }
            }
        }
    }

}

When I try to get the foreach for the crew for each mission it fail to compile and it gives me the following error.

The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions

Not sure what I'm doing wrong.

2      

I see you posted this in May. It's now the end of August. Hope you found a solution?

One hint I'd give you. Your view should only reflect the state of your model.

So think about moving the "who is the crew" logic out of the view and back into the Mission structure. SwiftUI is a declarative language. Just declare what you want the view to show! Tell SwiftUI to SHOW the property allCrew if that's the current state.

The Mission structure already has a property named crew that is an array of [CrewRole]

So consider making a computed property named "allCrew" which returns a string containing just the names of the crew.

var allCrew: String {
        return crew.map{ $0.name.capitalized }.joined(separator: ", ")
    }

Break the big problem into a few smaller problems.

MAP: Map creates a new array from an existing array. You must provide the rules for changing the objects in the old array into an object in the new array. In my solution, I simply pull the name from the object in the old array. This becomes the object in the new array.

CAPITALIZED: For readability, I change the name string with a capitalized modifier. Oddly enough, this capitalizes the astronaut's name before putting it into the new array. Lucky we don't have any astronauts named Paul McHudson. Not sure if the capitalized modifier is saavy enough to properly capitalize surnames, such as this. If we were doing test driven development, we might add this as a test case!

JOINED: Joined is a nice array method. It takes every object in an array and "joins" them together into a single string. Joined provide the option to separate each object with, well, a string separator. In my solution, I separate the names with a "comma space". You could try something more clever such as an emoji, like a rocket! 🚀

This computed property returns a string! Just what you need in your view!

This makes it easy to use this in your view! If the showCrew binding is set to true, then declare your intentions. Tell SwiftUI to show the mission.allCrew string, otherwise show the formattedLaunchDate.

Try to keep the business logic out of your views. Remember, you're in charge. Declare your intentions.

Text(showCrew ?  mission.allCrew : mission.formattedLaunchDate )

2      

BUILD THE ULTIMATE PORTFOLIO APP Most Swift tutorials help you solve one specific problem, but in my Ultimate Portfolio App series I show you how to get all the best practices into a single app: architecture, testing, performance, accessibility, localization, project organization, and so much more, all while building a SwiftUI app that works on iOS, macOS and watchOS.

Get it on Hacking with Swift+

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.