A站 的 Swift 实践 —— 上篇
source link: https://mp.weixin.qq.com/s/rUZ8RwhWf4DWAa5YHHynsQ
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
A站 的 Swift 实践 —— 上篇
小编导读
经过不断迭代,Swift如今已成iOS乃至苹果全平台首选开发语言,A站也已经完全投入到Swift浪潮中,享受到Swift语言带来的舒适和高效开发体验。作为A站的Swift实践上篇,本文介绍了Swift的技术背景,A站使用Swift的架构演进过程,以及对最新框架SwiftUI和Combine等技术的选型。同时也请大家关注本公众号,下篇内容会详细介绍混编和Swift动态性。
编辑 / 贞霓
本文作者
#
背景介绍
Swift赋能AcFun iOS技术团队
苹果推出Swift的原因
苹果采取的实际行动
Swift在AcFun的演进
#
框架选择
SwiftUI
RxSwift
Combine
#
如何混编
内容将在本系列下篇中推送,请持续关注。
#
动态性
内容将在本系列下篇中推送,请持续关注。
背景介绍
AcFun俗称为“A站”,作为一款二次元内容社区产品,以“认真你就输了”为文化导向,倡导轻松欢快的亚文化。AcFun涵盖了中长视频,小视频,番剧,文章等众多内容,支撑这些内容的大部分功能都选择了用Swift开发,早在2019年,AcFun的iOS技术团队就已将Swift作为AcFun app和水母app的开发首选语言。Swift的出现为用户提供了更稳定的使用体验和更快的产品更新节奏,同时也为研发工程师创造了更高效舒适的开发体验。Objective-C已成过去时,AcFun正全面拥抱Swift,驶入iOS开发快车道。苹果推出Swift的原因
谷歌作为苹果最大的竞争对手,除了Android上的Kotlin,还推出了Flutter和Fuchsia里在用的Dart语言,这些语言的口碑和易用性远高于苹果的Objective-C(后面简称OC)。OC历史久远,是C语言的超集,因此其发展受C语言限制。在这样的背景下,大家都以为苹果会忽视其它新语言,但其实苹果对于那些新语言特性垂涎已久,将想法施于行动的是当时还在苹果的 Chris Lattner。Chris是狂热的编译器和编程语言爱好者,C、C++、OC语言编译器LLVM的创造者,在LLVM开发过程中,Chris对类C语言有着很多不满意的地方,比如预处理器、Trigraphs还有多年积累的奇怪东西。为了能够兼顾类似React一样的编程范式和具备Java等正流行的语言的优秀特性,Swift经历了长期的ABI稳定和语言特性迭代增加的过程,最终推出了能和JSX声明式UI匹敌的Result Builders,并且通过SwiftUI和Combine这种能极大提升开发效率的框架让开发者收获了惊喜。可能是Swift的ABI稳定得太晚,不止各大APP里已经积累了大量的OC库和业务代码,苹果系统里的OC占比也依然很高,博客《Evolution of the programming languages from iPhone OS 1.0 to iOS 14》[1] 统计了 iOS 历史版本 OC 占比,从文章中可以看到最近的iOS 14版本里OC占比高达88%,C和C++主要用于音视频、电话、网络等比较基础的模块,其占比相对稳定,特别是C并没有明显增加。不过在最近几个版本中,Swift占比持续增高,iOS 14达到了8%,可以看出苹果正在使用Swift重构以前的库。苹果采取的实际行动
Swift在AcFun的演进
Swift语言的演进
Swift的演进比较稳定,并没有在初期版本一股脑把特性都加上,而是每个版本迭代增加特性。演进之路如下图所示:Swift第一个版本推出了基本语法,Swift2.0主要是将泛型和协议能力做了提升,并对Linux进行了支持,后端框架Vapor和Perfect也是在Swift2.0时出现的,Swift也是在这个版本开源的。Swift3.0出了Swift Package Manager,对标准库API进行了重新的设计。4.0 推出Codable协议和Key path。5.0终于ABI稳定,Swift运行时内置到了iOS12系统里。5.1版本推出了让大家感到苹果活力的SwiftUI和Combine,新增了一大堆围绕提升开发舒适度的Property Wrapper、Opaque Type等语言特性,随之,社区开始异常活跃起来,与之对应的技术文章大量输出。AcFun就使用了5.1版本的Property Wrapper包装了UserDefaults,Codable,RxSwift Relay等,业务开发过程中避免雷同代码的编写。Swift 6的 Roadmap[3] 表明了Swift下一步发展方向是优化Swift部署安装,比如LSP和包管理等;丰富开源生态,包括完善标准库,开发类似科学计算这样的新库;围绕开发体验的构建和代码补全提速、丰富诊断信息、稳定调试体验等;DSL能力提升;完善低级别系统编程和机器学习等重要领域的拓展;提供内存所有权和并发等主要语言特性的方案,要做到出色为止。
目前Swift这个项目的负责人叫 Ted Kremenek[4],斯坦福博士,他之前还是Rust的主力开发。在苹果工作的十年,一个人做了Clang的静态分析器,后面一直管理着Clang和Swift项目,向Chris汇报。Swift项目团队核心成员还有Dave Abrahams(已退出)、John McCall、Doug Gregor、Joe Groff、Saleem Abdulrasool(移植Swift到windows)、Tom Doron(创建SwiftNIO)等,他们的身影活跃在Github的Swift各个提案中。
回到我们身边,国内Swift用的情况怎么样?一些耳熟能详的App,比如微信、淘宝、百度、支付宝、拼多多、京东、哔哩哔哩、优酷、小红书等都已经开始尝试使用Swift,这些App无一例外都采用了Swift和OC混编开发。由于国内业务竞争压力大,很难像国外公司Uber那样花大半年时间全部用Swift重构,因此如果要在现有工程基础上引入Swift开发,不可避开采用混编开发。很多App使用Swift混编,也是因为苹果对Widget功能开发语言设置了限制,即只能使用Swift,看来苹果公司这个策略是相当有效的。框架选择
而正式进入混编开发前,需要先做开发框架的选型,我们先从架构演进开始说起。架构演进
一般App经过多年发展,架构都会经过如下四个阶段:如图所示,App架构从单Module,MVC架构到几百个Module,无依赖,动态跳转。团队从小变大,如今App的架构更偏重高质量、稳定性和高可维护性。苹果公司也是顺应发展趋势,先后推出提高稳定性的Swift语言,而后推出提高可维护性的SwiftUI和Combine。SwiftUI
对于一个基于UIKit的项目是没有必要全部用SwiftUI重写的,在UIKit里使用SwiftUI的视图非常容易,UIHostingController是UIViewController的子类,可以直接用在UIKit里,因此直接将SwiftUI视图加到UIHostingController中,就可以在UIKit里使用SwiftUI视图了。SwiftUI的布局核心是 GeometryReader、View Preferences和Anchor Preferences。如下图所示:SwiftUI的数据流更适合Redux结构,如下图所示:如上图,Redux结构是真正的单向单数据源结构,易于分割,能充分利用SwiftUI内置的数据流Property Wrapper。UI组件干净、体量小、可复用并且无业务逻辑,因此开发时可以聚焦于UI代码。业务逻辑放在一起,所有业务逻辑和数据Model都在Reducer里。ACHNBrowserUI[5] 和 MovieSwiftUI[6] 开源项目都是使用的Redux架构。最近比较瞩目的TCA(The Composable Architecture)也是类Redux/Elm的架构的框架,项目地址见[7]。提到数据流就不得不说下苹果公司新出的Combine,对标的是RxSwift,由于是苹果公司官方的库,所以应该优先选择。不过和SwiftUI一样,这两个新库对APP支持最低的系统版本都要求是iOS13及以上。那么怎么能够提前用上SwiftUI和Combine呢?或者说现在使用什么库可以以相同接口方式暂时替换它们,又能在以后改为SwiftUI和Combine时成本最小化呢?
对于SwiftUI,AcFun自研了声明式UI Ysera,类似SwiftUI的接口,并且重构了AcFun里收藏模块列表视图和交互逻辑,如下图所示:通过上图可以看到,swift代码量相比较OC减少了65%以上,原先使用Objective-C实现的相同功能代码超过了1000行,而Swift重写只需要350行,对于AcFun的业务研发工程师而言,同样的需求实现代码比之前少了至少30%,面对单周迭代这样的节奏,团队也变得更从容。代码可读性增加了,后期功能迭代和维护更容易了,Swift让AcFun驶入了iOS开发生态的“快车道”。SwiftUI全部都是基于Swift的各大可提高开发效率特性完成的,比如前面提到的,能够访问只给语言特性级别行为的Property Wrapper,通过Property Wrapper包装代码逻辑,来降低代码复杂度,除了SwiftUI和Combine里@开头的Property Wrapper外,Swift还自带类似@dynamicMemberLookup[8] 和@dynamicCallable[9] 这样重量级的Property Wrapper。还有ResultBuilder[10] 这种能够简化语法的特性,有些如GraphQL、REST和Networking实际使用ResultBuilder的范例可以参考[11]。这些Swift的特性如果也能得到充分利用,即使不用SwiftUI也能使开发效率得到大幅提升。网飞(Netflix)App已使用SwiftUI重构了登录界面,网飞增长团队移动负责人故胤道长记录了SwiftUI在网飞的落地过程,详细描述了SwiftUI的收益[12]。网飞能够直接使用SwiftUI得益于他们最低支持iOS 13系统。不过如最低支持系统低于iOS 13,还有开源项目AltSwiftUI[13] 也实现了SwiftUI的语法和特性,能够向前兼容到iOS 11。对于Combine,也有开源实现OpenCombine[14],目前都未完全实现所有特性。因此,具体在工程中使用还是需要了解Combine的核心原理。
RXSwift
Combine的灵感来源于RxSwift。RxSwift的核心,这里有份实现了RxSwift核心逻辑的简版样例代码[15],可以窥视其核心逻辑。整体流程如下图:如上图所示,RxSwift整体流程非常简单,主要就是订阅者和发布者之间进行订阅、发布、取消操作,订阅者会监听和处理这些事件。具体RxSwift数据传递关系如下图:上图中的Observable是发布者,Observer是订阅者。取消订阅是通过CompositeDisposable来进行管理,管理方式就是加个中间订阅者来决定是否发送事件给原订阅者。SinkDisposable是一个中间层用来把中间订阅者和原订阅者还有事件转发的逻辑放到一起。新增一个操作符就会新增一个SinkDisposable,比如新增filter操作符就会新增FilterObserver和FilterObservable,如果没有操作符就是AnoymousObserver和AnoymousObservable。订阅是通过Disposer类来管理的,会判断是否完成或者出错,执行Dispose方法。Combine
Combine的思路基本和RxSwift一样,只是接口命名不同,这里有份表格[16],列出了Combine和RxSwift功能的对应关系,可以看出目前Combine相较于RxSwift还缺少很多能力,Combine毕竟新生儿,还需要时间成长。但是Combine有个特性是RxSwift没有的,那就是Backpressure,Backpressure可自定义策略控制Subscribe能够接收的数量。除了SwiftUI和Combine,在Swift开发中还有哪些库是可以直接拿来使用的呢?这里有份 Swift开源库的awesome[17],在这里可以查缺补漏。AcFun主要使用了Swift开源库有Protobuf[18], RxSwift[19], Cache[20], Observable[21]。以上,为《A站 的 Swift 实践》的上篇内容,下篇我们会继续详细介绍OC和Swift是怎么混编的,以及Swift的动态性。相关链接
[1]https://blog.timac.org/2020/1019-evolution-of-the-programming-languages-from-iphone-os-to-ios-14/
[2]https://atp.fm/205-chris-lattner-interview-transcript
[3]https://forums.swift.org/t/on-the-road-to-swift-6/32862
[4]https://twitter.com/tkremenek
[5]https://github.com/Dimillian/ACHNBrowserUI
[6]https://github.com/Dimillian/MovieSwiftUI
[7]https://github.com/pointfreeco/swift-composable-architecture
[8]https://github.com/apple/swift-evolution/blob/master/proposals/0195-dynamic-member-lookup.md
[9]https://github.com/apple/swift-evolution/blob/master/proposals/0216-dynamic-callable.md
[10]https://github.com/apple/swift-evolution/blob/main/proposals/0289-result-builders.md
[11]https://github.com/carson-katri/awesome-result-builders
[12]https://mp.weixin.qq.com/s/oRPRCx78owLe3_gROYapCw
[13]https://github.com/rakutentech/AltSwiftUI
[14]https://github.com/OpenCombine/OpenCombine
[15]https://github.com/sergdort/HandMadeRx/tree/master/HandMadeRx.playground/Sources
[16]https://github.com/CombineCommunity/rxswift-to-combine-cheatsheet
[17]https://github.com/matteocrippa/awesome-swift
[18]https://github.com/apple/swift-protobuf
[19]https://github.com/ReactiveX/RxSwift
[20]https://github.com/hyperoslo/Cache
[21]https://github.com/slazyk/Observable-Swift
快手主站技术部客户端团队由业界资深的移动端技术专家组成,通过领先的移动技术深耕工程架构、研发工具、动态化、数据治理等多个垂直领域,积极探索创新技术,为亿万用户打造极致体验。团队自2011年成立以来全面赋能快手生态,已经建立起业内领先的大前端技术体系,支撑快手在国内外的亿万用户。
在这里你可以获得:
提升架构设计能力和代码质量
通过大数据解决用户痛点的能力
持续优化业务架构、挑战高效研发效能
和行业大牛并肩作战
我们期待你的加入!请发简历到:
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK