Swiftの@Persisted属性を完全解説!その方法12選

Swiftの@Persistedを使ったコードのスクリーンショットSwift
この記事は約20分で読めます。

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

基本的な知識があればサンプルコードを活用して機能追加、目的を達成できるように作ってあります。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を凌駕する現役のプログラマチームによって監修されています。

サイト内のコードを共有する場合は、参照元として引用して下さいますと幸いです

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

Swiftの開発を行う中で、データの永続性を簡単に扱いたいと思うシチュエーションは多いでしょう。

そんな時、Swiftが提供している@Persisted属性は非常に便利なツールとして役立ちます。

この記事では、Swiftでのデータの永続性を扱う@Persistedを、その基本から応用、カスタマイズ方法まで、12のサンプルコードを交えて徹底的に解説します。

●@Persisted属性とは

Swiftの@Persisted属性は、データの永続性を容易に取り扱うためのプロパティラッパーとして導入されました。

この属性を使用することで、アプリのデータを安全かつ効率的に永続的に保存できます。

具体的には、データベースやUserDefaultsなどのストレージに、データを保存したり、読み込んだりする処理を大幅にシンプルにすることができます。

○@Persisted属性の基本

@Persisted属性を使用する際の基本的な構文は次のようになります。

@Persisted var name: String?

上記のコードでは、nameというString型の変数に@Persisted属性を付与しています。

このname変数に値を代入すると、その値は自動的に永続ストレージに保存されます。

また、アプリを再起動しても、この変数の値は保存された状態でアクセスできるのが特徴です。

この仕組みによって、アプリ内でのデータの保存や読み込みの手間が大幅に減少します。

従来の方法であれば、データを保存するためのコードや、データベースとの接続、エラーハンドリングなど、多くの処理を記述する必要がありましたが、@Persistedを使用することでその大部分が不要となります。

その結果として、次のような特徴が挙げられます。

  1. シンプルなコード:@Persisted属性を使用することで、データの保存や読み込みに関するコードが大幅にシンプルになります。
  2. 効率的なデータアクセス:従来の方法に比べて、データへのアクセスが高速になります。
  3. 安全性の向上:@Persisted属性は、データの保存や読み込みの際のエラーハンドリングを内部で行ってくれるため、データの破損や不整合のリスクが低くなります。

●@Persisted属性の使い方

Swiftを使用したアプリケーション開発において、データの永続性は非常に重要な要素となります。

ユーザーがアプリを使用する中での設定やデータを保存し、次回起動時にもその状態を維持することが求められることは少なくありません。

こうした要件を簡単に実現するためにSwiftには@Persistedという属性が導入されています。

ここでは、@Persisted属性をどのように使用するのか、その使い方の基本から始めて、具体的なサンプルコードを交えながら徹底的に解説していきます。

○サンプルコード1:基本的なデータの保存

このコードでは、最もシンプルな形で@Persistedを用いてString型のデータを保存するコードを表しています。

この例では、ユーザー名を保存し、アプリを再起動してもその値が維持されることを目指しています。

import SwiftUI
import RealmSwift

struct ContentView: View {
    // @Persistedでユーザー名を保存
    @Persisted var username: String = ""

    var body: some View {
        VStack {
            TextField("ユーザー名を入力してください", text: $username)
            Text("保存されたユーザー名: \(username)")
        }
        .padding()
    }
}

上記のコードでは、SwiftUIのTextFieldを使用してユーザー名を入力し、その入力された値が@Persisted属性を持つusername変数に保存されます。

そして、Textで保存されたユーザー名を表示します。

これにより、アプリを再起動しても以前入力されたユーザー名が表示されるようになります。

このように、@Persistedは非常に簡単にデータの永続性を実現できる強力なツールです。

○サンプルコード2:@Persistedを使ったオブジェクトの保存

次に、もう少し複雑なデータ構造、具体的にはオブジェクトを@Persistedを用いて保存する方法を解説します。

この例では、ユーザープロフィールというオブジェクトを保存し、それをアプリ内で使用します。

import SwiftUI
import RealmSwift

// ユーザープロフィールオブジェクト
class UserProfile: Object {
    @Persisted var name: String = ""
    @Persisted var age: Int = 0
}

struct ContentView: View {
    // @Persistedでユーザープロフィールを保存
    @Persisted var userProfile: UserProfile?

    var body: some View {
        VStack {
            TextField("名前を入力してください", text: $userProfile?.name ?? "")
            Stepper("年齢: \(userProfile?.age ?? 0)", value: $userProfile?.age ?? .constant(0))
            Text("保存された名前: \(userProfile?.name ?? "")")
            Text("保存された年齢: \(userProfile?.age ?? 0)")
        }
        .padding()
    }
}

