VIPER架构下网络请求发起及跨页面结果通知方案咨询
我来帮你梳理这两个VIPER架构下的实践问题,结合实际项目经验给你具体的实现思路和示例:
在VIPER架构中,单个页面内发起网络请求并获取结果的流程
VIPER的核心是职责分离,所以网络请求的流程会严格按照View → Presenter → Interactor → Presenter → View的链路走,每个角色只做自己的事:
- View层触发请求:比如用户点击"获取数据"按钮,View(通常是ViewController)只负责把交互事件传递给Presenter,调用类似
presenter.initiateDataFetch()的方法,不做任何业务逻辑处理。 - Presenter层协调调度:Presenter收到请求后,先通知View显示加载状态(比如
view.showLoadingIndicator()),然后调用Interactor对应的网络请求方法,比如interactor.fetchTargetData(with: params)。Presenter在这里可以做参数合法性校验,避免无效请求。 - Interactor层执行核心逻辑:Interactor是业务逻辑和数据获取的核心,这里会调用你的网络请求function。为了把结果回传给Presenter,通常会定义一个回调协议(或者用闭包、Combine),比如:
// Interactor的回调协议 protocol DataFetchDelegate: AnyObject { func didFetchDataSuccess(_ data: TargetEntity) func didFetchDataFailure(_ error: Error) } class DataInteractor { weak var delegate: DataFetchDelegate? func fetchTargetData(params: [String: Any]) { // 调用你的耗时网络请求function NetworkService.shared.requestData(params: params) { [weak self] result in switch result { case .success(let rawData): // 这里还可以做数据解析,把原始响应转成Entity let entity = TargetEntity.parse(from: rawData) self?.delegate?.didFetchDataSuccess(entity) case .failure(let error): self?.delegate?.didFetchDataFailure(error) } } } } - Interactor回调结果给Presenter:请求完成后,Interactor通过协议回调把Entity或错误传给Presenter。
- Presenter更新View:Presenter拿到结果后,把Entity转换成View需要的ViewModel(比如把日期格式化成字符串、拼接显示文本),然后调用View的方法更新UI:成功就调用
view.displayData(viewModel: viewModel),失败就调用view.showError(message: error.localizedDescription),同时隐藏加载状态。
应用启动后提前发起耗时请求,通知Presenter结果的实现方案
要实现启动后预加载数据,核心是在启动阶段发起请求、存储结果、在目标页面Presenter中获取/监听结果,具体步骤如下:
1. 选择启动时机发起请求
不要直接在AppDelegate/SceneDelegate里写请求逻辑,封装到一个全局的预加载Interactor里:
class PreloadInteractor { func startCriticalDataPreload() { // 调用你的耗时请求function HeavyDataService.shared.fetchCriticalData { result in switch result { case .success(let data): // 存储结果到全局数据容器 AppGlobalStore.shared.criticalPreloadedData = data AppGlobalStore.shared.isPreloadCompleted = true // 发送通知告知所有监听者请求完成 NotificationCenter.default.post(name: .preloadCompleted, object: nil) case .failure(let error): AppGlobalStore.shared.preloadError = error AppGlobalStore.shared.isPreloadCompleted = true NotificationCenter.default.post(name: .preloadFailed, object: error) } } } } // 在AppDelegate中启动预加载 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { PreloadInteractor().startCriticalDataPreload() return true }
2. 存储预加载结果
用一个全局单例存储结果和状态,方便后续Presenter访问:
class AppGlobalStore { static let shared = AppGlobalStore() var criticalPreloadedData: CriticalEntity? var preloadError: Error? var isPreloadCompleted = false } // 定义通知名称 extension Notification.Name { static let preloadCompleted = Notification.Name("PreloadCompleted") static let preloadFailed = Notification.Name("PreloadFailed") }
3. 通知Presenter结果的两种常用方式
- 方式一:依赖注入(推荐)
当用户进入目标页面时,Presenter初始化时注入AppGlobalStore,直接检查预加载状态:class TargetPresenter { weak var view: TargetViewProtocol? private let globalStore: AppGlobalStore init(globalStore: AppGlobalStore = .shared) { self.globalStore = globalStore checkPreloadedData() } private func checkPreloadedData() { if globalStore.isPreloadCompleted { if let data = globalStore.criticalPreloadedData { let viewModel = CriticalViewModel(from: data) view?.displayPreloadedData(viewModel) } else if let error = globalStore.preloadError { view?.showPreloadError(error.localizedDescription) } } else { // 预加载未完成,显示加载状态并监听通知 view?.showLoading() NotificationCenter.default.addObserver(self, selector: #selector(handlePreloadUpdate), name: .preloadCompleted, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(handlePreloadUpdate), name: .preloadFailed, object: nil) } } @objc private func handlePreloadUpdate(notification: Notification) { view?.hideLoading() checkPreloadedData() // 移除监听避免内存泄漏 NotificationCenter.default.removeObserver(self) } } - 方式二:Combine/RxSwift响应式监听
如果项目用了响应式框架,可以把预加载状态做成可观察对象,Presenter订阅后自动收到更新,代码会更简洁,避免手动管理通知监听。
内容的提问来源于stack exchange,提问作者Ozx
相关产品推荐
相关产品推荐

