技術ブログ

プログラミング、IT関連の記事中心

UserDefaultsに独自クラスの配列を設定する方法【Swift】

■はじめに

UserDefaultsには通常は、独自クラスの設定ができません。
そのため、ここでは独自クラスをエンコードして設定する方法を記載します。
※ついでに、配列として設定する方法を記載していますが、配列にしない場合には、通常のクラス型で設定してください。

■手順

保存するクラスを作成する。
※「NSObject」「NSSecureCoding」を継承する必要があります。

class SampleClass: NSObject, NSSecureCoding {
}

上記のクラスに設定したいパラメータを追加します。

class SampleClass: NSObject, NSSecureCoding {
    var intt: Int?
    var stringgg: String?
    var array: [String] = []
}

追加したら、以下のパラメータを追加します。
※セキュリティの強いエンコードをするためのフラグなので、おまじないだと思ってください。

static var supportsSecureCoding: Bool = true

エンコードの関数「encode」を作成し、パラメータをエンコードするコードを記載します。(詳細はソースを参照してください)
「forKey」の部分は他のパラメータと被らない、一意の値にするようにしてください。
※デコードの時にこのKeyを元にデコードします。

class SampleClass: NSObject, NSSecureCoding {
    var intt: Int?
    var stringgg: String?
    var array: [String] = []

    static var supportsSecureCoding: Bool = true

    func encode(with coder: NSCoder) {
        coder.encode(index, forKey: "dataKey")
        coder.encode(zyuusyo, forKey: "dataKey1")
        coder.encode(todoArray, forKey: "dataKey2")
    }
}

デコードの関数「init?」を作成し、パラメータをデコードするコードを記載します。
「forKey」の部分はエンコードで利用したKeyを記載してください。

class SampleClass: NSObject, NSSecureCoding {
    var intt: Int?
    var stringgg: String?
    var array: [String] = []

    static var supportsSecureCoding: Bool = true

    func encode(with coder: NSCoder) {
        coder.encode(index, forKey: "dataKey")
        coder.encode(zyuusyo, forKey: "dataKey1")
        coder.encode(todoArray, forKey: "dataKey2")
    }

    required init?(coder: NSCoder) {
        self.index = (coder.decodeObject(forKey: "dataKey") as? Int)
        self.zyuusyo = (coder.decodeObject(forKey: "dataKey1") as? String)
        self.todoArray = (coder.decodeObject(forKey: "dataKey2") as! [String])
    }
}

このままでは、「init?」関数の影響で、インスタンス作成時に引数「NSCoder」が必要になってしまうので、インスタンス作成用に空のinitメソッドを追加します。

class SampleClass: NSObject, NSSecureCoding {
    var intt: Int?
    var stringgg: String?
    var array: [String] = []

    static var supportsSecureCoding: Bool = true

    func encode(with coder: NSCoder) {
        coder.encode(index, forKey: "dataKey")
        coder.encode(zyuusyo, forKey: "dataKey1")
        coder.encode(todoArray, forKey: "dataKey2")
    }

    required init?(coder: NSCoder) {
        self.index = (coder.decodeObject(forKey: "dataKey") as? Int)
        self.zyuusyo = (coder.decodeObject(forKey: "dataKey1") as? String)
        self.todoArray = (coder.decodeObject(forKey: "dataKey2") as! [String])
    }

    override init(){
    }
}

これで、設定するクラスの作成は完了です。
ViewControllerの「ViewDidLoad」で値を設定して、その後に設定した値を取得する処理を記載して動作確認を行いましょう。

まずは、作成したクラス型の配列に適当な値を設定します。

override func viewDidLoad() {
    super.viewDidLoad()

    let samp = SampleClass()
    samp.intt = 1
    samp.stringgg = "住所"
    samp.array.append("TODO1")
    samp.array.append("TODO2")
    
    
    let sampp = SampleClass()
    sampp.intt = 2
    sampp.stringgg = "住所2"
    sampp.array.append("TODO3")
    sampp.array.append("TODO4")
    
    var array = [SampleClass]()
    array.append(samp)
    array.append(sampp)
}

「NSKeyedArchiver」クラスの「archivedData」関数を利用してエンコードし、if letを利用してエンコードされている場合にはUserDefaultsに保存する処理を記載してください。

override func viewDidLoad() {
    super.viewDidLoad()

    let samp = SampleClass()
    samp.intt = 1
    samp.stringgg = "住所"
    samp.array.append("TODO1")
    samp.array.append("TODO2")
    
    
    let sampp = SampleClass()
    sampp.intt = 2
    sampp.stringgg = "住所2"
    sampp.array.append("TODO3")
    sampp.array.append("TODO4")
    
    var array = [SampleClass]()
    array.append(samp)
    array.append(sampp)

    if let archiveData = try? NSKeyedArchiver.archivedData(withRootObject: array, requiringSecureCoding: true) {
        // Userdefaultsにデータを保存する
        UserDefaults.standard.set(archiveData, forKey: "array")
        UserDefaults.standard.synchronize()
    }
}

UserDefaultsから設定したデータをData型で取得し、取得できた場合には「NSKeyedUnarchiver」クラスの「unarchiveTopLevelObjectWithData」関数でデコードし、if letを利用してコンソール出力させます。

override func viewDidLoad() {
    super.viewDidLoad()

    let samp = SampleClass()
    samp.intt = 1
    samp.stringgg = "住所"
    samp.array.append("TODO1")
    samp.array.append("TODO2")
    
    
    let sampp = SampleClass()
    sampp.intt = 2
    sampp.stringgg = "住所2"
    sampp.array.append("TODO3")
    sampp.array.append("TODO4")
    
    var array = [SampleClass]()
    array.append(samp)
    array.append(sampp)

    if let archiveData = try? NSKeyedArchiver.archivedData(withRootObject: array, requiringSecureCoding: true) {
        UserDefaults.standard.set(archiveData, forKey: "array")
        UserDefaults.standard.synchronize()
    }

    if let loadedData = UserDefaults().data(forKey: "array") {
        if let sample = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(loadedData) as? [SampleClass] {
            for aaaaaa in sample {
                print(aaaaaa.intt)
                print(aaaaaa.stringgg)
            }
        }
    }
}

変数名などは適当ですが、これで独自のクラスを保存し、取得することができました。