このコードでは、UserProfileというオブジェクトを定義しています。

そして、@Persisted属性を用いてこのオブジェクトをuserProfileとして保存します。

アプリ内では、ユーザーが名前と年齢を入力できるようにTextFieldStepperを使用しています。

入力された値は、@Persistedを使用して保存されます。

○サンプルコード3:@Persistedの初期値設定

Swiftでデータの永続性を実現する際に、@Persisted属性は非常に便利です。

しかし、初めて使う方や使いこなせていない方も多いのではないでしょうか。

今回は@Persistedの初期値設定に関するサンプルコードを紹介します。

このコードでは@Persistedを使ってデータの永続性を持たせる変数を定義し、その初期値を設定する方法を表しています。

この例では、整数型の変数に初期値として10を設定しています。

import RealmSwift

class User: Object {
    @Persisted var age: Int = 10
}

上記のコードは、RealmSwiftのライブラリを使用して、ユーザーの年齢を表すageという変数を定義しています。

そして、このage変数には@Persisted属性を付与しており、初期値として10が設定されています。

これにより、Userクラスのインスタンスが新しく作成された際、ageの値は自動的に10に設定されます。

こちらのコードを実行すると、新しいユーザーオブジェクトが生成されるとそのユーザーの年齢は10として設定されます。

例えば、次のように新しいユーザーオブジェクトを作成した場合、年齢は10として初期化されるでしょう。

let newUser = User()
print(newUser.age) // 10

このように、Swiftで@Persisted属性を使用することで、簡単にデータの永続性を持たせることができ、初期値も簡単に設定することができます。

○サンプルコード4:複数の@Persistedを組み合わせる方法

データの永続性を持たせるために@Persistedを使う際、一つのクラス内で複数の@Persistedを組み合わせることがよくあります。

このコードでは、複数のデータを永続的に保持するための変数を定義し、それぞれの変数に@Persisted属性を付与しています。

この例では、ユーザーの名前と年齢、趣味を表す変数をそれぞれ定義しています。

import RealmSwift

class UserMultiple: Object {
    @Persisted var name: String = "John Doe"
    @Persisted var age: Int = 20
    @Persisted var hobbies: List<String> = .init()
}

上記のコードでは、nameagehobbiesという三つの変数が@Persisted属性を持っており、それぞれの変数の初期値も設定されています。

このように、一つのクラス内で複数の@Persistedを組み合わせることで、複数のデータを効率よく永続的に保持することができます。

実際に上記のコードを実行すると、新しいユーザーオブジェクトが生成された際、名前は”John Doe”、年齢は20、趣味は空のリストとして初期化されるでしょう。

例えば、次のようなコードを実行すれば、その結果を確認することができます。

let userMultiple = UserMultiple()
print(userMultiple.name) // John Doe
print(userMultiple.age) // 20
print(userMultiple.hobbies.isEmpty) // true

このように、@Persisted属性を使うことで、Swiftでのデータの永続性を効果的に実現することができます。

特に、一つのクラス内で複数のデータを永続的に保持する際には、このような組み合わせの方法が非常に有効です。

●@Persistedの応用例

Swiftでデータの永続性を扱う@Persistedは、基本的な使い方だけでなく、さまざまな応用例が存在します。

ここでは、@Persistedの応用例として、ユーザー設定の保存や配列との組み合わせについて、サンプルコードと共に詳しく解説していきます。

○サンプルコード5:@Persistedを使ったユーザー設定の保存

ユーザーの設定情報をアプリケーション内で永続的に保存する場面は頻繁にあります。

例えば、ダークモードの有効・無効、通知設定など、ユーザーがカスタマイズした設定を次回のアプリ起動時にも引き継ぎたい場合が考えられます。

このコードでは、@Persistedを使用してユーザーのダークモード設定を保存するコードを表しています。

この例では、Bool型の変数を@Persistedでラップして、ユーザーの選択に応じて値を保存しています。

import SwiftUI

struct SettingsView: View {
    @Persisted var isDarkModeEnabled: Bool = false

    var body: some View {
        Toggle("ダークモード", isOn: $isDarkModeEnabled)
    }
}

上記のコードでは、isDarkModeEnabledというBool型の変数を@Persistedでラップしています。

ユーザーがToggleをオンにすると、isDarkModeEnabledの値がtrueになり、アプリを終了しても再起動時にこの設定が反映されます。

アプリを再度起動すると、先ほどの設定が維持されていることが確認できます。

このように、@Persistedを利用することで手軽にデータの永続性を実現することができます。

○サンプルコード6:@Persistedと配列を組み合わせる

