Swift在好大夫APP醫患兩端的打怪升級

swift作爲蘋果的親兒子,從2014年開始,在6年的發展過程中,終於在2019年3月份迎來了ABI(Application Binary Interface)的穩定。

背景

ABI的穩定意味着Binary接口穩定,也就是運行的時候只要是通過swift 5或者以上的編譯器編譯出來的binary,就可以跑在任意的swift 5.0及以上的runtime上。這樣,我們就不需要像以前那樣在app裏面放一個swift runtime,Apple會把相應的ABI整合到iOS或者macOS中。同時App的尺寸會變小,在iOS12.2以上系統,會預裝swift5 runtime,所以不在需要swift的庫,它們會從app bundle中被刪除,對於iOS12.2以下的系統,還是照舊需要引入。同時因爲系統集成了swift,app啓動的時候也就不需要額外加載swift,在新系統中會更加的節省內存空間。

對於好大夫的醫患兩端,在這時接入swift是一個不可多得的好機會。那麼,從好大夫的iOS客戶端團隊角度出發,接入swift需要有哪些準備工作?基礎框架如何設計?如何解決在swift開發中出現的問題?這些都會在下文中一一解答。

接入swift的準備工作

好大夫的iOS客戶端代碼年份非常久遠,整體使用Objective-C編寫,最早的代碼可以追溯到2011年8月份,對於接入swift來說,首先需要進行技術調研,什麼版本的swift適合好大夫客戶端,而不是一味的追求最新的swift版本。其次,對於swift三方框架的調研,國內的swift社區活躍度並不是特別高,相反國外的swift社區反而更加的活躍,從github上的Alamofire網絡庫到Uber早已使用swift重構客戶端,可見swift在過國外早已盛行已久。

通過調研,swift在開發效率和性能上明顯優於Objective-C,蘋果開發者官網上各種Programming Guide也已經以swift作爲首要顯示語言,從蘋果對swift的推廣來看,如果我們一直堅守Objective-C陣營,在未來蘋果強制推進swift,對我們來說衝擊還是比較大的。WWDC2019中,蘋果同時推出了swiftUI,正式統一了Apple全平臺UI開發解決方案,未來屬於swift,可見好大夫iOS客戶端接入swift刻不容緩。

基礎框架設計

iOS客戶端整體採用組件化的架構,使用cocoapods導入各個組件,業務之間通過中間件進行通信,基礎組件包括網路庫、圖片庫、支付庫等。在接入swift之後,雖然基礎私有庫可以通過修改podspec文件進行swift私有庫調用,但是爲了統一和壯大iOS客戶端的swift生態系統,對於部分私有庫進行了swift化重構,生成一套專供swift調用的基礎庫,當然不是所有的私有庫都進行了重構,而是根據具體的業務需求以及公司未來發展綜合考慮,對一些工程中經常用到的宏定義,遍歷構造函數以及視圖約束框架等進行了swift化。

對於UI常用控件,網絡庫,圖片庫,支付庫等,如果進行重構,則會出現影響範圍較大,測試耗時很長,影響項目迭代,拿UI控件庫來說只是進行了podspec文件修改,使其支持了swift私有庫的調用,同時如果有新增的基礎控件,首選swift語言來編寫,減少Objective-C代碼的使用。

經過一年時間的沉澱,iOS客戶端目前全量swift業務有直播業務、留言板業務,其他的業務的主流業務也已經使用了swift和OC混編的方式進行開發。

推進工程swift化遇到的問題

傳統Objective-C項目接入swift一些問題始終是繞不過去的,如:

  • 主工程或者模塊化中swift和Objective-C的混編怎麼實現
  • Module系統
  • swift代碼的規範性怎麼解決

一、swift和OC混編

針對上面的第一個問題,我們分爲兩種情況,第一種情況爲App Target內部swift和OC混編,對於好大夫的醫患兩端來說之前一直使用Objective-C編寫,所以在創建第一個swift文件時,會提示生成一個bridging header文件,點擊create即可。

在swift文件中如果需要引入某個OC類,將需要使用的OC類在bridging文件進行#import即可。在OC類中如果需要引入swift文件,只需要在OC類中引入import “ProductModuleName-Swift.h”,其中ProductModuleName表示當前Target的名稱。ProductModuleName-Swift.h文件爲編譯產物,可以看到該文件將swift文件中的代碼生成爲對應OC中的interface和implementation。

