QCon北京「鸿蒙专场」火热来袭!即刻报名,与创新同行~ 了解详情
写点什么

SwiftUI 真的适合大规模应用吗?三家初创公司的实践经验

  • 2024-10-16
    北京
  • 本文字数:4805 字

    阅读完需:约 16 分钟

大小:2.28M时长:13:16
SwiftUI真的适合大规模应用吗?三家初创公司的实践经验

导读:在 SwiftUI 的舞台上,其以现代化的声明式编程语法和卓越的性能赢得了业界的广泛赞誉。然而,作为一个仅发展了四年的新兴框架,SwiftUI 仍面临一些亟待解决的挑战。尤为突出的是,导航管理的不足成为限制其广泛应用的绊脚石。尽管 UIKit 的传统导航方式已相当成熟,但如何在保持 SwiftUI 现代特性的同时,有效融合这两种框架,成为众多开发者面临的难题。

 

如何借助 Coordinator 模式破解 SwiftUI 中的导航难题。我们将通过实战演示,利用 UIHostingController 和导航协议,构建一套既高效又灵活的应用导航系统。此方案旨在融合 UIKit 的经典优势与 SwiftUI 的创新特性,为开发者提供一套完美的解决方案。无论你是 SwiftUI 的新手还是资深专家,本文都将为你奉上实用的技巧和深刻的洞见,助你跨越复杂应用开发中的导航难关。加入我们,一同探索如何在 SwiftUI 与 UIKit 之间搭建桥梁,解锁更多强大的功能潜力,让你的应用导航更加流畅、高效!

 

今天,我将与大家分享我在三家初创公司中运用 SwiftUI 的实践经验,并深入探讨我是如何克服那些阻碍你全面采用 SwiftUI 的关键挑战的。

 


本文改编自我于 2023 年 6 月在 Monzo 伦敦办公室举办的 NSLondon 演讲。

 

SwiftUI 的演进历程

 

自 SwiftUI 问世以来,我有幸一直是其主要用户。

 

作为 SwiftUI 的忠实拥趸,我亲眼见证了它的成长与发展。回溯至 2019 年,当 SwiftUI 的初版测试版横空出世时,我正投身于一个名为 Patcher 的副业初创项目之中,该项目旨在成为汽车维修领域的 Uber。如同许多初创团队的工程师一样,我们怀揣着满腔热情,决定在赢得首批用户青睐之前,先全力以赴地完善所有功能。

 

Patcher 应用功能繁多,涵盖了地图导航、新手引导、用户资料管理、工作请求处理、支付系统、维修流程跟踪等各个环节,甚至还包括了一个专为维修技师设计的配套应用。然而,这一项目的复杂性不言而喻,而那时的 SwiftUI,尤其是在 2019 年底至 2020 年初的阶段,显然还难以驾驭如此庞大的系统。

 

初期的 UIKit 与 SwiftUI 之间的互操作性并不理想,状态管理也成为了一项挑战,尤其是在整合像 Google Maps SDK(当时 SwiftUI 中的 MapView 尚未问世)和摄像头功能时,这些问题尤为凸显。更令人沮丧的是,SwiftUI 1.0 版本的导航功能几乎形同虚设,迫使我们不得不大量依赖模态视图来实现应用内的导航。

 

时间推移至 2020 年末,我参与了 Carbn 的创立,这是一家旨在引导人们培养更环保生活方式并减少碳足迹的初创企业。幸运的是,此时 iOS 14 已经发布,SwiftUI 也迎来了它的黄金时期。随着 @StateObject、惰性堆栈(lazy stacks)、ScrollViewReader 等强大工具的加入,以及我个人极为偏爱的 matchedGeometryEffect 效果的引入,SwiftUI 的功能与性能得到了显著提升。但更为重要的是,开发者们开始逐渐掌握如何利用 SwiftUI 构建出既合理又高效的应用程序。

 

现在,作为 Gener8 公司的移动工程负责人,我带领团队致力于帮助用户掌握并利用自己的数据创造价值。我们非常幸运地能够专注于 iOS 15 及以上版本的开发,这使得我们能够充分利用材质效果、可刷新视图、任务修饰符以及 Markdown 渲染等先进功能。

 

可以说,SwiftUI 已经全面成熟,从四年前的一个实验性工具,成长为能够支撑起严肃商业项目的强大 UI 框架。

 

它真的适合在生产环境中大展拳脚吗?

 

简而言之,如果你在生产环境中选择使用 SwiftUI,那么它绝对能够胜任!

 