次に、@Persistedを配列と組み合わせてデータを保存する方法を見ていきましょう。

例として、ユーザーがお気に入り登録したアイテムのリストを保存する場合を考えます。

このコードでは、String型の配列を@Persistedでラップして、お気に入りアイテムのリストを保存するコードを表しています。

この例では、ユーザーが新しいアイテムを追加するたびに配列にそのアイテムを追加しています。

import SwiftUI

struct FavoritesView: View {
    @Persisted var favoriteItems: [String] = []

    var body: some View {
        List(favoriteItems, id: \.self) { item in
            Text(item)
        }
    }

    func addItem(_ item: String) {
        favoriteItems.append(item)
    }
}

このコードで、ユーザーがaddItem関数を呼び出して新しいアイテムを追加すると、favoriteItems配列にそのアイテムが保存されます。

そして、アプリを再起動してもお気に入り登録したアイテムのリストが維持されることが確認できます。

このように、@Persistedと配列を組み合わせることで、簡単にリストの永続性を実現することができます。

実際にアプリ開発を行う際には、このような方法で様々なデータを永続的に保存することが可能です。

○サンプルコード7:@PersistedとSwiftUIを組み合わせる

SwiftUIと@Persistedを組み合わせることで、ユーザーインターフェースの状態を永続的に保持することが可能となります。

この組み合わせは、アプリの設定画面やユーザーの好みに合わせたテーマの保存など、さまざまな場面での活用が考えられます。

具体的なサンプルコードを見てみましょう。

import SwiftUI
import RealmSwift

struct ContentView: View {
    @Persisted var userName: String = ""
    @State private var editingName: String = ""

    var body: some View {
        VStack {
            TextField("ユーザー名を入力してください", text: $editingName, onCommit: {
                userName = editingName
            })
            .textFieldStyle(.roundedBorder)

            Text("保存されたユーザー名: \(userName)")
        }
        .padding()
    }
}

このコードでは、TextFieldを使用してユーザー名を入力させ、入力が確定された際に@Persisted変数userNameに値を保存しています。

保存されたユーザー名は、画面下部のTextビューで表示されます。

SwiftUIのビュー更新メカニズムと、@Persistedが提供する自動的なデータ永続性の連携により、アプリを再起動しても以前に入力されたユーザー名が表示されるようになります。

この方法により、SwiftUIのビュー内で簡単にデータの永続性を持たせることが可能となります。

○サンプルコード8:@Persistedでのデータの暗号化

データの永続性を確保するだけでなく、セキュリティの観点からデータの暗号化も重要です。

@Persistedを使用する際に、データの暗号化を組み込む方法を見ていきましょう。

import RealmSwift

class EncryptedUser: Object {
    @Persisted(encrypted: true) var userName: String = ""
}

上記のコードは、RealmSwiftを使用して、@Persisted属性でデータの暗号化を実現しています。

この例では、encrypted: trueというオプションを指定することで、userNameというプロパティの内容が暗号化されて保存されます。

このように、@Persistedを使用する際に特定のオプションを指定することで、データの暗号化やその他の高度な設定を適用することができます。

●注意点と対処法

Swiftでデータの永続性を実現するための@Persisted属性は非常に便利ですが、使用する際に注意すべき点やその対処法があります。

ここでは、それらの注意点をピックアップし、それぞれの対処法をサンプルコードとともに解説していきます。

○サンプルコード9:@Persistedのデータ型制限

@Persistedは、すべてのデータ型をサポートしているわけではありません。

特定のデータ型での使用に制限があるため、その点を理解しておくことが重要です。

このコードでは、@Persistedを使用してString型とInt型のデータを保存する例を表しています。

この例では、String型とInt型を保存する際の方法を示しています。

import Foundation

class SampleData: ObservableObject {
    @Persisted var name: String = ""
    @Persisted var age: Int = 0
}

このコードの実行後、nameageの変数にデータを保存できます。

ただし、すべてのデータ型でこのように簡単に使用できるわけではなく、対応していないデータ型を使用する場合には、別の方法でデータを永続化する必要があります。

○サンプルコード10:@Persistedの更新時の注意点

@Persistedで保存されたデータを更新する際には、特定の条件下でデータが正しく保存されないことがあります。

そのため、更新操作を行う際の注意点と、それを回避するための方法を知っておくことが重要です。

このコードでは、@Persistedを使用して保存されたデータを更新する際の注意点を表しています。

この例では、データを更新する前後での変数の状態を確認する方法を表しています。

import Foundation

class UpdateData: ObservableObject {
    @Persisted var score: Int = 0

    func updateScore() {
        score += 10
        print("更新後のスコア:", score)
    }
}