第二種情況爲模塊內部進行swift和OC混編,或者OC私有庫需要提供給外部的swift文件或者swift私有庫使用,針對這種情況我們可以統一配置,好大夫客戶端採用的cocoapods方式引入組件,所以通過對組件podspec進行配置,添加 s.pod_target_xcconfig = {‘DEFINES_MODULE’ => ‘YES’} 將組件模塊進行module化,可以使用cocoapods自動生成的umbrella.h文件或者自己新建一個 umbrella header 將需要暴露給swift調用的ObjC頭文件在這個 umbrella header 中導入,在需要調用的swift文件中直接 import Module 即可。

二、Module系統

2.1 LLVM Module系統

既然說到了OC和swift混編,那麼不得不提蘋果在2012年11月提出LLVM的Module系統,簡單講就是用樹形的結構化描述來取代以往#include,例如傳統的#include <stdio.h>變成了 import std.io,逼格更高,這樣做的好處主要有:

  • 語義上完整描述了一個框架的作用;
  • 提高編譯時的可擴展性,同一模塊只需要編譯或導入一次,避免了頭文件的多次引用、解析;
  • 減少碎片化,每個模塊只處理一次,環境的變化不會導致不一致;
  • 對工具友好,工具(語言編譯器)可以獲取更多關於 module 的信息,比如鏈接庫,比如語言是 C++ 還是 C;

2.2 modulemap文件

modulemap文件就是對一個框架,一個庫所有文件的結構化描述,默認文件名是module.modulemap。如下圖,可以看到使用 umbrellar header 關鍵字導入 HDFIMComponent-umbrella.h 中所有的.h文件。

umbrellar header關鍵字的意思爲 Master Header File,可以使用

#import <UIKit/UIKit.h>

替代

#import <UIKit/UIViewController.h>
#import <UIKit/UILabel.h>
#import <UIKit/UIButton.h>
#import <UIKit/UIDatePicker.h>

2.3 Swift Module

項目中的每一個Target(無論是framework還是app),就叫做一個Swift Module。這是Swift分發代碼的方式,我們可以通過import命令,來使用定義在其他Module中的代碼,而每個Module意味着:

  • 一個和Module同名的命名空間
  • 一個獨立的訪問控制範圍

對第一條來說,這也就是爲什麼在一個項目中,定義在不同Swift文件中的類可以在不同的文件中直接使用而不需要include的原因,因爲它們本身就在同一個namespace裏。

對於第二條來說,當我們通過import在項目中引入一個module時,就相當於打開了這個module對應的命名空間,就可以使用這個module中所有標記爲public或者open的代碼。

三、規範swift代碼

不同的人有不同的編碼習慣,導致了工程中代碼風格的多樣性,爲了提供swift代碼的可讀性以及統一性,iOS客戶端引入了swiftlint,swiftlint是Realm開源的一個加強檢查swift語言規範的庫,可以滿足強迫症的所有幻想,通過對swift代碼進行掃描,統一編碼風格,從每一個文件到每一個函數,甚至每一個變量都進行規範,將swiftlint掃描後的結果接入到SonarQube,使用網頁進行展示,方便分類修改,將具體的代碼分配給對應的負責人,嚴格按照要求進行修改,保證客戶端swift代碼壞味道爲0。

未來規劃

swift是一門優秀的編程語言,就目前蘋果主推swift語言以及編程語言排行榜上swift已經從18位上升到12位,可見未來國內swift語言也將會盛行。接入swift簡單,但是如果想要完美的兼容當前工程並非一件容易的事,只有選擇正確的方式,合適的解決方案才能將swift與原工程成功融合在一起。

好大夫iOS客戶端目前已經全量使用swift進行開發,計劃每年做新系統適配時會升級swift版本,緊跟時代的發展。後續接入swift package manager替代cocoapods、聲明式UI替代現有命令式UI。

作者介紹:

陶慶瑋:好大夫iOS開發工程師,主要負責組件化工程、基礎庫開發和客戶端穩定性相關工作,以及Swift語言落地。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章