23

I tried a SwiftUI tutorial, "Handling User Input".

https://developer.apple.com/tutorials/swiftui/handling-user-input

I tried implementing it with for instead of ForEach. But an error arose: "Closure containing control flow statement cannot be used with function builder 'ViewBuilder'".

FROM:

import SwiftUI

struct LandmarkList: View {
    @State var showFavoritesOnly = true
    
    var body: some View {
        NavigationView{
            List{
                Toggle(isOn: $showFavoritesOnly){
                    Text("Show FavatiteOnly")
                }
                
                ForEach(landmarkData) { landmark in
                    if !self.showFavoritesOnly || landmark.isFavorite {
                            NavigationLink(destination: LandmarkDetail(landmark: landmark)) {
                                LandmarkRow(landmark: landmark)
                            }
                        }
                }
            }
            .navigationBarTitle(Text("Landmarks"))
        }
    }
}

TO (I wrote):

import SwiftUI

struct LandmarkList: View {
    @State var showFavoritesOnly = true
    
    var body: some View {
        NavigationView{
            List{
                Toggle(isOn: $showFavoritesOnly){
                    Text("Show FavatiteOnly")
                }
                
                for landmark in landmarkData {
                    if $showFavoritesOnly || landmark.isFavorite {
                        NavigationLink(destination: LandmarkDetail(landmark: landmark)){
                            LandmarkRow(landmark: landmark)}
                        }
                }
            }
            .navigationBarTitle(Text("Landmarks"))
        }
    }
}
Tamás Sengel
  • 55,884
  • 29
  • 169
  • 223
msoniku
  • 231
  • 1
  • 2
  • 4
  • 4
    `ForEach` is a special view, but `for` is control flow statement which is not allowed in ViewBuilder where you try to put it in. – Asperi Jun 28 '20 at 04:11
  • Why do you want to use `for`? The `ForEach` view exists precisely because a regular `for` loop can't be used within a view builder function. But there really isn't anything you could do with a `for` loop that you can't do with `ForEach`, so knowing what you want to achieve would help us answer your question. – Jonas Zell Dec 27 '21 at 18:13

2 Answers2

14

The ForEach confirms to View, so at its core, it is a View just like a TextField. ForEach Relationships

You can't use a normal for-in because the ViewBuilder doesn't understand what is an imperative for-loop. The ViewBuilder can understand other control flow like if, if-else or if let using buildEither(first:), buildEither(second:), and buildif(_:) respectively.

Hamza Jadid
  • 388
  • 4
  • 17
5

Try to comment out the if statement, and Xcode might reveal the real error to you. Here's one example: I was getting this error because of a missing associated value of an enum passed to one of my views.

Here's how it looked like before and after:

Before

Group {
    if let value = valueOrNil {
        FooView(
            bar: [
                .baz(arg1: 0, arg2: 3)
            ]
        )
    }
}

After

Group {
    if let value = valueOrNil {
        FooView(
            bar: [
                .baz(arg1: 0, arg2: 3, arg3: 6)
            ]
        )
    }
}
Tamás Sengel
  • 55,884
  • 29
  • 169
  • 223
  • Add this to the list of non-sensical compiler issues ¯\\_(ツ)_/¯ really misled me when I had just added a param in a function. Thanks! – Skwiggs Aug 27 '22 at 15:25
  • Thanks, this was exactly my issue with the same error. – KevinS Feb 21 '23 at 23:39