let data = UpdateData()
data.updateScore()

このコードを実行すると、score変数の値が10増加し、「更新後のスコア: 10」という結果が表示されます。

しかし、アプリを終了し再度起動すると、scoreの値は再び0にリセットされてしまいます。

これは、@Persistedの内部処理において、一定のタイミングでのデータの保存が保証されていないためです。

このような現象を避けるためには、データの更新を行うたびに明示的にデータの保存処理を呼び出す必要があります。

●カスタマイズ方法

Swiftにおける@Persisted属性は非常に有用で、データの永続性を扱う際の魔法のような存在です。

しかし、標準の@Persisted属性だけでは、特定のニーズや要求を満たすのが難しい場面もあります。

そこで、カスタマイズ方法を学ぶことで、もっと柔軟に@Persisted属性を使用することができます。

○サンプルコード11:@Persistedのカスタムプロパティラッパー

このコードでは、@Persistedのカスタムプロパティラッパーを作成しています。

この例では、@Persistedのデフォルトの動作をオーバーライドして、特定の処理を追加する方法を表しています。

@propertyWrapper
struct CustomPersisted<T: Codable> {
    private var key: String
    private var defaultValue: T

    // UserDefaultsのインスタンス
    private let userDefaults = UserDefaults.standard

    init(wrappedValue: T, key: String) {
        self.defaultValue = wrappedValue
        self.key = key
    }

    var wrappedValue: T {
        get {
            // キーからデータを取得し、デシリアライズする
            if let data = userDefaults.data(forKey: key),
               let value = try? JSONDecoder().decode(T.self, from: data) {
                return value
            }
            return defaultValue
        }
        set {
            // データをシリアライズし、キーで保存する
            if let data = try? JSONEncoder().encode(newValue) {
                userDefaults.set(data, forKey: key)
            }
        }
    }
}

上記のコードでは、@CustomPersistedという新しいプロパティラッパーを定義しています。

このラッパーは、内部でUserDefaultsを使用してデータを永続化しています。

また、Codableプロトコルを採用することで、様々なデータ型をサポートしています。

このカスタムプロパティラッパーを使用することで、デフォルトの@Persistedよりも細かいカスタマイズや追加の処理を行うことができます。

このコードを適用すると、次のような実装が可能です。

class SampleModel {
    @CustomPersisted(key: "sampleString") var sampleString: String = "default"
}

このコードを実行すると、SampleModelクラスのsampleStringプロパティは、デフォルト値として”default”を持ち、その値が変更されるとUserDefaultsに保存されます。

○サンプルコード12:@Persistedと他のプロパティラッパーの組み合わせ

Swiftでは、プロパティラッパーを組み合わせて使用することも可能です。

このコードでは、@Persisted属性と他のプロパティラッパーを組み合わせる方法を表しています。

この例では、@Persistedと@Loggingという2つのプロパティラッパーを組み合わせて、データの変更をログに記録する方法を表しています。

@propertyWrapper
struct Logging<T> {
    private var value: T

    init(wrappedValue: T) {
        self.value = wrappedValue
    }

    var wrappedValue: T {
        get {
            return value
        }
        set {
            print("Value changed from \(value) to \(newValue)")
            value = newValue
        }
    }
}

class SampleModel {
    @Logging @Persisted var sampleValue: Int = 0
}

上記のコードでは、@Loggingプロパティラッパーが、値の変更を検出してログに記録する役割を果たしています。

このラッパーと@Persistedを組み合わせることで、データの変更を永続化しつつ、変更のログを取得することができます。

このコードを実行した際、次のような出力が得られる可能性があります。

Value changed from 0 to 10

これは、sampleValueプロパティの値が0から10に変更されたことを表しています。

まとめ

Swiftの@Persisted属性は、データの永続性を簡単かつ効率的に扱うための強力なツールとして登場しています。

この属性を使用することで、UserDefaultsの操作を大幅にシンプルにし、コードの可読性や保守性を向上させることができます。

また、カスタマイズの可能性は無限で、特定のニーズや要求に合わせて属性を調整することが可能です。

今回の記事を通じて、@Persisted属性の基本的な使い方から、さまざまな応用例、注意点、カスタマイズ方法に至るまで、幅広くその魅力や可能性を解説しました。

サンプルコードを交えての詳細な説明は、Swift開発者の皆さんの実際のコーディングの際の参考となることでしょう。

常に最新の情報を追い求めることで、より効果的にこの属性を活用していくことが求められます。

データの永続性を扱う際の最適なパートナーとして、@Persisted属性を上手く取り入れ、アプリケーションの品質やユーザーエクスペリエンスを向上させましょう。