Home > Software engineering >  Get a nested json in URL session?
Get a nested json in URL session?

Time:03-10

{
"data":{
"email":"[email protected]", 
"password":"123",
"token":""
} 
}



struct JsonResult: View{
    @State private var results = [GetData]()
    var body: some View{
        List(results, id: \.email){ item in
            VStack(alignment: .leading) {
                Text(item.password)
                    .font(.headline)
                Text(item.token)
                    .font(.headline)
            }
            
        }.task {
            await loadData()
        }
    }

struct Response : Codable {
        var results: [GetData]
    }
    
    struct GetData: Codable{
        var data : [Result]
    }
    
    struct Result: Codable {
        var email: String
        var password: String
        var token: String
    }
    func loadData() async{
        guard let url = URL(string: "MYURL") else {
            print("invalid URL")
            return
        }
        do{
            let(data,_) = try await URLSession.shared.data(from: url)
            // more code
            if let decodedResponse = try? JSONDecoder().decode(Response.self, from: data)
            {
                results = decodedResponse.results
            }
        } catch {
            print("Invalid Data")
        }
    }

}

i need to know if the codable structure is right according to the structure of data i gave ? and also the fetching in list ? please i need help in the URLsession i am still new and i barely know about url alot !

i would be grateful if you help me ! thank you verrrry much !!!!

CodePudding user response:

In the JSON there is no array involved (no [] at all).

The model corresponding to the JSON is

struct Response: Decodable {
    let data : UserData
}

struct UserData: Decodable {
    let email: String
    let password: String
    let token: String
}

So the data source cannot be declared as an array. To avoid an optional type create an enum with associated values indicating a state. The benefit is that you can show different views depending on the state

struct JsonResult: View {

    enum LoadingState {
        case idle, loading, loaded(UserData), failure(Error)
    }

this is the rest of the struct, consider that there is no List either because UserData is a single object.

    @State private var state : LoadingState = .idle
    
    var body: some View {
        VStack {
            switch state {
                case .idle: EmptyView()
                case .loading: ProgressView()
                case .loaded(let userData):
                    
                    VStack(alignment: .leading) {
                        Text(userData.password)
                            .font(.headline)
                        Text(userData.email)
                            .font(.headline)
                    }
                    
                case .failure(let error): Text(error.localizedDescription)
            }
        }.task {
            await loadData()
        }
    }
    
    func loadData() async {
        
        state = .loading
        guard let url = URL(string: "MYURL") else {
            state = .failure(URLError(.badURL))
            return
        }
        do {
            let (data,_) = try await URLSession.shared.data(from: url)
            // more code
            let decodedResponse = try JSONDecoder().decode(Response.self, from: data)
            state = .loaded(decodedResponse.data)
            
        } catch {
            state = .failure(error)
            print(error) // this shows the real DecodingError
        }
    }
}

CodePudding user response:

struct Response : Decodable, Hashable {
    var results: [GetData]
}

struct GetData: Decodable, Hashable{
    var data : [Result]
}

struct Result: Decodable, Hashable {
    var email: String
    var password: String
    var token: String
}

enum RequestError: Error {
    case invalidURL
    case missingData
}

class JsonResultViewModel: ObservableObject{
    
    @Published var response = [Response]()
    
    func performHTTPRequest(urlString: String) async throws{
        guard let url = URL(string: urlString) else {throw RequestError.invalidURL}
        guard let (data, response) = try? await URLSession.shared.data(from: url) else{throw RequestError.invalidURL}
        guard (response as? HTTPURLResponse)?.statusCode == 200 else {throw RequestError.invalidURL}
        let decoder = JSONDecoder()
        guard let jsonResponse = try? decoder.decode(Response.self, from: data) else {throw RequestError.missingData}
        DispatchQueue.main.async {
            self.response.append(jsonResponse)
        }
    }
}



struct ContentView: View {
    @StateObject private var results = JsonResultViewModel()
    var body: some View {
        List(results.response.indices, id: \.self){ index in
            VStack(alignment: .leading) {
                Text(results.response[index].results[index].data[index].email)
                    .font(.headline)
                Text(results.response[index].results[index].data[index].token)
                    .font(.headline)
            }
        }
        .onAppear(perform: {
            Task{
                do {
                    try await results.performHTTPRequest(urlString: "wwww.url.com")
                } catch RequestError.invalidURL{
                    print("invalid URL")
                } catch RequestError.missingData{
                    print("missing data")
                }
            }
        })
        
    }
}
  • Related