1

I keep coming back to the same problem. For other parts of my app I used coredata directly after parsing the API and that works fine. However, now I want to parse a JSON received through an API and just get one value that I use to calculate other values which I then put in Coredata.

Everything works fine and I have set up the URLSessions code as follows:

func fetchData(brand: String, completion: @escaping ((Double) -> Void)) {
    let urlString = "\(quoteUrl)\(brand)"
    if let url = URL(string: urlString) {
        var session = URLRequest(url: url)
        session.addValue("application/json", forHTTPHeaderField: "Accept")
        session.addValue("Bearer \(key)", forHTTPHeaderField: "Authorization")
        let task = URLSession.shared.dataTask(with: session) { (data, response, error) in
            if error != nil {
                print(error!)
                return
            }
            if let safeData = data  {
                let decoder = JSONDecoder()
                do {
                let decodedData = try decoder.decode(DataModel.self, from: safeData)
                let bid = decodedData.quotes.quote.bid
                let ask = decodedData.quotes.quote.ask
                let itemPrice: Double = (bid + ask)/2
                completion(itemPrice)
                } catch {
                        print(error)
                }
            }
        }
        task.resume()
        } 
    }

I am using the completionHandler to retrieve the part I need which I use in another file as follows:

func getGainLossNumber(brand: String, quantity: Int, price: Double) -> Double {
    var finalPrice = 0.0
    APImodel.fetchData(brand: brand) { returnedDouble in
    let currentPrice = returnedDouble

    if quantity < 0 {
    let orderQuantity = quantity * -1
    finalPrice = price + (currentPrice*(Double(orderQuantity))*100)
    } else {
    finalPrice = price - (currentPrice*(Double(quantity))*100)
    }
    
    }
    return finalPrice
}

finalPrice eventually returns 0.0. If I print currentPrice in the closure I do get the correct result. I used the completion handler in order to retrieve a number from the API because of the issues I was facing but it stil is not doing what I would like to have. The second function should return the value that was calculated using the value I got from the API that I retrieved with the completion handler.

I just can't figure out how to do it.

Mr_Bull3t
  • 67
  • 1
  • 6
  • use completion handler in `getGainLossNumber`, the current problem is function return value immediately, you need to wait until API call is complete. check this [Link](https://stackoverflow.com/questions/59925825/how-make-function-of-returning-address-but-not-only-getting-address/59925903#59925903) – Pratik Prajapati Nov 15 '21 at 17:24
  • How are you going to use finalPrice after getGainLossNumber? In what scope are you calling this function? – Joakim Danielson Nov 15 '21 at 18:28
  • @JoakimDanielson I want to store in in coredata later but I guess that I have to do it in the closure as per balazs mention. So I will do that. – Mr_Bull3t Nov 15 '21 at 18:45

1 Answers1

1

The problem is that you are calculating finalPrice inside a closure, which is asynchronous. Your getGainLossNumber method however, is synchronous, so it actually returns before your closure is finished calculating finalPrice. Restructure your code so that getGainLossNumber takes a closure as a parameter, and invokes it once finalPrice has been calculated. Something like:

func getGainLossNumber(brand: String, quantity: Int, price: Double, _ completion: @escaping (Double) -> Void) {
    APImodel.fetchData(brand: brand) { returnedDouble in
        let currentPrice = returnedDouble

        let finalPrice: Double
        if quantity < 0 {
            let orderQuantity = quantity * -1
            finalPrice = price + (currentPrice*(Double(orderQuantity))*100)
        }
        else {
            finalPrice = price - (currentPrice*(Double(quantity))*100)
        }
        
        completion(finalPrice)
    }
}

Also note, that finalPrice does not need to be var as it will be assigned a value only once.

EDIT

Usage:

getGainLossNumber(brand: "brand", quantity: 1, price: 120, { finalPrice in
    // You can access/use finalPrice in here.
}
Balázs Vincze
  • 3,550
  • 5
  • 29
  • 60
  • Thanks but how would I go about and use finalPrice outside of this function in order to use in another? So my original code returned a value but yours doesn't, anyway around this? – Mr_Bull3t Nov 15 '21 at 17:37
  • 1
    You it the same way you used the result from fetchData, in a closure – Joakim Danielson Nov 15 '21 at 17:40
  • @Mr_Bull3t See my edit. – Balázs Vincze Nov 15 '21 at 17:41
  • Thanks for the help. So the endresult should go like this: let gainLossNumber = getGainLossNumber(brand: brandAll, quantity: quantity, price: costBasis) I dont see how I can do that with the last edit of yours. – Mr_Bull3t Nov 15 '21 at 17:49
  • 1
    You will not be able to assign the result of getGainLossNumber to a variable/constant as it does not return anything (return type is implicitly Void). To use finalPrice outside the function, do any work you need that involves finalPrice inside the closure of getGainLossNumber, as I have shown in my edit. – Balázs Vincze Nov 15 '21 at 18:26