新闻详情

新闻详情

首页 / 资讯中心 / 详情

在 UIKit 中集成 Composable Architecture:状态订阅、弹窗呈现与栈式导航实战指南

发布时间:2026/10/2 8:18:04来源:尧图网络
在 UIKit 中集成 Composable Architecture:状态订阅、弹窗呈现与栈式导航实战指南
前端移动开发【免费下载链接】swift-composable-architectureA library for building applications in a consistent and understandable way, with composition, testing, and ergonomics in mind.项目地址https://gitcode.com/GitHub_Trending/sw/swift-composable-architecture点击查看免费下载本指南以 Composable Architecture 官方 DocC 扩展文档 UIKit.md 为骨架系统讲解如何把 TCA 的状态管理与架构能力接入 UIKit 应用从observe最小化状态订阅、UIAlertController(store:)弹窗呈现到NavigationStackController栈式导航再到 Combine 时代的Store.publisher集成。读完本文你将掌握在UIViewController中完整落地一个 TCA 功能模块含列表、导航、弹窗的实战方案并了解新旧两代观察 API 的取舍。一、UIKit 集成概览为 UIKit 应用而生的官方工具Composable Architecture 虽然在设计上以 SwiftUI 为主要目标Reducer、ObservableState等宏与Store的响应式状态管理但它同样为 UIKit 应用提供了一等公民的集成工具。官方文档明确指出While the Composable Architecture was designed with SwiftUI in mind, it comes with tools to integrate into application code written in UIKit.这些工具由两部分构成TCA 自身Store、StoreOf、AlertState/ConfirmationDialogState等核心类型以及针对 UIKit 的桥接扩展swift-navigation 库的 UIKit 能力observe(_:)、ObservationToken、NavigationStackController、UIPushAction、present(item:)等。在 Package.swift 中可以看到ComposableArchitecturetarget 显式依赖了UIKitNavigation产品。从平台条件编译看这些 UIKit 桥接位于#if canImport(UIKit) !os(watchOS)分支内见 AlertStateUIKit.swift 与 NavigationStackControllerUIKit.swift并在macCatalyst 13可用、macOS/watchOS不可用适用于 iOS/iPadOS 与 tvOS 上的UIViewController场景。文档按四个主题组织 UIKit 集成能力订阅状态变化、呈现 Alert 与 Action Sheet、栈式导航、Combine 集成。下面逐一展开。二、订阅状态变化用observe(_:)最小化 UI 刷新2.1 核心 APINSObject.observe(_:)UIKit 集成中最基础也最常用的是定义在NSObject上的observe(_:)方法对应文档ObjectiveC/NSObject/observe(_:)-94oxy符号。它允许你在viewDidLoad中以声明式闭包订阅 store 状态并在状态变化时更新 UI。官方迁移指南 MigratingTo1.7.md 给出了标准用法func viewDidLoad() { super.viewDidLoad() observe { [weak self] in guard let self else { return } self.countLabel.text \(self.store.count) } }observe的关键特性是最小化观察minimal observation只有闭包内实际读取的 state 字段才会被跟踪。如果其他 state 字段发生变化该闭包不会被触发也就不会做多余的工作。这正是 TCA 在 UIKit 场景下性能友好的核心机制。2.2 观察令牌手动控制观察生命周期observe会返回一个观察令牌observation token文档中的ObservationToken。当你需要精确控制某个观察的生命周期——例如在UITableView的单元格复用场景中每个 cell 的观察都必须在下一次复用时被取消——可以保存令牌并在适当时机调用cancel()。仓库示例工程 ListsOfState.swift 展示了这一模式控制器用一个[IndexPath: ObserveToken]字典保存每个 cell 的观察令牌在cellForRowAt中先取消旧观察、再为当前行建立新观察final class CountersTableViewController: UITableViewController { private let store: StoreOfCounterList var observations: [IndexPath: ObserveToken] [:] override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) - UITableViewCell { let cell tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) observations[indexPath]?.cancel() observations[indexPath] observe { [weak self] in guard let self else { return } cell.textLabel?.text \(store.counters[indexPath.row].count) } return cell } }这里同样使用了store.scope(\.counters[id: id], action: \.counters[id: id])来取出行级的局部 store配合observe实现列表单元格级别的细粒度刷新。2.3 推荐用法[weak self]与主线程从示例可以看出observe闭包内普遍配合[weak self]捕获列表避免控制器持有 store 形成循环引用并在闭包开头guard let self解包。整个订阅在viewDidLoad建立、随控制器生命周期自然释放是官方示例 CounterViewController.swift 采用的标准写法。三、条件内容呈现从Store.ifLet(then:else:)到观察 scope的演进3.1 旧式 APIStore.ifLet(then:else:)文档 Combine integration 主题中列出的Store/ifLet(then:else:)是基于 Combine 的旧式条件订阅 API。它在State Wrapped?可选状态的前提下分别提供「状态非 nil 时解包并处理」与「状态为 nil 时」两个分支store.ifLet( then: { store in // state 非 nil拿到解包后的子 store构建子控制器 }, else: { // state 为 nil显示占位内容 } )在源码 Deprecations.swift 中可以看到其实现本质它基于self.publisher.removeDuplicates(by: { ($0 ! nil) ($1 ! nil) })订阅可选状态仅在「nil ↔ 非 nil」切换时触发then/else分支并用IfLetCore建立子 store 的 scope。仓库示例 IfLetStoreController.swift 正是用它实现了「有状态时展示目标控制器、无状态时展示占位控制器」的通用容器store.ifLet( then: { [weak self] store in viewController ifDestination(store) }, else: { [weak self] in viewController elseDestination() } ) .store(in: cancellables)3.2 重要该 API 已进入废弃流程需要特别提示的是这一 API 目前已被标记为deprecated废弃信息明确写着 Use observe and if let store.scope, instead.见 Deprecations.swift。Store.publisher、StorePublisher同样在 MigratingTo1.25.md 中被标注为 Use observation APIs (observe,Observations) instead。因此新代码推荐用「observestore.scope(\.optional, action: \.optional)」替代observe { [weak self] in guard let self else { return } if let store store.scope(\.optionalCounter, action: \.optionalCounter) { navigationController?.pushViewController(CounterViewController(store: store), animated: true) } else { navigationController?.popToViewController(self, animated: true) } }上面的写法来自示例 LoadThenNavigate.swift每当可选子状态翻转时观察闭包自动执行 push/pop 导航配合 reducer 中clock.sleep延迟加载与.cancellable取消同文件 LoadThenNavigate.swift实现了「先加载再导航」的典型场景。四、呈现 Alert 与 Action SheetUIAlertController(store:)4.1 两个便捷构造器TCA 在 UIKit 桥接中为UIAlertController提供了两个便捷初始化器见 AlertStateUIKit.swift初始化器输入状态用途UIAlertController(store: StoreAlertStateAction, Action)AlertState呈现 Alert警告框UIAlertController(store: StoreConfirmationDialogStateAction, Action)ConfirmationDialogState呈现 Action Sheet确认对话框/操作表两者的实现都是「用当前状态构造控制器 将所有 action 映射到store.send」public convenience initAction( store: StoreAlertStateAction, Action ) { self.init(state: store.currentState) { _ $0.map(store.send) } }这意味着AlertState/ConfirmationDialogState中声明的每个按钮动作都会被转发为对应 reducer action 发送给 store弹窗逻辑可以完全放在 feature 的 reducer 中测试与复用。4.2 与present(item:)组合的标准用法官方文档注释给出了推荐组合方式——配合 swift-navigation 的UIViewController.present(item:)使用UIBindablestore 投影class FeatureController: UIViewController { UIBindable var store: StoreOfFeature // ... func viewDidLoad() { // ... present(item: $store.scope(\.alert, action: \.alert)) { store in UIAlertController(store: store) } } }当 reducer 中的alert状态通常由Presents var alert: AlertState...?承载变为非 nil 时控制器自动以模态方式弹出当状态回到 nil 时自动 dismiss。同样的模式可用于ConfirmationDialogStatepresent(item: $store.scope(\.dialog, action: \.dialog)) { store in UIAlertController(store: store) }迁移指南 MigratingTo1.13.md 印证了这一模式是 1.13 版本引入 UIKit 一等工具后的标准姿势用Presents描述树状导航的弹出状态用present(item:)驱动呈现实现「child 状态翻转为非 nil 即呈现对应 view controller」的声明式流程。五、栈式导航NavigationStackController与UIPushAction5.1 用NavigationStackController驱动 StackState当功能具有层级栈可压栈/弹栈时TCA 推荐用StackState/StackAction建模并用NavigationStackController子类驱动导航对应文档UIKitNavigation/NavigationStackController符号。官方示例 MigratingTo1.13.md 给出完整写法Reducer struct AppFeature { struct State { var path StackStatePath.State() // ... } Reducer enum Path { case addItem(AddFeature) case detailItem(DetailFeature) case editItem(EditFeature) } // ... } class AppController: NavigationStackController { private var store: StoreOfAppFeature! convenience init(store: StoreOfAppFeature) { UIBindable var store store self.init(path: $store.scope(state: \.path, action: \.path)) { RootViewController(store: store) } destination: { store in switch store.case { case .addItem(let store): AddViewController(store: store) case .detailItem(let store): DetailViewController(store: store) case .editItem(let store): EditViewController(store: store) } } self.store store } }其中path:参数接收一个栈状态绑定destination:闭包根据当前栈元素类型通过store.case解包 reducer 枚举返回对应的UIViewController。源码 NavigationStackControllerUIKit.swift 展示了该初始化器的内部机制它接收UIBindingStoreStackStateState, StackActionState, Action为StackState.Component注册navigationDestination并对每个压栈元素用IfLetCore建立按 id 索引的子 store scope\.[id: component.id]最后把子 store 交给destination闭包。5.2 编程式压栈UIPushActionUIPushAction提供callAsFunction(state:)方法对应文档UIKitNavigation/UIPushAction符号是 SwiftUI 中NavigationLink(state:)的 UIKit 等价物用于把新的栈元素编程式地压入当前栈见 NavigationStackControllerUIKit.swiftavailable(iOS 17, macOS 14, tvOS 17, watchOS 10, *) MainActor extension UIPushAction { public func callAsFunctionElement( state: Element, fileID: StaticString #fileID, filePath: StaticString #filePath, line: UInt #line, column: UInt #column ) { ... } }实现中通过Dependency(\.stackElementID)为元素生成唯一StackState.Componentid再调用底层的self(value:fileID:filePath:line:column:)完成压栈。也就是说压栈动作在 UIKit 侧与 SwiftUI 侧共享同一套StackState语义导航状态完全由 reducer 驱动。5.3 相关文档与平台注意栈式导航的完整理论见官方文章 StackBasedNavigation.md注意UIPushAction的压栈扩展标注了available(iOS 17, ...)而NavigationStackController本身的栈绑定用法不受此限制编写代码时需留意部署目标。六、Combine 集成Store.publisher与ViewStore.publisher6.1 API 形态与能力文档 Combine integration 主题列出两个 publisher 入口Store.publisherStorePublisherState在状态变化时发出事件。它支持动态成员查找可以直接提取 state 的某个字段进行订阅见 Store.swiftstore.publisher.alert .sink { ... }其底层实现为self.core.didSet.receive(on: UIScheduler.shared).map { self.withState(\.self) }——即每次 store 状态didSet时在主线程调度器上重放完整 state。由于didSet会在任何状态变化时触发publisher 天然无法做到observe那种按字段的最小化订阅。ViewStore.publisherViewStore是 TCA 早期的视图观察层ObservableObject其publisher属性把ViewStore内部CurrentValueRelay包装为StorePublisher见 Deprecations.swift。配合ViewStore.init(_:observe:send:removeDuplicates:)可自定义投影函数与去重规则同文件 Deprecations.swift。6.2 历史用法与当前地位在早期版本1.7 之前中标准做法是在viewDidLoad里用store.publishersink订阅并手动管理cancellablesfunc viewDidLoad() { super.viewDidLoad() store.publisher.count .sink { [weak self] in self?.countLabel.text \($0) } .store(in: cancellables) }这段代码出自 MigratingTo1.7.md而迁移指南随后明确指出这可以更简单地用observe替代。当前状态判断Store.publisher在 Store.swift 中被标记deprecated无论是否开启ComposableArchitecture2Deprecationstrait 都会收到废弃警告消息均为 Use observation (Observations, observe) insteadStore.ifLet(then:else:)同样已废弃ViewStore整体位于废弃区段文件 Deprecations.swift。因此在新代码中应优先使用observe观察 APICombine 系 API 主要用于理解历史代码与渐进式迁移。若需按 trait 启用更激进的一批废弃警告以备战 2.0可在 Package 层开启ComposableArchitecture2Deprecationstrait见 Package.swift。七、实战总览从示例工程看完整集成链路仓库中的 UIKit 示例工程 UIKitCaseStudies 是一套完整的落地范本其入口 RootViewController.swift 汇聚了四个案例案例源码文件涉及技术点BasicsCounterViewController.swiftStoreOfobserve订阅、store.send发 actionListsListsOfState.swiftIdentifiedArrayOfforEach、scope(\.counters[id:])行级 scope、ObserveToken令牌管理Navigate and loadNavigateAndLoad.swiftisNavigationActive 延迟加载 effect、.ifLetreducer 组合、push/popLoad then navigateLoadThenNavigate.swift可选子状态scope驱动 push/pop、loading 指示器、onDisappear取消 effect这些案例完整覆盖了本文介绍的三大新式能力observe订阅Basics/Lists、可选状态条件导航Navigate and load/Load then navigate基于store.scopeobserve替代已废弃的ifLet(then:else:)、以及store.send驱动 reducer。工程还包含IfLetStoreControllerIfLetStoreController.swift这一基于旧式ifLet(then:else:)的通用容器可作为阅读旧代码的对照参考。结语从 UIKit.md 列出的四个主题可以清晰看到 TCA 的 UIKit 集成脉络新代码统一走observescopepresent(item:)NavigationStackController的观察驱动路线而Store.publisher、ViewStore.publisher、ifLet(then:else:)属于已进入废弃流程的 Combine 遗产。理解这套演进关系既能让你在既有 UIKit 代码库中平滑引入 TCA也能确保新代码从一开始就走最省事、性能最好、最易测试的现代路径。赞分享前端移动开发【免费下载链接】swift-composable-architectureA library for building applications in a consistent and understandable way, with composition, testing, and ergonomics in mind.项目地址https://gitcode.com/GitHub_Trending/sw/swift-composable-architecture点击查看免费下载相关推荐BasePopup状态栏和导航栏处理实现沉浸式弹窗体验的终极指南BasePopup状态栏和导航栏处理实现沉浸式弹窗体验的终极指南 Android开发中弹窗与系统状态栏、导航栏的交互处理一直是开发者面临的挑战。BasePo移动开发UI组件SwiftUI状态管理终极方案Swift Composable Architecture实战指南SwiftUI状态管理终极方案Swift Composable Architecture实战指南 Swift Composable Architecture前端移动开发Valdi 多屏导航实战基于 valdi_navigation 的页面栈、模态弹窗与平台交互Valdi 多屏导航实战基于 valdi_navigation 的页面栈、模态弹窗与平台交互 本篇技术指南聚焦 Valdi 跨平台 UI 框架中的多屏导航体系跨平台UI组件前端移动开发上一篇5. References下一篇OpenViking 与 OpenClaw 端到端记忆测试框架实战从环境搭建到记忆 CRUD 自动化验证创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Spring Boot社区健康管理系统开发实战:从数据库设计到远程调试 2026/10/2 8:57:17

