King faheem
King faheem

Reputation: 21

Getting an error when trying to decode the data in Swift through backend API

I'm using trying to get the data through backend API and I'm getting the data on requisition function in networking class like

if let getData = data{
                result = .success(getData)
                let respondString = String(data:getData, encoding: .utf8) ?? "could not convert our data into string"
              //  print("backend response is \(respondString)") //here i'm getting data perfeclty
          }

but I don't know why data is not decoded perfectly when I'm calling the networking calls in home controller to load the data it's not working fine I'm attached all the screenshots as well as networking class code please help to resolve this issue.

NETWORKING CALSS:

struct NetworkService{
    static let  shared = NetworkService()
    private init(){
    }
   
    func fetchAllCategories(completion: @escaping(Result<AllDishes,Error>)-> Void){
        request(route: .fetchAllCategories, method: .get, completion: completion)
    }
    
    
     // 1.private function access in this struct not outsude the struct
    private func createRequest(route: Route, method: Method, parameters : [String:Any]? = nil) -> URLRequest?
    {
        //baseUrl = "https://yummie.glitch.me/temp"
        let urlString1 = Route.baseUrl + route.description //calling from route class
        guard let  url = URL(string: urlString1) else { return nil }//convet url into string
        var urlRequest =  URLRequest(url: url)
        urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
        //rawvlaue return cases as a string
        urlRequest.httpMethod = method.rawValue
        
        if let params = parameters{
            
            switch method{
            
            case .get:
                // https:// google.com/books?type=cartoon&page=1
                var urlComponent = URLComponents(string: urlString1)
                urlComponent?.queryItems = params.map {
                    URLQueryItem(name: $0, value: "\($1)")}
                    urlRequest.url = urlComponent?.url
            
            case .post, .delete, .patch:
                let bodyData = try? JSONSerialization.data(withJSONObject: params, options: [])
                urlRequest.httpBody = bodyData
            }
        }
        return urlRequest
    }
    
    //2.  Create One Generic Function to Make Different Types of API Requests
    private func request<T:Decodable>(route: Route,method: Method, parameters: [String: Any]? = nil, completion: @escaping(Result<T, Error>) -> Void){
        
        guard let request = createRequest(route: route, method: method, parameters: parameters) else {//caling 1st function
            completion(.failure(AppError.unknownError)) //calling error that define in APPErro class
            return
            
        }
        
  
        URLSession.shared.dataTask(with: request){ (data, response, error) in
            var result: Result<Data, Error>?
          
            if let getData = data{
                result = .success(getData)
                let respondString = String(data:getData, encoding: .utf8) ?? "could not convert our data into string"
              //  print("backend response is \(respondString)") //here i'm getting data perfeclty
            }
            
            else if let error = error{
                result = .failure(error)
                print("error is:\(error.localizedDescription)")
            }
            DispatchQueue.main.async {
                print("success \(result)")
                self.handleRequest(result: result , completion: completion)
            }
        }.resume()
        
    }
    
    //4.hnalde the api response from api response class and show to decoadlbe data that shows on api
    
    private func handleRequest<T:Decodable>(result:Result<Data, Error>?, completion: @escaping(Result<T, Error>) -> Void){
        guard let result = result else {
            completion(.failure(AppError.unknownError))
            return
        }
        
        switch result {
        case .success(let data):
            let decoder = JSONDecoder()
            guard let response = try? decoder.decode(ApiResponse<T>.self, from: data) else {
                completion(.failure(AppError.errorDecoding))
                return
            }
            if let error = response.error{
                completion(.failure(AppError.ServerError(error)))
            }
            
            if let decodedData = response.data{
                completion(.success(decodedData))
                
            }else{
                completion(.failure(AppError.unknownError))
            }
        case .failure(let error):
            completion(.failure(error))
        }
      
    }
    
}

HOME CONTROLLER CLASS:

