SwiftUI MVVM架构下如何统一处理会话过期问题
统一处理会话过期的几种方案
1. 封装全局API服务层,集中拦截会话过期
把所有API请求统一放到一个APIService类中,在这个类里集中处理响应错误——一旦检测到会话过期(比如HTTP 401状态码、后端约定的特定错误码),直接触发登出逻辑并更新UserDataVM的状态,从根源上避免重复代码。
示例代码:
import Combine class APIService { private let userVM: UserDataVM private var cancellables = Set<AnyCancellable>() init(userVM: UserDataVM) { self.userVM = userVM } // 通用请求方法,所有API调用走这里 func request<T: Decodable>(_ endpoint: URLRequest) -> AnyPublisher<T, Error> { URLSession.shared.dataTaskPublisher(for: endpoint) .tryMap { data, response in guard let httpResponse = response as? HTTPURLResponse else { throw APIError.invalidResponse } // 检测会话过期 if httpResponse.statusCode == 401 { self.userVM.logout() throw APIError.sessionExpired } return data } .decode(type: T.self, decoder: JSONDecoder()) .eraseToAnyPublisher() } } // 自定义API错误类型 enum APIError: Error { case invalidResponse case sessionExpired // 可扩展其他错误类型 } // 在UserDataVM中补充登出方法 class UserDataVM: ObservableObject { @Published var isLogedIn = false func logout() { // 清除本地存储的会话信息(如token) UserDefaults.standard.removeObject(forKey: "authToken") // 更新登录状态 isLogedIn = false } }
使用时,所有页面的API调用都通过APIService发起,无需在每个请求里重复写会话过期的判断逻辑。
2. 自定义Combine Operator,复用错误处理逻辑
如果不想把所有API都收拢到单一服务类,可自定义一个Combine操作符,专门处理会话过期错误,之后在每个API请求链中附加这个操作符即可。
示例代码:
import Combine extension Publisher where Failure == Error { func handleSessionExpiration(userVM: UserDataVM) -> AnyPublisher<Output, Error> { self.catch { error -> AnyPublisher<Output, Error> in if let apiError = error as? APIError, apiError == .sessionExpired { userVM.logout() } return Fail(error: error).eraseToAnyPublisher() } .eraseToAnyPublisher() } } // 使用示例 URLSession.shared.dataTaskPublisher(for: targetEndpoint) .tryMap { data, response in // 常规响应解析逻辑 guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else { throw APIError.invalidResponse } return data } .decode(type: TargetModel.self, decoder: JSONDecoder()) .handleSessionExpiration(userVM: userDataVM) // 附加统一处理逻辑 .sink(receiveCompletion: { completion in // 处理请求完成逻辑 }, receiveValue: { data in // 处理返回数据 }) .store(in: &cancellables)
这种方式更灵活,适合已有分散API调用的场景,只需在现有请求链上添加一行代码即可实现统一处理。
3. 利用通知中心全局监听会话过期事件
如果API调用逻辑过于分散,可在检测到会话过期时发送全局通知,让UserDataVM监听该通知并执行登出操作,无需大规模修改现有代码。
示例代码:
// 定义会话过期通知名 extension Notification.Name { static let sessionExpired = Notification.Name("SessionExpiredNotification") } // 在API请求的错误检测中发送通知 if httpResponse.statusCode == 401 { NotificationCenter.default.post(name: .sessionExpired, object: nil) throw APIError.sessionExpired } // 在UserDataVM中监听通知并处理登出 class UserDataVM: ObservableObject { @Published var isLogedIn = false private var cancellables = Set<AnyCancellable>() init() { NotificationCenter.default.publisher(for: .sessionExpired) .sink { [weak self] _ in self?.logout() } .store(in: &cancellables) } func logout() { UserDefaults.standard.removeObject(forKey: "authToken") isLogedIn = false } }
这种方式对现有代码侵入性最低,但要注意避免通知滥用导致的模块耦合问题。
内容的提问来源于stack exchange,提问作者Sanjay Kochrekar
相关产品推荐
相关产品推荐

