0

I have a custom view based on TheNeil's answer, that has two levels: MainContent and SubviewContent, both are Views.
The user can navigate from a single MainContent to many SubviewContent. This means I make the destination view's content dynamically.

The hierarchy looks like this:

struct StackNavigationView<MainContent, SubviewContent>: View where MainContent: View, SubviewContent: View {
    subView: () -> SubviewContent
    //...
}

struct SomeView: View {

    var body: some View {
        StackNavigationView(/*init parameters*/) {
            //MainView
        }
    }

    private func  subView(forIndex index: Int) -> AnyView {
        //some stuff...
    }

}

SubviewContent is built from a method passed from the MainContent and stored as a variable like so:

let subView: () -> SubviewContent

During initialization, I pass an @escaping method and assign it to the variable subView.

init(@ViewBuilder subView: @escaping () -> SubviewContent, /*more stuff*/) {
    self.subView = subView
    //more stuff...
}

That method returns a view based on an index, like so:

private func subView(forIndex index: Int) -> AnyView {
    switch index {
    case 0: return AnyView(/*some content*/)
    case 1: return AnyView(/*some content*/)
    //etc...
    default: return AnyView(ViewNotFound())
    }
}

The problem is that if I pass a view that requires parameters, the parameters must be hardcoded. Otherwise, the parameters are nil and the app crashes.

Example case with parameter:

case 1: return AnyView( DestinationView(parameter: some_parameter) )

This crashes because some_parameter is nil, when the user tries to navigate.
The same issue appears with optional parameters. When unwrapped, they are nil.

I have tried removing the @escaping bit to solve this issue, but then the app crashes instantly because there are no parameters to pass (again, it passes nil but for a different reason, obviously).


How can I pass a fully constructed View only after the users select the navigation destination?

UPDATE 1

Upon review and discussion of the question, I believe a better way to clarify the problem would be this:

How is it possible to maintain in-method parameters while passing the method as a variable?

Lae
  • 832
  • 1
  • 13
  • 34
  • What kind of parameter are you trying to send in? If it is model data, have you tried making it a `Singleton` and letting the subview have direct access, instead of as a parameter? – Yrb Oct 04 '21 at 15:24
  • @Yrb It is parameters for the views to fill content. Things like image paths, text etc... They are organized inside a `struct`. However, I use several different views and each requires a slightly different parameter set. This is why I need to construct the view and send it beforehand. – Lae Oct 04 '21 at 15:28
  • Your use of `AnyView` is a SwiftUI Anti-pattern. Apple specifically called it out in the 2021 WWDC presentation titled "Demystify SwiftUI" . The specific discussion I'm talking about starts about 28 minutes into the presentation, but you might review the whole session. https://developer.apple.com/videos/play/wwdc2021/10022/ – Scott Thompson Oct 04 '21 at 15:37
  • I think you are trying to put a square peg in a round hole. The use case in the answer you cited was for relatively static views. @ScottThompson is correct, and Apple did call out the pattern for good reason. It messes with animations, etc. It sounds like you need a more generic view that can handle the different parameters that then calls on subviews to show the results. – Yrb Oct 04 '21 at 15:42
  • @ScottThompson @Yrb I reviewed the video you mentioned and I believe I understand the point you are making. I should clarify that the destinations in my problem, come from a __database__ and are uniquely identified. They are all loaded from a _template view_ that fills the content based on the `id` I pass. The problem is here: I am unable to pass different `id`s with the current setup, because as @Yrb pointed out, it is setup for `static` views. When the method ends up running, the values of the parameters are missing. Thank you for your valuable feedback so far! – Lae Oct 04 '21 at 15:57

1 Answers1

1

If you are just trying to pass imagepaths/text, maybe just make the subviews read it directly from somewhere else?

Class ParamsToPass {
    static var data = [
        "viewID" : [
            ["param1key": "param1value"]
            ["param2key": "param2value"]
        ]
    ]
} 

On your update:

How is it possible to maintain in-method parameters while passing the method as a variable?

Maybe make the method return said parameters you need?