0Pricing
Swift Academy · レッスン

ViewModelとViewのバインディング

SwiftUIとCombineでViewModelを接続します。

「ViewModelとViewのバインディング」はCoddyKit上の無料Swift Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはSwift Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Swift Academyコースには全4レッスンが含まれています。

ViewModelとViewの接続

ViewModelは、Viewがその変更に反応して初めて役立ちます。SwiftUIでは、この反応性をCombineベースのプロパティラッパーである @Published、@StateObject、@ObservedObject が支えています。

これらが自動的なバインディングを形成するため、ViewModelが変化するとViewが再描画されます。

ObservableObject

SwiftUIから監視できるようにするには、ViewModelが ObservableObject に準拠する必要があります。このプロトコルによって、変更の直前に通知を発行する objectWillChange publisherが提供されます。

final class CounterViewModel: ObservableObject {
    @Published var count: Int = 0

    func increment() {
        count += 1
    }
}

@Publishedプロパティ

プロパティに @Published を付けると、その値が設定されるたびに変更通知が自動的に発行されます。

すべての @Published プロパティはオブジェクトの objectWillChange publisherに通知を送り、Viewはいつ更新すべきかを正確に把握できます。

final class SearchViewModel: ObservableObject {
    @Published var query: String = ""
    @Published var results: [String] = []
}

@StateObject

@StateObject は、ViewがViewModelを作成して所有することを示します。SwiftUIはViewModelを一度だけインスタンス化し、そのViewが再描画されても保持します。

ViewModelを最初に作成する場所で @StateObject を使用します。

struct CounterView: View {
    @StateObject private var viewModel = CounterViewModel()

    var body: some View {
        Button("Count: \(viewModel.count)") {
            viewModel.increment()
        }
    }
}

@ObservedObject

@ObservedObject は、別の場所で作成され、渡されてきたViewModelに使用します。Viewはそれを監視しますが、ライフサイクルは所有しません。

親からViewModelを受け取る子ビューで使用します。

struct BadgeView: View {
    @ObservedObject var viewModel: CounterViewModel

    var body: some View {
        Text("Total: \(viewModel.count)")
    }
}

StateObjectとObservedObjectの違い

重要な違いは所有権です。

  • @StateObject — Viewが所有し、再描画後も存続します
  • @ObservedObject — 外部から渡され、Viewは所有しません

@StateObject を使うべき場所で @ObservedObject を使うと、親が更新されるたびにオブジェクトが再作成され、状態が失われることがあります。

ViewModelを子へ渡す

親で @StateObject を使ってViewModelを一度だけ作成し、その後、子では @ObservedObject を付けて渡されたViewModelを受け取ります。

struct ParentView: View {
    @StateObject private var viewModel = CounterViewModel()

    var body: some View {
        VStack {
            BadgeView(viewModel: viewModel)
            Button("Add") { viewModel.increment() }
        }
    }
}

双方向バインディング

テキストフィールドなどの入力には、双方向の Binding が必要です。SwiftUIでは、ラッパーに $ プレフィックスを付けることで、@Published プロパティからBindingを生成できます。

struct SearchView: View {
    @StateObject private var viewModel = SearchViewModel()

    var body: some View {
        TextField("Search", text: $viewModel.query)
    }
}

UI状態を制御する

Viewは、表示するすべてのものを公開されたプロパティから導出するべきです。読み込みインジケーター、エラーバナー、コンテンツはすべてViewModelの状態から決まります。

final class FeedViewModel: ObservableObject {
    enum State { case loading, loaded([String]), error(String) }
    @Published var state: State = .loading
}

状態に反応する

ViewはViewModelの状態に応じて分岐し、適切なUIを描画します。命令的な表示・非表示のコードはなく、宣言的な対応付けだけで表現します。

struct FeedView: View {
    @StateObject var viewModel = FeedViewModel()
    var body: some View {
        switch viewModel.state {
        case .loading: ProgressView()
        case .loaded(let items): List(items, id: \.self) { Text($0) }
        case .error(let message): Text(message)
        }
    }
}

更新をメインスレッドで行う

SwiftUIでは、公開された状態の変更をメインスレッドで行う必要があります。ViewModelが非同期処理の後に状態を更新する場合は、@MainActor を付けてこれを保証します。

@MainActor
final class ProfileViewModel: ObservableObject {
    @Published var name: String = ""

    func load() async {
        name = await fetchName()
    }
}

クイックチェック

バインディングに関する知識を確認しましょう。

まとめ

SwiftUIのバインディングを使って、ViewModelとViewを接続しました。

  • ObservableObject と @Published が変更通知を発行します
  • @StateObject はViewModelを作成して所有します
  • @ObservedObject は外部から渡されたViewModelを監視します
  • @MainActor は非同期更新をメインスレッド上で行います

リアクティブなバインディングを設定すると、Viewは常に最新のViewModelの状態を自動的に反映します。

よくある質問

「ViewModelとViewのバインディング」レッスンは無料ですか?

はい。「ViewModelとViewのバインディング」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Swift Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Swift Academyコースには全4レッスンが含まれています。

「ViewModelとViewのバインディング」で何を学びますか?

SwiftUIとCombineでViewModelを接続します。 ブラウザで直接実行するハンズオンコードでSwift Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Swift Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのSwift Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「ViewModelとViewのバインディング」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このSwift Academyレッスンでコードを書いて実行できますか?

はい。すべてのSwift Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. MVVMパターン
  2. ViewModelとViewのバインディング
  3. Coordinatorパターン
  4. ViewModelのテスト
← Swift Academyに戻る