Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

@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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

@ViewLoading プロパティラッパ get アクセス時にViewのロードを行ってくれる get アクセス時 → loadView() が実行される → viewDidLoad() が実行される → プロパティの値が返される このため、 viewDidLoad() でプロパティの値を設定すれば良い @ViewLoadingプロパティラッパの紹介と自前で実装する方法 10

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

利用例 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

Slide 13

Slide 13 text

利用例 var date: Date? { didSet { self.dateLabel.text = dateString } } date の didSet の中で dateLabel にアクセスしている この時点で dateLabel の値が設定されている必要がある @ViewLoadingプロパティラッパの紹介と自前で実装する方法 13

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

@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

Slide 22

Slide 22 text

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