Shiloni
Shiloni

Reputation: 41

How to represent a JSON file in SwiftUI?

I'm quite a beginner and trying to get the OpenWeather API JSON to show up in my challenge project.

I managed to model it


struct WeatherRespose: Codable {
    var weather: [Weather]
    var name: String
}

&

import Foundation


struct Weather: Hashable, Codable {
    var main: String
    var description: String
}

In addition to fetch the data in ContentView. However, when I try to present it:

@State var weatherForcast = Weather() or @State var weatherForcast = WeatherResponse() I get this error: Missing argument for parameter 'from' in call, insert 'from: <#Decoder#>'

The only thing that worked for me is to present the data in an array:

@State var weatherForcast = [Weather]()

Any idea what am I missing? thank you so much! Ran

Upvotes: 0

Views: 236

Answers (1)

M. Kuz.
M. Kuz.

Reputation: 73

I made pretty simple example of how you can do this. There are several additional files, so it's easier to understand how it works in details:

  1. Create additional file called NetworkService, it will fetch weather data:
import Foundation

final class NetworkService{

    private let url = "https://example.com"
    
    func performWeatherRequest(completion: @escaping (Result<WeatherResponse, Error>) -> Void){
            URLSession.shared.dataTask(with: URL(string: url)!) { data, response, error in
                guard let data = data, error == nil else {
                    completion(.failure(WeatherError.failedToDownload))
                    return
                }
                                
                let weatherResponse: WeatherResponse = try! JSONDecoder().decode(WeatherResponse.self, from: data)
            
                completion(.success(weatherResponse))
                
            }.resume()
    }
    public enum WeatherError: Error {
            case failedToDownload
    }
}
  1. Create simple ViewModel which will retrieve data from our NetworkService and prepare to present it in ContentView
import Foundation
import SwiftUI

extension ContentView {
    @MainActor class ContentViewVM: ObservableObject {
        private var networkService = NetworkService()
        
        @Published var currentWeatherMain: String?
        @Published var currentWeatherDescription: String?
        
        func fetchWeather(){
            networkService.performWeatherRequest { [weak self] result in
                DispatchQueue.main.async {
                    switch result {
                    case .success(let weatherResponse):
                        self?.currentWeatherMain = weatherResponse.weather[0].main
                        self?.currentWeatherDescription = weatherResponse.weather[0].description
                    case .failure(_):
                        print("oops, error occurred")
                    }
                }
            }
        }
    }
}
  1. Add our ContentViewVM to our ContentView:
import SwiftUI

struct ContentView: View {
    @StateObject var viewModel = ContentViewVM()
    
    var body: some View {
        VStack {
            Text("The main is: \(viewModel.currentWeatherMain ?? "")")
            Text("The description is: \(viewModel.currentWeatherDescription ?? "")")
        }
        
        .onAppear{
            viewModel.fetchWeather()
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Hope it helps.

Upvotes: 1

Related Questions