線上收聽與節目筆記
簡短介紹一個超實用的 Swift 冷門技巧。寫 SwiftUI 很有機會用到。
👋開場 & 節目回顧
再補充一下 weak self Discord 的玩法。請按我加入(每集更新連結)。
⭐️@dynamicMemberLookup
- 常用情境:model 需要被拓展,但不能或不想用繼承的方式
struct FooModel {
var string: String
}
@dynamicMemberLookup
struct FooWrapper {
var wrapped: FooModel
subscript<Value>(dynamicMember keyPath: KeyPath<FooModel, Value>) -> Value {
wrapped[keyPath: keyPath]
}
}
// 使用:可直接取用 wrapped 裡的 property
let string = FooWrapper().string
- 或者做成 generic 的版本
@dynamicMemberLookup
struct AnyWrapper<T> {
var wrapped: T
subscript<Value>(dynamicMember keyPath: KeyPath<T, Value>) -> Value {
wrapped[keyPath: keyPath]
}
}
- 要支援寫入則需要用到
WritableKeyPath
subscript<Value>(dynamicMember keyPath: WritableKeyPath<T, Value>) -> Value {
get {
wrapped[keyPath: keyPath]
}
set {
wrapped[keyPath: keyPath] = newValue
}
}
- 13 提到的 generic id wrapper,看請況可用在 SwiftUI 的 ForEach 使用
struct IdentifiableWrapper<T>: Identifiable {
let id = UUID()
var wrapped: T
init(_ wrapped: T) {
self.wrapped = wrapped
}
}
struct Model {
var title: String
}
// 使用方式:再適當的時機用 wrapper 包起來
// 請不要在 SwiftUI 裡面做 .map,因為 id 要盡可能保持一致
let models = [Model(title: "標題")...].map(IdentifiableWrapper.init)
// 再送至 SwiftUI 的 ForEach 使用
ForEach(models) {
Text($0.wrapped.title)
}
- Generic id wrapper 的
@dynamicMemberLookup
的版本
@dynamicMemberLookup
struct IdentifiableWrapper<T>: Identifiable {
let id = UUID()
var wrappedValue: T
init(_ wrappedValue: T) {
self.wrappedValue = wrappedValue
}
subscript<Value>(dynamicMember keyPath: KeyPath<T, Value>) -> Value {
wrappedValue[keyPath: keyPath]
}
}
// 這段同上
struct Model {
var title: String
}
// 這段同上
let models = [Model(title: "標題")...].map(IdentifiableWrapper.init)
// 再送至 SwiftUI 的 ForEach 使用,不用再寫 .wrapped 便可使用
ForEach(models) {
Text($0.title)
}
- 嵌套的
@dynamicMemberLookup
是可行的 - property name 相同的話,會取上層的
💸勸敗話題
- 預告:Swift Playgrounds 4 還來不及深入研究,有機會再聊
- 13:腦航員2
- Pofat:地獄之刃
- 兩款都在 XGP 裡,充分發揮低成本試錯的優點
- 擁有選擇權
👋收尾 & 預告
- 預告:喬喬回歸
更多 weak self
- 聽眾信箱 [email protected]
- 官網 https://weakself.dev
- 主持人 一三、波肥
- Twitter @weak_self
- 提問箱
- 贊助 weak self