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

Moonshot getting stuck

Forums > 100 Days of SwiftUI

Hi everyone, I am on project 8 now and finishing off the challenge section. I got a bit confused as to why my app doesn't run (blank white screen) when Xcode says that it compiles my codes successfully. underneath at the debugger output, it shows nothing.

has anyone experienced this before and what exactly went wrong? Thank you!

3      

it sounds like the JSON files are not loading check your code.

3      

The code loaded okay. The debugger output did come up with not loading one of the files, but I fixed that and it compiles without further warning in the debugger. screen remained blanked and white. I just wonder if there is a chance maybe its a fault with the canvas or the console preview. Or it generally boils down to my codes to cause an unknown error?

3      

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!

Without see your code can not see where the error is because mine Moonshot project works fine.

3      

okay. Its a really long one, divided in 4 sections for loading_data, ContentView, MissionView and AstroView

loading data

import Foundation

struct Astronauts : Codable, Identifiable {
    var id : String
    var name : String
    var description : String
}

struct CrewAndRole {
    var role : String
    var theastronaut : Astronauts
}

struct Missions : Codable, Identifiable {
    struct CrewRole : Codable {
        var name : String
        var role : String
    }
    var crew : [CrewRole]

    var id : Int
    var launchDate : Date?
    var description : String
    var displayname : String {
        "Apollo \(id)"
    }
    var image : String {
        "apollo\(id)"
    }
    var launchDateFormatted : String {
        if let launchDate1 = launchDate {
        let formatter = DateFormatter()
            formatter.dateStyle = .long
            return formatter.string(from: launchDate1)
        }
        else { return "N/A" }
    }
}

extension Bundle {
    func obtainingDecodedData<T : Codable>(_ file: String) -> T {
        guard let url = self.url(forResource: file, withExtension: nil) else {
            fatalError("cannot locate url file \(file)")
        }

        guard let data1  = try? Data(contentsOf: url) else {
            fatalError("cannot load up url file \(file) as data")
        }

        let decoder = JSONDecoder()

        let e1 = DateFormatter()
        e1.dateFormat = "y-MM-dd"
        decoder.dateDecodingStrategy = .formatted(e1)

        guard let decodedData1 = try? decoder.decode(T.self, from: data1) else {
            fatalError("cannot decode data from \(file)")
        }

        return decodedData1
    }
}

let missionNoView : [Missions] = Bundle.main.obtainingDecodedData("missions.json")
let allAstronauts : [Astronauts] = Bundle.main.obtainingDecodedData("astronauts.json")

ContentView

import SwiftUI

struct ContentView: View {

    let missionFirstView : [Missions] = Bundle.main.obtainingDecodedData("missions.json")
    let allAstronautsFirstView : [Astronauts] = Bundle.main.obtainingDecodedData("astronauts.json")

    var body: some View {
        NavigationView {
            List(missionFirstView) { each in
                NavigationLink(destination: MissionView(mission: each, astronaut: self.allAstronautsFirstView)) {
                Image(each.image)
                    .resizable()
                    .frame(width: 44, height: 44)
                    .aspectRatio(contentMode: .fit)

                    VStack (alignment: .leading) {
                        Text(each.displayname)
                            .font(.headline)
                        Text(each.launchDateFormatted)
                            .foregroundColor(.orange)
                    }
                }
            }
        .navigationBarTitle("MoonShot")
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        /*@START_MENU_TOKEN@*/Text("Hello, World!")/*@END_MENU_TOKEN@*/
    }
}

MissionView

import SwiftUI

struct MissionView : View {

    let missionSecondView : [Missions] = Bundle.main.obtainingDecodedData("missions.json")
    let allAstronauts : [Astronauts] = Bundle.main.obtainingDecodedData("astronauts.json")

    let mission : Missions
    var allCrewAndRole : [CrewAndRole]

    init(mission: Missions, astronaut: [Astronauts]) {
        self.mission = mission

        var match = [CrewAndRole]()
        for each in mission.crew {
            if let matching = astronaut.first(where: { $0.id == each.name } ) {
                match.append(CrewAndRole(role: each.role, theastronaut: matching))
        }
            else { fatalError("Cannot load page") }
        }
        allCrewAndRole = match
    }

