※日本版編注:この記事には下品な表現が含まれますが、原文を尊重してそのまま翻訳しています。あらかじめご了承ください。
UserDefaultsとはいったいなんでしょう? 分かりにくい名前ではないですか? なにに使うのでしょう? そして、なぜUserDefaultsについて書いているのでしょうか? これらの質問に1つでも答えられるなら、UIColorについて書いているパート2に飛んでください。
参考:もし型変換を熟知していてas、as!、as?を区別できるなら、英語ですがYouTubeで動画を見てください。
ステージ1:アナロジー
UserDefaultsを深く理解するために、いつもの通り噛み砕いて説明します。実際、とてもシンプルです。説明の必要すらないとも思います。UserDefaultsオブジェクトはユーザーデータを保存します。アプリを最初にダウンロードしたときに背景色や画像などの設定しますがUserDefaultsを使えばバッテリが切れても保存しておけます。なんでも保存できます。携帯電話に254GBの空き容量があれば、254GBのユーザーデータを保存できます。しかし、大きな問題があります。
実行時にすべてを吐き出します。抽象的な言い方で分かりにくいですね。これではどうでしょう。アプリ起動時やビューの読み込み時に、すべてを吐き出します。うーん、? 。もっと良い例えがあります。トイレで1度に5日間溜めたう○こを出すようなものです。身体はどうなりますか? びっくりしますね。ちゃんと出ないかもしれません。そしてもっとも重要なことは、身体をひどく傷つけることです。大腸の中で大きなう○こを扱うことと同じようにUserDefaultsも大切に扱いたいですね。
わはは、はじめての人はびっくりしたかもしれませんね。これが私の説明の仕方です。すみません。でも誰も私を止めることはできません。
もちろん、この重大な問題を解決するためには、別の方法があります。それはCoreDataです。聞いたことがあるかもしれません。CoreDataについては、別の記事を公開する予定です。乞うご期待!
ステージ2:実際にやってみる
ここまで楽しかったですね(少なくとも私は楽しみました)。それでは実際に手を動かす時間です。通常通り、以下のようにUserDefaultsのインスタンスオブジェクトを作成します。
let defaults = UserDefaults.standard
これでオブジェクトに値を入れられます。正確には、それぞれの「う○こ」に名前を付けられます。たとえば、Int値を格納してみます。
defaults.set(20, forKey: "myAge")
ここまでのたとえはちょっと不快だと思うので、以下のようなかわいいハムスターにしてみます。
そうです、以下のようになります。
class ViewController: UIViewController {
let defaults = UserDefaults.standard
override func viewDidLoad() {
super.viewDidLoad()
defaults.set(20, forKey: "myAge")
print(defaults.integer(forKey: "myAge"))
}
}
もしお父さんが値を変更したい場合は、アプリケーションのmyAgeの値を変更します。
defaults.set(47, forKey: "myAge")
以上です。UserDefaultsを使って、2行のコードでデータをオフラインに保存できました。性別、血液型、身長、体重、靴のサイズ、ゲームレベルなども保存できます。しかし、もちろんこれで終わりではありません。ハムスターや人間と同じように、岩やダイヤモンドのようなものをすべて食べられるわけではありません。生後15日の赤ちゃんにお米は与えられません。あなたはなんでも食べられますか?(親愛なるApple UIKitのエンジニア、どうして私たちは食べられないのでしょう…?)
// You can only feed these
func set(Bool, forKey: String)
func set(Float, forKey: String)
func set(Int, forKey: String)
func set(Any?, forKey: String)
func set(Double, forKey: String)
func set(URL?, forKey: String)
しかし、驚くべきことにUserDefaultsはあらゆる種類のものを吐き出せます。どうしてか分かりません。私たちの身体も同じように機能するかもしれません。
// Catharsis
func array(forKey: String)
func bool(forKey: String)
func data(forKey: String)
func dictionary(forKey: String)
func float(forKey: String)
func integer(forKey: String)
func object(forKey: String)
func stringArray(forKey: String)
func string(forKey: String)
func double(forKey: String)
func url(forKey: String)
func value(forKey: String)
もう1つの問題は、どのようにDictionaryを保存/取得するかです。Dictionaryの値を直接設定するメソッドはありません。しかし、もちろん解決策はあります。以下に2つ紹介します。
第1の方法
let name = ["Real": "SangJoon Lee"]
defaults.set(name, forKey: "name") // Store as Any?
if let name = defaults.value(forKey: "name") as? [String: String] {
print(name) // Downcast from Any? to [String: String]
}
// ["Real": "SangJoon Lee"]
第2の方法
if let name = defaults.dictionary(forKey: "name") as? [String: String] {
print(name) // Downcast from [String : Any]? to [String: String]
}
// ["Real": "SangJoon Lee"]
すばらしい。しかし、私の父が突然、背景色をピンクに保存したいと言い出しました…。 ピンク? つまりUIColorを保存しないといけなくなりました? 。それはかなり深刻です。覚悟してください。
パート2:UIColor
すでにUIColorを保存する直接的な方法がないことに気づいたかもしれません。そうなんです、少しタイプを操作する必要があります。たとえば、UIColorをDataに変換してAny?として保存できます。いいですよね? それがばかげたことだと知っています。詳しく説明します。
UIColorをDataに変換するには、特別なエンコーダNSKeyedArchiverを使います。このクラスは、岩と未加工金属から剣と盾を作るかじ屋のようなものだと想像してください。もちろん、元の素材に戻すこともできます。
詳細については、AppleのAPI、NSKeyedArchiverを参照してください。
NSCoderのサブクラスNSKeyedArchiverは、オブジェクト(およびスカラー値)をアーキテクチャに依存しない形式/ファイルにエンコードして格納できる方法を提供する。ー Apple
コードは以下のようになります。
extension UserDefaults {
func setColor(color: UIColor?, forKey key: String) {
var colorData: NSData?
if let color = color {
colorData = NSKeyedArchiver.archivedData(withRootObject: color) as NSData?
}
set(colorData, forKey: key)// UserDefault Built-in Method into Any?
}
}
これでUIColorをDataに変換し、それをAny?として保存できました。これが唯一の方法です。ちなみに、Anyはほぼすべてのとても壮大なスーパークラスのようなものです。
さて、いま保存した「色」を吐く時間です。archievedDataを使って人間的に理解できないものにしたのでunarcheiveする時間です。しかし、おもしろいことにAny?の代わりにoutdata?を吐き出せます。技術的にはAny?を吐き出して、dataに変換したあと、UIColor?にできます。ただ、ここはシンプルに、data?からUIColor?にします。
extension UserDefaults {
func colorForKey(key: String) -> UIColor? {
var color: UIColor?
if let colorData = data(forKey: key) {
color = NSKeyedUnarchiver.unarchiveObject(with: colorData) as? UIColor
}
return color
}
func setColor(color: UIColor?, forKey key: String) {
var colorData: NSData?
if let color = color {
colorData = NSKeyedArchiver.archivedData(withRootObject: color) as NSData?
}
set(colorData, forKey: key)
}
}
これで以下のように使えます、
defaults.setColor(color: UIColor.red, forKey: "myColor") // set
let myColor = defaults.colorForKey(key: "myColor") // get
すばらしいですよね? 以上です。
ソースコードはGithubにあります。
Andyy Hopeは、Protocolを使用したUserDefaultsのレベルに引き上げる方法についてすばらしい記事を書いています。私も彼からたくさんのことを学びました。彼の手法は、私よりも確実にスマートです。ぜひ彼の記事『Swift: UserDefaults Protocol』をチェックしてください。後悔しません。
※この記事はもともとiOS Geek Communityで公開された記事です。
(原文:Store UIColor with UserDefaults in Swift 3)
[翻訳:萩原伸悟/編集:Livit]