WWDC 20 SwiftUI 的重大改變及核心優(yōu)勢

6月23日凌晨 1 點,蘋果 WWDC20 開發(fā)者大會在線上以主題演講的方式,在 Apple Park 進行直播。

23-26日,蘋果公開了 100 多個面向開發(fā)者的視頻,內容涵蓋Swift / SwiftUI 、App Clips、Widgets、Privacy & Security 等等方面。

對于開發(fā)者和程序員來說,我們有哪些新發(fā)現和新思考?

前言


SwiftUI 是蘋果公司于 2019 年推出的 Apple Platform 的新一代聲明式布局引擎,但 SwiftUI 1.0 基本沒有任何公司敢用在正式上線的主 APP 上,API 在 Beta 版本之間各種廢棄,UI 樣式經常不兼容,大列表性能差,彼時都標識著 SwiftUI 還稱為一個 Toy Framewrok.隨著 WWDC 20 相關新特性和介紹視頻的釋出,都明確的宣告著 SwiftUI 元年已經到了,SwiftUI 已經成長為新時代的布局引擎。以下從幾個方面分享關于 SwiftUI 的重大改變及核心優(yōu)勢。
image

PS: 需要讀者對 Swift 及 SwiftUI 1.0 有一定熟悉。SwiftUI Apps


蘋果在最近幾年的動作中一直在搞 Apple Platform 統(tǒng)一的事情,從最近幾年的 iPad 多任務 多窗口,到 Mac Catalyst 再到今年更進一步直接推出了 Apple silicon 芯片更是從硬件上做到了真正統(tǒng)一(話外音:你們在軟件上玩的那些跨平臺的都是小玩意,硬件才是王道)。 還提供了 Rosetta2 Universal2 幫助開發(fā)者基本無成本的遷移到新平臺上。但是作為軟件工程師還是要更多的關注軟件生態(tài)的變化。首先了解下創(chuàng)建 APP 時的變化


image

可以看到創(chuàng)建新工程時有了一套全新的模板基于 SwiftUI App Lifecycle 的跨平臺項目。
代碼也從原本的基于 UI/NS HostViewController 變成了基于 APP 的聲明式描述,下面是代碼的前后對比.

  • Before

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        let contentView = ContentView()
        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            window.rootViewController = UIHostingController(rootView: contentView)
            self.window = window
            window.makeKeyAndVisible()
        }
    }
}
  • After

import SwiftUI 
@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

其中@main 是Swift 5.1 新增的 Attribute 標記了應用程序的入口點,更多請參看 SE-0281-main-attribute.md乍看好像只有代碼精簡了不少,很多人會認為這個簡潔程度還不如Flutter 的 main() => runApp(MyApp());. 但最重要的變化是這是第一次跨平臺代碼,完全無需引入任何 UIKit APPKit WatckKit 等相關Framewok, 即可直接運行在不同平臺上。這意味著我們后續(xù)在UI布局系統(tǒng)上可以逐漸擺脫對傳統(tǒng)命令式 UI 編程的依賴。達到真正的平臺無關。SwiftUI 將整個原有的平臺差異部分抽象為 App 和 Scene,對于一個 mac/iOS/iPad/watch/tv/..應用,來說 App 代表了整個應用,Scene 代表了與 Window 相關的多窗口,有些設備只有一個 Scene 有些則有多個,雖然不同的 OS 確實存在差異,但是在語義層面達到了一致。其次一個沒有歷史包袱的 APP,也可以完整的從 Swift APP lifecycle 風格式的模板開始,無需再和傳統(tǒng)的 UIKit/APPKit 等混合。這也意味著可以達到 APP 完全 Declared and State-Driven。

image

image

Viusal Editing


? Preview

在傳統(tǒng)的利用 DSL 可視編程框架或者平臺,諸如 Web Flutter 等技術,都是開發(fā)者編寫好對應的代碼,運行在對應的平臺或者調試工具上。SwiftUI 作為蘋果最重要的軟件層戰(zhàn)略框架,更是和 Xcode 深度結合,在運行之前就可以完整的預覽你所編寫的界面。 強大的 Preview 可以讓你既可以從編寫 DSL 到立即預覽效果,也可從預覽的 Canvas 畫布中直接修改效果在代碼編輯器中生成代碼,這對于日常開發(fā)的效率有非常大的提高,尤其是在 UI 微調時,效果尤為突出。 Xcode12 可以在 Canvas 上同時預覽多個不同設備環(huán)境的界面,也可以直接投射到真實的設備上來預覽。
image

對于日常開發(fā)來說,編寫一個UI界面通常依賴外部的網絡/磁盤/其他數據,才能正常的構建,這也造成了UI開發(fā)雖然是開發(fā)中較為簡單的一步,但同時也是最耗時的一步,有了預覽功能,可以把很多繁瑣的工作前置解決掉,對于研發(fā)效率會有非常大的提高。

? Xcode Library

在編寫真實項目中,一個公司的 APP UI 包含成百上千種風格的 View 組件,對于 UI 組件豐富的產品,如果一個新需求可以由現有的組件組合,那么需求交付的時間也會大大縮短。 但是對于一個大型的開發(fā)團隊而言,一個開發(fā)同學是很難知道公司內到底有多少種組件庫,而且即便知道有某種組件庫,開發(fā)同學初期看到的也是代碼,一般需要書寫一定的 Demo 才可以用眼睛感知到這個組件到底是否是我想要的。 在 Xcode 12 中提供了更強大的工具,一個自定義組件,只需要遵守一個 LiberyContentProvider 協議就可被Xcode識別,可以像系統(tǒng)控件一樣直接從 Xcode 里面識別并預覽。對于一個大型團隊來說,此功能可以大大提高找尋組件和查看組件樣式的效率。
image
// Without trailing closure:
        UIView.animate(withDuration: 0.3, animations: {
          self.view.alpha = 0
        }, completion: { _ in
          self.view.removeFromSuperview()
        })
        // With trailing closure
        UIView.animate(withDuration: 0.3, animations: {
          self.view.alpha = 0
        }) { _ in
          self.view.removeFromSuperview()
        }
        // Multiple trailing closure arguments
        UIView.animate(withDuration: 0.3) {
          self.view.alpha = 0
        } completion: { _ in
          self.view.removeFromSuperview()
        }

DSL

隨著 Swift5.3 和 SwiftUI2.0 的推出,SwiftUI 在 DSL 上也更富有表現力, Swift 支持了多重尾閉包語法和在 ViewBuilde 里面支持 Switch Case 語句。

? Multiple Trailing Closures

雖然社區(qū)對多重尾閉包的討論上一直存在爭議問題,但最終 Swift5.3 還是接受并實現了,在普通命令式編程的地方使用會有一定的困惑性,但是在 SwiftUI 中 DSL 也更有聲明式的味道。

  // Without trailing closure:
        UIView.animate(withDuration: 0.3, animations: {
          self.view.alpha = 0
        }, completion: { _ in
          self.view.removeFromSuperview()
        })
        // With trailing closure
        UIView.animate(withDuration: 0.3, animations: {
          self.view.alpha = 0
        }) { _ in
          self.view.removeFromSuperview()
        }
        // Multiple trailing closure arguments
        UIView.animate(withDuration: 0.3) {
          self.view.alpha = 0
        } completion: { _ in
          self.view.removeFromSuperview()
        }

? Switch Case Support

在 SwiftUI 的 ViewBuilder DSL體系中也支持了 Switch case 語法。


      var body: some View {
            switch c {
            case .a:
                return Text("A")
            case .b:
                return Text("B")
            case .c:
                return Text("C")
            }
        }

Data Flow


在使用傳統(tǒng)命令式編程編寫 UI 代碼時,開發(fā)者需要手動處理 UIView 和 數據之間的依賴關系,每當一個 UIView 使用了外部的數據源,就表明了 UIView 對外部的數據產生了依賴,當一個數據產生變化時,如果意外的沒有同步UIView的狀態(tài),那么 Bug 就產生了。

處理簡單的依賴關系是可控的,但是在真實項目中,視圖之間的依賴關系是非常復雜的,假設一個視圖只有 4 種狀態(tài),組合起來就有 16 種,再加上時序的不同,情況就更加復雜。 人腦處理狀態(tài)的復雜度是有限的,狀態(tài)的復雜度一旦超過人腦的復雜度,就會產生大量的 Bug,并且修掉了這個產生了新的Bug。
image
image

那么 SwiftUI 是如何解決這個問題的? SwiftUI 的框架提供了幾個核心概念: 1. 統(tǒng)一的 body 屬性,SwiftUI 自動從當前 App 狀態(tài)集自動生成基于當前狀態(tài)的快照 View。 2. 統(tǒng)一的數據流動原語。
image

今年 SwiftUI 2.0 新增的 StateObject 數據流原語讓 SwiftUI 在重復創(chuàng)建 View 時避免重復創(chuàng)建 ObservedObject 從而提高 View 重建的性能。 SceneStorage 和 APPStorgae 讓一些可持久化的數據變得更加簡單且具有語義化。

New Controls


前面提到的,新增的 DSL 語法 SwiftUI App Lifecycle,以及 Xcode Library Preview 其實本質上都是對去年 SwiftUI 1.0 錦上添花的新擴展。 真正重要的是今年新增的各類新控件,其中通過導出來自 Xcode11.5 和 Xcode12.0 beta 版本的 Swift 聲明文件,可以觀察到整個聲明文件從原來的 10769 行增加到 20564行。 新增了約 87 個 struct 16 個 protocol。有了這些豐富的組件才可以更好的構建我們的 APP 。

? ** 大列表組件**

在任何一款 APP 中都會存在類似大列表組件,如淘寶 APP 里面的某家店鋪里面商品列表流,首頁的信息流,都是具有超長內容的列表頁數據。對于長列表頁來說,過長的 UI 頁面會導致過多的內存占用,在用戶的設備中,內存是最為重要的指標,對于目前國內的 APP 市場,低端手機仍然占據大量的市場,對于這些設備來說,一旦內存超標,APP 就很容易 OOM,這會導致用戶體驗非常差,在現有競爭關系激烈的市場環(huán)境下,體驗差意味著會失去用戶。對于傳統(tǒng)的命令式編程來說,我們可以主動控制 UITableViewCell 的重用,自建緩沖池等一系列手段去優(yōu)化我們的 APP 內存占用,但是對于 SwiftUI 1.0 來說,系統(tǒng)提供的控件并沒有有效的辦法去讓我們控制頁面的渲染,對于大列表頁面就容易出現內存占用過高的問題。 SwiftUI 2.0 推出了 LazyHStack 和 lazyVStack 加上 List 渲染模式默認就是 Lazy 的直接解決了最大的性能問題。有大佬去年使用 SwiftUI 編寫的APP,當列表頁(并無大圖)加載到 500個時, APP 使用內存已經達到了將近 360MB 。而只需要切換到 Xcode12 API 調整為到 LazyVStack 內存占用直接降低 300MB 。
image
image

Widget and Clips


蘋果與 WWDC 20 推出的 WidgetKit 支持的 API 是 SwiftUI Only,雖然已經可以混合部分UIkit 里面的View,但相信沒有歷史包袱 最低支持版本為 iOS14 的 Widget 沒有人會選擇笨重的命令式 API。
同理 Clips 也一樣。這里因為篇幅原因就不做展開.


一直有一些質疑的聲音, 引入 Swift 到底有什么用? SwiftUI 又是 N 年后才可以用上的小玩意,Objective-C 不夠用嗎? Swift 未來的機會在 效率,體驗和蘋果的技術紅利。

? 效率

從研發(fā)效率上來說, Swift 對比 Objective-C 的精簡程度不言而喻,代碼量下降了 40 %。 但更進一步,如果編寫 UI 界面從 UIKit 轉向了 SwiftUI 代碼量直接少了不止一倍。更少的代碼意味著更快的交付,在目前競爭激烈的市場會有更多的試錯場景。關于使用 UIKit 編寫代碼轉向 SwiftUI 的代碼量對比,讀者可以參考開源 APP MovieSwiftUI https://github.com/Dimillian/MovieSwiftUI
直觀了解。

? 體驗

讀者可能比較困惑對于切換語言和框架,對體驗看上去沒有任何幫助,但事實真是這樣嗎? 首先引入 Swift 后,由于 Swift 語言設計之初便對安全性列為最重要的目標,Swift的引入會讓代碼盡可能的減少未定義的行為,減少 Crash 意味著APP的穩(wěn)定性提高,體驗自然更佳。其次雖然 Swift 同樣的語言出于對安全性考慮編譯處理的指令會比 Objective-C 更多,但是如果UI部分都用 SwiftUI 來寫呢? 更少的代碼意味者更小的包大小,目前國內巨頭 APP iOS 端 APP 包大小都朝著 200 MB 奔去,如果能減少更多的代碼對包大小也可以在 200MB 的限制下承載更多而業(yè)務。對用戶的體驗也有較大的提升。更進一步由于 Swift 選擇使用值類型構建整個APP,值類型的有點在于更扁平化的內聯數據結構去分配內存,而不是使用更多間接指針引用,減少了大量不必要的堆內存消耗,意味著整體內存使用量的降低。對整個 APP 的穩(wěn)定性也有較大的提高。
image
image

? 蘋果的選擇

Swift 做為蘋果的戰(zhàn)略語言已經發(fā)展的越來越壯大,自 2019 年 Swift ABI 穩(wěn)定后,蘋果在 Swift 的投入越來越大。我們可以進入 /Applications/Xcode-beta.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/lib/swift , /Applications/Xcode-beta.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks , https://github.com/applehttps://github.com/swift-server 看到, 自 iOS 13 以來 蘋果新增了約 10+ Pure Swift Library , 10+ Open Source Swift Library, 以及針對 144 個公開 Framework,根據 Swift Style 重新設計了 57 個 Framework 的API。 從以下數據:

  1. 從 WWDC17 后 蘋果已經不再使用 Objective-C 做 Sample Code 演示

  2. https://developer.apple.com/不再更新 Objective-C 相關的文檔

  3. WidgetKit 是 SwiftUI only。

  4. App Clips 10M的包大小, SwiftUI 是最合適的框架

  5. 開源社區(qū)逐步放棄 Objecive-C 如 Lottie。

可以判斷,Swift 是未來 Apple 平臺的唯一選擇,越是有包袱的大廠 APP,從現在還不盡早儲備,在未來越會寸步難行。
image
image
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容