BLACK FRIDAY SALE: Save big on all my Swift books and bundles! >>

SwiftUI Moonshot problem

Forums > 100 Days of SwiftUI

I think I am misunderstanding something basic about passing variables that are structs between views, would appreciate any help!

I am trying to break out the crew horizontal scroll view from the MissionView in Moonshot into a separate view. MissionView defines a struct called CrewMember as

struct CrewMember {
        let role: String
        let astronaut: Astronaut
    }

and populates a variable 'crew' with an init as an array of CrewMember. So to break a ScrollView out into a separate view I am defining a CrewView as

import SwiftUI

struct CrewView: View {
    struct CrewMember {
        let role: String
        let astronaut: Astronaut
    }
    var crew : [CrewMember]

var body: some View {
        ScrollView(.horizontal, showsIndicators: false) {
        // other items
  }

When I pass CrewView(crew), though, I get an error:

Cannot convert value of type '[MissionView.CrewMember]' to expected argument type '[CrewView.CrewMember]'

I've tried eliminating the struct in CrewView, re-initializing the whole thing in CrewView, etc. But nothing works. I think my logic is right for how this should work but perhaps I'm missing something fundamental about how structs work and get passed between views? Thanks in advance for any help

   

If you are going to share CrewMember in two Views, you should pull it out into a standalone struct rather than having it defined within MissionView or CrewView. When it is defined as a nested type like that, then you actually end up with two completely different types, as the error message points out.

So, instead of:

struct MissionView: View {
    struct CrewMember {
        ...
    }

    ....
}

struct CrewView: View {
    struct CrewMember {
        ...
    }

    ....
}

you would have:

struct CrewMember {
    ...
}

struct MissionView: View {
    ...
}

struct CrewView: View {
    ...
}

   

So, just to make sure I understand, the struct isnt just defining a type of data structure its creating something more like an object? Since those structs are each children of different parents they arent the same thing? I've been thinking of structs as a data structure definition rather than as something more akin to class. Am I still missing it?

   

No, you're right, structs are data structure definitions that you instantiate as instances of those definitions. The problem here is that in Swift you can define nested types. A type defined inside another type is different from a type of the same name defined outside of an parent type and is different from a type nested inside a different parent type.

IOW, the type CrewMember is not the same thing as the type MissionView.CrewMember is not the same thing as the type CrewView.CrewMember. That's why you get the error message Cannot convert value of type '[MissionView.CrewMember]' to expected argument type '[CrewView.CrewMember]' Swift is a typesafe language and will be very, very picky about your types. You know and I know that MissionView.CrewMember and CrewView.CrewMember describe the same information, but to the compiler they are completely different things.

Sometimes nested types are defined as such because they are only used inside some other type. Sometimes nested types are defined as such as a sort of type of namespacing. You see the latter a lot in Apple's frameworks.

For instance, in SwiftUI there is a type called Font. Defined within Font are several other nested types, such as Design, TextStyle and Weight. These types only really make sense when used with a Font, so they are defined within it (as Font.Design, Font.TextStyle and Font.Weight) to scope them to that parent type and indicate that they don't have independent usage.

In the Moonshot example, CrewMember only has meaning when talking about a mission in the original project (because crew members are constructed in the init and used in the body and nowhere outside of MissionView), so it's defined within MissionView. However, you are trying to create a separate CrewView that also needs CrewMembers. So it would be better to make CrewMember a standalone type rather than nesting it under another type.

(Alrhough technically you could do this too:

struct CrewView: View {
    var crew : [MissionView.CrewMember]

    ...
}

But I think it would be better to define CrewMember outside of any particular View.)

   

Makes so much sense when you describe it that way ;) -- thank you so much for your help. I was really confused and this has really helped me to understand structs. I really appreciate it!

   

Hacking with Swift is sponsored by RevenueCat

SPONSORED In-app subscriptions are a pain to implement, hard to test, and full of edge cases. RevenueCat makes it straightforward and reliable so you can get back to building your app. Oh, and it's free if your app makes less than $10k/mo.

Learn more

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.