Upgrade to Pro — share decks privately, control downloads, hide ads and more …

SwiftのKeyPathを使いこなす

Ryu
May 15, 2023

 SwiftのKeyPathを使いこなす

Ryu

May 15, 2023
Tweet

More Decks by Ryu

Other Decks in Technology

Transcript

  1. AttributedStringを便利に • AttributedStringとは、部分的に⽂字を⼤きくしたり⾊を変えたりできるもの • NSAttributedStringをより今⾵に便利にしたもの • UIKit, SwiftUIどちらにも対応 • iOS15+

    このようにすることで”⼤”のみ⼤きくできるが、var body: some View などの@ViewBuilder内に書く とエラーがでてしまう。 let text = "⼤⼩" let attributedString = AttributedString(text) if let range = attributedString.range(of: "⼤") { attributedString[range].font = .boldSystemFont(ofSize: 30) } KeyPathを⽤いてStringのメソッドで使えるように
  2. AttributedStringを便利に extension String { func attribute<Value>( range: String, keyPath: WritableKeyPath<AttributedSubstring,

    Value>, value: Value ) -> AttributedString { var attributedString = AttributedString(self) if let range = attributedString.range(of: range) { attributedString[range][keyPath: keyPath] = value } return attributedString } } 引数にAttributedSubstringへのKeyPathを持つことで、AttriutedSubstringのプロパティを指定できる。 valueの型はKeyPathで指定したpropertyの型に応じて変化する。
  3. AttributedStringを便利に • After let text = "⼤⼩" let attributedString =

    AttributedString(text) if let range = attributedString.range(of: "⼤") { attributedString[range].font = .boldSystemFont(ofSize: 30) } • before ViewBuilder内にかけるようになった
  4. AttributedStringを便利に extension AttributedString { func attribute<Value>( range: String, keyPath: WritableKeyPath<AttributedSubstring,

    Value>, value: Value ) -> AttributedString { var copy = self if let range = copy.range(of: range) { copy[range][keyPath: keyPath] = value } return copy } } AttributedStringは構造体なので、var copy = self でselfをコピーせずにselfを使ってしまう と、mutatingキーワードをつけなければいけなくなってしまい、メソッドチェーンができない。
  5. AttributedStringを便利に extension String { func attribute<Value>( ranges: [String], keyPath: WritableKeyPath<AttributedSubstring,

    Value>, value: Value ) -> AttributedString { ranges.reduce(AttributedString(self)) { attributedString, element in attributedString.attribute(range: element, keyPath: keyPath, value: value) } } }
  6. AttributedStringを便利に extension AttributedString { func attribute<Value>( ranges: [String], keyPath: WritableKeyPath<AttributedSubstring,

    Value>, value: Value ) -> AttributedString { ranges.reduce(self) { attributedString, element in attributedString.attribute(range: element, keyPath: keyPath, value: value) } } }
  7. KeyPath Member Lookup • Swift 5.1で追加された機能 • Dynamic Member Lookupをコンパイラレベルで安全にしたもの

    let animal = Animal() print(animal.cat, animal.dog, animal.elephant) // 出⼒: Optional("まる") Optional("ぽち") nil @dynamicMemberLookup struct Animal { private let animalNames = ["cat": "まる", "dog": "ぽち"] subscript(dynamicMember key: String) -> String? { animalNames[key] } } 以下はDynamic Member Lookupの例。存在しないプロパティにアクセスすることができる。
  8. KeyPath Member Lookup @dynamicMemberLookup struct Animal<T> { private let animal:

    T init(animal: T) { self.animal = animal } subscript<U>(dynamicMember keyPath: KeyPath<T, U>) -> U { return animal[keyPath: keyPath] } } 以下はKeyPath Member Lookupの例
  9. KeyPath Member Lookup KeyPath Member Lookupは存在しないプロパティにはアクセスできない。 Animalがあたかもnameプロパティを持っているかのような挙動をする。 struct Cat {

    let name: String } let cat = Cat(name: "まる") let animal = Animal(animal: cat) print(animal.name) // 出⼒: まる また、Dynamic Member Lookupと違いXcode上でサジェストが出るので便利 ⾃分はまだ使い道が思いつかないが、TCAではこの技術が導⼊されていて、これがないとTCAが成り⽴たな いような強⼒な機能 (時間がないので、TCAでの導⼊事例はスキップ)