LoginSignup
3
4

More than 3 years have passed since last update.

Swift:App GroupsとUserDefaultsとNSKeyedUnarchiverの話

Last updated at Posted at 2020-03-23

iOSでToday Extensionと母機アプリを連携したいときに,App Groupsを使います.そして,UserDefaultsでデータを共有するのですが,独自クラスのデータを共有するとなると少し厄介でした.

App Groupsの準備

データを共有するターゲットのそれぞれにて下記の作業をします.

  1. [Signing & Capabilities]よりApp Groupsを追加
  2. グループIDを追加する(データを共有する全てのターゲットにて同じグループIDを使用すること).

そして,UserDefaultsを使うときはUserDefaults.standardではなくUserDefaults(suiteName: "グループID")を使います.なお,グループIDはgroup.から始まるのが通常らしいです.

独自クラスの準備

基本的にはこの記事と同様ですが,NSCodingだけでなくNSSecureCodingも使います.

独自クラスのサンプル
class Sample: NSObject, NSCoding, NSSecureCoding {

    var dataA: Bool
    var dataB: Double
    var dataC: String

    static var supportsSecureCoding: Bool {
        return true
    }

    init(_ dataA: Bool, _ dataB: Double, _ dataC: String) {
        self.dataA = dataA
        self.dataB = dataB
        self.dataC = dataC
    }

    required init?(coder: NSCoder) {
        dataA = coder.decodeBool(forKey: "dataA")
        dataB = coder.decodeDouble(forKey: "dataB")
        dataC = coder.decodeObject(of: NSString.self, forKey: "dataC") as String? ?? ""
    }

    func encode(with coder: NSCoder) {
        coder.encode(dataA, forKey: "dataA")
        coder.encode(NSNumber(value: dataB), forKey: "dataB")
        coder.encode(dataC as NSString, forKey: "dataC")
    }

}

supportsSecureCodingをtrueにする必要があります.
ここで,ターゲットごとの初期化の場面(AppDelegateのdidFinishLaunchingとかViewDidLoadとか)でNSKeyedArchiverNSKeyedUnarchiverに独自クラスを登録します.

override func viewDidLoad() {
    super.viewDidLoad()
    NSKeyedArchiver.setClassName("Sample", for: Sample.self)
    NSKeyedUnarchiver.setClass(Sample.self, forClassName: "Sample")
}

データの取得/保存

let userDefaults = UserDefaults(suiteName: "group.com.hoge.sampleApp")!

var sample: Sample {
    get {
        guard
            let data = userDefaults.data(forKey: "sample"),
            let unarchived = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data),
            let sample = unarchived as? Sample
            else {
                return Sample(false, 0.0, "")
        }
        return sample
    }
    set(newSample) {
        let data = try? NSKeyedArchiver.archivedData(withRootObject: newSample,
                                                     requiringSecureCoding: true)
        userDefaults.set(data, forKey: "sample")
    }
}

このようにgetsetを定義してしまうとデータの出し入れがしやすいかもしれません.

参照

3
4
2

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
4