UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

SOLVED: Day 73 Project 14 Challenge | Trailing closure passed to parameter of type 'FormStyleConfiguration' that does not accept a closure

Forums > 100 Days of SwiftUI

Hello everyone!

I'm almost finished with BucketList's challenge but I found this error: Trailing closure passed to parameter of type 'FormStyleConfiguration' that does not accept a closure and I thought it had to do with opening in closing { } but I can't find the problem and I can't build the app to see if my solution is correct.

This is my code:

struct EditView: View {

    @StateObject private var viewModel: EditViewModel

    @Environment(\.dismiss) var dismiss
    var onSave: (Location) -> Void

    var body: some View {
        NavigationView {
            Form {  //The error is here
                Section {
                    TextField("Place name:", text: $viewModel.name)
                    TextField("Description:", text: $viewModel.description)
                }

                Section("Nearby...") {
                    switch $viewModel.loadingState {
                    case .loading:
                        Text("Loading...")
                    case .loaded:
                        ForEach($viewModel.pages, id: \.pageid) { page in
                            /*@START_MENU_TOKEN@*/Text(page.title)/*@END_MENU_TOKEN@*/
                                .font(.headline)
                            + Text(": ")
                            + Text(page.description)
                                .italic()
                        }
                    case .failed:
                        Text("Please try again later.")
                    }
                }
            }
            .navigationTitle("Place details")
            .toolbar {
                Button("Save") {
                    onSave(viewModel.save)
                    dismiss()
                }
            }
            .task {
                await viewModel.fetchNearbyPlaces()
            }
        }
    }

    init(location: Location, onSave: @escaping (Location) -> Void) {
        _viewModel = StateObject(wrappedValue: EditViewModel(location: location))
        self.onSave = onSave
    }
}

3      

Try to retype Form, and when XCode will show you two prompts Form(_ configuration:) and Form(content:) choose the latter. I think this should solve the issue.

3      

Thanks for replying!

I did that but now it appears a new error in the .loading case, which is: Type 'Binding<EditView.LoadingState>' has no member 'loading'. I read on Stack Over Flow and in another Forum Post that it might have to do with how I created the EditViewModel in the EditView --> @StateObject private var viewModel: EditViewModel

In the ContentView, the ViewModel was created like this --> @StateObject private var viewModel = ViewModel(). But I used this logic in the EditViewModel (which I'm not sure about the difference), a Location is required: @StateObject private var viewModel = EditViewModel(location: ).

I'm working in the challenge and I created the ViewModel for the EditView. This is it:

extension EditView {
    enum LoadingState {
        case loading, loaded, failed
    }

    @MainActor class EditViewModel: ObservableObject {

        @Published var name: String
        @Published var description: String

        @Published var loadingState = LoadingState.loading
        @Published var pages = [Page]()

        @Published var location: Location

        init(location: Location) {
          name = location.name
          description = location.description
          self.location = location
        }

        func save() -> Location {
            var newLocation = location
            newLocation.id = UUID()
            newLocation.name = name
            newLocation.description = description

            return newLocation
        }

        func fetchNearbyPlaces() async {
            let urlString = "https://en.wikipedia.org/w/api.php?ggscoord=\(location.coordinate.latitude)%7C\(location.coordinate.longitude)&action=query&prop=coordinates%7Cpageimages%7Cpageterms&colimit=50&piprop=thumbnail&pithumbsize=500&pilimit=50&wbptterms=description&generator=geosearch&ggsradius=10000&ggslimit=50&format=json"

            guard let url = URL(string: urlString) else {
                print("Bar URL: \(urlString)")
                return
            }

            do {
                let (data, _) = try await URLSession.shared.data(from: url)
                let items = try JSONDecoder().decode(Result.self, from: data)
                pages = items.query.pages.values.sorted()
                loadingState = .loaded
            } catch {
                loadingState = .failed
            }
        }

    }

}

3      

BUILD THE ULTIMATE PORTFOLIO APP Most Swift tutorials help you solve one specific problem, but in my Ultimate Portfolio App series I show you how to get all the best practices into a single app: architecture, testing, performance, accessibility, localization, project organization, and so much more, all while building a SwiftUI app that works on iOS, macOS and watchOS.

Get it on Hacking with Swift+

Sponsor Hacking with Swift and reach the world's largest Swift community!

@StateObject private var viewModel: ViewModel

That is correct

Just remove the $ at the switch:

  var body: some View {
    NavigationView {
      Form {
        Section {
          TextField("Place name", text: $viewModel.name)
          TextField("Description", text: $viewModel.description)
        }

        Section("Nearby…") {
          switch viewModel.loadingState { //remove the $ here as you do not need to bind anything, just read the value...
            case .loaded:
              ForEach(viewModel.pages, id: \.pageid) { page in
                    Text(page.title)
                        .font(.headline)
                    + Text(": ") +
                  Text(page.description)
                        .italic()
                }
            case .loading:
                Text("Loading…")
            case .failed:
                Text("Please try again later.")
            }
        }
      }
      .navigationTitle("Place details")
      .toolbar {
        Button("Save") {

          let newLocation = viewModel.createNewLocation()

          onSave(newLocation)
          dismiss()
        }
      }
      .task {
        await viewModel.fetchNearbyPlaces()
      }
    }
  }

  init(location: Location, onSave: @escaping (Location) -> Void) {
    self.onSave = onSave
    _viewModel = StateObject(wrappedValue: ViewModel(location: location))
  }

}

That is what I have...

3      

Thanks @VulcanCCIT ! That helped but now I'm getting this error: The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions at the var body: some View { } level.

3      

what happens if you close out of xcode and reopen?

3      

The error message "The compiler is unable to type-check this expression in reasonable time" is common in Swift. It means the compiler is confused, either because of a syntax error in your code or a bug in the compiler. Usually, refactoring your view into smaller views either will solve the problem or will result in a more informative error message.

In this case, try refactoring Section("Nearby…"){...} into a separate view as described here: https://www.hackingwithswift.com/books/ios-swiftui/view-composition

3      

I do not think the suggestin that @bobstern has is it as I just checked the HackingWithSwift+ solution and your code is almost identical with the exception of the .toolbar { } code, the solution has a slightly different save function but other than that your code is spot on. Just to be sure comment out the code in between the brackets after your Button("Save") { //comment out this code}

but I would suggest first to clean your build folder and or start xcode over...both have solved your issue for me in other code.

Hope that helps!

3      

Actually it does not look like you are passing information correctly to your save function...try this:

.toolbar {
                Button("Save") {
                    let newLocation = viewModel.save()
                    onSave(newLocation)
                    dismiss()
                }

3      

Thanks @VulcanCCIT and @bobstern !

I forgot to remove the $ at the ForEach inside the switch. It worked after I deleted it.

4      

@KeyBarreto glad I could help! I didnt catch the $ at the ForEach that has tripped me up before as well!

3      

BUILD THE ULTIMATE PORTFOLIO APP Most Swift tutorials help you solve one specific problem, but in my Ultimate Portfolio App series I show you how to get all the best practices into a single app: architecture, testing, performance, accessibility, localization, project organization, and so much more, all while building a SwiftUI app that works on iOS, macOS and watchOS.

Get it on Hacking with Swift+

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.