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

Swift 按下按钮时,API请求未在模拟器上显示,swift,api,swiftui,Swift,Api,Swiftui,我正试图通过API请求从网站获取股票数据。在这个例子中,我想通过按下按钮来提取股票的符号。代码本身没有错误,但我不显示API请求,也不向模拟器提供符号。你知道我做错了什么吗 struct助手:视图{ @国家私有变量quoteData:quoteData? var body:一些观点{ HStack{ 垫片() VStack(对齐:。前导){ 文本(引用数据?.symbol??“) 按钮(操作:loadData){ 文本(“按此处”) } } } .onAppear(执行:loadData) }

我正试图通过API请求从网站获取股票数据。在这个例子中,我想通过按下按钮来提取股票的符号。代码本身没有错误,但我不显示API请求,也不向模拟器提供符号。你知道我做错了什么吗

struct助手:视图{
@国家私有变量quoteData:quoteData?
var body:一些观点{
HStack{
垫片()
VStack(对齐:。前导){
文本(引用数据?.symbol??“)
按钮(操作:loadData){
文本(“按此处”)
}
}
}
.onAppear(执行:loadData)
}
私有函数loadData(){
guard let url=url(字符串:https://financialmodelingprep.com/api/v3/quote/AAPL?apikey=836129f3de1b99ff975e910a4541254d)其他{
返回
}
URLSession.shared.dataTask(with:url){data,response,guard let data=data else中的错误{return}
如果让decodedata=try?JSONDecoder().decode(QuoteData.self,from:data){
DispatchQueue.main.async{
self.quoteData=decodedData
}
}
}1.简历()
}
}
在这里,我只是尝试从string类型的股票中获取符号。API地址没有id。
结构QuoteData:可解码{
变量符号:字符串?
}
结构帮助器\u预览:PreviewProvider{
静态var预览:一些视图{
助手()
}
}

我重构了一些东西:

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{}
,这样您就可以实际捕获并处理错误,而不是无声的失败


第三(这是可选的),我将数据加载移动到了一个ObservieObject,而不是在视图本身中进行工作,从而分离了它们的责任。

我将向您介绍这一部分,以获得更详细的描述。我完全承认,在设计这个答案时,我偷了@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()
    }
}

谢谢大家给我这些有用的建议。我会实施的,非常感谢!如果你觉得这是可行的,请接受它作为答案。