iOSでToday Extensionと母機アプリを連携したいときに,App Groupsを使います.そして,UserDefaultsでデータを共有するのですが,独自クラスのデータを共有するとなると少し厄介でした.
App Groupsの準備
データを共有するターゲットのそれぞれにて下記の作業をします.
- [Signing & Capabilities]より
App Groups
を追加 - グループ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とか)でNSKeyedArchiver
とNSKeyedUnarchiver
に独自クラスを登録します.
例
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")
}
}
このようにget
とset
を定義してしまうとデータの出し入れがしやすいかもしれません.