API获取新数据时SwiftUI视图不更新?

pnwntuvh  于 2023-06-21  发布在  Swift
关注(0)|答案(2)|浏览(125)

在我说其他之前,我实际上是Swift和SwiftUI的初学者,但我总是试图完成不符合我知识水平的事情。我有一个想法,把巴士信息带到现场活动中。我设法找到了一个本地交通的API,并创建了一个SwiftUI列表视图,用于显示特定公交车站的下一辆公交车。
YouTube上有一个教程,展示了如何获取数据(https://www.youtube.com/watch?v=CimY_Sr3gWw&t=221),他使用了@Published var bus:[BusAPI] = []让视图知道当API数据改变时,它必须刷新。
我用这种方式获取API数据。

import Foundation
import SwiftUI

struct BusAPI: Hashable, Codable {
    let route_code: String
    let veh_code: String
    let btime2: String
}

class ViewModel: ObservableObject {
    @Published var bus: [BusAPI] = [] 
    func fetch() {
        guard let url = URL(string: "http://telematics.oasa.gr/api/?act=getStopArrivals&p1=060475") else {
            return
        }
        let task = URLSession.shared.dataTask(with: url) { [weak self] data, _, error in
            
            guard let data = data, error == nil else {
                return
            }
            //Convert to JSON
            do {
                let bus = try JSONDecoder().decode([BusAPI].self, from: data)
                DispatchQueue.main.async {
                    self?.bus = bus
                }
            }
            catch {
                print(error)
            }
        }
        task.resume()
    }
}

视图如下所示:

//
//  ContentView.swift
//  BusDemoAPI
//

import SwiftUI

struct ContentView: View {
    
    @StateObject var viewModel = ViewModel()
    
    var body: some View {
        NavigationView {
            List {
                ForEach(viewModel.bus, id: \.self) { bus in
                
                    HStack {
                        Text("N. Ελβετία - Άνω Πατήσια")
                        Spacer()
                        Text("\(bus.btime2)'")
                            
                    }.padding(3)
                    
                }
            }.navigationTitle("Στάση ΟΠΑ")
            .onAppear {
                viewModel.fetch()
            }
            
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

我的实际问题是,为什么当数据更改时视图不刷新?它总是停留在第一次获取数据的时间。
我试图在谷歌上找到一个解决方案,但没有任何实际帮助。

jslywgbw

jslywgbw1#

在您的代码中,您正确地使用了@Published来指示总线数组是一个可观察属性。每当数据发生更改时,这将触发视图的刷新。但是,代码中可能存在一个小错误,导致视图无法刷新。
这个问题似乎与以下事实有关:在fetch()方法中有两个同名的变量(bus)。一个是视图模型中的BusAPI对象数组,另一个是do-catch块中的本地常量,在这里解码JSON响应。
要解决此问题,可以为do-catch块内的局部常量指定不同的名称。下面是修改后的fetch()方法:

func fetch() {
guard let url = URL(string: "http://telematics.oasa.gr/api/?act=getStopArrivals&p1=060475") else {
    return
}

let task = URLSession.shared.dataTask(with: url) { [weak self] data, _, error in
    guard let data = data, error == nil else {
        return
    }
    
    do {
        let decodedBus = try JSONDecoder().decode([BusAPI].self, from: data)
        DispatchQueue.main.async {
            self?.bus = decodedBus
        }
    } catch {
        print(error)
    }
}

task.resume()  }

通过为局部常量提供不同的名称(在本例中为decodedBus),可以避免隐藏视图模型的总线属性,从而允许将获取的数据正确分配给observable属性。每当获取新数据时,这应该会按预期触发视图刷新。
尝试一下这个修改,看看它是否解决了数据更改时视图不刷新的问题。

htzpubme

htzpubme2#

你的问题My actual question is why doesnt the view refreshes when the data changes?的答案是,当服务器上的数据更新时,你的代码不会自动更新。当您决定这样做时,您需要自己获取new数据。
一个简单的方法是在List中添加一个.refreshable修饰符,这样当用户执行拉刷新手势(快速向下滑动列表)时,列表内容会被重新获取,从而更新为最新的巴士信息。

List {...}
   .refreshable {
      viewModel.fetch()
   }

注意,您使用的是不安全的http连接,Apple要求使用https

相关问题