5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

environmentでプロトコルを介してインスタンスを注入する方法(Swift)

Last updated at Posted at 2025-03-02

はじめに

SwiftUIにおいて、インスタンスを伝播させるためには environment() モディファイアを使います。
ただ受け取り側がその具象型を知らない場合、そのままでは受け取れません。

本記事では具象型を知らなくても受け取れるよう、プロトコルを介してインスタンスを注入する方法を紹介します。

環境

  • OS: macOS Sonoma 14.5
  • Xcode: 16.2 (16C5032a)
  • Swift: 6.0.3

注意

iOS 17.0+からはObservationフレームワークを使えるので、Combineフレームワークを使うときと書き方が異なります。
本記事ではiOS 16以前をサポートしているプロジェクトでも使えるよう、両方の書き方を併記します。

environment() でプロトコルを介してインスタンスを注入する

プロトコルの定義

注入の受け口となるプロトコルを定義します。

Foo.swift
import SwiftUI

// Use Observation (iOS 17.0+)
@MainActor
protocol Foo: AnyObject, Observable, Sendable {
    func bar()
}

// Use Combine (iOS 13.0+)
@MainActor
protocol Foo: ObservableObject, Sendable {
    func bar()
}

公式ドキュメントに記載されている通り、Observationを使うときは AnyObjectObservable 、Combineを使うときは ObservableObject に準拠する必要があります。

Sendable への準拠は必須ではありませんが、準拠する場合が多いと思うので記述しています。

プロトコルに準拠したクラスの定義

先ほど作成したプロトコルに準拠したクラスを定義します。

DefaultFoo.swift
// Use Observation (iOS 17.0+)
@Observable
final class DefaultFoo {}
extension DefaultFoo: Foo {
    bar() {
        print("Bar")
    }
}

// Use Combine (iOS 13.0+)
final class DefaultFoo {}
extension DefaultFoo: Foo {
    bar() {
        print("Bar")
    }
}

Observationを使うときは @Observable マクロを付ける必要があります。違いはこれだけです。

インスタンスの生成と注入

先ほど作成したクラスのインスタンスを生成して注入します。

RootScreen.swift
import SwiftUI

struct RootScreen: View {
    private let foo = DefaultFoo()

    var body: some View {
        FooScreen<DefaultFoo>()
            .environment(foo) // Use Observation (iOS 17.0+)
            .environmentObject(foo) // Use Combine (iOS 13.0+)
        }
    }
}

Observationでは environment() 、Combineでは environmentObject() を使ってインスタンスを注入します。
型パラメータで具象型を渡す必要があるのに注意です。

インスタンスの受け取り

注入されたインスタンスを受け取ります。

FooScreen.swift
import SwiftUI

struct FooScreen<F: Foo>: View {
    @Environment(F.self) private var foo // Use Observation (iOS 17.0+)
    @EnvironmentObject private var foo: F // Use Combine (iOS 13.0+)

    var body: some View {
        Text("Foo")
            .onAppear {
                foo.bar()
            }
    }

Observationでは @Environment 、Combineでは @EnvironmentObject を使ってインスタンスを取得します。

おわりに

これで environment() でプロトコルを介してインスタンスを注入できました。
依存性逆転の原則(DIP)の実現に有用なので、みなさんも使ってみてください :relaxed:

参考リンク

5
3
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
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?