VASILYのiOSエンジニアにこらすです。
今回のテックブログではiOS・macOS・watchOS・tvOSのUserDefaultsにユーザー設定などを保存するのに便利なラッパーライブラリ Default を作ったので紹介します。
Defaultとは?
Defaultは、Codableに準拠するカスタムオブジェクトを保存するための拡張機能を提供するライブラリです。プロトコルDefaultStorable
を介して、UserDefaultsに以下で説明する新しいインタフェースを提供することで、UserDefaultsを拡張します。 Codableサポート拡張機能とDefaultStorableプロトコル拡張機能いずれかを使うこともできますし、両方つかうこともできます。
Defaultを使うメリットは?
UserDefaultsには、保存したキーをtypoしたり、読み書きしている場所を探すのが難しかったりといった難点があります。
ですが、このDefaultを使って保存されるオブジェクト型を定義すると、DefaultStorable
に準拠する型をプロジェクト内で検索することで保存されるデータを追うのが簡単になります。 UserDefaults
に格納する専用のオブジェクトを定義すれば、特定のデータを論理的にグループ化することができます。
NSCoding 時代の実装
Swiftでカスタムオブジェクトを宣言した後は、 NSCoding
に準拠し、NSObject
から継承し、適切なEncode / Decodeメソッドを実装すれば、 UserDefaults
に直接保存することができるようになります。
これを自前で対応しようとすると、以下のような煩雑なコードが必要になります。
クラスを定義し、 NSCoding
に準拠し、必要なDecode / Encodeメソッドを実装する
class VolumeSetting: NSObject, NSCoding { let sourceName: String let value: Double init(sourceName: String, value: Double) { self.sourceName = sourceName self.value = value } required init(coder decoder: NSCoder) { self.sourceName = decoder.decodeObject(forKey: "sourceName") as? String ?? "" self.value = decoder.decodeDouble(forKey: "value") } func encode(with coder: NSCoder) { coder.encode(sourceName, forKey: "sourceName") coder.encode(value, forKey: "value") } }
オブジェクトを作成し、 NSKeyedArchiver
を使用してインスタンスをData
にアーカイブして保存する
let setting = VolumeSetting(sourceName: "Super Expensive Headphone Amp", value: 0.4) let encodedData = NSKeyedArchiver.archivedData(withRootObject: setting) UserDefaults.standard.set(encodedData, forKey: "volume")
読み出すときは NSKeyedUnarchiver
を使う
if let data = UserDefaults.standard.data(forKey: "volume"), let volumeSetting = NSKeyedUnarchiver.unarchiveObject(with: data) as? VolumeSetting { // do something }
Defaultを使った実装
一方、 Default を使えば、以下のようなシンプルなコードで実現できます。
保存対象のオブジェクトを定義する
DefaultStorable
プロトコルに準拠したstructを定義します。
struct VisualSettings: Codable, DefaultStorable { let themeName: String let backgroundImageURL: URL? }
保存処理
let settings = VisualSettings(themeName: "bright", backgroundImageURL: URL(string: "https://...")) settings.storeToDefaults()
読み込み
if let settings = VisualSettings.fetchFromDefaults() { // Do something }
もう一つのメリット
このアプローチのもう一つの利点は、すべてのオブジェクトを一つのファイルに定義することで、UserDefaults
に格納されるものを非常に簡単に見ることができることです。
Defaultの設計について
UserDefaults
にカスタムオブジェクトを保存するためには、そのオブジェクトはNSCoding
に準拠する必要があります。
NSCoding
に準拠するにはDecoding / Encodingメソッドを実装する必要があり、少し手間がかかります。
一方、嬉しいことにSwiftのData
型がNSCoding
に準拠しています。オブジェクトをData
に変換する方法を見つけることができれば、それをUserDefaults
に格納することができます。
Swift 4から追加されたCodableプロトコルは簡単にData
に変換する事ができます。
Swift 4以降のプロジェクトであれば、Codable
に準拠したモデルオブジェクトを作ることが多くなると思います。このライブラリは、そういうCodable
プロトコルに準拠したオブジェクトをUserDeafaults
に読み書きすることができるようになっています。
結構シンプルですね!
まとめ
Default は、非常に軽くてシンプルなカスタムオブジェクトを扱う UserDefaults
のラッパーです。
GitHubに公開してあるので、Default をインストールして遊んでみたい場合は、Carthage か CocoaPods を使って試してみてください。
VASILYでは、OSSなどSwift 4での開発に興味があるエンジニアを募集しています。ぜひオフィスに遊びに来てください。
ー にこらす 👍