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

Day 42 - Challenge. Problem with subviews

Forums > 100 Days of SwiftUI

Hello everyone!

struct MissionShotView: View {
    let astronauts: [String: Astronauts] = Bundle.main.decode("astronauts.json")
    let missions: [Missions] = Bundle.main.decode("missions.json")

    @State private var showingList = false

    var body: some View {
        NavigationView {
            VStack {
                if showingList {
                    ListLayout(astronauts: astronauts, missions: missions) // Cannot convert value of type '[Missions]' to expected argument type 'Missions'
                } else {
                    GridLayout(astronauts: astronauts, missions: missions) // Cannot convert value of type '[Missions]' to expected argument type 'Missions'
                }
            }

GRID LAYOUT:

struct GridLayout: View {
    let astronauts: Astronauts
    let missions: Missions

    let columns = [
        GridItem(.adaptive(minimum: 150))
    ]

    var body: some View {
        ScrollView {
            LazyVGrid(columns: columns) {
                ForEach(missions, id: \.id) { mission in // Key path value type '_' cannot be converted to contextual type '_'
                                              // Generic struct 'ForEach' requires that 'Missions' conform to 'RandomAccessCollection'
                    NavigationLink {
                        MissionView(mission: mission)
                    } label: {

Same problem in ListLayout

What am I doing wrong?

2      

@TooMaa has a coding problem:

Error: Cannot convert value of type '[Missions]' to expected argument type 'Missions'

When I coach programmers, I steer them away from short repetitive class and variable names. Instead, I encourage them to use longer, descriptive names.

Why? Yours is a great example where short names may have tripped you up. You're thinking you have a problem with understanding Swift, or SwiftUI. But I think the problem is with your variables leading you astray.

What is the difference between mission, missions, Missions, and [Missions] ?
I think you are confusing yourself!

You have a json file that contains a large collection of mission objects.
You call that collection missions. But you also call your mission class Missions

let missions: [Missions] = Bundle.main.decode("missions.json")  // Ooooof! Much duplicate. So confusion.

How would more descriptive names help you?

I might recommend you call your class: SingleMission
This contains all the data, and functions that support one, unique, single mission.

Then when you decode the JSON file, you have a collection of SingleMission objects, like this: [SingleMission] Consider giving this variable a descriptive name such as apolloMissions.

// this tells you that apolloMissions is a collection (array) of SingleMission objects.
let apolloMissions: [SingleMission] = Bundle.main.decode("missions.json")

Now, what exactly do you want to send to your GridLayoutView ? A single mission? or a collection of single missions?

Much Confusion

In your definition, you declare your GridLayout view to have a variable named missions. You define this variable to contain just one Missions object. Indeed, you are initializing the GridLayout with a variable also named missions. But that missions object is a collection of Missions objects. Single object versus collection of objects.

Yuck! Do you see how confusing this is?

// Your code is confusing you....
// all the missions
let missions: [Missions] = Bundle.main.decode("missions.json")

// ... snip .....
// You initialize a GridLayou like this...
// You send in data via the missions variable. missions suggests MANY missions.
        } else {
            GridLayout(astronauts: astronauts, missions: missions)  // <- Send in data via the missions variable
        }

struct GridLayout: View {
    let astronauts: Astronauts
    // Your view only expects ONE Missions (misnamed. Should be Mission (singular)
    let missions: Missions   // <-- This (plural) variable expects a single object. But the name suggest MULTIPLE objects.
    // ....snip.....
}

More Descriptive Names

If you revise your code, and rename your classes, structs, and variables, you'll have a better understanding.

//  Declare your SingleMission struct
struct SingleMission {
    // This contains data and functions to support one and only ONE mission.
}
// Clearly state that apolloMissions is a collection of many SingleMission objects
let apolloMissions = [SingleMission]  // <-- Redefine

// Declare GridLayout
struct GridLayoutView : View {
    // This grid will show a collection of SingleMission objects
    let missionsInThisGrid : [SingleMission] 
    // ... snip ....
}

// Initialize the GridLayoutView
// ... snip ....
  } else {
      // With descriptive names, it's hard to make a mistake when initializing a new GridLayoutView
      // Your GridLayoutView expects an array of SingleMission objects.
      // You are providing an array of SingleMission objects
      GridLayoutView(astronauts: astronauts, missionsInThisGrid: apolloMissions)  // <-- No mistakes in initializer!
  }

Please let us know how you solved your problem.

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.