skip to Main Content

I am creating some reusable views, but to keep the question and example short, I am sharing limited code for now. Let’s say I have created a CustomActionsView which displays the different messages and buttons in various styles as needed.

I created a view builder which returns the view based on different inputs. Sharing code below:

import SwiftUI

struct Destinations {
    var messageText: String = ""
    var viewStyle: Int = 0
    @Binding var showingModal:Bool

    enum CustomDestinations: Int, CaseIterable, Hashable {
        case view1 = 0, view2 = 1, view3 = 2

        @ViewBuilder var view: some View {
            switch self {
            case .view1: CustomActionsView(mainMessageText: self.messageText, viewStyle: 0, showingModal: self.$showingModal)
            case .view2: CustomActionsView(mainMessageText: self.messageText, viewStyle: 1, showingModal: self.$showingModal)
            case .view3: CustomActionsView(mainMessageText: self.messageText, viewStyle: 2, showingModal: self.$showingModal)
            }
        }
    }
}

I have created a similar enum(not wrapped in struct) in other places in my app and it works fine since the view does not require any inputs. However in this case, CustomActionView expects messageText, viewStyle and a binding property, so I wrapped it within a struct thinking I can assign property values from struct and it will work.

But with this design I am getting the following error and similar error for other 2 properties too:

Value of type 'Destinations.CustomDestinations' has no member 'messageText'

I am not able to store properties within enum either.

Can we pass values to view’s within enum?

Note: I am working on watch app with min watchOS version as 7.0.

Thanks!

2

Answers


  1. Well, first, the way that you’re using @Binding in a struct that isn’t a view is bound to bite you back at some point. Bindings are SwiftUI-specific and using them outside of that context is bound to cause bugs and unexpected behaviours at some point.

    If you want to pass in some data when using enum cases, you must use associate values:

    enum Foo {
        case number(Int)
        case text(String)
    }
    
    switch Foo.text("Hello, World") {
    case .number(let number):
        print("the number is: (number)")
    
    case .text(let text):
        print("the text is: (text)")
    }
    

    That said, using associated values is incompatible with raw values, which means you’d need to drop the Int and CaseIterable conformance.

    I think it’s over complicating things when Destinations is not a view, so why not just make it a view?

    struct Destinations: View {
        var messageText: String = ""
        var viewStyle: Int = 0
    
        @Binding var showingModal: Bool
    
        var body: some View {
            CustomActionsView(
                mainMessageText: messageText,
                viewStyle: 0,
                showingModal: $showingModal
            )
        }
    }
    

    And then, you can drop the whole enum dance.

    Login or Signup to reply.
  2. In such cases we can use function instead calculable property and get needed things via arguments, like

    enum CustomDestinations: Int, CaseIterable, Hashable {
        case view1 = 0, view2 = 1, view3 = 2
    
        @ViewBuilder 
        func view(message: String, isModal: Binding<Bool>) -> some View {
            switch self {
            case .view1: CustomActionsView(mainMessageText: message, viewStyle: 0, showingModal: isModal)
            case .view2: CustomActionsView(mainMessageText: message, viewStyle: 1, showingModal: isModal)
            case .view3: CustomActionsView(mainMessageText: message, viewStyle: 2, showingModal: isModal)
            }
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search