    var body: some View {
        NavigationView {
            GeometryReader { geometry in
                ScrollView {
                    VStack {
                        Image(self.mission.image)
                        .resizable()
                        .scaledToFit()
                        .frame(maxWidth: geometry.size.width * 0.8)
                        .padding(10)

                        Text(self.mission.launchDateFormatted)
                            .foregroundColor(.orange)

                        Text(self.mission.description)
                            .padding(.top, 5)
                    }

                    ForEach (self.allCrewAndRole, id: \.theastronaut.id) { each in
                        NavigationLink (destination: astroViewEachAstro(crEachAstro: self.allCrewAndRole, singleAstro: each.theastronaut)) {
                            HStack {
                                Image(each.theastronaut.id)
                                    .renderingMode(.original)
                                    .resizable()
                                    .aspectRatio(contentMode: .fill)
                                    .frame(width: 88, height: 88)
                                    .clipShape(Circle())
                                    .overlay(Circle().stroke(Color.orange, lineWidth: 2))

                                VStack(alignment: .leading) {
                                    Text(each.theastronaut.name)
                                        .font(.headline)
                                        .foregroundColor(.primary)
                                        .padding(5)
                                    Text(each.role)
                                        .foregroundColor(.purple)
                                        .padding(5)
                                }
                                Spacer()
                            }
                            .padding(.horizontal)
                        }
                    }
                    Spacer(minLength: 25)
                        .navigationBarTitle("\(self.mission.displayname)", displayMode: .inline)
                    }
                }
            }
        }
    }

struct MissionView_Previews: PreviewProvider {
    static var previews: some View {
        /*@START_MENU_TOKEN@*/Text("Hello, World!")/*@END_MENU_TOKEN@*/
    }
}

and Lastly, AstroView - showing astronaut detail after clicking from missionView

import SwiftUI

struct astroViewEachAstro : View {

    var crEachAstro : [CrewAndRole]
    var singleAstro : Astronauts    
    var roleAstro : String = ""
    var allMissions : [String]

    init(crEachAstro: [CrewAndRole], singleAstro: Astronauts) {
        self.crEachAstro = crEachAstro
        self.singleAstro = singleAstro
        var match = ""
        if let theMatch = crEachAstro.first(where: { $0.theastronaut.id == singleAstro.id } ) {
            match = theMatch.role
        } else { fatalError("Cannot load into astronaut role") }
        roleAstro = match
        // need to run over crEachAstro.

        var allMissionMatch = [String]()
        for i in missionNoView {
            if i.crew.first(where: { singleAstro.id == $0.name } ) != nil {
                allMissionMatch.append(i.displayname)
            } else { fatalError("Cannot formulate an array of missions this guy gone to") }
        }
        allMissions = allMissionMatch
    }

    var body: some View {
        NavigationView {
            GeometryReader { geo in
                ScrollView {
                    VStack(spacing: 10) {
                        Image(self.singleAstro.id)
                                .resizable()
                                .scaledToFit()
                            .frame(maxWidth: geo.size.width * 0.8)
                                .clipShape(RoundedRectangle(cornerRadius: 15))
                                .padding(.bottom, 10)

                        Text(self.singleAstro.name)
                                .font(.headline)

                        Text(self.roleAstro)
                            .foregroundColor(.purple)

                        Text(self.singleAstro.description)
                    }
                }
                .navigationBarTitle(Text(self.singleAstro.name))
   //         }
  //          VStack(alignment: .leading, spacing: 10){
  //              ForEach (0 ..< allMissions.count) { each in
   //                 Text("All involved missions:")
   //                     .font(.headline)
   //                 Text("\(each)")
   //                     .foregroundColor(.green)
   //             }
            }
            .navigationBarTitle(Text(self.singleAstro.name))
        }
    }
}

struct astroViewEachAstro_Previews: PreviewProvider {
    static var previews: some View {
        /*@START_MENU_TOKEN@*/Text("Hello, World!")/*@END_MENU_TOKEN@*/
    }
}

here you go!

3      

Your problem might be in your ContentView. In your list you have a NavLink. Inside your Navlink you have an image and a vstack. This is whats causing it i believe. Its not showing because it doesnt know what to do with whats inside NavLink. To fix, put the image and vstack inside a hstack and that should do it.

Dave

3      

I have run the project as is. And First are you talking about the canvas (on the side)? This will not show with

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        /*@START_MENU_TOKEN@*/Text("Hello, World!")/*@END_MENU_TOKEN@*/
    }
}

change that to

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

then you should see the MoonShot missions.

When you run in simulator it crash with your fatalError("Cannot load into astronaut role")

However I will say that this project is different code from Moonshot that Paul did in his tutorial So you may want to relook at it.

3      

re @Newy11527,

Thanks for the advice. I believe its definitely to do with my codes. I tried your VStack method, it didn't work. I will just do a one-to-one comparison with paul's to see what exactly the program may be.

re @NigelGee,

I changed ContentView() to Text in the preview in the hope that it'll at least show a simple text on the canvas, that didn't work, changing it back to ContentView didn't work as well. It was one of the things I tried to see if it's a fault with Xcode instead of my codes. And yes my codes are different from Paul's because paul's one is confusing to me as to how he uses var names across different views.

