HOME » Natsu note » 古い投稿 » Core Data 勉強日記 (10):More iPhone 3 Development / chapter 6 (Validation)

Core Data 勉強日記 (10):More iPhone 3 Development / chapter 6 (Validation) 2010/02/23/|古い投稿

この記事は情報が古い可能性があります。参照する際にはご注意ください。

More iPhone 3 Development: Tackling iPhone SDK 3 (Beginning) Chapter 6 のまとめ(後半)。

カスタムモデルの生成方法に関してはChapter 6(カスタムモデル)でまとめたが、ここでは、値の有効性チェック"Validation"についてまとめる。

Validationとは、値を変更する際にその正当性をチェックし不当ならエラーを返すというもの。簡単な有効範囲の指定(最大値や最小値など)であれば、データモデルエディタでできるが、細かいチェックや複数の変数をまたいだチェックを行いたければ、NSManagedObjectのサブクラスでチェック用のメソッドの実装する必要がある。


なお、Core Data のValidationメソッドは、NSManagedObjectContextの保存時に呼び出されるようになっている。自分でValidationメソッドを呼ぶ必要はない。実装のみしておけばよいのだ。

こちらも参考に:Core Data Programming Guide : Managed Object Validation

Single-Attribute Validation

これは至って簡単。それぞれの属性(Attribute)ごとに有効性のチェックをするための枠組みだ。

まず、Chapter 6(カスタムモデル)の後半部分で説明している方法でNSManagedObjectのサブクラスとなる独自のクラスを生成する。Validation用のメソッドはすべてここに実装する。

実装するのは、

- (BOOL) validate<Key>:(id *)value error:(NSError **)error

というメソッド。ここで<Key>はプロパティ名(一般的には属性の名前と同じ)を示す。例えば、nameという名前を持つ属性のvalidationは、

- (BOOL) validateName:(id *)value error:(NSError **)error

が担当することになる。このメソッド内で、valueが属性nameの値として適しているかをチェックし、適していればYESを、そうでなければNOを返せばよい。

Validation用のメソッドが呼ばれる順番については後述。

Multiple-Attribute Validation

複数の属性にまたがってチェックを行いたい場合(例えば、2つある属性のうち、必ずどちらかに値が設定されている必要がある、など)、上記のValidationメソッドでは事足りない。この場合、NSManagedObjectが持つ以下のメソッドを上書きする必要がある。

- (BOOL)validateForDelete:(NSError **)error
- (BOOL)validateForInsert:(NSError **)error
- (BOOL)validateForUpdate:(NSError **)error

それぞれ、ManagedObjectの削除、追加、更新時に呼ばれる。

まず、必要となるValidation機能をすべてプライベートメソッドで作成し、上記のメソッド内で呼び出すのが一番スマートだ。

呼び出し順

単独の属性用にValidationメソッドを実装したり、NSManagedObjectのVaidationメソッドを上書きしたとき、どのような順で呼び出されるのか。これは結構重要。

本書では、まずSingle-Attribute Validation (valideteHoge: など自分で実装したメソッド)が呼び出され、その後、validateForXXXが呼び出されると説明されている。が、どうやらこれは間違っているようだ。というのは、validateForUpdate: を上書きしたら、vaidateHoge: が呼ばれなくなってしまったから。

デバッガでNSManagedObjectが持ついくつかのメソッドにブレークポイントを貼り検証してみた。
結果は以下。

  1. validateForUpdate (validateForInsert, validateForDelete) (NSManagedObject)
  2. _validateValue:forProperty:andKey:withIndex:error: (NSManagedObject)
  3. validateHoge: (自分で作成したサブクラス)


つまり、自分で追加した、Single-Attribute Validation (validate<Key>: という名前を持つメソッド)を呼び出すのは、NSManagedObjectのvalidateForUpdate (validateForInsert, validateForDelete) らしい。

したがって、validateForUpdateをサブクラスで上書きした場合、この中で必ず

[super validateForUpdate];

しておかないと、それぞれのvalidation メソッドがコールされない。

Core Data Programming Guide : Managed Object Vaidation の説明を見ても、おそらくこれで間違いはないだろう。つまりこうすればよいのだ。

- (BOOL)validateForUpdate:(NSError **)error
{
	BOOL propertiesValid = [super validateForUpdate:error];
	// could stop here if invalid
	BOOL consistencyValid = [self validateConsistency:error];
	return (propertiesValid && consistencyValid);
}

NSErrorの設定

Validationメソッド内で正当性のチェックをし、「正当ではない」と判断した場合、引数であるerrorに独自のエラーを設定することができる。例えばこんな感じ。

- (BOOL) validateBirthDate:(id *)ioValue error:(NSError **)outError {
	NSDate *date = *ioValue;
	if ([date compare:[NSDate date]] == NSOrderedDescending) {
		if (outError != NULL) {
			NSString *errorStr = NSLocalizedString(@"Birthdate connot be in the future", nil);
			NSDictionary *userInfoDict = [NSDictionary dictionaryWithObject:errorStr
			                                                         forKey:NSLocalizedDescriptionKey];
			NSError *error = [[[NSError alloc] initWithDomain:kHeroValidationDomain
		                                                 code:kHeroValidationBirthdateCode
	                                                 userInfo:userInfoDict] autorelease];
			*outError = error;
		}
		return NO;
	}
	return YES;
}

これで、エラーメッセージまで含めてValidationメソッドの中で管理することができる。これはとても便利。

冒頭でも書いたように、各Validationメソッドはデータを保存する際に呼び出されるようになっている。
したがって、呼び出し側では、saveのあとに例えば以下のようにチェックするだけでよい。これで、Validationメソッド内で設定した独自のエラーメッセージまで取得することができる。

NSError *error;
if (![managedObject.managedObjectContext save:&error]) {
	NSString *message = nil;
	if ([[error domain] isEqualToString:@"NSCocoaErrorDomain"]) {
		NSDictionary *userInfo = [error userInfo];
		message = [NSString stringWithFormat:NSLocalizedString(@"Validation error on %@rFailed condition: %@", nil),
		[userInfo valueForKey:@"NSValidationErrorKey"],
		[userInfo valueForKey:@"NSValidationErrorPredicate"]];
	} else {
		message = [error localizedDescription];
	}
	UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Validation Error", nil)
	                                                message:message
											       delegate:self
									      cancelButtonTitle:NSLocalizedString(@"Cancel", nil)
									      otherButtonTitles:NSLocalizedString(@"Fix", nil), nil];
	[alert show];
	[alert release];
}

上のコードの、

message = [error localizedDescription];

としている部分で、自分で設定したエラーメッセージが返されることになる。

まとめ

Validationは、データ保存時に各値のチェックを行い不正な値が入力された場合エラーとする仕組み。
おそらく上記の仕組みを知らなくても、各値の設定時に独自のチェックをして実装することは可能だろう。でも、Core Dataを使うのであれば、ここに紹介されている方法で実装することで、かなりシンプルかつ安全なコードになると思う。

More iPhone 3 Development: Tackling Iphone Sdk 3 (Beginning)
Dave Mark Jeff Lamarche
Apress
売り上げランキング: 29207