首届AICon深圳正式启动|AI实践哪家强?来 AICon,解锁技术前沿,探寻产业新机! 了解详情
写点什么

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:1310257

评论

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

揭穿数据分析的六大谎言

葡萄城技术团队

电商难做?低代码开发平台为企业转型升级保驾护航

加入高科技仿生人

低代码 电商 数据管理 b2b

5分钟入门物联网大数据计算——实践类

阿里云AIoT

阿里云 物联网 IoT

公众号接入 chatGPT 教程(附源码)

攻城狮杰森

Python AI openai ChatGPT

设备掉线怎么办?数据分析让冰冷的设备“开口说话”——实践类

阿里云AIoT

阿里云 物联网 IoT

Python 基础知识入门(三)

漫步桔田

451个PyPI包通过安装Chrome扩展窃取加密信息

墨菲安全

供应链安全 投毒

NFTScan 与 KNN3 在 NFT 数据层面达成合作伙伴关系

NFT Research

NFT web3

【2.17-2.24】写作社区优秀技术博文一览

InfoQ写作社区官方

热门活动 优质创作周报

Teradata在华落幕,国产化崛起,袋鼠云数栈会是更好的选择吗?

袋鼠云数栈

不被同事骂的代码规范,程序员们值得拥有!

程序员小毕

Java 程序员 程序人生 架构师 代码

【kafka】常用命令汇总

石臻臻的杂货铺

kafka

ChatGPT对于普通人有什么机会和影响?

引迈信息

人工智能 AI AIGC ChatGPT

3DMAX中的7个基本建模小窍门

Finovy Cloud

blender 3ds Max Cinema 4D

Python基础知识入门(二)

漫步桔田

活动报名:Tapdata Cloud V3 最新功能全解与核心应用场景演示

tapdata

瓴羊Quick BI率先提供移动端自助分析整体解决方案,成为行业的领导者!

流量猫猫头

阿里张勇:全力投入生成式AI大模型建设,为行业发展提供好算力支撑

阿里技术

人工智能 云计算

父母、离别

毛广斌

生活

DawnSql快速入门

陈飞

微服务 分布式数据库 实时数仓

Python基础知识入门(四)

漫步桔田

手把手教你如何快速检测设备异常——实践类

阿里云AIoT

阿里云 物联网 IoT

技术分享| anyRTC回声消除算法进化

anyRTC开发者

人工智能 音视频 回声消除 语音通话 视频通话

企业经常需要进行传输文件,大文件传输有哪些方法?

镭速

开源面对面:浅谈数据库技术与人工智能的结合与实践

阿里云数据库开源

postgresql 阿里云 PolarDB for PostgreSQL ChatGPT 数据库技术

瓴羊Quick BI智能报表,让报表化复杂为简单!

巷子

LED屏逐点校正指南

Dylan

像素 LED显示屏 全彩LED显示屏

ChatGPT模拟MySQL数据库 | 社区征文

NineData

MySQL 数据库 编程 技术栈 ChatGPT

热点面试题:为什么 0.1+ 0.2 != 0.3,如何让其相等?

Immerse

JavaScript 面试 大前端 前端面试题 超全前端面试题

吞吐量最高提升400%!百度智能云联合NVIDIA完成首批17个自动驾驶模型优化

科技热闻

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