$30 off During Our Annual Pro Sale. View Details »

@ViewLoadingプロパティラッパの紹介と自前で実装する方法 / @ViewLoading property wrapper implementation

@ViewLoadingプロパティラッパの紹介と自前で実装する方法 / @ViewLoading property wrapper implementation

YUMEMI.grow Mobile #6 - connpass
https://yumemi.connpass.com/event/290375/

YUMEMI.grow Mobile #6 - YouTube
https://www.youtube.com/watch?v=gNd-3YjaUwg

USAMI Kosuke

August 10, 2023
Tweet

More Decks by USAMI Kosuke

Other Decks in Programming

Transcript

  1. @ViewLoadingプロパティラッパの
    紹介と自前で実装する方法
    宇佐見公輔 / 株式会社ゆめみ
    2023-08-10
    @ViewLoadingプロパティラッパの紹介と自前で実装する方法 1

    View Slide

  2. 自己紹介
    宇佐見公輔(うさみこうすけ)
    株式会社ゆめみ / iOSテックリード
    大阪在住
    iOSDC Japan 2023
    パンフレット記事を執筆しました
    現地参加予定
    @ViewLoadingプロパティラッパの紹介と自前で実装する方法 2

    View Slide

  3. 今日の内容
    @ViewLoading
    プロパティラッパの紹介
    @ViewLoading
    を自前で実装する方法
    @ViewLoadingプロパティラッパの紹介と自前で実装する方法 3

    View Slide

  4. @ViewLoading
    プロパティラッパの紹介
    @ViewLoadingプロパティラッパの紹介と自前で実装する方法 4

    View Slide

  5. @ViewLoading
    とは
    iOS 16.4で追加された
    2023年3月リリース
    マイナーアップデートでのSDKの機能追加はめずらしい
    UIViewController
    のプロパティが扱いやすくなる
    @ViewLoadingプロパティラッパの紹介と自前で実装する方法 5

    View Slide

  6. UIViewController
    のプロパティ
    class DateViewController: UIViewController {
    private var dateLabel: UILabel! // Optional

    override func viewDidLoad() {
    super.viewDidLoad()
    let label = UILabel(frame: self.view.bounds)
    self.view.addSubview(label)
    self.dateLabel = label
    }
    }
    (このコード例はAppleのドキュメントから引用)
    @ViewLoadingプロパティラッパの紹介と自前で実装する方法 6

    View Slide

  7. UIViewController
    のプロパティ
    class DateViewController: UIViewController {
    private var dateLabel: UILabel! // Optional

    一度設定したら
    nil
    にならないのでOptional型は冗長に感じる
    しかし、非Optional型にする場合は初期化時に値の設定が必要
    viewDidLoad()
    時に値を設定するにはOptional型にする必要がある
    @ViewLoadingプロパティラッパの紹介と自前で実装する方法 7

    View Slide

  8. @ViewLoading
    プロパティラッパ
    class DateViewController: UIViewController {
    @ViewLoading private var dateLabel: UILabel //
    非Optional

    override func viewDidLoad() {
    super.viewDidLoad()
    let label = UILabel(frame: self.view.bounds)
    self.view.addSubview(label)
    self.dateLabel = label
    }
    }
    @ViewLoadingプロパティラッパの紹介と自前で実装する方法 8

    View Slide

  9. @ViewLoading
    プロパティラッパ
    class DateViewController: UIViewController {
    @ViewLoading private var dateLabel: UILabel //
    非Optional

    非Optional型で宣言できる
    内部的にはOptional型で値を持っている
    get
    アクセス時に内部の値をunwrapして返してくれる
    → アクセス時にまだ値が設定されていなかったら?
    @ViewLoadingプロパティラッパの紹介と自前で実装する方法 9

    View Slide

  10. @ViewLoading
    プロパティラッパ
    get
    アクセス時にViewのロードを行ってくれる
    get
    アクセス時

    loadView()
    が実行される

    viewDidLoad()
    が実行される
    → プロパティの値が返される
    このため、
    viewDidLoad()
    でプロパティの値を設定すれば良い
    @ViewLoadingプロパティラッパの紹介と自前で実装する方法 10

    View Slide

  11. 注意点
    viewDidLoad()
    で値を設定し忘れると実行時エラーになる
    nil
    のunwrapになってしまうため
    @ViewLoading
    プロパティラッパを指定したプロパティは、必ず
    viewDidLoad()
    で値を設定すること
    @ViewLoadingプロパティラッパの紹介と自前で実装する方法 11

    View Slide

  12. 利用例
    class DateViewController: UIViewController {
    var date: Date? {
    didSet {
    guard let date else { return }
    let dateString = self.dateFormatter.string(from: date)
    self.dateLabel.text = dateString
    }
    }
    }
    let dateViewController = DateViewController()
    dateViewController.date = Date()
    @ViewLoadingプロパティラッパの紹介と自前で実装する方法 12

    View Slide

  13. 利用例
    var date: Date? {
    didSet {
    self.dateLabel.text = dateString
    }
    }
    date

    didSet
    の中で
    dateLabel
    にアクセスしている
    この時点で
    dateLabel
    の値が設定されている必要がある
    @ViewLoadingプロパティラッパの紹介と自前で実装する方法 13

    View Slide

  14. 利用例
    let dateViewController = DateViewController()
    dateViewController.date = Date()
    date
    へのアクセス時点ではViewのロードが行われていない
    このため
    dateLabel
    について
    @ViewLoading
    を使っていない場合はエラーになる
    @ViewLoading
    を使っている場合は正常に動作する
    @ViewLoadingプロパティラッパの紹介と自前で実装する方法 14

    View Slide

  15. @ViewLoading
    を自前で実装する方法
    @ViewLoadingプロパティラッパの紹介と自前で実装する方法 15

    View Slide

  16. @ViewLoading
    を古いOSでも使いたい
    便利な機能だがiOS 16.4以降でしか使えない
    それ以前のバージョンで使うために自前で実装することを考える
    @ViewLoadingプロパティラッパの紹介と自前で実装する方法 16

    View Slide

  17. @ViewLoading
    を自前で実装する
    プロパティラッパはSwiftの機能であり自作できる
    しかし
    @ViewLoading
    の実現は通常の方法ではどうも難しい
    とくに
    UIViewController
    のメソッドを呼ぶ手段がわからない
    リファレンス実装を作っている人がいたので参考にする
    https://github.com/danielpunkass/MagicLoading
    @ViewLoadingプロパティラッパの紹介と自前で実装する方法 17

    View Slide

  18. プロパティラッパの通常の実装方法
    @propertyWrapper
    struct TwelveOrLess {
    private var number = 0
    var wrappedValue: Int {
    get { return number }
    set { number = min(newValue, 12) }
    }
    }
    wrappedValue
    を実装する
    このプロパティを含む構造体やクラスからは独立している
    @ViewLoadingプロパティラッパの紹介と自前で実装する方法 18

    View Slide

  19. プロパティラッパの第二の実装方法
    @propertyWrapper
    struct MagicViewLoading {
    static subscript(
    _enclosingInstance instance: T,
    wrapped wrappedKeyPath: ReferenceWritableKeyPath,
    storage storageKeyPath: ReferenceWritableKeyPath
    ) -> Value {
    ...
    }
    }
    @ViewLoadingプロパティラッパの紹介と自前で実装する方法 19

    View Slide

  20. プロパティラッパの第二の実装方法
    この実装方法なら、プロパティを含むクラスにアクセスできる
    この実装方法はSwiftのドキュメントには記載されていない
    プロポーザルには記載がある
    swift-evolution SE-0258 Property Wrappers
    Swift by Sundellで紹介されている
    Accessing a Swift property wrapper’s enclosing instance |
    Swift by Sundell
    @ViewLoadingプロパティラッパの紹介と自前で実装する方法 20

    View Slide

  21. @ViewLoading
    の実装
    static subscript(
    _enclosingInstance instance: T,
    wrapped wrappedKeyPath: ReferenceWritableKeyPath,
    storage storageKeyPath: ReferenceWritableKeyPath
    ) -> Value {
    get {
    instance.loadViewIfNeeded()
    return instance[keyPath: storageKeyPath].stored!
    }
    set {
    instance[keyPath: storageKeyPath].stored = newValue
    }
    }
    @ViewLoadingプロパティラッパの紹介と自前で実装する方法 21

    View Slide

  22. まとめ
    iOS 16.4で
    @ViewLoading
    という便利機能が追加された
    それ以前のバージョンでも同様の機能の実現が可能
    ただしSwiftのドキュメントに記載されていない方法なので注意
    @ViewLoadingプロパティラッパの紹介と自前で実装する方法 22

    View Slide