swiftswiftuiviewwatchos

Type '()' cannot conform to 'View'


I'm struggling to output a random String inside the View.

Due to the error:

Type '()' cannot conform to 'View

I've learned that View isn't the appropriate place to write down the functions itself.

What should I do to output itemX.randomElement() and itemY.randomElement()?

struct SummaryView: View {
    var avgValue = 1.5
    var roundedKarvonenValue: Double
    var itemX = ["A", "B", "C", "D", "E", "F", "G", "H"]
    var itemY = ["I", "J", "K", "L", "M", "N", "O", "P"]

    var body: some View {
        ScrollView {
            VStack(alignment: .leading) {
                if workoutManager.averageHeartRate < roundedKarvonenValue {
                    print(itemX.randomElement()!)
                } else {
                    print(itemY.randomElement()!)
                }
            }
        }
    }
}

Solution

  • There is a great deal to unpack in this question, but we will start with the simple first. Views are for displaying something on screen. Nothing more. The reason you are having issues with the print() statements is that they are for printing to the console, not the screen. Therefore, the compiler gives you an error. To fix this, use Text like @JoakimDanielson said:

    struct SummaryView: View {
        var workoutManager: WorkoutManager //This var must be declared
        
        var avgValue = 1.5
        var roundedKarvonenValue: Double
        var itemX = ["A", "B", "C", "D", "E", "F", "G", "H"]
        var itemY = ["I", "J", "K", "L", "M", "N", "O", "P"]
        
        var body: some View {
            ScrollView {
                VStack(alignment: .leading) {
                    if workoutManager.averageHeartRate < roundedKarvonenValue{
                        Text(itemX[0])
                    } else {
                        Text(itemY[0])
                    }
                }
            }
        }
    }
    

    As to your second part as to where to place your functions, is not as clear cut. You should look up MVVM architecture to gain an understanding of the standard way to structure your app in SwiftUI. Right now, you are simply displaying data from HealthKit using Apple's WWDC class WorkoutManager(I presume).

    At the very basic level, if your function has to do with changing how the data is displayed, it stays in the View. If the function fundamentally changes something about your Model or is needed by multiple views, it should go into the Model. That is a judgment call.

    So, for your code, you are showing a random element from a variable declared in a view. That is all local stuff, so the function stays in the view. Adding the func randomElement() and cleaning your code up a bit more leaves you with this:

    struct SummaryView: View {
        // If you are not changing the variables, declare them as constants. This uses less memory.
        let workoutManager: WorkoutManager
        let avgValue = 1.5
        let roundedKarvonenValue: Double
        let itemX = ["A", "B", "C", "D", "E", "F", "G", "H"]
        let itemY = ["I", "J", "K", "L", "M", "N", "O", "P"]
        
        var body: some View {
            ScrollView {
                VStack(alignment: .leading) {
                    // I turned your if statement into a Terniary Conditional Operator to shrink the amount of code.
                    Text(workoutManager.averageHeartRate < roundedKarvonenValue ? randomElement(itemX) : randomElement(itemY))
                }
            }
        }
                         
        func randomElement(_ stringArray: [String]) -> String {
            let end = stringArray.count - 1
            let index = Int.random(in: 0...end)
            return stringArray[index]
        }
    }