class HomeController: UIViewController {

@IBOutlet weak var tableView: UITableView!
var categories: [String] = ["Food Category","Popular Dishes","Chief Specials"]

var categoresdish: [DishCategory] = []{
    didSet{
        DispatchQueue.main.async {
            self.tableView.reloadData()
        }
    }
}
var popular: [Dish] = []{
    didSet{
        DispatchQueue.main.async {
            self.tableView.reloadData()
        }
    }
}
var specials: [Dish] = []{
    didSet{
        DispatchQueue.main.async {
            self.tableView.reloadData()
        }
    }
}

override func viewDidLoad() {
    super.viewDidLoad()
    self.loadData()
    
    self.tableView.delegate = self
    self.tableView.dataSource = self
    self.tableView.register(UINib(nibName: String(describing: HomeTableViewCell.self), bundle: .main), forCellReuseIdentifier: String(describing: HomeTableViewCell.self))
    

}
private func loadData(){
    ProgressHUD.show()
    NetworkService.shared.fetchAllCategories { [weak self] (result) in
        switch result{
            
        case .success(let allDishes):
            ProgressHUD.dismiss()
            self?.categoresdish = allDishes.categories ?? []
            self?.popular = allDishes.populars ?? []
            self?.specials = allDishes.specials ?? []
            
        case .failure(let error): // when i load the load the data the control goes to here
            print("faheem error is \(error.localizedDescription)")
            ProgressHUD.showError()
        }
    }
}

}

extension HomeController : UITableViewDelegate,UITableViewDataSource{
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return categories.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    self.configureDisehsCell(tableView, cellForRowAt: indexPath)
}

    func configureDisehsCell(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{

        guard let dishesCell = tableView.dequeueReusableCell(withIdentifier: String(describing: HomeTableViewCell.self), for: indexPath) as? HomeTableViewCell else {return UITableViewCell()}
        dishesCell.categoreisData = self.categories[indexPath.row]
        dishesCell.row = indexPath.row
        dishesCell.categoriesDishsLoad = self.categoresdish
        dishesCell.popularDishesLoad = self.popular
        dishesCell.chiefSpecialsLoad = self.specials
        
        return dishesCell
    }

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
   // return indexPath.row == 0 ? 140: 240
    let row = indexPath.row
    switch row {
    case 0:
        return 140
    case 1:
        return 260
    case 2:
        return 200
        
    default:
        return 0
    }
    
 }

}

enter code here

ROUTE CLASS

enum Route{
    static let baseUrl = "https://yummie.glitch.me"
  
    case fetchAllCategories
    
    var description: String{
        switch self{
        case .fetchAllCategories:
            return "/dish-categories"
        }
    }
    
}

METHOD CLASS

enum Method: String{
    case get = "GET"
    case post = "POST"
    case delete = "DELETE"
    case patch = "PATCH"
}

APPERROR CLASS

enum AppError: LocalizedError{
    case errorDecoding
    case unknownError
    case inValidUrl
    case ServerError(String)
    
    var errorDescription: String?{
        switch self{
        
        case .errorDecoding:
            return "response could not be decoded"
        case .unknownError:
            return "i have no idea what the error is"
        case .inValidUrl:
            return "give me a valid url"
        case .ServerError(let error):
            return error
        }
    }
}

APIRESPONSE CLASS

struct ApiResponse<T:Decodable>: Decodable{
    let status: Int //like 100,200,300
    let message: String //message from the backend
    let data: T? // we get the different types of data like int,string, custom types etc and convert json into particular strucures
    let error: String? //errro from backend comes or not
    
}

ALL MODEL CLASSES

struct DishCategory: Decodable{
    var id: String
    var name: String
    var image: String
}

struct Dish: Decodable{
    var id:String
    var name:String
    var image:String
    var description:String
    var calories:Double
    
    var formatedCalories:String{
        return String(format: "%2f" , calories)
    }
}


struct Orders{
    var id: String
    var name: String
    var dish: Dish // kis dish pr order howa he
}

struct AllDishes: Decodable{
    let categories: [DishCategory]?
    let populars: [Dish]?
    let specials: [Dish]?
}

Upvotes: 0

Views: 669

Answers (0)

Related Questions