回顾过去,Swift 在实现 ABI(应用程序二进制接口)稳定性之前,许多 Objective-C 开发者曾对 Swift 能否应对大型项目持怀疑态度。同样地,到了 2030 年,我们或许还会听到有人坚持推崇 UIKit(尽管我尚未见到有人仍在使用 Storyboard)。

 

接下来,我将简要分析 SwiftUI 的优势、存在的不足,以及为何一些工程师尚未完全全面转向 SwiftUI。

 

SwiftUI

优势

 

关于 SwiftUI 的优点,我不再一一赘述,仅简要提及几点核心优势。它速度快、采用声明式编程模式、具备高度响应性,并内置了单向数据流,让开发者能够轻松驾驭数据流向。无疑,SwiftUI 代表着未来的发展方向。

不足

 

当然,SwiftUI 也非尽善尽美。其导航功能稳定性一直是个问题,对于传统 UIKit 框架中的 AV(音频视频)、Camera(相机)和 Maps(地图,尤其是 iOS 17 之前的版本)等功能的支持较为有限。由于 SwiftUI 尚处于不断完善之中,有时开发者可能仍需借助 UIKit 来实现特定功能。此外,SwiftUI 的渲染引擎背后是 CoreAnimation、UIKit 和 Metal 的复杂交织,这种 “幕后魔法” 使得精确的性能调优变得更具挑战性。

 

难题

 

坦率而言,苹果似乎更倾向于将焦点放在单视图或主从结构上,对其他类型的应用架构则略显忽视。与所有新兴框架一样,SwiftUI 所依赖的操作系统版本不断变化,这意味着开发者需要在代码中频繁使用 if #available 语句来确保兼容最新功能。此外,SwiftUI 的状态管理机制与现有的 UIKit 代码体系存在较大差异,两者之间的无缝融合仍是一个待解的难题。

 

SwiftUI 虽然备受赞誉,但仍有其不足之处。作为一个仅诞生四年的新兴框架,某些缺陷在预料之中,然而,有一个核心问题显著地限制了它的广泛应用:

 

导航难题亟待解决

 

导航问题无疑是开发者在全面拥抱 SwiftUI 时遇到的首要障碍。特别是对于构建大型、复杂的应用程序而言,目前还缺乏一个理想的导航解决方案。虽然利用应用数据状态来控制导航逻辑在小规模应用中颇为有效,但尝试将这种方法扩展到大型项目时,其复杂性和工作量会急剧增加。开发者不应被迫去记忆整个应用的状态变化,以此推断出应该展示哪个界面。

 

问题的根源在于……

 

“大型视图控制器” 问题重现江湖

 

为了更深入地理解这一挑战,我们来看看苹果在 iOS 16 中对 SwiftUI 导航模式的改进尝试:

 

NavigationStackNavigationLink.navigationDestination.sheetNavigationSplitView

 

这些功能都紧密集成在 SwiftUI 的视图体系中,允许视图根据用户的交互行为或应用的状态变化来动态决定哪些子视图应该被呈现。

 

这迫使开发者不得不将视图与导航逻辑紧密绑定在一起,极大地挑战了代码的可测试性,也违背了工程开发的最佳实践原则。实际上,iOS 开发界的工程师们才刚刚挣脱出这种不良习惯的束缚。回想起过去,使用字符串类型的 segue、在 viewDidLoad() 方法中直接调用 URLSession、以及将整个应用逻辑堆砌在单个 Main.storyboard 文件中的日子,简直如同梦魇般让人心有余悸。

 

对于初涉 iOS 开发领域的你,若想深入了解 “大型视图控制器” 综合症的根源与影响,Paul Hudson 的这篇教程无疑是极佳的入门指南。它不仅深刻剖析了因关注点混乱导致的种种问题,还系统介绍了 UIKit 的最佳实践方法,为开发者指明了优化代码结构的路径。

 

导航逻辑的抽象化

 

我们已经明确了问题的症结所在:SwiftUI 尚不支持将导航逻辑进行有效的抽象化处理,这给大型工程团队在协作开发复杂应用时带来了不小的困扰。

 

但好消息是,解决方案并非遥不可及,它其实就隐藏在我们日常的编码实践中。

 


Coordinator 模式

 

这一模式,有时也被称为 Router 模式或 Navigator 模式,其核心思想高度一致:即将导航逻辑进行封装,实现与视图层的解耦。

 

