4
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[SwiftUI]データバインディングを理解する

Posted at

はじめに

SwiftUIで開発をしてると、データバインディングという言葉をよく聞くかもしれません。
データバインディングは、UIとデータの同期を自動で行う強力な機能です。

データバインディングを学ぶと他にも
「MVVM」「状態管理」「プロパティラッパー」等、様々なワードが関係してきます。

本記事は上記の説明だけでなく、
サンプルコードも紹介しますので参考になれば嬉しいです!

データバインディングとは?

データの状態を監視する仕組みを言います。

データとビューの間の双方向のデータフローを自動的に管理できたり、
SwiftUIではユーザ操作により画面の表示をリアルタイムで変更が可能になります!

データが変更される
→そのデータを参照しているUI要素も自動的に更新される
※逆に、UIから入力された値をデータに反映も可能!

MVVMとデータバインディングの関係

1. アーキテクチャを簡単にイメージしよう

アーキテクチャの種類にMVVMというのを聞いたことがありますでしょうか?
データバインディングではこのMVVMの理解も重要です。

2. MVVMとは?

SwiftUIでは、 MVVM(Model-View-ViewModel) がよく使われます。
MVVMは、以下の3つのコンポーネントに分かれます:

MVVMの詳細

・Model: データやビジネスロジックを管理 (DB等に管理されるデータ)
・View: ユーザーに表示するUIを管理(画面に表示される部分)
・ViewModel: Model と View を繋ぎ、Viewが直接Modelに依存しないようにする

3. データバインディングとの関係は?

「View」と「ViewModel」の間で、データバインディングが重要な役割を果たします。

「View」は、「ViewModel」内のデータとバインドされます。
そしてViewModelがデータを更新するとViewも自動的に更新されます。

Model-View-ViewModel.png

データバインディングには、状態管理が必要

データバインディングが効果的に機能するためには、状態管理が必要です。
SwiftUIでは、状態管理に関するプロパティがいくつか提供されています。

ここでプロパティラッパーという単語について触れます。

プロパティラッパーについて

プロパティに対し、なんらかの特定の動作や機能を提供するために
使える仕組みのことを言います。

いくつか例を挙げておきます。

具体的なプロパティラッパー

種類 説明
State ローカルの状態管理に使用されます。変更されるとviewが再描画されます。
Binding 親viewから受け取ったデータをバインドするために使用します。子viewがこのデータを変更でき、その変更は親viewに反映されます。
ObservedObject 外部のオブジェクトの状態を監視するために使用します。通常は、ViewModelとして利用します。
StateObject viewが自身で管理するViewModelなどのオブジェクトに使用され、ライフサイクルを管理します。
EnvironmentObject 複数view間で共有されるオブジェクトに使用します。グローバルな状態管理が必要な場合に便利です。

プロパティラッパーの導入と説明

SwiftUIにおけるプロパティラッパーは、データバインディングや状態管理を簡単にするための便利な機能です。上記で述べた各プロパティラッパーについて、さらに詳しく見ていきましょう。

State

Stateは、ビューがローカルに保持する状態を宣言します。
ローカルなデータが変更されるたびにビューが再描画されます。

state.swift
struct ContentView: View {
    // ローカルに「counter」を保持する
    @State private var counter = 0

    var body: some View {
        VStack {
            // 「counter」値を表示
            Text("Counter: \(counter)")

            // 「counter」値を1増やす
            Button("Increment") {
                counter += 1
            }
        }
    }
}

Binding

Bindingは、親viewが管理する状態を子viewに渡すときに使います。
親viewの状態と子viewの状態が同期されます。

binding.swift
struct ContentView: View {
    @State private var isOn = false

    var body: some View {
        ToggleView(isOn: $isOn)
    }
}

struct ToggleView: View {
    @Binding var isOn: Bool

    // 状態が変わると、データが同期される
    var body: some View {
        Toggle("Switch", isOn: $isOn)
    }
}

ObservedObject

クラスベースの外部オブジェクトの変更を監視します。
このオブジェクトは ObservableObject プロトコルを準拠しており、
状態が変更されたときにviewに通知されます。

observedObject.swift
class CounterModel: ObservableObject {
    @Published var count = 0
}

struct ContentView: View {
    @ObservedObject var model = CounterModel()

    var body: some View {
        VStack {
            Text("Count: \(model.count)")

            // 状態が変わると、viewに通知される
            Button("Increment") {
                model.count += 1
            }
        }
    }
}

サンプルコード

これまでの内容を踏まえて、
データバインディングを活用したシンプルなカウンターアプリの例を見てみましょう。

sample.swift
class CounterModel: ObservableObject {
    @Published var count = 0
}

struct ContentView: View {
    // CounterModelを管理
    @StateObject private var model = CounterModel()

    var body: some View {
        VStack {
            // 「model」の値が記載される
            Text("Count: \(model.count)")
                .font(.largeTitle)

            // 「model」の値を1増やす
            Button("Increment") {
                model.count += 1
            }
            .padding()
        }
    }
}

上記は「StateObject」を使って「CounterModel」を管理し、
カウントが増加するたびにviewが自動的に更新されます。

「StateObject」を宣言時に付与(wrap)しないと、
Cannot assign to property: 'self' is immutable
というエラーが発生します。

まとめ

SwiftUIのデータバインディングで、画面とデータをリアルタイムで同期できる仕組みを
理解いただけましたでしょうか?

MVVMやプロパティラッパーといった、
状態管理に関しても理解が深まると幸いです!

4
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?