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

【SwiftUI】基本的な Property Wrapper の使い方

Last updated at Posted at 2024-12-26

SwiftUIでよく利用するアノテーション(Property Wrapper)を簡単にまとめました

テーマ

  • 各PropertyWrapperの関係が ひと目でわかる
  • 1つ1つについて簡潔にまとまっている
  • チートシートとしてアプリ作成時に使える

経緯

SwiftUIアプリ開発をする中でよく出てきたのが Property Wrapper でした
しかし、初学者には それぞれどうやって使うのかが分かりにくかったため、チームメンバーと一緒に勉強し、その内容をまとめることにしました
この投稿では、SwiftUIを学ぶときに、最初に欲しかった SwiftUI Property Wrapperについての説明をします

Property Wrapper: プロパティ ラッパー

まず Property Wrapper って何?
プロパティに付加的な機能を与えるためのSwiftUIの言語機能です
監視対象のデータが更新されるとViewを更新してくれます

この記事で解説する各 Property Wrapper の関係を図で表したものが以下になります
SwiftUI_PropertyWrapper関係図.png

State

値の更新に応じてViewが自動更新されるようになります
プリミティブ型プロパティ(IntやStringなど)にのみ付加できます

struct ItemResisterView: View {
    @State var newItem: String = "" // @Stateを付加
    var body: some View {
        VStack {
            TextField("新規アイテム名", text: $newItem) // 入力による操作
            Text(newItem) // 入力したものがリアルタイムで画面に表示される

Binding

親Viewが持っている@Stateプロパティと状態を共有し、双方向で更新ができるようになります

  • 親View側
    • 子Viewを呼び出す際に@Stateプロパティを渡します
    • $を付けることで子Viewの持つ@Bindingプロパティに紐づきます
  • 子View側
    • @Bindingプロパティを用意します
    • 親View側のプロパティを参照するので 初期値は不要です
// 親View
struct TabBarView: View {
    @State var listName: String = "買うものリスト" // @Stateプロパティ(対象)
    var body: some View {
        ShoppingListView(listName: $listName) // 子Viewの呼び出し(@Stateプロパティを乗せる)
// 子View
struct ShoppingListView: View {
    @Binding var listName: String // @Bindingを付加(親ViewのlistNameと状態を共有する)
    var body: some View {
        VStack {
            TextField("リスト名", text: $listName) // 入力内容が親Viewにも反映される

Environment

環境値を利用することで、親Viewから孫Viewへ(経由される子Viewを意識せず)状態の共有ができるようになります

  • 読み取りの形式
    • @Environment(\.keyPath)
  • 書き込みの形式
    • .environment(Environment用のKeyPath, 書き込む値)
// CoreDataで必要なNSManagedObjectContextの状態を環境値に保存して利用する場合
@main // 起動時
struct ZeroApp: App {
    let persistenceController = PersistenceController.shared
    var body: some Scene {
        // 書き込み
        .environment(\.managedObjectContext, persistenceController.container.viewContext)

// 値を利用したいView
struct ShoppingListView: View {
    // 読み取り
    @Environment(\.managedObjectContext) var viewContext

ObservedObject

オブジェクト型プロパティの変更を監視し、値の更新に応じてViewが自動更新されるようになります

  • :ObservableObject
    • 監視対象のクラスはObservableObjectプロトコルに準拠する必要があります
  • @Published
    • 監視対象のクラスの変更を検知するプロパティに付加します
  • @StateObject
    • 監視対象のインスタンスを保持するView側で、インスタンス定義時に付加します
    • ライフサイクルは「自身のViewが表示されてから非表示になるまで」です
    • データの発生源は自身のViewであるため 監視対象のオブジェクトをインスタンス化して自身で保持します
  • @ObservedObject
    • 親Viewから参照している監視対象のインスタンスに対して付加します
    • ライフサイクルは「親ビューが表示されてから非表示になるまで」です
    • 親Viewから渡されるデータを参照する際に用いるため 子View自身でインスタンス化して保持するのはNGです
    • 親Viewと同じ監視対象のオブジェクトを参照するため 状態は共有されます
// データオブジェクトの準備
class User: ObservableObject { // ObservedObjectに準拠したクラスを用意する
    @Published var name = "" // 監視対象のプロパティに@Publishedを付加する
}
// 親View
struct TabBarView: View {
    @StateObject var user = User() // 状態オブジェクトとしてインスタンス化する
    var body: some View {
        Text(user.name)
        ShoppingListView(user: user) // そのインスタンスを監視対象オブジェクトとして子ビューに渡す
// 子View
struct ShoppingListView: View {
    @ObservedObject var user: User // @ObservedObject属性を付加(親Viewのuserを参照する)
    var body: some View {
        TextField("名前", text: $user.name) // 入力による操作
        Text("\(user.name)さんのリストです") // 入力したものがリアルタイムで画面に表示される

FetchRequest

CoreDataに保存しているデータベースからソート条件を指定してデータを取得することができます

例えば、CoreDataに以下のようなEntityが存在する場合
zeroEntity例.png

@FetchRequestを利用してデータを取得する実装は以下となります

  • sortDescriptors: 取得した結果のソート順を定義します
  • predicate: 取得された結果をフィルタリングします
  • animation: 変更アニメーションを指定します
@FetchRequest (
    // nameの昇順でソートする
    sortDescriptors: [NSSortDescriptor(keyPath: \ZeroEntity.name, ascending: true)],
    // stateがtrueの場合に絞る
    predicate: NSPredicate(format: "state == \(true)") ,
    animation: .default
)
private var shoppingList: FetchedResults<ZeroEntity> // ソートされたデータが入る

まとめ

各 Property Wrapper の使い時をざっくりまとめると以下になります

Property Wrapper いつ使うか
State プリミティブ型の値の変更をViewに自動反映させたいとき
Binding 親Viewと値を共有したいとき
Environment 親Viewから孫Viewへ値を反映させたいとき
ObservedObject/StateObject/Published オブジェクト型インスタンスの変更を監視したいとき
FetchRequest CoreDataからデータを取得したいとき
2
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
2
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?