since: 2011/09/23
update: 2011/09/24
參考: Core Data Programming Guide: Managed Object Validation
備註: 已不採用此方式, 僅供參考.
目前使用: 此方式
A. 說明:
1. 需要先從 data model 產生出繼承自
NSManagedObject 的類別.
這已在
Lala's Program Note 實作記錄: 20. search criteria 中的
B. 建立用來表現
managed object 的客製物件: 建好了
NoteBook entity 與
NoteArticle entity 的類別.
----------------------------------------------------------------------------------------------------------
2.
Property-Level Validation:
以下三種自行覆寫的方法, 由於
(id *)ioValue /
(id *)value 是一個
物件參照的指標,
允許對輸入的值作更改, 因此在 Apple 的文件中也不建議使用, 因為可能造成
記憶體管理的問題.
- (BOOL)validate<AttributeName>:(id *)ioValue error:(NSError **)outError;
- (BOOL)validateValue:(id *)value forKey:(NSString *)key error:(NSError **)error;
- (BOOL)validateValue:(id *)ioValue forKeyPath:(NSString *)inKeyPath error:(NSError **)outError;
----------------------------------------------------------------------------------------------------------
3.
Inter-Property validation:
a. 在此採用覆寫以下三個方法來做資料驗證的工作.(目前僅處理:
Insert 與
Update)
- (BOOL)validateForInsert:(NSError **)error;
- (BOOL)validateForUpdate:(NSError **)error;
- (BOOL)validateForDelete:(NSError **)error;
b. 在覆寫的方法內, 首先要先呼叫 superclass 的實作, 以取得合適的 Validation.
ex:
BOOL valid = [super validateForInsert:error];
c. 當發現到有錯誤時, 可利用
errorFromOriginalError:error: 方法來組合所有的錯誤,
以避免覆寫掉 superclass validation 裡的任何錯誤.
Apple 預設的
errorFromOriginalError:error: 方法, 有需要也可以自行覆寫.
d. 預設情況下,
validation 要一直到對 managed object 執行
save 時, 才會發生作用,
如果想提前呼叫的話, 可手動呼叫.
ex:
NSError *validationError;
if (![newNoteBook validateForInsert:&validationError]) {
// Invalidate
} else {
// contine to save ....
}
B. 對 NoteBook 資料的 Validate:
1. 開啟
NoteBook.h 檔案, 新增 method 如下:
//@add
- (BOOL)validateBookName:(NSError **)error;
- (NSError *)errorFromOriginalError:(NSError *)originalError error:(NSError *)secondError;
2. 開啟
NoteBook.m 檔案, 新增 method 如下
//@add
- (BOOL)validateForInsert:(NSError **)error {
BOOL superValid = [super validateForInsert:error];
BOOL bookNameValid = [self validateBookName:error];
return (superValid && bookNameValid);
}
----------------------------------------------------------------------------------------------------------
//@add
- (BOOL)validateForUpdate:(NSError **)error {
BOOL superValid = [super validateForUpdate:error];
BOOL bookNameValid = [self validateBookName:error];
return (superValid && bookNameValid);
}
----------------------------------------------------------------------------------------------------------
//@add
- (BOOL)validateBookName:(NSError **)error
{
BOOL valid = YES;
// p.s. name is NoteBook model's attribute
if (self.name != nil) {
if (([self.name length] < 1) || ([self.name length] > 20)) {
valid = NO;
if (error != NULL) {
NSBundle *myBundle = [NSBundle bundleForClass:[self class]];
NSString *bookNameErrorString = [myBundle localizedStringForKey:@"StringLengthError" value:@"Book name length is too long or 0." table:@"BookNameErrorStrings"];
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
[userInfo setObject:bookNameErrorString forKey:NSLocalizedFailureReasonErrorKey];
[userInfo setObject:self forKey:NSValidationObjectErrorKey];
NSError *bookNameError = [NSError errorWithDomain:@"NoteBook" code:NSManagedObjectValidationError userInfo:userInfo];
// if there was no previous error, return the new error
if (*error == nil) {
*error = bookNameError;
}
// if there was a previous error, combine it with the existing one
else {
*error = [self errorFromOriginalError:*error error:bookNameError];
}
}
}
}
return valid;
}
----------------------------------------------------------------------------------------------------------
//@add
- (NSError *)errorFromOriginalError:(NSError *)originalError error:(NSError *)secondError
{
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
NSMutableArray *errors = [NSMutableArray arrayWithObject:secondError];
if ([originalError code] == NSValidationMultipleErrorsError) {
[userInfo addEntriesFromDictionary:[originalError userInfo]];
[errors addObjectsFromArray:[userInfo objectForKey:NSDetailedErrorsKey]];
}
else {
[errors addObject:originalError];
}
[userInfo setObject:errors forKey:NSDetailedErrorsKey];
return [NSError errorWithDomain:NSCocoaErrorDomain
code:NSValidationMultipleErrorsError
userInfo:userInfo];
}
3. 當輸入的 NoteBook
名稱長度
大於 20 或者
沒有輸入時, Debug area 就會出現
以下的錯誤訊息, 並且程式會終止(crash? 以後再作
錯誤訊息的處理)
"The operation couldn't be completed. Book name length is too long or 0."
"NSLocalizedFailureReason = "Book name length is too long or 0.";
C. 對 NoteArticle 資料的 Validate:
1. 開啟
NoteArticle.h 檔案, 新增 method 如下:
//@add
- (BOOL)validateArtitleTitle:(NSError **)error;
- (BOOL)validateArtitleContent:(NSError **)error;
- (NSError *)errorFromOriginalError:(NSError *)originalError error:(NSError *)secondError;
2. 開啟
NoteArticle.m 檔案, 新增 method 如下
//@add
- (BOOL)validateForInsert:(NSError **)error {
BOOL superValid = [super validateForInsert:error];
BOOL artitleTitleValid = [self validateArtitleTitle:error];
BOOL artitleContentValid = [self validateArtitleContent:error];
return (superValid && artitleTitleValid && artitleContentValid);
}
----------------------------------------------------------------------------------------------------------
//@add
- (BOOL)validateForUpdate:(NSError **)error {
BOOL superValid = [super validateForUpdate:error];
BOOL artitleTitleValid = [self validateArtitleTitle:error];
BOOL artitleContentValid = [self validateArtitleContent:error];
return (superValid && artitleTitleValid && artitleContentValid);
}
----------------------------------------------------------------------------------------------------------
//@add
- (BOOL)validateArtitleTitle:(NSError **)error
{
BOOL valid = YES;
// p.s. title is NoteArtitle model's attribute
if (self.title != nil) {
if (([self.title length] < 1) || ([self.title length] > 20)) {
valid = NO;
if (error != NULL) {
NSBundle *myBundle = [NSBundle bundleForClass:[self class]];
NSString *artitleTitleErrorString = [myBundle localizedStringForKey:@"StringLengthError" value:@"Artitle title length is too long or 0." table:@"ArtitleTitleErrorStrings"];
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
[userInfo setObject:artitleTitleErrorString forKey:NSLocalizedFailureReasonErrorKey];
[userInfo setObject:self forKey:NSValidationObjectErrorKey];
NSError *artitleTitleError = [NSError errorWithDomain:@"NoteArtitle" code:NSManagedObjectValidationError userInfo:userInfo];
// if there was no previous error, return the new error
if (*error == nil) {
*error = artitleTitleError;
}
// if there was a previous error, combine it with the existing one
else {
*error = [self errorFromOriginalError:*error error:artitleTitleError];
}
}
}
}
return valid;
}
----------------------------------------------------------------------------------------------------------
//@add
- (BOOL)validateArtitleContent:(NSError **)error
{
BOOL valid = YES;
// p.s. content is NoteArtitle model's attribute
if (self.content != nil) {
if ([self.content length] < 1) {
valid = NO;
if (error != NULL) {
NSBundle *myBundle = [NSBundle bundleForClass:[self class]];
NSString *artitleContentErrorString = [myBundle localizedStringForKey:@"StringLengthError" value:@"Artitle content length is 0." table:@"ArtitleContentErrorStrings"];
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
[userInfo setObject:artitleContentErrorString forKey:NSLocalizedFailureReasonErrorKey];
[userInfo setObject:self forKey:NSValidationObjectErrorKey];
NSError *artitleContentError = [NSError errorWithDomain:@"NoteArtitle" code:NSManagedObjectValidationError userInfo:userInfo];
// if there was no previous error, return the new error
if (*error == nil) {
*error = artitleContentError;
}
// if there was a previous error, combine it with the existing one
else {
*error = [self errorFromOriginalError:*error error:artitleContentError];
}
}
}
}
return valid;
}
----------------------------------------------------------------------------------------------------------
//@add
- (NSError *)errorFromOriginalError:(NSError *)originalError error:(NSError *)secondError
{
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
NSMutableArray *errors = [NSMutableArray arrayWithObject:secondError];
if ([originalError code] == NSValidationMultipleErrorsError) {
[userInfo addEntriesFromDictionary:[originalError userInfo]];
[errors addObjectsFromArray:[userInfo objectForKey:NSDetailedErrorsKey]];
}
else {
[errors addObject:originalError];
}
[userInfo setObject:errors forKey:NSDetailedErrorsKey];
return [NSError errorWithDomain:NSCocoaErrorDomain
code:NSValidationMultipleErrorsError
userInfo:userInfo];
}
3. 當輸入的 NoteArtitle
標題長度
大於 20 或者
沒有輸入時, Debug area 就會出現
以下的錯誤訊息, 並且程式會終止(crash? 以後再作
錯誤訊息的處理)
"The operation couldn't be completed. Artitle title length is too long or 0."
"NSLocalizedFailureReason = "Artitle title length is too long or 0.";
而當
沒有輸入 NoteArtitle 的
內容時, Debug area 就會出現以下的錯誤訊息,
並且程式會終止(crash? 以後再作
錯誤訊息的處理)
"The operation couldn't be completed. Artitle content length is 0."
"NSLocalizedFailureReason = "Artitle content length is 0.";