一、Swift介紹
- Swift是蘋果於2014年WWDC(蘋果開發者大會)發佈的新開發語言,可與Objective-C共同運行於iOS、MAC OS平臺,用於搭建基於蘋果平臺的應用程序。
- 截止至2022.5月,Swift更新到了5.6版本,從swift5開始ABI基本穩定。
- Swift 使用自動引用計數(Automatic Reference Counting, ARC)來簡化內存管理
- Swift Foundation框架可無縫對接現有的 Cocoa 框架,並且兼容 Objective-C 代碼,支持面向過程編程和麪向對象編程,Swift也是一門類型安全的語言。
- Swift中泛型是用來使代碼能安全工作,可以在函數數據和普通數據類型中使用,例如類、結構體或枚舉。泛型可以解決代碼複用的問題
//舉個簡單例子:這兩個方法很類似,主要就一個參數類型的區別。
func isIntEqual(x:Int,y:Int) -> Bool {
return x == y
}
func isStringEqual(x:String,y:String) -> Bool {
return x == y
}
//我們可以利用範型合併一下:
func isObjEqual<T:Equatable>(x:T,y:T) -> Bool {
return x == y
}
蘋果對於swift的描述:
對比OC語言
我們來總結下它與 Objective-C 相比的優缺點。一、Swift優點
1、簡潔的語法,性能較好
swift語言比OC精簡,整個項目中丟掉了.h頭文件,以及頭文件的引入。性能是Objective-C的1.3倍
2、定義變量簡單
定義變量不用區分整型,浮點型等等,變量使用var,常量使用let。
3、可視化互動效果
開發工具帶來了Xcode Playgrounds功能,該功能提供強大的互動效果,能讓Swift源代碼在撰寫過程中實時顯示出其運行結果。
4、函數式編程的支持(Map、FlatMap、Filter、Reduce等函數方法)
Swift 可以面向協議編程、函數式編程、面向對象編程 Objective-C 以面向對象編程爲主,通過引入 ReactiveCocoa 這個庫纔可支持函數式編程。
二、Swift缺點
1、語言覆蓋率還比較低
可能是Swift的ABI穩定得太晚,不止各大APP裏已經積累了大量的OC庫和業務代碼,蘋果系統裏的OC佔比也依然很高,_博客《Evolution of the programming languages from iPhone OS 1.0 to iOS 14》 _統計了 iOS 歷史版本 OC 佔比,從文章中可以看到最近的iOS 14版本里OC佔比高達88%,C和C++主要用於音視頻、電話、網絡等比較基礎的模塊,其佔比相對穩定,特別是C並沒有明顯增加。不過在最近幾個版本中,Swift佔比持續增高,iOS 14達到了8%,可以看出蘋果正在使用Swift重構以前的庫。
Out of all the binaries in iOS 14:
- 88% are using Objective-C
- 17% are using C++
- 8% are using Swift
- 8% are entirely written in C
- 1% are using SwiftUI
2、第三方庫的支持不夠多
老版本的swift還不穩定,每次更新API變動極大,到了5.0後ABI基本穩定,技術社區的開源項目相對於沉澱多年的OC偏少。當遇到一些問題的時候,解決問題的方案很少,網上的資源也很稀缺。對於不支持Swift的一些第三方類庫,如果非得使用,只能混合編程,利用橋接文件實現。
3、App體積變大
使用 Swift 後, App 體積大概增加 5-8 M左右,對體積大小敏感的慎用。(體積變大的原因是因爲 Swift 還在變化,所以 Apple 沒有在 iOS 系統裏放入 Swift 的運行庫,反而是每個 App 裏都要包含其對應的 Swift 運行庫。)
4、適配版本較高
大部分三方庫支持的版本較高,測試的demo中最低支持iOS13,同時SwiftUI最低支持也是iOS13
Swift代碼舉例
(1) 更好用的switch...case
不需要break,一個case可以寫多個條件,使用fallthrough繼續執行
switch num {
case 1, 2:
print("1, 2")
case 3..<5:
print("3, 4")
case ..<7:
print("6")
case ...8:
print("8")
default:
print("defalut")
}
基本數據類型都能判斷,並且能使用where語句
switch testArray {
case let array1 where array1.first == 1:
print(array1.first!)
default:
print(testArray)
}
(2) for循環
}
for i in 0..<8 where i % 2 == 0 {
}
for i in (0..<8).reversed() {
}
for i in stride(from: 0, to: 8, by: 2) {
}
for i in stride(from: 0, through: 8, by: 2) {
}
(3) 枚舉
enum Language: String {
case Swift
case ObjectiveC = "Objective-C"
case C
case Java
}
print(Language.Swift.rawValue)
Language.init(rawValue: "Swift")
// 可以綁定值
enum RequestResult {
case Success(Int)
case Error(String)
}
let requestResult = RequestResult.Success(1)
switch requestResult {
case .Success(let code):
print(code)
case .Error(let errorTint):
print(errorTint)
}
// 可以定義方法和計算型屬性
enum Device {
case iPad, iPhone, AppleTV, AppleWatch
func introduced() -> String {
switch self {
case .iPad: return "iPad"
case .iPhone: return "iPhone"
case .AppleWatch: return "AppleWatch"
case .AppleTV: return "AppleTV"
}
}
var year: Int {
switch self {
case .iPhone: return 2007
case .iPad: return 2010
case .AppleTV: return 2011
case .AppleWatch: return 2012
}
}
}
let introduce = Device.iPhone.introduced()
(4) 結構體
結構體在Swift中的地位很重要,Array Dictionary Set Int Float Double Bool String都是結構體
什麼時候用結構體,什麼時候用類
把結構體看做值
位置(經緯度)座標(二維座標,三維座標)溫度
把類看做是物體 人 車 動物
*/
/*
結構較小,適用於複製操作,相比一個class的實例被多次引用,struct更加安全
無須擔心內存泄漏或者多線程衝突問題
*/
struct Location {
var longitude: Double
var latitude: Double
// 使用Failable-Initializer
init?(coordinateString: String) {
// 非常簡潔的非空判斷,安全可靠
guard
let commaIndex = coordinateString.index(of: ","),
let firstElement = Double(coordinateString[coordinateString.startIndex..<commaIndex]),
let secondElement = Double(coordinateString[coordinateString.index(commaIndex, offsetBy: 1)..<coordinateString.endIndex])
else {
// 可能會失敗的init return nil
return nil
}
self.longitude = firstElement
self.latitude = secondElement
}
}
(5) 函數
let testString = "林哲生"
//(4)函數
func sayHello(name: String = testString, greeting: String = "你們好啊", extra: Dictionary<String, String>? = nil) {
print(name + greeting)
}
/*
1.支持重載
2.函數參數可設置默認值
*/
sayHello()
sayHello(name: "1")
sayHello(greeting: "2")
sayHello(extra: ["one": "1"])
// 改變外部傳入的參數
var array = [1,2,3,4]
func sayHello(array: inout [Int]) {
array.append(5)
}</pre>
#### (6) 可選性,安全的語言
縮減代碼量,安全處理數據邏輯。
<pre data-language="swift" id="Ai0N3" class="ne-codeblock language-swift" style="border: 1px solid rgb(232, 232, 232); border-radius: 2px; background-color: rgb(249, 249, 249); padding: 16px; font-size: 13px; color: rgb(89, 89, 89);">var string1: String? = "Hello"
var string2: String? = "Hi"
// 解包判斷1
if let string1 = string1 {
print(string1)
}
func sayHello(name: String = "gy", greeting: String = "你們好", extra: Dictionary<String, String>? = nil) {
// 解包判斷2
guard let uExtra = extra else {
return
}
print(uExtra)
}
func sayHello(greeting: String = "你們好", extra: Dictionary<String, String>? = nil) {
// 解包判斷2
guard let uExtra = extra else {
return
}
print(uExtra)
}
二、SwiftUI介紹
什麼是 SwiftUI
SwiftUI 於 2019 年度 WWDC 全球開發者大會上發佈,它是基於 Swift 建立的聲明式框架。該框架可以用於 watchOS、tvOS、macOS、iOS 等平臺的應用開發,等於說統一了蘋果生態圈的開發工具。
官方的定義寫得非常明確:
SwiftUI is a user interface toolkit that lets us design apps in a declarative way.
可以理解爲 SwiftUI 就是⼀種描述式的構建 UI 的⽅式。
爲什麼蘋果要推出 SwiftUI
SwiftUI 的兩個組成部分,Swift + UI,即是這個問題的答案。
Swift:編程語言和體驗的一致性
蘋果希望直接優化語言本身,並統一所有設備的開發體驗,讓開發者更容易上手,也更容易將心裏的想法轉化爲運行的程序。先有了Swift,緊接着又推出了 SwiftUI。SwiftUI 使用了大量 Swift 的語言特性,特別是 5.0 之後新增的特性。Swift 5.1 的很多特性幾乎可以說都是爲了 SwiftUI 量身定製的,比如 Opaque return types、Property Delegate 和 Function builder 等。
UI:開發的困局
在 SwiftUI 出現之前,蘋果不同的設備之前的開發框架並不互通,移動端的⼯程師和桌⾯端的⼯程師需要掌握的知識,有很⼤⼀部分是差異化的。
從 iOS SDK2.0 開始,移動端的開發者⼀直使⽤ UIKit 進⾏⻚⾯部分的開發。UIKit 的思想繼承了成熟的 AppKit 和MVC(Model-View-Controller)模式,作出了⼀些改進,但本質上改動不⼤。UI 包括了⽤⼾能看到的⼀切,包括靜⽌的顯⽰和動態的動畫。
再到後來蘋果推出了Apple Watch,在這塊狹小屏幕上,又引入了一種新的佈局方式。這種類似堆疊的邏輯,在某種程度上可以看做 SwiftUI 的未完全體。
截止此時,macOS 的開發需要使用 AppKit,iOS 的開發需要使用 UIKit,WatchOS 的開發需要使用堆疊,這種碎片化的開發體驗無疑會大大增加開發者所需消耗的時間精力,也不利於構建跨平臺的軟件體驗。
即使單看 iOS 平臺,UIKit 也不是完美的開發⽅案。
UIKit 的基本思想要求 ViewController 承擔絕⼤部分職責,它需要協調 model,view 以及⽤⼾交互。這帶來了巨⼤的 sideeffect 以及⼤量的狀態,如果沒有妥善安置,它們將在 ViewController 中混雜在⼀起,同時作⽤於 view 或者邏輯,從⽽使狀態管理愈發複雜,最後甚⾄不可維護⽽導致項⽬崩潰。換句話說,在不斷增加新的功能和⻚⾯後,同⼀個ViewController會越來越龐雜,很容易在意想不到的地⽅產⽣ bug。⽽且代碼糾纏在⼀起後也會⼤⼤降低可讀性,傷害到維護和協作的效率。
SwiftUI特點
聲明式的界面開發方式
近年來,隨着編程技術和思想的進步,使用聲明式或者函數式的方式來進行界面開發,已經越來越被接受並逐漸成爲主流。最早的思想大概是來源於 Elm,之後這套方式被 React 和 Flutter 採用,這一點上 SwiftUI 也幾乎與它們一致。總結起來,這些 UI 框架都遵循以下步驟和原則:
- 使用各自的 DSL 來描述「UI 應該是什麼樣子」,而不是用一句句的代碼來指導「要怎樣構建 UI」。
比如傳統的 UIKit,我們會使用這樣的代碼來添加一個 “Hello World” 的標籤,它負責“創建 label”,“設置文字”,“將其添加到 view 上”:
func viewDidLoad() {
super.viewDidLoad()
let label = UILabel()
label.text = "Hello World"
view.addSubview(label)
// 省略了佈局的代碼
}
而相對起來,使用 SwiftUI 我們只需要告訴 SDK 我們需要一個文字標籤:
var body: some View {
Text("Hello World")
}
- 如果 View 需要根據某個狀態 (state) 進行改變,那我們將這個狀態存儲在變量中,並在聲明 view 時使用它:
var body: some View {
Text("Hello \(name)")
}
對比同一個場景界面的實現
作爲常用的列表視圖,在UIKit中被稱爲 TableView,而在 SwiftUI 中被叫做 List 或 Form。同樣是實現一個列表,在 SwiftUI 僅需要聲明這裏有一個 List 以及每個單元的內容即可。而在UIKit 中,需要使用委託代理的方式定製每個單元的內容,還需要事無鉅細的設置行和組的數量、對觸摸的反應、點擊過程等各方面。
蘋果的全力支持
Swift 做爲蘋果的戰略語言已經發展的越來越壯大,自 2019 年 Swift ABI 穩定後,蘋果在 Swift 的投入越來越大。
- 蘋果與 WWDC 20 推出的 WidgetKit 支持的 API 是 SwiftUI Only,同理 Clips 也一樣。
- App Clips 10M的包大小, SwiftUI 是最合適的框架
- 從 WWDC17 後 蘋果已經不再使用 Objective-C 做 Sample Code 演示
- https://developer.apple.com/****不再更新 Objective-C 相關的文檔
- 開源社區逐步放棄 Objecive-C 如 Lottie
不足
雖然前文提到了 SwiftUI 的衆多優點,包括研發效率,體驗的提高,但是在國內的環境中 SwiftUI 也有它致命的弊端
- iOS 14 纔可放心的使用。
- 只支持 Apple Platform,這和國內的要支持 Mobile Platform 從理念上衝突。
大型 APP 要解決的是如何部署到低版本操作系統上和安卓平臺上,畢竟很多公司還在支持 iOS 9 對於升級到最低支持 iOS 14 好像還需要一個世紀那麼漫長,而且國內的設備佔比大頭還是以 Android 巨多 。
SwiftUI VS Flutter
下面我們看一段簡單的聲明式語法
SwiftUI
struct ContentView : View {
var body: some View {
VStack {
MapView()
.edgesIgnoringSafeArea(.top)
.frame(height: 300)
CircleImage()
.offset(y: -130)
.padding(.bottom, -130)
VStack(alignment: .leading) {
Text("Turtle Rock")
.font(.title)
HStack(alignment: .top) {Text("Joshua Tree National Park")
.font(.subheadline)
Spacer()
Text("California")
.font(.subheadline)
}
}
.padding()
Spacer()
}
}
}
flutter
Widget _listItemBuilder(BuildContext context, int index) {
return Container(
color: Colors.white,
margin: EdgeInsets.all(8.0),
child: Stack(
children: <Widget>[
Column(
children: <Widget>[
AspectRatio(
aspectRatio: 16/9,
child: Image.network(posts[index].imageUrl, fit: BoxFit.cover),
),
SizedBox(height: 16.0),
Text(
posts[index].title,
style: Theme.of(context).textTheme.title
),
Text(
posts[index].author,
style: Theme.of(context).textTheme.subhead
),
SizedBox(height: 16.0),
],
),
Positioned.fill(
child: Material(
color: Colors.transparent,
child: InkWell(
splashColor: Colors.white.withOpacity(0.3),
highlightColor: Colors.white.withOpacity(0.1),
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => PostShow(post: posts[index]))
);
}
),
),
),
],
),
);
}
觀察語法的細節,我們可以注意到一些特徵,Flutter 使用典型的聲明式語法,開發者聲明 UI 的佈局方式,一切的佈局都交給引擎來解決。在寫代碼層面 Dart 使用 , 來分割不同的 Widget,這會造成在複雜的佈局中,()語法嵌套極其複雜,配上 VSCode 的插件也看的眼花繚亂。
而 Swift 雖然也是聲明式語法,但是仔細注意到,Swift 的 View 組合並不是由, 分割,而是由換行分割,在 Swift 中 函數調用是可以換行分割的。這樣的 DSL 對開發者的體驗更爲友好,推測 SwiftUI 使用了類似標記的特徵,在統一的時機去做佈局。
總結
更簡潔、更穩定、更安全且效率更高的Swift,以及聲明式、跨平臺且體積更小的SwiftUI,相比於"老舊不堪"的OC是如此的優秀,加上蘋果的全力支持、推廣,RealityKit、CareKit、Create ML、System、WidgetKit、CryptoKit、Combine、SwiftUI等框架在與OC混編時都非常困難,從這些方面可以看出,蘋果所有新開發的框架都在避免和OC產生關係,甚至自WWDC2020起新增加的App Widget只能用SwiftUI開發。
一些耳熟能詳的App,比如微信、淘寶、百度、支付寶、拼多多、京東、嗶哩嗶哩、優酷、小紅書等都已經開始嘗試使用Swift,這些App無一例外都採用了Swift和OC混編開發。由於國內業務競爭壓力大,很難像國外公司Uber那樣花大半年時間全部用Swift重構,因此如果要在現有工程基礎上引入Swift開發,不可避開採用混編開發。很多App使用Swift混編,也是因爲蘋果對Widget功能開發語言設置了限制,即只能使用Swift,看來蘋果公司這個策略是相當有效的。我們團隊恰巧也有開發Widget小組件的意願,第一個Swift+UI的落地場景即將到來。
作爲一個客戶端開發者,跟上時代的步伐!
參考資料
如何評價SwiftUI-阿里巴巴大淘寶技術
A站 的 Swift 實踐
SwiftUI與Swift的區別
SwiftUI 的一些初步探索 (一)
OC開發轉Swift - OC映射Swift速查表