Austin Griffith
Austin Griffith

Reputation: 63

Swift- Problem making multiple GET requests with Alamofire

I am having issues loading data from a GET request with Alamofire in swift to load items into a UITableView.

I have 2 methods fetchAllBeerOrders and fetchAllCocktailOrders which both are working properly and fetching the correct items. The issue I am having is in the viewWillAppear method where I call both of these fetch methods and reload the tableView. In the order I have it only the items from the fetchAllCocktailOrders method are loading in the tableView and I have verified by switching the order and getting the items loaded from fetchAllBeerOrders.


class DrinkOrdersTableViewController: UITableViewController { 
    var orders: [Order] = [] 
    override func viewDidLoad() {
        super.viewDidLoad()
        navigationItem.title = "Current Orders"
        
    }
    
    override func viewWillAppear(_ animated: Bool) {
        
        fetchAllBeerOrders { orders in
            self.orders = orders!
            //print("Beer fetch: ", self.orders)
            self.tableView.reloadData()
        }
        
        fetchAllCocktailOrders { orders in
            self.orders = orders!
            //print("Cocktail fetch: ", self.orders)
            self.tableView.reloadData()
        }
        
    }
    
    private func fetchAllCocktailOrders(completion: @escaping([Order]?) -> Void) {
        Alamofire.request("http://127.0.0.1:4000/orders", method: .get)
            .validate()
            .responseJSON { response in
                guard response.result.isSuccess else { return completion(nil) }
                guard let rawInventory = response.result.value as? [[String: Any]?] else { return completion(nil) }
                let currentOrders = rawInventory.compactMap { ordersDict -> Order? in
                    guard let orderId = ordersDict!["id"] as? String,
                        let orderStatus = ordersDict!["status"] as? String,
                        var pizza = ordersDict!["cocktail"] as? [String: Any] else { return nil }
                    pizza["image"] = UIImage(named: pizza["image"] as! String)
                    
                    return Order(
                        id: orderId,
                        pizza: Pizza(data: pizza),
                        status: OrderStatus(rawValue: orderStatus)!
                    )
                    
                }
                completion(currentOrders)
        }
        
    }

    private func fetchAllBeerOrders(completion: @escaping([Order]?) -> Void) {
        Alamofire.request("http://127.0.0.1:4000/orders", method: .get)
            .validate()
            .responseJSON { response in
                guard response.result.isSuccess else { return completion(nil) }
                guard let rawInventory = response.result.value as? [[String: Any]?] else { return completion(nil) }
                let currentOrders = rawInventory.compactMap { ordersDict -> Order? in
                    guard let orderId = ordersDict!["id"] as? String,
                        let orderStatus = ordersDict!["status"] as? String,
                        var pizza = ordersDict!["pizza"] as? [String: Any] else { return nil }
                    pizza["image"] = UIImage(named: pizza["image"] as! String)

                    return Order(
                        id: orderId,
                        pizza: Pizza(data: pizza),
                        status: OrderStatus(rawValue: orderStatus)!
                    )

                }
                completion(currentOrders)
        }
    }
    
    
    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        print("Debugging ROWS", orders.count)
        return orders.count
    }
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "order", for: indexPath)
        let order = orders[indexPath.row]        
        cell.textLabel?.text = order.pizza.name
        cell.imageView?.image = order.pizza.image
        cell.detailTextLabel?.text = "$\(order.pizza.amount) - \(order.status.rawValue)"
        
        return cell
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        performSegue(withIdentifier: "orderSegue", sender: orders[indexPath.row] as Order)
    }
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "orderSegue" {
            guard let vc = segue.destination as? OrderViewController else { return }
            vc.order = sender as? Order
        }
    }
}

Im not sure if I need to combine the GET requests into a single request or if my logic on the approach is wrong but I need the tableView to load orders from both fetchAllBeerOrders and fetchAllCocktailOrders. Please help

Upvotes: 1

Views: 761

Answers (3)

Austin Griffith
Austin Griffith

Reputation: 63

Update:

To achieve my goal of loading data from both GET requests I needed to append the data to the orders array with the .append(contentsOf: orders!) method instead of with self.orders = orders!. In the viewWillAppear method the second call to fetchCocktailOrders will always overwrite the contents from the first call to fetchBeerOrders. Clearly I am a swift rookie!

        fetchAllBeerOrders { orders in
            //self.orders.append(orders)
            self.orders.append(contentsOf: orders!)
            self.tableView.reloadData()
        }

        fetchAllCocktailOrders { orders in
            //self.orders.append(orders)
            self.orders.append(contentsOf: orders!)
            self.tableView.reloadData()
        }

This is the correct code.

Upvotes: 0

milo
milo

Reputation: 976

Like Levi Yonder says, it is indeed a problem with the results of your second request replacing the order, but his provided answer is not optimal.

You have to keep in mind that network requests are asynchronous. This means fetchAllCocktailOrders can finish before the fetchAllBeerOrders request is completed. In that case the same problem would occur:

  1. Requests fire at the same time
  2. fetchAllCocktailOrders completes first, appends the data to orders
  3. fetchAllBeerOrders completes, replaces current batch of data with result of this request.

Solution:

fetchAllBeerOrders { orders in
    self.orders.append(orders)
    self.tableView.reloadData()
}

fetchAllCocktailOrders { orders in
    self.orders.append(orders)
    self.tableView.reloadData()
}

Upvotes: 1

Levi Yoder
Levi Yoder

Reputation: 177

The reason you are having this issue is that fetchAllCocktailOrders replaces everything in orders that the fetchAllBeerOrders initially had fetched, versus appending to.

So viewWillAppear is calling fetchAllBeerOrders, which populates orders, and then fetchAllCocktailOrders is replacing everything in orders instead of appending to everything in orders.

To fix this, when you call the fetchAllCocktailOrders, append orders with the contents of what is being returned by the function, instead of replacing:

override func viewWillAppear(_ animated: Bool) {

        fetchAllBeerOrders { beerOrders in
            self.orders = beerOrders!
            //print("Beer fetch: ", self.orders)
            self.tableView.reloadData()
        }

        fetchAllCocktailOrders { coctailOrders in
          self.orders.append(coctailOrders)
            //print("Cocktail fetch: ", self.orders)
            self.tableView.reloadData()
        }

    }

Upvotes: 0

Related Questions