如何调用连接到具有嵌套JSON属性的远程API的AsyncThrows函数



在前言中,我自学成才,我知道有关于如何做这样的事情的教程和类似的问题,但由于上下文的微小差异,有些事情就是不成功。因此,任何与我尝试做的事情更密切相关的解决方案或教程的帮助都将非常有用。

我正在尝试访问远程API,但在执行AsyncThrows函数的调用函数方面遇到了困难。我正试图从JSON中获取要存储的信息,以便以后访问它进行计算,但不知道该在Task中放入什么。

最小再现性示例-

代码:

// Struct Declarations
struct Response: Decodable {
let data: [StockValues]
}
struct StockValues: Decodable {
let high: Decimal
let low: Decimal
let close: Decimal
let volume: Decimal
}
// Async Throws Function
class dataFetcher {
static func Fetch() async throws -> [StockValues] {
guard let url = URL(string: "http://api.marketstack.com/v1/eod/latest")
else {
throw APIError.invalidServerResponse
}
var request = URLRequest(url: url)
let (data, _) = try await URLSession.shared.data(from: url)
let stockValues = try JSONDecoder().decode(Response.self, from: data)
return stockValues.data
}
}
// Call Async Throws Function
func CallFunction() {
Task {
let fetchedInfo = try await dataFetcher.Fetch()
Response.data = fetchedInfo // Error: Instance member 'data' cannot be used on type 'Response'; did you mean to use a value of this type instead?
}
}

此代码假设您有API的key,并且您的Response是正确的

//Your error refers to
//`Response` is a type it can't hold a value so `Response.data` is unacceptable
struct Response: Decodable {
let data: [StockValues]
}
actor MarketStackService {
//What you are attempting to do is something like this setup
//Something global that anything can access. Using `static` to hold a response is not approriate.
static func fetchLatest() async throws -> [StockValues] {
//Add API key
guard let url = URL(string: "https://api.marketstack.com/v1/eod/latest")
else {
throw APIError.invalidURL
}
let (data, _) = try await URLSession.shared.data(from: url)
let stockValues = try JSONDecoder().decode(Response.self, from: data)
return stockValues.data
}
}
enum APIError: LocalizedError{
case invalidURL
case error(Error)
var errorDescription: String?{
switch self {
case .error(let error):
return error.localizedDescription
default:
let string = String(describing: self)
return NSLocalizedString(string, comment: "AppError")
}
}
}

有很多方法可以设置它,但这是一个基本的class,可以很好地与SwiftUI配合使用。它可以保存API调用的值,并触发对body的更改

@MainActor
class MarketStackViewModel: ObservableObject{
//Variables are lowercased
//A variable can hold a value. That is why you need a `class` or `struct`
@Published var data: [StockValues] = []
@Published var alert: (isPresented: Bool, error: APIError?)  = (false, nil)
//`func` are lowercased
func getLatest() async {
do{
let fetchedInfo = try await MarketStackService.fetchLatest()
data = fetchedInfo
}catch{
alert = (true, .error(error))
}
}
}

View是显示值的地方,也是可以触发调用的地方。

//`class` and `struct` are uppercased
struct MarketStackView: View {
@StateObject var vm: MarketStackViewModel = .init()
var body: some View {
VStack{
if vm.data.isEmpty{
Text("Hello, World!")
.task {
//Runs the request as soon as the View displays.
await vm.getLatest()
}
}else{
Text(vm.data.description)
//This is another way of making the call
Button("Get latest") {
Task{
await vm.getLatest()
}
}
}
}.alert(isPresented: $vm.alert.isPresented, error: vm.alert.error) {
Button("Ok") {
vm.alert = (false, nil)
}
}
}
}
struct MarketStackView_Previews: PreviewProvider {
static var previews: some View {
MarketStackView()
}
}

如果您将此代码粘贴到.swift文件中,它应该可以工作,并且您可以看到流程。我建议你试试Apple SwiftUI教程。它们可能有助于澄清一些概念。

相关内容

  • 没有找到相关文章

最新更新