oh and btw the fatalError("Cannot load into astronaut role") part, this only came up once, and since fixed, the code compiles every time okay without any errors popping up anymore in debugger output.

update1: I sorted my code, its now working. It has to do with each individual views being self-sufficient to run on their own. Thank you guys. In case anyone is interested. here are the working codes. I finished the challenges as well but I had a workaround with the missions attended. Getting all the missions attended was what broke my code in the first place.

astroViewEachAstro (3rd view)

import SwiftUI

struct astroViewEachAstro : View {

    struct CrewAndRole2 {
        let role : String
        let theastronaut : Astronauts
    }

    let missionAstroView : [Missions] = Bundle.main.obtainingDecodedData("missions.json")

    var singleAstro : Astronauts
    var roleAstro : String = ""
    var allCrewAndRole2 : [CrewAndRole2]
    var mission : Missions
    var allMissions : [Int]

    init(mission: Missions, singleAstro: Astronauts, astronaut: [Astronauts]) {
        self.mission = mission
        self.singleAstro = singleAstro

        var match = [CrewAndRole2]()
        for each in mission.crew {
            if let matching = astronaut.first(where: { $0.id == each.name } ) {
                match.append(CrewAndRole2(role: each.role, theastronaut: matching))
        }
            else { fatalError("Cannot load page") }
        }
        self.allCrewAndRole2 = match

        var match1 = ""
        if let theMatch = allCrewAndRole2.first(where: { $0.theastronaut.id == singleAstro.id } ) {
            match1 = theMatch.role
        } else { fatalError("Cannot load into astronaut role") }
        roleAstro = match1

        var allMissionMatch = [Int]()
        for i in missionAstroView {
            for o in i.crew {
                if o.name == singleAstro.id {
                    allMissionMatch.append(i.id)
                    break
                }
            }
        }
        allMissions = allMissionMatch
    }

   //             allMissionMatch.append(matching1)
   //         } else { fatalError("Cannot load missions") }
   //     }
   //     allMissions = allMissionMatch
 //       allMissions = allMissionMatch

     //       if i.crew.first(where: { singleAstro.id == $0.name } )  {
     //           allMissionMatch.append(i.displayname)
     //       } else { fatalError("Cannot formulate an array of missions this guy gone to") }
     //   }
    //    allMissions = allMissionMatch

 //   func test1() {
 //       for i in allMissions {
 //           print(i)
 //       }
 //   }

    var body: some View {
            GeometryReader { geo in
                ScrollView {
                    VStack(spacing: 10) {
                        Image(self.singleAstro.id)
                                .resizable()
                                .scaledToFit()
                            .frame(maxWidth: geo.size.width * 0.8)
                                .clipShape(RoundedRectangle(cornerRadius: 15))
                                .shadow(radius: 15)
                                .padding(.bottom, 10)

                        Text(self.roleAstro)
                            .foregroundColor(.purple)

                        Text(self.singleAstro.description)

                        Text("All involved missions: ")
                            .font(.headline)

                        ForEach(self.allMissions,id: \.self) { each in
                            Text("Apollo " + "\(each)")
                        }.foregroundColor(.blue)
                    }
                }
            }
        .navigationBarTitle(Text(self.singleAstro.name))
    }
}

struct astroViewEachAstro_Previews: PreviewProvider {
    static let missionFirstView : [Missions] = Bundle.main.obtainingDecodedData("missions.json")
    static let allAstronauts : [Astronauts] = Bundle.main.obtainingDecodedData("astronauts.json")

    static var previews: some View {
        astroViewEachAstro(mission: missionFirstView[0], singleAstro: allAstronauts[0], astronaut: allAstronauts)
    }
}

MissionView (2nd View)

import SwiftUI

struct MissionView : View {

    struct CrewAndRole1 {
        var role : String
        var theastronaut : Astronauts
    }
    var allCrewAndRole1 : [CrewAndRole1]
    let allAstronauts : [Astronauts] = Bundle.main.obtainingDecodedData("astronauts.json")
    let mission : Missions

    init(mission: Missions, astronaut: [Astronauts]) {
        self.mission = mission
        var match = [CrewAndRole1]()
        for each in mission.crew {
            if let matching = astronaut.first(where: { $0.id == each.name } ) {
                match.append(CrewAndRole1(role: each.role, theastronaut: matching))
        }
            else { fatalError("Cannot load page") }
        }
        self.allCrewAndRole1 = match
    }

