繁体   English   中英

API 按下按钮时请求未显示在模拟器上

[英]API Request not showing on simulator when button pressed

我正在尝试使用 API 请求从网站获取库存数据。 在那个例子中,我想通过按下按钮来提取股票的符号。 代码本身没有错误,但我没有显示 API 请求并在这种情况下将符号AAPL提供给模拟器。 你知道我做错了什么吗?

struct Helper: View {
    @State private var quoteData: QuoteData?
    var body: some View {
        HStack {
            Spacer()
            
            VStack(alignment: .leading) {
                Text(quoteData?.symbol ?? ".")

                Button(action: loadData) {
                    Text("Press here")
                }
                
            }
        }
        .onAppear(perform: loadData)
    }
    


    private func loadData() {
        guard let url = URL(string: "https://financialmodelingprep.com/api/v3/quote/AAPL?apikey=836129f3de1b99ff975e910a4541254d") else {
            return
        }
        URLSession.shared.dataTask(with: url) { data, response, error in guard let data = data else { return }
            if let decodedData = try?JSONDecoder().decode(QuoteData.self, from: data) {
                DispatchQueue.main.async {
                    self.quoteData = decodedData
                }
            }
        }.resume()
    }
    
}

Here i just try to take the symbol from the stock which is of type string. The API adress does not have an id.

struct QuoteData: Decodable {
    var symbol: String?
}

struct Helper_Previews: PreviewProvider {
    static var previews: some View {
        Helper()
    }
}

我将向您推荐此答案以获得更详细的描述。 我完全承认在制作这个答案时窃取了@nicksarno 的工作。 请阅读整个答案,因为他和我对这个问题给出了非常彻底的回应。 我本来只是把你介绍到那里,但这里还有很多东西要解压。

首先,请确保更改您的 API 密钥。 您不应该发布私人 API 密钥。 您为访问该数据付费(或将付费)。

继续回答。 您的请求代码不正确。 我发布的是使用 Combine 的现代请求,在我发布的答案链接中进行了充分讨论。 此外,我通过简单地通过QuickType运行它来纠正您的解码器,答案中也引用了它。

解析 JSON 很痛苦,如果不出意外,请从Quicktype或其他解析器开始。 在这种情况下,JSON 数据,即使您只请求一个股票,也是一个数组。 您的解码器必须与 JSON 的结构完全匹配,尽管它不需要具有 JSON 的每个元素。 它还必须符合Codable协议。

import SwiftUI

struct Helper: View {
    @State private var symbol: String?
    var body: some View {
        HStack {
            Spacer()
            
            VStack(alignment: .leading) {
                Text(symbol ?? ".")
                
                Button(action: { loadData() }) {
                    Text("Press here")
                }
                
            }
        }
    }
    
    
    
    private func loadData() {
        guard let url = URL(string: "https://financialmodelingprep.com/api/v3/quote/AAPL?apikey=836129f3de1b99ff975e910a4541254d") else {
            return
        }
        URLSession.shared.dataTaskPublisher(for: url)
            // fetch on background thread
            .subscribe(on: DispatchQueue.global(qos: .background))
            // recieve response on main thread
            .receive(on: DispatchQueue.main)
            // ensure there is data
            .tryMap { (data, response) in
                guard
                    let httpResponse = response as? HTTPURLResponse,
                    httpResponse.statusCode == 200 else {
                    throw URLError(.badServerResponse)
                }
                return data
            }
            // decode JSON data to QuoteDecoder
            .decode(type: QuoteDecoder.self, decoder: JSONDecoder())
            // Handle results
            .sink { (result) in
                // will return success or failure
                print("completion: \(result)")
            } receiveValue: { (value) in
                // if success, will return QuoteDecoder
                // here you can update your view
                print("value: \(value)")
                // you can handle your data however you need to. Remember that it is an array of QuoteData.
                if let quote = value.first {
                    self.symbol = quote.symbol
                }
            }
            // After recieving response, the URLSession is no longer needed & we can cancel the publisher
            .cancel()
    }
}

struct QuoteData: Codable {
    let symbol, name: String?
    let price, changesPercentage, change, dayLow: Double?
    let dayHigh, yearHigh, yearLow: Double?
    let marketCap: Int?
    let priceAvg50, priceAvg200: Double?
    let volume, avgVolume: Int?
    let exchange: String?
    let quoteDatumOpen, previousClose, eps, pe: Double?
    let earningsAnnouncement: String?
    let sharesOutstanding, timestamp: Int?
    
    enum CodingKeys: String, CodingKey {
        case symbol, name, price, changesPercentage, change, dayLow, dayHigh, yearHigh, yearLow, marketCap, priceAvg50, priceAvg200, volume, avgVolume, exchange
        case quoteDatumOpen = "open"
        case previousClose, eps, pe, earningsAnnouncement, sharesOutstanding, timestamp
    }
}

typealias QuoteDecoder = [QuoteData]

struct Helper_Previews: PreviewProvider {
    static var previews: some View {
        Helper()
    }
}

我重构了几件事:

struct ContentView: View {
    @ObservedObject var loader = Loader()
    
    var body: some View {
        HStack {
            Spacer()
            
            VStack(alignment: .leading) {
                Text(loader.quoteData?.symbol ?? ".")

                Button(action: loader.loadData) {
                    Text("Press here")
                }
                
            }
        }
    }
}

class Loader : ObservableObject {
    
    @Published var quoteData: QuoteData?

    func loadData() {
        guard let url = URL(string: "https://financialmodelingprep.com/api/v3/quote/AAPL?apikey=836129f3de1b99ff975e910a4541254d") else {
            return
        }
        URLSession.shared.dataTask(with: url) { data, response, error in guard let data = data else { return }
            do {
                let decodedData = try JSONDecoder().decode([QuoteData].self, from: data)
                DispatchQueue.main.async {
                    self.quoteData = decodedData.first
                }
            } catch {
                print("Error: \(error)")
            }
        }.resume()
    }
}

struct QuoteData: Decodable {
    var symbol: String?
}

解释:

最基本的问题是 API 端点返回的是数组,而不是字典。 因此,我将您的解码方法更改为[QuoteData]而不是QuoteData

其次,它默默地失败了,因为你正在使用try? . 相反,我使用了do { } catch { }以便您可以实际捕获并处理错误,而不是静默失败。

第三(这是可选的),我将数据加载移动到 ObservableObject 中,而不是在 View 本身中完成工作,分离它们的职责。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM