13 的話
SwiftUI 專欄是一個付費專欄,只是我沒有設立付費牆,而是採用「誠實訂閱」制度。
我的目標是累積到 100 位 Patreon 支持者,目前進度來到 28%,感謝新舊朋友們的支持。
前言
相隔一個多月,又與大家見面了。
坦白說,在過去這個月裡,我對 SwiftUI 的感受是五味雜陳,直接導致這個專欄的難產。
怎麼說呢?隨著 iOS 16.0 的正式發佈,開發者社群也發現大量的 SwiftUI bugs。有些 bugs 在 iOS 16 的 beta 版甚至是沒出現過的。那些 bugs 都是 Apple 不該交給開發者的東西。
這就讓我感到為難。雖然這個專欄開宗明義就說是讀完就「入坑」,但這個情況也實在慘烈。
下面我會轉載一些 SwiftUI 在 iOS 16 上的問題,然後談談我對這些現象的猜想。
框架融合的難處
有個觀點不一定對,但我常常講,就是:SwiftUI 這個框架本身的問題其實不多。通常最嚴重的問題都是它跟其他框架串接的時候。
Text
、Color
、Shape
、VStack
這些「原生 SwiftUI」的東西很少遇到問題。
但像是用 UIKit 做的 List
,從 iOS 13 以來就是重災區。也是我在 #3 那些不該碰的 SwiftUI API 裡面特別不推薦的功能。
以下兩個 iOS 16 bugs 都跟 List
有關。
List
搭配 ScrollViewProxy
進行捲動,有可能會 crash:
List
有時不會觸發 .task
:
Apple 在 WWDC22 指出,iOS 16 裡的 List
實作已經從 UITableView
改成 UICollectionView
。
我們可以合理猜想,SwiftUI 工程師在修改呼叫 UICollectionView
那邊,有些情況沒有考慮好。
或者我們也可以說,這兩種截然不同原理類型的框架要融合,天生就有難度?
SwiftUI 的原則是「改變狀態就改變畫面」。要用 UIKit 來實作這個要果,就變成「框架要明確追蹤到狀態改變,並且使用 UIKit 的方式把畫面設定正確」。
那麼這些橋接與融合的程式碼就要面臨挑戰:SwiftUI 的畫面更新週期,能夠與 UIKit 的能夠完全同步嗎?UIKit 的事件能夠完美地傳回 SwiftUI 嗎?
如果有任何一點做不到的話,就是災難的開始。
我沒有用過 React Native,但我猜 RN 在與 UIKit 溝通也會有類似的挑戰。
其他 Bugs
不過下面這幾個問題,我就沒辦法單純用 UIKit 來當擋箭牌了😅
ScrollView
(雖說它目前還是 UIScrollView
實作)裡的 lazy View
的狀態沒有維持住。奇怪的是這個 View
裡面有幾層還有差🤔
onPreferenceChange
寫在 .background
裡會壞掉,但是改成放在 .overlay
裡面就正常。這真的顛覆我對這兩者差異的觀念🤯
至於 SwiftUI 在有 ProMotion 的 iPhone 上無法達到 120 Hz,可能只是 Apple 沒有設定好。畢竟 iPad Pro 上的 SwiftUI 一直都可以。
開發者自救會?
框架的 bug 有很多種,但最慘的肯定是「原本好好的,升級之後就壞掉了」這種。
我說的是正統的 API 使用,沒有用 workaround,而且開發者也用了新版的 SDK。但原本的語法或呼叫方式,在新的作業系統壞掉了。
SwiftUI 目前在 iOS 16.0 就是屬於這種。
那麼問題是,在 Apple 還沒修復的情況下,開發者能夠處理這些 bug 嗎?能繞過嗎?
使用 UIKit 的時候,如果有 bug,通常很好繞過。因為開發者有很多招可以使用:
在 runtime 拿到 UIKit 物件的實體,然後針對各種參數進行監控與修改。也就是說,官方的實作有問題,我們就把實際上發生的事情再改成別的
很多時候,延後一點時間來觸發,做到調整畫面更新順序就能解決問題😌
寫下繼承原本物件的新物件,並且覆蓋某些行為。這是標準的物件導向程式給開發者的自由
這 1、3 兩個技巧也可以組合,比如前者就是在 runtime 可以「看到」UIKit 的部分實作,所以才要覆蓋哪些部分。
但是 SwiftUI 的實作是看不到的,它的 API 把這些細節藏起來。
更重要的是,因為 API 是封裝過的。就算開發者用了各種技巧得知某個作業系統版本的實作並加以修改,在下一個版本它被改掉的機率遠高於 UIKit。這方面可以參見本專欄之前的「不要使用 Introspect」。
無奈的就是目前是 Apple 自己把實作改壞,開發者除了換個方式實作(比如把那個部分改成用 UIKit 來寫),也沒別的辦法。
有很多開發者在成熟的產品上不想用 SwiftUI──除非它是開源的框架──現在你可以理解背後的原因了。關鍵就是,在系統框架有 bugs 的時候,開發者有多少自救管道的問題。可能對於這些開發者來說,只要自己無法動手修改,SwiftUI 就只能是玩具。
當然,從另一個角度來說,任何一個處理掉很多 UIKit 細節毛病的 app,裡面應該都是充滿了 workaround 掉 Apple bug 的程式碼🌚 想要有「潔癖」地使用 UIKit 是不可能的,而 SwiftUI 就更不可能了。
結論
我想到 SwiftUI 的坑大概可以分成以下幾種情況:
某個 API 設計地不是很好,所以你得用某種不那麼爽的姿勢使用它
某個 API 沒有開放自訂或參數,所以你得自己重新實作
這個 API 提供新功能,但是在舊版 OS 沒有,所以只能等新版 OS 時才能使用
某個既有 API 在新版 OS 上是壞的
前 3 種坑,我覺得都能隨著開發經驗的增長,學會優雅地應對。但是今天列出來的問題都是第 4 種吧。
讀到這裡你可能會想說,還好我還沒有開始學 SwiftUI。
本來寫這個專欄的目的是引起讀者對 SwiftUI 的興趣,並且降低學習的障礙跟焦慮。
但現在的情況比較像是我們只能說,看看 iOS 16.1 或是後續版本會不會比較好。
我個人是不抱太大期待就是了🌚
雖然今天的內容沒一句好話,但是我最近還是寫了不少 SwiftUI,搭配 Xcode 14 的 Preview 更新速度還是滿愉快的開發體驗。也不能說 SwiftUI 在 iOS 16 就完全壞掉,只是問題比較多。
對於熟悉 SwiftUI 的朋友還是可以繼續用,總之哪裡有坑就避開。而對於初學或在觀望的朋友,我已經跟你說坑在哪了,哈!現階段只能降低對 SwiftUI 的期待了,這樣就不怕受傷害?
那麼你可能會問:「這個世界以後會變好嗎?我是說 SwiftUI。」我猜,如果 Apple 繼續用 UIKit 來實現 SwiftUI 的功能,那麼狀況應該不會變好。
但是有個現象值得觀察。今年 UIKit 在 UITableView
與 UICollectionView
裡新增了 UIHostingConfiguration 這個機制,也就是讓 cell 直接放入 SwiftUI 的 View
。耶,這思路是顛倒的──用 SwiftUI 來實作 UIKit。你覺得有希望嗎?
總之,我還是會密切觀察 SwiftUI 的發展,請繼續支持本專欄~
喜歡本專欄的讀者,請到 Patreon 訂閱支持我🙏 我的目標是累積到 100 位支持者,目前 28%。也請按下愛心❤️、留言💬、回信✉️與我交流。這些回饋都會直接影響到我繼續寫作的動力與頻率喔。
希望今天的內容讓你可以少走一些彎路。我們下一期見!
那刚开始学习iOS开发的话,是比较推荐从UIKit学起吗?