    var body: some View {
        NavigationView {
            GeometryReader { geometry in
                ScrollView {
                    VStack {
                        Image(self.mission.image)
                        .resizable()
                        .scaledToFit()
                        .frame(maxWidth: geometry.size.width * 0.8)
                        .padding(10)
                        .shadow(radius: 15)

                        Text(self.mission.launchDateFormatted)
                            .foregroundColor(.orange)

                        Text(self.mission.description)
                            .padding(.top, 5)
                    }

                    ForEach (self.allCrewAndRole1, id: \.theastronaut.id) { each in
                        NavigationLink(destination: astroViewEachAstro(mission: self.mission, singleAstro: each.theastronaut, astronaut: self.allAstronauts)) {
                            HStack {
                                Image(each.theastronaut.id)
                                    .renderingMode(.original)
                                    .resizable()
                                    .aspectRatio(contentMode: .fill)
                                    .frame(width: 88, height: 88)
                                    .clipShape(Circle())
                                    .overlay(Circle().stroke(Color.orange, lineWidth: 2))
                                    .shadow(radius: 15)

                                VStack(alignment: .leading) {
                                    Text(each.theastronaut.name)
                                        .font(.headline)
                                        .foregroundColor(.primary)
                                        .padding(5)
                                    Text(each.role)
                                        .foregroundColor(.purple)
                                        .padding(5)
                                }
                                Spacer()
                            }
                            .padding(.horizontal)
                        }
                    }
                    Spacer(minLength: 25)
                    }
                }
            .navigationBarTitle("\(self.mission.displayname)", displayMode: .inline)
        }
    }
}

struct MissionView_Previews: PreviewProvider {
    static let allAstronauts : [Astronauts] = Bundle.main.obtainingDecodedData("astronauts.json")
    static let missionFirstView : [Missions] = Bundle.main.obtainingDecodedData("missions.json")

    static var previews: some View {
        MissionView(mission: missionFirstView[0], astronaut: allAstronauts)
    }
}

ContentView (1st View)

import SwiftUI

struct ContentView: View {

    let missionFirstView : [Missions] = Bundle.main.obtainingDecodedData("missions.json")
    let allAstronauts : [Astronauts] = Bundle.main.obtainingDecodedData("astronauts.json")

    var body: some View {
        NavigationView {
            List(missionFirstView) { each in
                NavigationLink(destination: MissionView(mission: each, astronaut: self.allAstronauts)) {
                    Image(each.image)
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .frame(width: 44, height: 44)

                    VStack (alignment: .leading) {
                        Text(each.displayname)
                            .font(.headline)
                        Text(each.launchDateFormatted)
                            .foregroundColor(.orange)
                        }
                }
            }
        .navigationBarTitle("MoonShot")
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Data_management page

import Foundation

struct Astronauts : Codable, Identifiable {
    let id : String
    let name : String
    let description : String
}

struct Missions : Codable, Identifiable {
    struct CrewRole : Codable {
        var name : String
        var role : String
    }
    var crew : [CrewRole]
    var id : Int
    var launchDate : Date?
    var description : String
    var displayname : String {
        "Apollo \(id)"
    }
    var image : String {
        "apollo\(id)"
    }
    var launchDateFormatted : String {
        if let launchDate1 = launchDate {
        let formatter = DateFormatter()
            formatter.dateStyle = .long
            return formatter.string(from: launchDate1)
        }
        else { return "N/A" }
    }
}

extension Bundle {
    func obtainingDecodedData<T : Codable>(_ file: String) -> T {
        guard let url = self.url(forResource: file, withExtension: nil) else {
            fatalError("cannot locate url file \(file)")
        }

        guard let data1  = try? Data(contentsOf: url) else {
            fatalError("cannot load up url file \(file) as data")
        }

        let decoder = JSONDecoder()

        let e1 = DateFormatter()
        e1.dateFormat = "y-MM-dd"
        decoder.dateDecodingStrategy = .formatted(e1)

        guard let decodedData1 = try? decoder.decode(T.self, from: data1) else {
            fatalError("cannot decode data from \(file)")
        }

        return decodedData1
    }
}

3      

Glad you got it sorted. Just a couple of notes about your project.

  1. You call the JSON request three times, which it ok here as it in bundle however if calling from a website will be unnessary data transfer.
  2. The reason Paul using the same "names" is that they are usually the same eg astronaults in one view is astronaults in the other, so if helps create less confusion. Also when you come do bigger project you may find hard to come with different name for property, arrays, view etc.
  3. You also repeating code with CrewAndRole etc when you have it in CrewRole struct, as far as I can see.

As I said it great work that you got it to work, just some food(code) for thought going forward. 👍🏻

3      

re @NigelGee ,

thanks for the feedback, the JSON request might have been called more than it shoud, but that seemed the only way I could get my console to run, else the code compiles, but the preview gives error. The var names from paul are the same, differentiated by lowercase and uppercase, that's the part I found confusing. CrewAndRole and CrewRole, even though repeated, the words involved are different.

I guess eventually I'd have to get used to the small and big case difference in vars of the same name. I am sure Paul would have taught us with codes under some sort of industrial protocol on how they name their vars

3      

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.