我正在编写一个 SwiftUI 应用程序,我希望它定期从服务器刷新数据:
- 首次打开应用时
- 如果应用进入前台且数据在过去5分钟内没有更新
下面是我到目前为止的代码。
首次在 SwiftUI 应用程序中打开应用程序时触发此更新代码的最佳方式是什么?在 onAppear
中添加观察者是否是在应用程序进入前台时触发更新的好习惯? (这是应用程序中的唯一 View )
class InfoStore {
var lastValueCheck: Date = .distantPast
}
struct ContentView : View {
var infoStore: InfoStore
private func updateValueFromServer() {
// request updated value from the server
// if the request is successful, store the new value
currentValue = 500
UserDefaults.cachedValue = 500
// hardcoded for this example
infoStore.lastValueCheck = Date()
}
private func updateValueIfOld() {
let fiveMinutesAgo: Date = Date(timeIntervalSinceNow: (-5 * 60))
if infoStore.lastValueCheck < fiveMinutesAgo {
updateValueFromServer()
}
}
@State var currentValue: Int = 100
var body: some View {
Text("\(currentValue)")
.font(.largeTitle)
.onAppear {
NotificationCenter.default.addObserver(forName: UIApplication.willEnterForegroundNotification,
object: nil,
queue: .main) { (notification) in
self.updateValueIfOld()
}
}
}
}
extension UserDefaults {
private struct Keys {
static let cachedValue = "cachedValue"
}
static var cachedValue: Int {
get {
return standard.value(forKey: Keys.cachedValue) as? Int ?? 0
}
set {
standard.set(newValue, forKey: Keys.cachedValue)
}
}
}
最佳答案
1) 关于第一点(应用首次打开):获得所需内容的最佳方法可能是使用 DataBinding
和 提取 View 外部的逻辑(如 MVVM 所建议的那样) ObservableObject
。为了向您展示我的意思,我尽可能少地更改了您的代码:
import SwiftUI
class ViewModel: ObservableObject {
@Published var currentValue = -1
private var lastValueCheck = Date.distantPast
init() {
updateValueFromServer()
}
func updateValueIfOld() {
let fiveMinutesAgo: Date = Date(timeIntervalSinceNow: (-5 * 60))
if lastValueCheck < fiveMinutesAgo {
updateValueFromServer()
}
}
private func updateValueFromServer() {
// request updated value from the server
// if the request is successful, store the new value
currentValue = 500
UserDefaults.cachedValue = 500
// hardcoded for this example
lastValueCheck = Date()
}
}
struct ContentView : View {
@ObservedObject var viewModel: ViewModel
var body: some View {
Text("\(viewModel.currentValue)")
.font(.largeTitle)
.onAppear {
NotificationCenter.default.addObserver(forName: UIApplication.willEnterForegroundNotification,
object: nil,
queue: .main) { (notification) in
self.viewModel.updateValueIfOld()
}
}
}
}
extension UserDefaults {
private struct Keys {
static let cachedValue = "cachedValue"
}
static var cachedValue: Int {
get {
return standard.value(forKey: Keys.cachedValue) as? Int ?? 0
}
set {
standard.set(newValue, forKey: Keys.cachedValue)
}
}
}
#if DEBUG
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(viewModel: ViewModel())
}
}
#endif
这样,一旦创建了 ViewModel
,就会更新 currentValue
。此外,每次服务器调用更改 currentValue
时,都会自动为您重新创建 UI。请注意,您必须以这种方式修改 sceneDelegate
:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: ContentView(viewModel: ViewModel()))
self.window = window
window.makeKeyAndVisible()
}
}
2) 关于第二点(应用程序进入前台):你在这里应该小心,因为你要多次注册观察者(每次 onAppear
被触发)。根据您的需要,您应该决定:
- 移除观察者
onDisappear
(这个很频繁) - 只需添加一次观察者,检查您是否已经添加了它。
在任何情况下,实现以下内容都是一个很好的做法:
deinit {
}
方法并最终移除观察者。
关于ios - 我应该如何触发 SwiftUI 应用程序中的网络调用以刷新应用程序打开时的数据?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57392717/