SwiftUIのサンプルで気づいた点をメモ

新しい技術については、必要に迫れられることがない限り、体系立てた知識の整理ができていません。あまり利用しない技術についてはすぐ忘れてしまうのです。(笑)
SwiftUIについても数年前から、調べては忘れてを繰り返しているのですが、再度、調べる際にメモがあると早く思い出せるようなので、今回もメモしておきます。

【PropertyWrapperの内部変数にアクセス】

PropertyWrapperを定義したプロパティに「_」をつけることで、PropertyWrapperの内部変数にアクセスする。
(ObjCのメンバ変数の表記に似ているので抵抗がありますが、間違いみたいではないのでメモしておきます。)

@Binding var isPresented: Bool

init(isPresented: Binding) {
    self._isPresented = isPresented
}

【$ と projectedValue】

「$」をつけると射影プロパティにアクセスできる。また、内部変数(_をつけた変数)から projectedValue というプロパティへアクセスするのと等価になる。

_isPresented.projectedValue

$isPresented

【SwiftUI Non-constant range 警告】

Non-constant range: argument must be an integer literal」という warning が出力される場合は、以下の様に対処できる。

ForEach(0 ..< myArray.count) { index in
    ...
}
	↓
ForEach(0 ..< myArray.count, id: \.self) { index in
    ...
}

参考:SwiftUI ForEachのNon-constant range警告

【KeyPath の利用】

List など作成する場合に表示データを以下の様な DataModel で表現した場合、初期化パラメータに KeyPath を利用した記述ができる。

