3
2

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】データ管理の基本(Property Wrapper)

Last updated at Posted at 2024-03-26

はじめに

@StateObject@ObservedObject の注意すべき点を知ったので、この機会に他の主要な Property Wrapper も含めて備忘録としてまとめたいと思います。

■ 値型のデータ

@State

  • その View にデータを保持
  • 外から渡されるデータではないので private が良さそう

@Binding

  • その View で、親 View に保持されているデータを更新する場合に使う
struct SuperView: View {
    @State private var value = 0
    var body: some View {
        SubView(value: self.$value)
    }
}

struct SubView: View {
    @Binding var value: Int
    var body: some View {
	    // SuperView の value を更新できる
    }
}

子 View がデータを更新しないなら、 @Binding をつけず普通に子 View に渡しておけば、親 View 側でデータが更新された際には、再描画によって更新後の値が渡された)子 View が新しく作り直されるのでオッケーということですね。

■ 参照型のデータ

以下、データの型は ObservableObject プロトコルへの準拠が必要。@Published を付与したデータが更新されるタイミングで SwiftUI へ通知される。

@StateObject

  • その View で参照型のデータオブジェクトを保持する場合に使用
  • 外から渡されるデータではないので private が良さそう
  • オブジェクトのインスタンスは View の表示中に一度しか作成されない
  • View インスタンスが破棄された後もオブジェクトは破棄されず SwiftUI から保持される(詳細は後述)

@ObservedObject

  • 親 View から渡されるオブジェクトを扱う場合に使用(するといい)
  • View インスタンスが破棄されるとオブジェクトも破棄される(詳細は後述)

@StateObject@ObservedObjectの注意すべき違い

struct MainView: View {
	@State private var isHoge: Bool = false
	var body: some View {
		SubView() // isHoge が更新されると再描画される
	}
}

このとき、 isHoge が更新されたときに SubView() が再描画されることによって、

@ObservedObject の場合は、 hogeDataSource はリセットされる

struct SubView: View {
    // @ObservedObject で保持しているデータの場合
    @ObservedObject private var hogeDataSource = HogeDataSource()
    var body: some View {
        // ...
    }
}

@StateObject の場合は、 hogeDataSource はリセットされない

struct SubView: View {
    // @StateObject で保持しているデータの場合
    @StateObject private var hogeDataSource = HogeDataSource()
    var body: some View {
        // ...
    }
}

SubViewインスタンス自体は一度破棄されているけど、SwiftUI の機能によってオブジェクトが保持されている(先祖の View の再描画によるインスタンスの破棄からオブジェクトだけは保管して守られ、作り直されたインスタンスに付け直す)ということでしょうか。こちらはなんとなく直感に反する感じがしました… 覚えておきます。

@EnvironmentObject

  • これで定義したオブジェクトは、すべての子孫の View からアクセスできる
struct MainView: View {
		@StateObject private var hogeDataSource = HogeDataSource()
    var body: some View {
		    // .environmentObject で渡す
        ParentView().environmentObject(isHoge)
    }
}

// 親子間で hogeDataSource の受け渡しをしていかなくても
struct ParentView: View {
    var body: some View {
        ChildView()
    }
}

struct ChildView: View {
    var body: some View {
        GrandChildView()
    }
}

// 子孫のどこからでも hogeDataSource にアクセスできる
struct GrandChildView: View {
		// @EnvironmentObject でアクセスする
    @EnvironmentObject var hogeDataSource: HogeDataSource
    var body: some View {
				// ...
    }
}

データを保持している View から深い階層の View からアクセスさせる必要がある場合、1階層ずつ @ObservedObject を渡すと冗長なので、 @EnvironmentObject が有効ということですね。

■ 環境値

@Environment

  • View の環境値を読み取るために使う

  • @Environment(\.isPresented) var isPresented
    @Environment(\.dismiss) var dismiss
    

具体的にはスクリーンの情報、言語や地域、アプリの起動状態などについての値を読み取るときに使うものですね。こちらは 公式リファレンス を参照すると具体例が浮かんでわかりやすいと思います。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?