Apollo-iOS缓存更新后UI未即时刷新问题求助
Apollo-iOS v0.53.0 Swift应用:关注/取关按钮UI无法即时刷新问题
使用Apollo-iOS v0.53.0开发Swift应用时,在用户页面实现了关注/取关按钮,点击后执行FollowUserMutation并更新本地缓存。目前突变功能和缓存更新均正常,但UI仅在切换视图后才会刷新,无法在按钮点击后即时更新。
相关代码
Apollo配置
import Foundation import Apollo class Network { static let shared = Network() private(set) lazy var apollo = ApolloClient(url: URL(string: "http://localhost:4000/graphql")!) }
查询数据并存储到状态
@State var user: UserQuery.Data.User? func loadUser() { Network.shared.apollo.fetch(query: UserQuery(username: username)) { result in switch result { case .success(let graphQLResult): if let result = graphQLResult.data?.user { user = result } case .failure(let error): print("Error: \(error)") } } } var body: some View { ScrollView(showsIndicators: false) { // 页面内容 } .onAppear(perform: loadUser) }
执行关注突变并更新缓存(位于ScrollView内)
Button(user.followingUser ? "Unfollow" : "Follow") { Network.shared.apollo.perform(mutation: FollowUserMutation(userFollowingId: user.uuid)) { result in switch result { case .success(_): Network.shared.apollo.store.withinReadWriteTransaction({ transaction in try transaction.update(query: UserQuery(username: user.username)) { (data: inout UserQuery.Data) in if (data.user?.followingUser == true) { data.user?.totalFollowers -= 1 } else { data.user?.totalFollowers += 1 } data.user?.followingUser.toggle() } }) case .failure(let error): print("Error: \(error)") } } }
问题原因与解决办法
核心问题:你用@State存储user数据,但Apollo缓存更新后不会自动通知@State变量触发视图刷新。Apollo-React的useQuery会自动订阅缓存变化并触发重渲染,但SwiftUI的@State没有这个机制。
方法一:手动更新本地@State变量
在缓存更新完成后,直接修改user变量的对应属性,触发SwiftUI视图刷新:
Button(user.followingUser ? "Unfollow" : "Follow") { Network.shared.apollo.perform(mutation: FollowUserMutation(userFollowingId: user.uuid)) { [weak self] result in guard let self = self else { return } switch result { case .success(_): // 更新缓存 Network.shared.apollo.store.withinReadWriteTransaction({ transaction in try transaction.update(query: UserQuery(username: self.user?.username ?? "")) { (data: inout UserQuery.Data) in if (data.user?.followingUser == true) { data.user?.totalFollowers -= 1 } else { data.user?.totalFollowers += 1 } data.user?.followingUser.toggle() } }) // 手动更新@State变量触发UI刷新 DispatchQueue.main.async { if var currentUser = self.user { currentUser.followingUser.toggle() currentUser.totalFollowers = currentUser.followingUser ? currentUser.totalFollowers + 1 : currentUser.totalFollowers - 1 self.user = currentUser } } case .failure(let error): print("Error: \(error)") } } }
注意要在DispatchQueue.main.async中修改@State,因为网络回调运行在后台线程。
方法二:使用Apollo的缓存订阅机制(推荐)
通过ObservableObject和Apollo的watch方法订阅缓存变化,自动触发视图刷新:
首先创建ViewModel:
import Apollo import SwiftUI class UserViewModel: ObservableObject { @Published var user: UserQuery.Data.User? private var cancellable: Cancellable? func loadUser(username: String) { // 用watch代替fetch,订阅缓存更新 cancellable = Network.shared.apollo.watch(query: UserQuery(username: username)) { [weak self] result in guard let self = self else { return } switch result { case .success(let graphQLResult): self.user = graphQLResult.data?.user case .failure(let error): print("Error: \(error)") } } } deinit { cancellable?.cancel() } }
然后在视图中使用ViewModel:
@StateObject var viewModel = UserViewModel() var body: some View { ScrollView(showsIndicators: false) { if let user = viewModel.user { Button(user.followingUser ? "Unfollow" : "Follow") { Network.shared.apollo.perform(mutation: FollowUserMutation(userFollowingId: user.uuid)) { result in switch result { case .success(_): // 仅更新缓存,ViewModel会自动接收缓存变化通知并更新UI Network.shared.apollo.store.withinReadWriteTransaction({ transaction in try transaction.update(query: UserQuery(username: user.username)) { (data: inout UserQuery.Data) in if (data.user?.followingUser == true) { data.user?.totalFollowers -= 1 } else { data.user?.totalFollowers += 1 } data.user?.followingUser.toggle() } }) case .failure(let error): print("Error: \(error)") } } } } } .onAppear { viewModel.loadUser(username: username) } }
这种方法利用Apollo的缓存订阅机制,缓存更新后watch方法会自动触发回调,更新@Published变量,进而触发SwiftUI视图刷新,更符合Apollo的最佳实践。
总结
方法一适合快速修复,手动控制UI更新;方法二更健壮,自动同步UI与缓存状态,推荐长期使用。
内容的提问来源于stack exchange,提问作者abesan
相关产品推荐
相关产品推荐