Spring Boot社区健康管理系统开发实战:从数据库设计到远程调试

最近帮学弟学妹看毕业设计,被问得最多的就是基于Spring Boot的社区健康管理系统。这类题目看起来是标准的管理系统CRUD,但真要把源码包、文档梳理清楚,再配合远程调试把项目在自己电脑上跑起来,隐藏的小坑多得超乎想象。这篇我就把…

阅读更多 →
GEO优化实战:让AI搜索引擎主动引用你的网站内容 2026/10/2 8:57:17

GEO优化实战:让AI搜索引擎主动引用你的网站内容

1. 2026年最值得警惕的“隐形危机”:搜索引擎排名靠前,AI答案里却没有你先说一个我最近真实遇到的事。我一个做工业检测设备的客户,官网在Google的“工业检测设备”这个核心词上排到首页前三,自然流量数据好看得不行。但从2025年年…

阅读更多 →
深度强化学习求解机组组合:MDP建模与开源实现 2026/10/2 8:57:17

深度强化学习求解机组组合:MDP建模与开源实现

简介:这份开源项目面向电力系统调度、能源管理与强化学习方向的研究生、算法工程师及科研人员,聚焦机组组合这一经典优化难题。机组组合需在满足负荷需求与设备约束的前提下安排各类型机组的启停与出力计划,以最小化运行成本,而深…

阅读更多 →
IDM报无法登录FTP服务器?一篇讲透排查与修复方法 2026/10/2 8:57:17

IDM报无法登录FTP服务器?一篇讲透排查与修复方法

用IDM(Internet Download Manager)去FTP服务器拉文件,弹出一个“无法登录到FTP服务器”的提示,这个问题我遇到过的次数,比我自己以为的要多得多。很多人的第一反应是重新输入一遍账号密码,或者干脆换个IDM版…

阅读更多 →
O(logn)的本质是问题空间收缩,不是速度标签 2026/10/2 8:57:16

O(logn)的本质是问题空间收缩,不是速度标签

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
LeetCode 215:快速选择与堆求解第K大元素,打通TopN思路 2026/10/2 8:57:10

LeetCode 215:快速选择与堆求解第K大元素,打通TopN思路

刷了这么多年的题,Hot100 里能同时横跨“排序、堆、分治、快选”四个知识点的题其实不多,215题“数组中的第 K 个最大元素”算是最典型的一道。不管你是准备国内大厂面试,还是在系统设计里处理 TopN 问题,这道题的几种解法几乎就是…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