2

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
        }
    

Richard Wilson
  • 297
  • 4
  • 17
Materia3D
  • 23
  • 4
  • 1
    You can't wait an asynchronous method finish to return the result with a synchronous method. You are returning the result before it finishes. – Leo Dabus Jul 19 '21 at 23:25
  • Do the answers to [this question](https://stackoverflow.com/questions/65417381/json-file-is-missing-struct-is-wrong/65418427#65418427) help? – Yrb Jul 20 '21 at 00:03

1 Answers1

1

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()
}