struct DataModel: Identifiable {
    let id = UUID()
    let name: String
    var subItems: [DataModel]?
}

  init(
    data: Data,
    children: KeyPath<Data.Element, Data?>
	:
	省略
	:

// パラメータ指定時には以下の様に KeyPath を用いて記述できる
children: \.subItems

SwiftUI、UIKit の相互利用について

現行のアプリにSwiftUIを用いる場合、UIKitとの相互利用について考慮する必要があるため、調べてみました。

【UIKit → SwiftUI】

UIHostingController クラス

【SwiftUI → UIKit】

UIViewRepresentable プロトコル
UIViewControllerRepresentable プロトコル

SwiftUIでUIKitのUIButtonを利用するサンプルは以下の通り

import SwiftUI

struct CounterView: View {
    @State var text: String = "Button_title"
    
    var body: some View {
        VStack(alignment: .leading) {
            CustomButton(text: $text)
        }
    }
}

struct CustomButton: UIViewRepresentable {
    @Binding var text: String

    func makeUIView(context: Context) -> UIButton {
        let button = UIButton(frame: CGRect(x: 0, y: 0, width: 200, height: 44))
        button.setTitle(self.text, for: .normal)
        button.setTitleColor(.black, for: .normal)
        button.addTarget(context.coordinator, action: #selector(Coordinator.didTapCustomButton(sender:)), for: .touchUpInside)
        return button
    }

    // SwiftUI → UIkit
    // ビューの状態が更新されるたび呼び出される
    func updateUIView(_ uiView: UIButton, context: Context) {
        print("\(#function)")
    }

    static func dismantleUIView(_ uiView: UIButton, coordinator: ()) {
        print("\(#function)")
    }
    
    // UIKit → SwiftUI
    func makeCoordinator() -> Coordinator {
        return Coordinator(button: self)
    }

    class Coordinator {
        var button: CustomButton

        init(button: CustomButton) {
            self.button = button
        }

        @objc func didTapCustomButton(sender: UIButton) {
            print("\(#function)")
        }
    }
}

【参考】

一部の画面だけSwiftUIを使いたいとき
UIViewRepresentable を理解して SwiftUI の足りないところを UIKit で補う

SwiftUIの個人的なメモ

SwiftUIもそろそろ業務で利用されるかと思い、少しずつ情報を整理しています。
というわけで個人的なメモを以下に記します。

2022年秋には、iOS16が提供されることにより市場のアプリのサポートバージョンは、iOS14、15、16が一般的になると思います。結果、SwiftUIもそろそろ積極的に採用されるとの判断です。
実際調べてみると、SwiftUIはiOSバージョンにより挙動の差異があるようです。
この点は、以下のページで詳しく報告されています。

iOS バージョン による SwiftUI の機能差分・制限まとめ

SwiftUIを採用するということは、UIKitからの移行ということだと改めて認識しました。現行のままでは、移行しきれないUIデザインというのもあるのでしょうね。
UIKitのあのコントロールは、SwiftUIではどのコントロールに該当するのかというのは、以下のページが参考になりました。

[Swift] SwiftUIのチートシート

SwiftUIサンプルコードを見ていて、新しく見つけたProperty Wrapperは以下の通り。

@mainエントリポイント
@PublishedCombineフレームワークが提供。Publisherを生成する。
$を付加して、Publisherにアクセスできる。
@ObservedObjectSwiftUIの監視対象となり、参照しているViewが自動的に再描画される。
@State変数の変更が監視され、変更時にViewが再描画される。
アクセスは宣言されたView内のみとなり、変更時には$を付与する必要がある。
@StateObject再描画されても値は保持される。
@EnvironmentObjectアプリ内でデータを共有する。
@Environment環境値を取得する。
@FocusStateiOS15で導入。表示要素のフォーカス制御を行う。
SwiftUI Property Wrapper

リアクティブプログラミング手法では、UIとモデルのバインディングがどのように行うのか?ということが一つのテーマであったと思うが、SwiftUIはProperty Wrapperという形で提供している。その点も踏まえて、今後はSwiftUIと相性の良いアーキテクチャについても考えたい。

SwiftUI 個人メモ

SwiftUIの情報をまとめました.
正直キャッチアップはめんどくさいですが,切り捨てられない程度に時代について行きます.
ある程度時代の方向性が定まってから,最短ルートでついて行くつもりですが,SwiftUIに関する仕様の変更点は,まだまだあるのかもしれません.

– 概要の把握には以下の記事が参考になります.
SwiftUIの考察

– コードのサンプルには以下の記事が参考になります.
Apple Developer – SwiftUI Tutorials
Introducing SwiftUI
Fucking SwiftUI
カピ通信 SwiftUI

– SwiftUIサンプルコードを見ていると見慣れない some というキーワードが散見されますが,Swift 5.1 で導入される Opaque Result Type は以下の記事が参考になります.
Swift 5.1 に導入される Opaque Result Type とは何か

– 実際のアプリにSwiftUIを用いる際には,一部のビューから導入するというのが現実だと思われます.
一部の画面だけSwiftUIを使いたいとき

– Xcode11.4を触っていて見慣れない SceneDelegate.swift というのがあったので,調べます.
SwiftUIを触って分かったこと:①初期画面の設定方法

その他,見たことがなかった属性についてもまとめました.
@State
– SwiftUIのViewはstructのため,通常の値を更新することができない.@Stateを宣言することで,メモリの管理がSwiftUIフレームワークに委譲されて値を更新することができる.
– @Stateで宣言されたプロパティを子Viewに渡す時は、プロパティ名の頭に$をつける.プロパティの値そのものではなく、プロパティへの参照を渡すイメージ.
– @Stateは,値をコピーする.

@ObservedObject
– データクラスに対しては,@ObservedObjectを使用する.
– @ObsevedObjectは,View の外部からデータを取得する際に利用する.
– @ObsevedObject に@Published 属性を持つプロパティを用意する場合、そのプロパティに変化があればそのプロパティを利用する View が更新される.

@Binding
【SwiftUI】@Stateとか@Bindingて何

– @BindingをつけてやるとView間での双方向のデータ共有が可能となる.
– 子View側では、@Stateではなく@Bindingでプロパティを宣言する.
– 自らは保有せずに、親のプロパティを参照する.
– @Binding は実体を保持せず、参照してデータソースの値を変更する.
– 利用するデータソースを複数にせず、一つの正しいデータソースのみを参照し変更することで不具合を避けることができる.

@Environment
– @EnvironmentObjectを付与したプロパティは複数のViewで共通のインスタンスを参照する.(アプリ全体で共通のプロパティ)

@ViewBuilder
SwiftUIのViewBuilderについて調べてみる