我应该如何触发 SwiftUI 应用程序中的网络调用以刷新应用程序打开时的数据?
How should I trigger network call in SwiftUI app to refresh data on app open?
我正在编写一个 SwiftUI 应用程序,我希望它定期从服务器刷新数据:
- 首次打开应用程序时
- 如果应用进入前台并且最近5分钟没有更新数据
下面是我目前的代码。
第一次在 SwiftUI 应用程序中打开应用程序时触发此更新代码的最佳方式是什么?在 onAppear
中添加观察者是否是应用程序进入前台时触发更新的良好做法? (这是应用程序中的唯一视图)
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
和 [=14= 将逻辑提取到视图之外(如 MVVM 所建议的那样) ]s。为了向您展示我的意思,我尽可能少地更改了您的代码:
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 {
}
方法并最终移除观察者。
我正在编写一个 SwiftUI 应用程序,我希望它定期从服务器刷新数据:
- 首次打开应用程序时
- 如果应用进入前台并且最近5分钟没有更新数据
下面是我目前的代码。
第一次在 SwiftUI 应用程序中打开应用程序时触发此更新代码的最佳方式是什么?在 onAppear
中添加观察者是否是应用程序进入前台时触发更新的良好做法? (这是应用程序中的唯一视图)
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
和 [=14= 将逻辑提取到视图之外(如 MVVM 所建议的那样) ]s。为了向您展示我的意思,我尽可能少地更改了您的代码:
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 {
}
方法并最终移除观察者。