You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 22:35:24