Extract data from closure using completion in swift

Viewed 200

First of all, this is my first attempt at Swift so I'm not really sure what I'm doing. I'm learning as I go right now and have hit a roadblock.

I'm trying to implement a WatchOS app that will call an API on a set timer to track fluctuations in some crypto prices.

I have figured out how to make the API call and get the JSON parsed to a point where I can print the data but I'm struggling to get it out of the closure and to my interface. I know the proper way to do this is with a completion handler but I can't seem to get a solid understanding of how to make that work in this scenario.

Any help would be appreciated

import SwiftUI

var refresh = bitcoin()
var btc: String = refresh
var eth: String = "ETH"
var doge: String = "DOGE"


struct ContentView: View {
    
    var body: some View {
        VStack(alignment: .leading ){
            
            Label("\(btc)", image: "eth").padding(.vertical, 10.0)
            Label("\(eth)", image: "eth").padding(.vertical, 10.0)
            Label("\(doge)", image: "doge").padding(.vertical, 10.0)
        
        }
        .scaledToFill()
    }

}



struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
    
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

struct responseData: Codable{
    
    let data: Response?
}

struct Response: Codable{
    var id: String
    var rank: String
    var symbol: String
    var name: String
    var supply: String
    var maxSupply: String
    var marketCapUsd: String
    var volumeUsd24Hr: String
    var priceUsd: String
    var changePercent24Hr: String
    var vwap24Hr: String
}

    func bitcoin() -> String{

            var result: String = "btc"
            var request = URLRequest(url: URL(string: "https://api.coincap.io/v2/assets/bitcoin")!,timeoutInterval: Double.infinity)
            request.httpMethod = "GET"
            
            
        let task = URLSession.shared.dataTask(with: request) { data, response, error in
          guard let data = data else {
            print(String(describing: error))
            return
          }
            let response = try! JSONDecoder().decode(responseData.self, from: data)
            result = (response.data?.priceUsd)!
            print(result)
        }
        
            task.resume()
        return result
        }
    

1 Answers

There many ways to achieve what you want, one way is to use "ObservableObject". Try something like this:

import SwiftUI

@main
struct TestApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

class CoinModel: ObservableObject {
    @Published var btcPriceUsd = "not yet available"
    @Published var ethPriceUsd = "not yet available"
    @Published var dogePriceUsd = "not yet available"
}

struct ContentView: View {
    @StateObject var coins = CoinModel()

    var body: some View {
        VStack(alignment: .leading ){
            Label("\(coins.btcPriceUsd)", image: "btc").padding(.vertical, 10.0)
            Label("\(coins.ethPriceUsd)", image: "eth").padding(.vertical, 10.0)
            Label("\(coins.dogePriceUsd)", image: "doge").padding(.vertical, 10.0)
        }
        .scaledToFill()
        .onAppear {
           // bitcoin()
           bitcoin2 { price in
              coins.btcPriceUsd = price
           }
        }
    }
    
    func bitcoin() {
        var request = URLRequest(url: URL(string: "https://api.coincap.io/v2/assets/bitcoin")!,timeoutInterval: Double.infinity)
        request.httpMethod = "GET"
        let task = URLSession.shared.dataTask(with: request) { data, response, error in
            guard let data = data else {
                return
            }
            let response = try! JSONDecoder().decode(responseData.self, from: data)
            if let respData = response.data {
                DispatchQueue.main.async {
                    coins.btcPriceUsd = respData.priceUsd
                }
            }
        }
        task.resume()
    }
    
}

EDIT: if you really want to use completion, then try this:

func bitcoin2(completion: @escaping (String) -> Void) {
    var request = URLRequest(url: URL(string: "https://api.coincap.io/v2/assets/bitcoin")!,timeoutInterval: Double.infinity)
    request.httpMethod = "GET"
    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        guard let data = data else {
            return completion("")
        }
        let response = try! JSONDecoder().decode(responseData.self, from: data)
        if let respData = response.data {
            DispatchQueue.main.async {
                completion(respData.priceUsd)
            }
        }
    }
    task.resume()
}
Related