swiftswiftuiasync-await

Task{} works but .task{} throws Invalid conversion from throwing function of type error?


I am trying to understand the reason behind why does Task{} works but when I do it with the VStack{}.task{} I am getting the error:

Invalid conversion from throwing function of type '@Sendable () async throws -> ()' to non-throwing function type '@Sendable () async -> Void'

I am trying to have the fetchWeather() function run the moment the view starts without having the user to tap on the button. (If the below way is the correct way to do so) but ran upon this error and am really curious to the reasoning behind this error

Below is my code:

struct WeatherView: View {
    var body: some View {
        VStack{
           Text("Hello, World!")
           Button("Get Weather", action: {
            // Works when I tap the button
//                Task {
//                    try await fetchWeather()
//                }
        })
    }
    //*******Xcode error with invalid conversion*******
    .task {
        try await fetchWeather()
    }
  }
}

struct WeatherView_Previews: PreviewProvider {
   static var previews: some View {
       WeatherView( )
   }
}

The function:

func fetchWeather () async throws  {
   let URLString = "https://api.openweathermap.org/data/2.5/weather?appid=someAPIKeyshere&q=seattle"
   let (data, response) = try await URLSession.shared.data(from:  URL(string: URLString)!)
   guard let httpResponse = response as? HTTPURLResponse,
      httpResponse.statusCode == 200 else {
          throw ResponseError.someError
      }
   let decodedResponse = try? JSONDecoder().decode(WeatherData.self, from: data)
   print("This is in decodedResponse: \(decodedResponse)")
}

thanks in advance!


Solution

  • Look at the error message you're getting…

    Your function is of type:

    @Sendable () async throws -> ()
    

    whereas the .task modifier is expecting…

    @Sendable () async -> Void
    

    The difference is the throws.

    To resolve this, you need to handle errors thrown by your function, e.g.…

    .task {
        do {
            try await fetchWeather()
        } catch {
            // handle error
        }
    }
    

    You can compare the parameters that the initialiser for Task and the .task modifier take:

    Task.init

    init(priority: TaskPriority? = nil, operation: @escaping @Sendable () async throws -> Success)
    

    .task modifier

    func task(priority: TaskPriority = .userInitiated, _ action: @escaping @Sendable () async -> Void) -> some View