我个人倾向于在 AppDelegateSceneDelegate 中引入一个 AppCoordinator 来实施此模式。该 Coordinator 作为整个应用的导航中枢,负责全局的导航管理,并可以根据需要创建多个子 Coordinator 来分别处理不同的业务逻辑流程。这些子 Coordinator 又可以进一步细化,管理更为具体的导航逻辑,从而形成一个层次分明、结构清晰的导航管理体系。

 


使用 Coordinator 模式的基础应用架构

 

Coordinator 的协议设计相对简洁明了:

 


Coordinator.swift

 

在 Coordinator 的设计中,Route 通常被定义为一个枚举类型,它列举了在该 Coordinator 所负责的导航流程中可能出现的各个界面。整个应用程序的导航逻辑都被巧妙地封装在 Coordinator 的 navigate(to:) 方法中。

 

尽管这种方法在 UIKit 框架中颇为常见,但接下来我将详细说明如何对其进行调整,以实现与 SwiftUI 框架的完全兼容。为此,我们只需引入两个额外的关键组件:

 

UIHostingController

 

UIHostingController 扮演了一个桥梁的角色,它将 SwiftUI 的上下文包裹在 UIViewController 之中,从而实现了这两种 UI 框架之间的无缝对接。

 

导航协议

 

为了进一步提升应用的灵活性和可维护性,我们将引入两个新的协议。这两个协议旨在抽象化 UINavigationController 和 UITabBarController 的功能,这两者都是构建复杂 UIKit 应用的基石。这样一来,无论你是否有 UIKit 的开发背景,都能轻松理解和应用你应用的导航结构。对于熟悉 UIKit 的开发者而言,这样的设计更是能够让他们迅速上手,并充分利用他们在 UIKit 中的经验来优化 SwiftUI 应用的导航体验。

 


通过 Coordinator 模式构建的导航架构

 

NavigationContext

 

此协议明确了我们对导航功能的基本需求:即能够展示和隐藏 SwiftUI 视图,同时将呈现逻辑与视图代码清晰分离。

 


NavigationContext.swift

 

在这个文档中,我们定义了一系列呈现函数,这些函数接收一个泛型视图作为参数,并实现了所有核心的导航操作,如推送、弹出视图,呈现和关闭视图,以及在导航结构的根位置创建初始视图。

 

协议的具体实现则是通过继承自 UINavigationController 的子类完成的:

 


MyNavController.swift

 

在这个文档中,你会发现实现过程与标准的 UIKit 导航方法非常相似,但多了一步操作 —— 即将 SwiftUI 视图包裹在 UIHostingController 中,以实现两者之间的无缝衔接。

 

接下来,我们可以轻松地为第一个 Coordinator 的 navigate(to:) 方法填充实现逻辑:

 


MyCoordinator.swift

 

NavigationRoot

 

此协议则是对 UITabBarController 功能的抽象,通常你的应用中仅会有一个这样的控制器,由 AppCoordinator 负责管理。

 


NavigationRoot.swift

 

尽管此部分不直接涉及 SwiftUI,但它负责管理多个导航上下文,并允许用户通过标签栏在不同上下文之间切换。此外,还提供了在标签栏控制器上模态展示上下文的功能,这在处理登录认证等场景时尤为实用。

 

拓展功能

 

在成功整合了所有主要构建块之后,我们不仅能够利用拥有 16 年历史的 Cocoa Touch API 来开发 SwiftUI 应用,还可以通过进一步子类化 UIHostingController 来增强功能。这样,我们可以:

 

  • 在每个视图上实现个性化的样式,无需依赖 UIAppearance API

  • 利用 UIViewController 的生命周期事件,在不同 NavigationContexts 之间传递状态

 

以下是一个基本实现的示例:

 


MyHostingController.swift

 

这一实现将引导我们创建一个更加复杂的 navigate(to:) 方法:

 


MyCoordinator.swift

 

通过利用视图的生命周期事件,在父视图上动态地显示和隐藏覆盖层。最终,我们展示了如何通过标准的 Coordinator 模式实现 UIKit 与 SwiftUI 之间的无缝互操作性,并以展示 SFSafariViewController 为例进行了演示。

 

结论

 

虽然 Coordinator 模式并非构建复杂 SwiftUI 应用的唯一途径,但它无疑是一个高效且实用的选择。当然,你也可以根据需求选择基于状态的范式,或者如果你的目标平台是 iOS 16 及以上版本,还可以考虑使用更新的导航 API,甚至尝试采用 The Composable Architecture 等先进架构。

 

