Swift 中的熱過載
前言
我們最新的 MacBook M30X 處理器可以感知到瞬間編譯大型 Swift 專案,除此之外,編譯程式碼庫只是我們迭代週期的一部分。包括:
- 重新啟動它(或將其部署到裝置)
- 導航到您在應用程式中的先前位置
- 重新生成您需要的資料。
如果您只需要做一次的話,聽起來還不錯。但是如果您和我一樣,在特別的一天中,對程式碼庫進行 200 - 500 次迭代,該怎麼辦呢?它增加了。
有一種更好的方法,被其他平臺所接受,並且可以在 Swift/iOS 生態系統中實現。我已經用了十多年了。
從今天開始,您想每週節省多達 10 小時的工作時間嗎?
熱過載
熱過載是關於擺脫編譯整個應用程式並儘可能避免部署/重新啟動週期,同時允許您編輯正在執行的應用程式程式碼並且能立即看到更改。
這種流程改進可以每天為您節省數小時的開發時間。我跟蹤我的工作一個多月,對我來說,每天節省了 1-2 小時。
坦白地說,如果每週節省10個小時的開發時間都不能說服您去嘗試,那麼我認為任何方法都不能說服你。
其他平臺在做什麼?
如果您只使用 Apple 平臺,您會驚訝地發現有好多平臺幾十年前已經採用了熱過載。無論您是編寫 Node 還是任何其他 JS 框架,都有一個使用熱過載的設定。 Go 也提供了熱過載(本部落格使用了該特性)
另一個例子是谷歌的 Flutter 架構,從一開始就設計用於熱過載。如果您與從事 Flutter 工作的工程師交談,你會發現他們最喜歡 Flutter 開發者體驗的一點就是能夠實時編寫他們的應用程式。當我為《紐約時報》寫了一個拼字遊戲時,我很喜歡它。
微軟最近推出了 Visual Studio 2022,併為 .NET 和 標準 C++ 應用程式提供熱過載,在過去的十年中,微軟在開發工具和經驗方面一直在大殺四方,所以這並不令人驚訝。
蘋果生態系統怎麼樣?
早在 2014 年推出時,很多人都對 Swift Playgrounds 感到敬畏,因為它們允許我們快速迭代並檢視程式碼的結果,但它們並不能很好地工作,因為它存在崩潰、掛起等問題。不能支援整個iPad環境。
在它們釋出後不久,我啟動了一個名為 Objective-C Playgrounds 的開源專案,它比官方 Playgrounds 執行得更快、更可靠。我的想法是設計一個架構/工作流程,利用我已經使用了幾年的 DyCI 程式碼注入工具,該工具已經由 Paul 製作。
自從 Swift Playgrounds 存在以來,已經過去了八年,而且它們變得更好了,但它們可靠嗎?人們是否在使用它們來推動開發?
以我的經驗:並非如此。Playgrounds 在大型專案中往往不太可靠或適用。
SwiftUI 出現了,它是一項了不起的技術(儘管仍然存在錯誤),它引入了與 Playgrounds 非常相似的 Swift Previews 的想法,它們有什麼好處嗎?
類似的故事,當它工作的時候是很好的,但是在更大的專案中,它的工作是不可靠的,而且往往中斷的次數比它們工作的次數多。如果你有任何錯誤,他們不會為你提供除錯程式碼的能力,因此,採用的情況有限。
我們需要等待 Apple 嗎?
如果你關注我一段時間,你就已經知道答案了,絕對不要。畢竟,我的職業生涯是構建普通 Apple 解決方案無法解決的問題:從像 Sourcery 這樣的語言擴充套件、像 Sourcery Pro 這樣的 Xcode 改進,再到 LifetimeTracker 以及許多其他開源工具。
我們可以利用我最初在 2014 Playgrounds 中使用的相同方法。我已經使用它十多年了,並且在數十個 Swift 專案中使用它並取得了巨大的成功!
許多年前,我從使用 DyCI 切換到 InjectionForXcode,通過利用 LLVM 互操作而不是任何 swizzling ,它的效果更好。它是一個完全免費的開源工具,您可以在選單欄中執行,它是由多產的工程師 John Holdsworth 建立的。你應該看看他的書 Swift Secrets。
我意識到 Playgrounds 的方法可能過於笨重,所以今天,我開源了。一個非常專注的名為 Inject 的微型庫,與 InjectionForXcode 搭配使用時,將使您的 Apple 開發更加高效和愉快!
但不要只相信我的話。看看 Alexandra 和 Nate 的反饋,在我將這個工作流程引入 The Browser Company 設定之前,他們已經非常精通了,這使得它更加令人印象深刻。
Inject
這個小型庫是完全通用的,無論您使用 UIKit
、 AppKit
還是 SwiftUI
,您都可以使用它。
您無需為生產應用程式新增條件或刪除 Inject
程式碼。它變成了無操作內聯程式碼,將在非除錯版本中被編譯過程剝離。您可以在每個檢視中整合一次,並持續使用數年。
請參考 GitHub repo 中關於配置專案的說明。現在讓我們來看看您有哪些工作流程選項。
工作流
SwiftUI
只需要兩行字就可以使任何 SwiftUI 啟用實時程式設計,而當您這樣做時,您將擁有比使用 Swift Previews 更快的工作流程,同時能夠使用實際的生產資料。
這是我的 Sourcery Pro 應用程式的示例,其中載入了我所有的實際資料和邏輯,使我能夠即時快速迭代整個應用程式設計,而無需任何重新啟動、重新載入或類似的事情。
看看這個開發工作流程有多快吧,告訴我你寧願在我每次接觸程式碼時等待Xcode的重新構建和重新部署。
UIKit / AppKit
我們需要一種方法來清理標準命令式UI框架的程式碼注入階段之間的狀態。
我建立了 Host
的概念並且在這種情況下工作的很好。有兩個:
shell
- Inject.ViewHost
- Inject.ViewControllerHost
我們如何整合它?我們把我們想迭代的類包裝在父級,因此我們不修改要注入的型別,而是改變父級的呼叫站點。
例如,如果你有一個 SplitViewController ,它建立了 PaneA 和 PaneB ,而你想在PaneA 中迭代佈局/邏輯程式碼,你就修改 SplitViewController 中的呼叫站點。
swift
paneA = Inject.ViewHost(
PaneAView(whatever: arguments, you: want)
)
這就是你需要做的所有改變。注入現在允許你更改 PaneAView 中的任何東西,除了它的初始化API。這些變化將立即反映在你的應用程式中。
一個更具體的例子?
-
我下載了 Covid19 App
-
新增
-Xlinker -interposable
到Other Linker Flags
-
交換了一行
Covid19TabController.swift:L63
行
從這句:
swift
let vc = TwitterViewController(title: Tab.twitter.name, usernames: Twitter.content)
替換為:
swift
let vc = Inject.ViewControllerHost(TwitterViewController(title: Tab.twitter.name, usernames: Twitter.content))
現在,我可以在不重新啟動應用程式的情況下迭代控制器設計。
這是如何運作的呢?
Hosts 利用了自動閉包,因此每次您注入程式碼時,我們都會使用與最初相同的引數建立您型別的新例項,從而允許您迭代任何程式碼、記憶體佈局和其他所有內容。你唯一不能改變的是你的初始化 API。
Host 的變化不能完全內聯,所以這些類在 Release 構建中被刪除。最簡單的方法是做一個單獨的提交,交換此單行程式碼,然後在工作流程的最後刪除它。
邏輯注入如何呢?
像 MVVM / MVC 這樣的標準架構可以獲得免費的邏輯注入,重新編譯你的類,當方法重新執行時,你已經在使用新程式碼了。
如果像我一樣,你喜歡 PointFree Composable Architecture,你可能想要注入 reducer 程式碼。 Vanilla TCA 不允許這樣做,因為 reducer 程式碼是一個免費功能,不能直接用注入替換,但我們在 The Browser Company 的分支 支援它。
當我最初開始諮詢 TBC 時,我想要的第一件事是將 Inject
和 XcodeInjection
整合到我們的工作流程中。公司管理層非常支援。
如果您切換到我們的 TCA 分支(我們保持最新),你可以在 UI 和 TCA 層上使用 Inject
。
它有多可靠?
沒有什麼是完美的,但我已經使用它十多年了。它比 Apple 技術(Playgrounds / Previews)可靠得多。
如果您投入時間學習它,它將為您和您的團隊節省數千小時!
Demo 原始碼
我正在參與掘金技術社群創作者簽約計劃招募活動,點選連結報名投稿。
- 在 SwiftUI 中建立一個環形 Slider
- Swift 週報 第二十五期
- Swift 週報 第二十四期
- 在 iOS 16 中用 SwiftUI Charts 建立一個折線圖
- Swift 中的 async/await ——程式碼例項詳解
- Swift AsyncSequence — 程式碼例項詳解
- Swift 週報 第十期
- SwiftUI 之 HStack 和 VStack 的切換
- 第三方庫並不是必須的
- Swift 週報 第十二期
- LeetCode - #146 LRU 快取(Top 100)
- LeetCode - #145 二叉樹的後序遍歷
- 現今 Swift 包中的二進位制目標
- LeetCode - #125 驗證迴文串
- 解決 iOS 15 上 APP 莫名其妙地退出登入
- 用 SwiftLint 保持 Swift 風格一致
- TCA - SwiftUI 的救星?(一)
- Swift 中的熱過載
- 在 Swift 中編寫指令碼:Git Hooks
- LeetCode - #124 二叉樹中的最大路徑和(Top 100)