SwiftUIのデータ管理 Single Source of Truth編

SwiftUIでアプリを開発していると@Stateや@Bindingの使い分けについて迷ったりしていませんか? SwiftUIではデータを管理するProperty Wrapperがたくさんあります。 @State、@Binding、@StateObject、@ObservedObjectなどなどです。 Property Wrapperそれぞれの特徴を理解できれば、SwiftUIのアプリ開発がはかどるでしょう。 今回はSwiftUIのデータ管理に欠かせないSingle Source of Truthという考え方について解説します。

SwiftUIのデータ管理 Single Source of Truth編

SwiftUIでアプリを開発していると@Stateや@Bindingの使い分けについて迷ったりしていませんか?
SwiftUIではデータを管理するProperty Wrapperがたくさんあります。
@State、@Binding、@StateObject、@ObservedObjectなどなどです。
Property Wrapperそれぞれの特徴を理解できれば、SwiftUIのアプリ開発がはかどるでしょう。

これから2つの記事に渡ってSwiftUIでデータ管理に使われるProperty Wrapperの特徴と使い分けについて解説します。

今回はSwiftUIのデータ管理に欠かせないSingle Source of Truthという考え方について解説します。

この記事は私が12月9日に発表した資料、
SwiftUIのデータ管理」を記事化したものです。

SwiftUIのデータ管理記事一覧

動作環境

この記事は以下の環境で動作を確認しています。

  • Xcode 12.2
  • iOS 14.2

Single Source of Truth

SwiftUIのProperty Wrapperを解説する前に、データ管理をするうえで重要な概念であるSingle Source of Truth(単一の信頼できる情報源) についてお話します。

一言でいうと管理するデータはひとつに絞った方がいいという考え方です。
マスターとなるデータを1つ決めて、コピーはつくらず、それだけを更新していき、他システムに反映していきます。

もともとは情報システムの理論の原則です。
wikiを読むと各システムでそれぞれDBもつと容易にデータ不整合が起こるからそれをどう解決するかから出発していることがわかります。
https://en.wikipedia.org/wiki/Single_source_of_truth

この考え方はSwiftUIにも適応できます。
Viewが参照するデータを1つに絞ることで、Viewの表示の不整合を防ぎます。
もしも参照するデータが複数ある場合は、開発者がデータの整合性を担保しなければいけません。
これは容易に不具合の温床になりえます。

まず例としてデータが複数ある場合の例、したがってSingle Source of Truthではない例をみてみましょう。

UIKitで作成した、スイッチボタンをタップするとラベルも変化するViewです。

----------2021-01-20-0.02.29

class ViewController: UIViewController {
    var isOn: Bool = false {
        didSet {
            label.text = "\(isOn ? "On" : "Off")になりました"
        }
    }
    @IBOutlet weak var label: UILabel!
    @IBAction func switchToggle(_ sender: UISwitch) {
        self.isOn = sender.isOn
    }
}

このコードにはデータの発生源が2つあるのがわかりますでしょうか?
自分で定義したisOnプロパティとUISwitchのisOnプロパティの2つですね。
なのでSingle Source of Truthではないわけです。
そうするとどういう問題が発生するでしょうか?
それは、同期する処理を自分で実装する必要があることです。
このコードではこのdidSetの部分です。

この同期する処理を忘れてたり、漏れているとView表示が不整合になり、不具合の原因になります。

同じ動きをSwiftUIで表現してみましょう。

struct ContentView: View {
    @State private var isOn = false
    var body: some View {
        VStack {
            Toggle(isOn: $isOn, label: {})
            Text("\(isOn ? "On": "Off")になりました")
                .padding()
        }
    }
}

isOnというプロパティを@StateのProperty Wrapperで定義します。
すると、データの発生源はこのisOnだけになります。
Single Source of Truthは守られます。

スイッチボタンを表すToggleではisOnプロパティを$を前につけて渡しています。
$をつけると@Stateの値をBinding型に変換でき、値が更新されたときにSwiftUIが検知できるようになります。
ユーザーがスイッチのボタンをタップするとToggleisOnプロパティを更新します。
@Stateの値が更新されるとSwiftUIはそれを検知し、自動的にisOnプロパティを参照しているすべてのViewを更新します。
ここではTextが参照しているのでTextが更新されます。

UIKitと比べてSwiftUIでは開発者はデータの同期の処理を自分で書いていません。
SwiftUIが自動で行ってくれます。
これによってViewの不整合が少なくなり不具合の少ないコードを簡単に実装できるようになります。

宣伝

インプレスR&D社より、「1人でアプリを作る人を支えるSwiftUI開発レシピ」発売中です。
「SwiftUIでアプリを作る!」をコンセプトにSwiftUI自体の解説とそれを組み合わせた豊富なサンプルアプリでどんな風にアプリ実装すればいいかが理解できる本となっています。

今回解説したSingle Source of Truthの解説もあります。

iOS 14対応、Widgetの作成も一章まるまるハンズオンで解説しています。
SwiftUIを学びたい方、ぜひこちらのリンクをチェックしてください!




https://nextpublishing.jp/book/12491.html