前言
本期是 Swift 編輯組自主整理週報的第二十四期,每個模塊已初步成型。各位讀者如果有好的提議,歡迎在文末留言。
Swift 週報在 GitHub 開源,歡迎提交 issue,投稿或推薦內容。目前計劃每兩週週一發佈,歡迎志同道合的朋友一起加入週報整理。
看那碧水藍天,波瀾又壯闊。淺讀Swift社區,充實而豁然。期許光亮,皆在其中!👊👊👊
週報精選
新聞和社區:App 內購買項目和訂閲即將實行價格與税率調整
提案:將 conformance 宏作為 extension 宏
Swift 論壇:討論
\和$的意義推薦博文:輕量化的 iOS 動畫框架實現
話題討論:
最新薪酬排行出爐,廣州平均月薪 10883 元,北京平均月薪 13438 元,你的月薪處於什麼水平?
上期話題結果
這個結果表明大多數人對 vision pro 在推動虛擬現實技術發展方面持樂觀態度。Vision pro 具備強大的圖像處理和感知能力,可以為虛擬現實應用提供更加逼真、沉浸式的體驗。
新聞和社區
App 內購買項目和訂閲即將實行價格與税率調整
App Store 的交易和支付機制旨在幫助你在覆蓋全球的 175 個國家和地區的店面中,以 44 種貨幣為你的產品和服務便捷地進行定價與銷售。當税務法規或外匯匯率變化時,App Store 中某些地區的價格有時會隨之更新,且你的收入亦將調整。這些調整將根據金融數據機構提供的公開匯率信息進行,以此確保 App 和 App 內購買項目的定價在所有店面中保持平衡。
從 7 月 25 日起,App 和 App 購買項目 (不包括自動續期訂閲) 在埃及、尼日利亞、坦桑尼亞和土耳其店面中的定價將會進行調整。這些調整還包含了以下税率變更:
埃及:收取 14% 的增值税 (VAT)
坦桑尼亞:收取 18% 的增值税和 2% 的數字服務税
土耳其:增值税率從 18% 上調至 20%
這些調整對定價的影響
如果你選擇了埃及、尼日利亞、坦桑尼亞或土耳其作為 App 或 App 內購買項目 (不包括自動續期訂閲) 的基準店面,則對應店面中的價格不會發生變化。其他店面中的價格將會進行更新,以便與你選擇的基準價格保持持平。
如果你為 App 或 App 內購買項目 (不包括自動續期訂閲) 選擇的基準店面不是埃及、尼日利亞、坦桑尼亞或土耳其,則埃及、尼日利亞、坦桑尼亞和土耳其店面中的價格將會上調。
如果你的 App 內購買項目是自動續期訂閲,或者如果你手動管理各個店面的價格,而不是使用自動均衡價格,那麼你的價格不會發生變化。
App Store Connect 中“我的 App”的“價格與銷售範圍”部分現已更新,以顯示這些即將進行的價格調整。一如既往,你可以隨時更改你的 App、App 內購買項目和自動續期訂閲的價格。
這些調整對收益和税務管理的影響
你從 App 和 App 內購買項目 (包括自動續期訂閲) 銷售中獲得的收益將會發生變化,以反映新的税率和更新後的價格。《付費 App 協議》的附錄 B 已更新,表明 Apple 在埃及和坦桑尼亞徵收和匯付適用税款。
為家庭提供安全的 App 體驗
App Store 的創建目的是為用户提供一個安全且值得信賴的 App 下載平台,併為開發者提供絕佳的商機。由於孩子們會使用我們的產品和服務,來探索數字世界並與家人和朋友進行交流,因此對許多家庭而言,Apple 平台和你構建的 App 變得非常重要。針對面向兒童的 App,以及那些具有用户生成內容和互動的 App,我們設立了極高的標準。為了繼續為家庭打造安全的體驗,謹在此提醒你,我們提供了各種工具和資源,並制定了相關要求,以幫助你保障用户在 App 中的安全。
提案
正在審查的提案
SE-0398 將 conformance 宏作為 extension 宏 提案正在審查。
該提案將 conformance 宏角色推廣為 extension 宏角色,除了協議和 where 子句外,還可以向擴展中添加成員列表。
Swift論壇
1) 討論這些是錯別字嗎?
提問:
在觀看 SwifUIi 視頻時,看到兩處看起來像是拼寫錯誤的東西。想知道為什麼它們的表達如此含糊:
反斜槓有什麼用?
var body: some View {
List (graphics, children: \.children) { graphic in
GraphicRow (graphic)
}
.listStyle(SidebarListStyle())
}
美元符號有什麼用?
var body: some View {
DocumentGroup (newDocument: SketchDocument()) { file in
DocumentView(file.$document)
}
}
回答:
這些不是拼寫錯誤。它們是用於訪問特定語言功能的符號,這些功能會生成與命名屬性相關的內容,而不是正常訪問該屬性。
Swift 在前綴運算符位置中使用 \ 來創建“關鍵路徑”,該對象通常表示(在本例中)Graphic.children 屬性,而不是特定 Graphic 的 Children 屬性;該對象可以應用於 Graphic 的任何實例以訪問其 Children 屬性。
在其他語言中,\ 字符在字符串文字中很常見,它開始一個“轉義序列”,但它很少用作運算符,並且使用它的語言之間幾乎沒有一致性。
這裏與“轉義”的想法有某種模糊的聯繫,因為在這兩種情況下,你都在逐步提升到更抽象的含義水平,但在大多數情況下,它被選擇是因為它是一個未使用的符號,通常是 易於打字並且看起來不錯。 該功能的演變提案實際上討論了幾種不同的語法,並解釋了為什麼選擇反斜槓。
$ 前綴意味着您正在訪問相關屬性的屬性包裝器提供的特殊功能。在這裏,該屬性是 FileDocumentConfiguration.document,[根據文檔](https://developer.apple.com/documentation/swiftui/filedocumentconfiguration/document)有一個 @Binding 屬性包裝器。 這意味着 $document 將公開一個到文檔的 Binding - 一個可用於訪問和修改該文檔屬性的對象,而無需關心它實際存儲的位置。
我們將此 $ 變量稱為“投影值”而不是“綁定值”或其他任何名稱,因為 $ 語法是通用語言功能,因此如果您使用 @Binding 以外的其他內容,$ 屬性可能不會創建綁定;可能被賦予一些其他功能。
選擇這兩種語法並不是因為它們會立即熟悉,而是因為我們確定沒有一種語法可以立即熟悉,最好選擇開發人員需要學習但一旦學習後會發現易於使用的語法。
2) 討論NSLock.Lock 加 Await 加 NSLock.Unlock 導致主線程凍結
提問:
以下代碼模擬了當外部庫的作者引入鎖時的情況,這可能包含等待調用。
有什麼辦法可以防止這種情況
noasync 註釋不是解決方案,因為:
1)如果函數包裝在另一個沒有 noasync 註釋的函數中,它不起作用;
2)第三方庫的作者可能會忘記添加這樣的註釋。
let lock = NSLock()
func thirdPartyLibLock() {
print("- do sum work and lock")
lock.lock()
/*
I also tried to replace it with:
await withCheckedContinuation({ c in
lock.lock()
c.resume()
})
*/
}
func thirdPartyLibUnlock() {
print("- do sum work and unlock")
lock.unlock()
/*
I also tried to replace it with:
await withCheckedContinuation({ c in
lock.unlock()
c.resume()
})
*/
}
func example() {
/*
Console:
- start 4
- do sum work and lock
- start 1
- do sum work and lock
And that's all. We have suspended main thread.
Numbers 4 and 1 could differ between app launches, it's ok.
*/
for i in 0...1000 {
Task {
print("- start \(i)")
thirdPartyLibLock()
try await Task.sleep(for: .seconds(1))
thirdPartyLibUnlock()
print("- end \(i)")
}
}
// Won't be executed.
DispatchQueue.main.asyncAfter(deadline: .now() + 3, execute: {
print("- ping")
})
}
回答:
由於多種原因,鎖定+解鎖 API 對在設計上是不安全的,這就是其中之一。 更好的設計是使用一個函數來獲取鎖,調用回調,然後在回調返回後釋放鎖。
(理想情況下,該函數還可以提供對受鎖保護的資源的回調訪問,否則將無法訪問。)只需使整個過程同步,就可以非常巧妙地表達在鎖定和解鎖之間不掛起的要求。
3) 討論所需的 Swift 語言功能可以提升 C++ 互操作性支持的狀態
內容:
Swift 5.9 可以在 Swift 中導入和使用多種 C++ 類型。 但是,並非所有類型類別都受支持。這篇文章列出了一組所需的 Swift 語言功能,這些功能使我們能夠支持 Swift 中的大多數 C++ 類型:
對不可複製類型的泛型支持。 雖然 Swift 5.9 添加了對不可複製結構和枚舉的支持,但這些類型仍然不允許用作泛型類型參數。
這是阻止我們在 Swift 中完全完成對僅移動 C++ 類型的支持的一個關鍵問題,因為我們需要形成像 UnsafePointer<MoveOnlyCxxType> 這樣沒有語言限制的類型。
添加到上面的一點,像 UnsafePointer 和 UnsafeMutablePointer 這樣的類型應該提供對借用和可變借用不可複製指針對象的支持。
不可移動/不可逃避的 Swift 類型類別。 不可轉義和不可複製的 Swift 類型將允許我們在 Swift 中導入和建模不可複製和不可移動的 C++ 類型。
此外,以下語言功能將有助於改善 Swift 中對 C++ 類型執行的一些常見操作的人體工程學:
能夠在 Swift 序列上執行借用 for 循環,這確實需要經過 IteratorProtocol,但可以使用索引迭代。 這將使我們能夠自動在 std::map 等非隨機訪問集合上使用 for 循環。
回答:
這些聽起來與我們計劃完善不可複製類型支持的項目一致,這很好。 在此列表中包含內部導入以及支持導入 C 和 C++ 類型而不間接公開其 ABI 是否也有意義,以便允許包在內部使用 C++ 互操作而不要求依賴項瞭解它?
4) 討論將協議添加到同名模塊
提問:
我有一個名為 HTML 的模塊,其中包含同名的類型 HTML。 它的樹看起來像這樣:
- HTML(模塊)
- HTML(結構)
- HTML.屬性(枚舉)
- HTML.ContainerElement(枚舉)
- HTML.VoidElement(枚舉)
到目前為止,一切都很好。 不可能限定對該模塊中的聲明的引用,因為它是同名的,但這沒關係,因為 HTML 類型本身在功能上是命名空間限定符。
現在想向這個模塊添加一個協議,稱之為 HTMLOutputStreamable。但是不能向未命名為 HTML 的 HTML 模塊添加頂級類型,因為該模塊是同名的,並且無法使用 HTML.HTMLOutputStreamable 來限定對此協議的引用。
該如何解決這個問題?
回答:
我發現的唯一方法就是使用不同的名稱。
從這個角度來看,Swift 仍然缺少完整的命名空間功能。 可以是模塊級命名空間,但更完整和可靠。 我更喜歡像 C++ 那樣的命名空間,或者像 Rust 那樣的顯式模塊定義,但這似乎不是 Swift 進化願景的一部分。
5) 討論嵌套函數和 @ViewBuilder:奇怪的編譯器錯誤
以下代碼給出了一個奇怪的編譯器錯誤,該錯誤似乎不相關:
struct ContentView: View {
var body: some View {
func world() -> String {
"world"
}
Text("Hello, \(world())!")
}
}
錯誤信息是:
包含聲明的閉包不能與結果生成器 “ViewBuilder” 一起使用
有趣的是,如果我在 world() 中添加 return (即 return "world"),編譯器會在其他地方顯示錯誤並添加警告,兩者也不是很相關。 這看起來更像是一個編譯器錯誤。
有什麼想法嗎?
回答:
從歷史上看,結果構建器對其內部運行的語法有一些限制。 其中許多限制在 SE-0373:解除結果構建器中變量的所有限制中被刪除,但如果仍然存在一些限制,我不會感到驚訝。
推薦博文
輕量化的 iOS 動畫框架實現
摘要: 在這篇博客中,介紹了日常開發中對視圖進行動畫處理的常見問題,並提供了一種解決方案。文章首先展示了普通的動畫代碼,並指出了其回調函數回溯的問題。接着介紹了一些流行的動畫庫,如 Spring , Hero 和 TweenKit ,但它們都存在一些限制。為了解決這些問題,引入了一種簡潔、易於使用和維護的動畫執行方式。該方案基於 Animator 和 Animation 的封裝實現,其中 Animator 定義了動畫執行器的基本協議,並封裝了幾種不同類型的動畫執行器。 Animation 定義了動畫執行的參數,併為不同的 Animato r制定了不同的協議。此外,文章還介紹了類型擦除的概念,以解決參數類型不一致的問題。具體實現方面,通過擴展UIView添加了串行和並行動畫的方法。最後,總結了該方案的優點和可能的改進點。
使用 Swift Package 插件將自定義字體加載到您的應用程序中
摘要: 本文介紹瞭如何使用 Swift Package 插件將自定義字體加載到應用程序中。通過創建一個 Swift Package 來包含共享的字體文件和字體加載代碼,可以加快新應用的發佈速度,減少代碼重複,並提供一個統一更新字體文件的地方。結合 Swift Package 的可重用性和 Swift Package 插件的強大功能,甚至可以在構建時從字體文件自動生成所有必要的代碼。本文使用 SwiftGen 來演示如何實現這一點。首先創建一個名為 "Fonts" 的 Swift Package ,並在其中添加自定義字體資源。然後添加 SwiftGen 插件來生成加載字體所需的代碼。最後,可以使用生成的代碼來在 SwiftUI 和 UIKit 中使用自定義字體。
掌握 Swift Foundation Formatter API 。自定義格式樣式
摘要: 本篇博客介紹瞭如何使用 Swift Foundation Formatter API 中的自定義格式樣式。作者分享了自己在每個項目中都使用該 API 並構建自定義格式化邏輯的經驗。博客中詳細講解了 FormatStyle 協議以及如何創建符合該協議的自定義格式樣式。通過示例,展示瞭如何創建短數字格式樣式和粗體數字格式樣式,並説明了如何在自定義類型中重用這些格式樣式。最後,作者還提供了一種封裝格式化邏輯的方法。
話題討論
最新薪酬排行出爐,廣州平均月薪 10883 元,北京平均月薪 13438 元,你的月薪處於什麼水平?
- 鉅富
- 豪
- 小富
- 小康
- 窮
歡迎在文末留言參與討論。
關於我們
Swift社區是由 Swift 愛好者共同維護的公益組織,我們在國內以微信公眾號的運營為主,我們會分享以 Swift實戰、SwiftUl、Swift基礎為核心的技術內容,也整理收集優秀的學習資料。
特別感謝 Swift社區 編輯部的每一位編輯,感謝大家的辛苦付出,為 Swift社區 提供優質內容,為 Swift 語言的發展貢獻自己的力量。