Help us understand the problem. What is going on with this article?

NSUserDefaultsを使うときはWrapして使うと便利

More than 5 years have passed since last update.

皆さんObjective-Cを楽しんでますか?
巷ではObjective-Cの事を「変態言語」なんて呼ばれたりしているそうですが、Modern Objective-Cなんかも出てきて、すっかり「大衆的」な言語に近づいて来ました。

さてそんな訳で今回はiOS開発でよく使うNSUserDefaultsの話。
NSUserDefaultsはアプリ固有の設定情報を永続化する時などによく使われる便利なクラスです。
普通に使うとこんな感じ。

// 設定情報を保存
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:@"hoge" forKey:@"Name"];
[userDefaults setBool:YES forKey:@"Setting"];
[userDefaults synchronize];

// 設定情報の読み込み
NSString *name = [userDefaults objectForKey:@"Name"];
BOOL setting = [userDefaults boolForKey:@"Setting"];;

これでも問題は無いのですが、自分で使っている時に困ることがあります。

1.Keyのタイプミス
2.Key値の忘却
3.保存値の型の忘却

人は間違える生き物です。
各画面で文字列リテラルを使ってKeyを書いていればタイプミスだってあります。
ビルドの時にコンパイラが「Key値が間違ってるよ!」っと教えてはくれません。
全て開発者の責任です。

人は忘れる生き物です。
開発が進むにつれて設定情報が増えてきます。
設定情報が増えるのと比例してKeyも増え開発は混沌としてきます。

そこでNSUserDefaultsをWrapしてあげます。

Configuration.h
@interface Configuration : NSObject
+ (NSString *)name;
+ (void)setName:(NSString *)value;
+ (BOOL)setting;
+ (void)setSetting:(BOOL)value;
+ (void)synchronize;
@end
Configuration.m
@implementation Configuration
static NSString *CONFIGURATION_NAME = @"Configuration.Name";
+ (NSString *)name {
  NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
  [userDefaults registerDefaults:@{CONFIGURATION_NAME : @"No Name"}];
  return [userDefaults objectForKey:CONFIGURATION_NAME];
}
+ (void)setName:(NSString *)value {
  NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
  [userDefaults setObject:value forKey:CONFIGURATION_NAME];
}
static NSString *CONFIGURATION_SETTING = @"Configuration.Setting";
+ (BOOL)setting {
  NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
  [userDefaults registerDefaults:@{CONFIGURATION_SETTING : @(YES)}];
  return [userDefaults boolForKey:CONFIGURATION_SETTING];
}
+ (void)setSetting:(BOOL)value {
  NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
  [userDefaults setBool:value forKey:CONFIGURATION_SETTING];
}
+ (void)synchronize {
  [[NSUserDefaults standardUserDefaults] synchronize];
}
@end

こんな感じで、Wrapしてあげます。
使う側では下記の様にしてあげます。

// 設定情報を保存
[Configuration setName:@"Hoge"];
[Configuration setSetting:YES];
[Configuration synchronize];

// 設定情報の読み込み
NSString *name = [Configuration name];
BOOL setting = [Configuration setting];

呼び出し側のコード量はそれほど変わらないのですが、

1.Xcodeのインテリセンスが効くのでタイプミスを防げる。
2.Xcodeのインテリセンスが効くのでKey値を覚えていなくても選ぶだけ。
3.型は戻り値や引数で決まっているので間違った型を渡す心配はなし。

また設定情報の呼び出しで[userDefaults registerDefaults:@{CONFIGURATION_NAME : @"No Name"}];と書いておくことで設定情報の初期値を設定できるので呼び出し側で初期値を意識する必要がありません。

今回の記事を書くにあたって作ったサンプルソースをgithubにあげておきます。
ConfigurationSample

Pl*ease enjoy the **d*evelopment.

tochi
DIGITALJET Inc. Programer. SonicGarden Inc. Programer.
http://www.aguuu.com
sonicgarden
「お客様に無駄遣いをさせない受託開発」と「習慣を変えるソフトウェアのサービス」に取り組んでいるソフトウェア企業
http://www.sonicgarden.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away