这些不同方案的共同之处在于,它们都在不断推动技术发展。通过采用 Coordinator 模式,你可以在享受 SwiftUI 带来的响应式 UI 和快速开发优势的同时,充分利用 UIKit 在导航和定制方面的强大功能。此外,你还可以轻松实现即插即用的互操作性,几乎无需担心状态管理的问题。如果你尚未全面转向 SwiftUI,那么现在就是利用这些经过时间考验的范式的好时机,让我们从今天开始,迈向更加高效的开发之路。

 

作者简介

 

Jacob,现任伦敦一家初创公司的首席 iOS 工程师。

 

原文链接:

 

https://jacobbartlett.substack.com/p/swiftui-apps-at-scale

 

声明:本文为 InfoQ 翻译整理,未经许可禁止转载。

2024-10-16 16:139934

评论

发布
暂无评论
发现更多内容

数据中台真的适合你的企业吗?

RestCloud

数据中台 BI 数仓 ETL

服务韧性工程(SRE)论坛演讲实录 | 浙江移动:云化网络可观测性的探索和实践

雅菲奥朗

人工智能 运维 可观测性

服务韧性工程(SRE)论坛演讲实录丨金山办公:云原生时代下金山办公SRE架构演进之路

雅菲奥朗

运维 k8s SRE

服务韧性工程(SRE)论坛演讲实录 | 混沌工程:复杂系统韧性实现之道

雅菲奥朗

运维 SRE 混沌工程

Docker镜像源失效,教你将源换成国内

JEECG低代码

Docker docker image

python函数参数定义中的这两个分隔符,还有人不知道吗?

我再BUG界嘎嘎乱杀

Python 编程 后端 开发语言

华鲲振宇携手华为云时习知,探索数字化培训新路径

轶天下事

JeecgBoot 中如何对敏感信息进行脱敏处理?

JEECG低代码

数据加密 数据脱敏 JeecgBoot jeecg

移动图形工作站那家好用?

青椒云云电脑

图形工作站 移动图形工作站

JeecgBoot v3.7.0 all 版本发布

JEECG低代码

低代码 低代码开发平台 JeecgBoot jeecg

【信创】JeecgBoot集成东方通TongRDS

JEECG低代码

信创 低代码平台 jeecg-boot JeecgBoot 信创国产化

Altair 签署协议收购 Metrics,扩大其在 EDA 行业的影响力

Altair RapidMiner

人工智能 仿真 altair

服务韧性工程(SRE)论坛演讲实录 | 老娘舅:协同移动云推进自动化运维,提升系统稳定性

雅菲奥朗

运维 自动化

服务韧性工程(SRE)论坛演讲实录 | 东信软件:基于SRE的智能运维建设

雅菲奥朗

运维 SRE CMDB

“零代码”智能组装,华为云新一代iPaaS超联接能力让集成更智能

轶天下事

30天拿下Rust之超级好用的“语法糖”

希望睿智

语法糖 rust语言

服务韧性工程(SRE)论坛演讲实录 | 数据中心服务能力成熟度新标准简介及应用展望

雅菲奥朗

运维 SRE

30天拿下Rust之网络编程

希望睿智

网络编程 rust语言

供配电学习笔记 day2

万里无云万里天

电力 工厂运维

数据分析的好帮手,小浣熊办公助手!

百里丶落云

AI 办公小浣熊 代码小浣熊 小浣熊家族 小浣熊

【信创国产化】JeecgBoot低代码适配达梦和人大金仓

JEECG低代码

信创 JeecgBoot 国产信创数据库

JeecgBoot 集成宝兰德 AppServer 部署方案

JEECG低代码

信创 低代码平台 JeecgBoot 信创国产化

伙伴活动|AI硬件大潮来袭,深圳的创客们在哪里?

声网

乘用车1-5月销量同比增长7%,火山引擎数据飞轮能帮车企盘活下半年增长吗?

新消费日报

GitHub星标3500的Python爬虫实战入门教程,限时开源!

我再BUG界嘎嘎乱杀

Python 编程 爬虫 后端 开发语言

专为运维工程师设计!阿里藏经阁出品的Python实战手册被我搞来了

我再BUG界嘎嘎乱杀

Python 编程 运维 后端 开发语言

云原生中间件,构筑软件安全可信的连接桥梁

轶天下事

图片链接一网打尽:利用JD商品详情API获取商品高清图

技术冰糖葫芦

API 安全 API 文档 API 开发 API 协议

百度智能云将大模型引入网络故障定位的智能运维实践

Baidu AICLOUD

智能运维 网络运维

中小企业和数智化的距离,只差一块华为IdeaHub

Alter

SwiftUI真的适合大规模应用吗?三家初创公司的实践经验_Android/iOS_Jacob_InfoQ精选文章