update: 2011/12/05
A. 先在 data model 裡, 限制 NoteBook 的 name 屬性與 NoteArticle 的 title 屬性
之字串長度限制. (說明: 最小皆設為 1; 最大皆設為 25)
B. 實作 Validation Error Handing
1. 開啟 Common.h 檔案, 修改如下:
//@add
+ (NSString *)validationErrorText:(NSError *)error;
2. 開啟 Common.m 檔案, 修改如下:
//@add
#pragma mark -
#pragma mark Validation Error Handling
+ (NSString *)validationErrorText:(NSError *)error {
// Create a string to hold all the error messages
NSMutableString *errorText = [NSMutableString stringWithCapacity:100];
// Determine whether we're dealing with a single error or multiples, and put them all in an array
NSArray *errors = ([error code] == NSValidationMultipleErrorsError?
[[error userInfo] objectForKey:NSDetailedErrorsKey]:[NSArray arrayWithObject:error]);
// Iterate through the errors
for (NSError *err in errors) {
// Get the property that had a validation error
NSString *propName = [[err userInfo] objectForKey:@"NSValidationErrorKey"];
NSString *message = nil;
// From an appropriate error message
switch ([err code]) {
// Nonoptional property with a nil value
//case NSValidationMissingMandatoryPropertyError:
// message = [NSString stringWithFormat:@"%@ required", propName];
// break;
// Some string value is too short
case NSValidationStringTooShortError:
// name, title: must be at least 1 character
if ([propName isEqualToString:@"name"] || [propName isEqualToString:@"title"]) {
message = [NSString stringWithFormat:@"%@ must be at least %d character", propName, 1];
}
break;
// Some string value is too long
case NSValidationStringTooLongError:
// name, title: can't be longer than 25 characters
if ([propName isEqualToString:@"name"] || [propName isEqualToString:@"title"]) {
message = [NSString stringWithFormat:@"%@ can't be longer than %d characters", propName, 25];
}
break;
default:
message = @"Unknown error. Press Home button to halt.";
break;
}
// Separate the error message with line feeds
if ([errorText length] > 0) {
[errorText appendString:@"\n"];
}
[errorText appendString:message];
}
return errorText;
}
--------------------------------------------------------------------------------------------------
C. 新增 / 編輯 NoteBook 的 Validation 錯誤處理
1. 開啟 NoteBookViewController.h 檔案, 修改如下://- (void)saveContext;
//@update: it will return error message
- (NSString *)saveContext;
//- (void)insertNoteBookWithName:(NSString *)name subName:(NSString *)subName;
//@update: When we added an object, if save failure, we can delete it by keeping a pointer to it
- (NSManagedObject *)insertNoteBookWithName:(NSString *)name subName:(NSString *)subName;
--------------------------------------------------------------------------------------------------
2. 開啟 NoteBookViewController.m 檔案, 修改如下:
//- (void)saveContext {
//@update: it will return error message
- (NSString *)saveContext {
NSManagedObjectContext *context = [fetchedResultsController managedObjectContext];
NSError *error = nil;
//@add
NSString *errorText = nil;
if (![context save:&error]) {
//NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
//abort();
//@add
errorText = [Common validationErrorText:error];
}
//@add
return errorText;
}
--------------------------------------------------------------------------------------------------
//- (void)insertNoteBookWithName:(NSString *)name subName:(NSString *)subName {
//@update: When we added an object, if save failure, we can delete it by keeping a pointer to it
- (NSManagedObject *)insertNoteBookWithName:(NSString *)name subName:(NSString *)subName {
NSManagedObjectContext *context = [fetchedResultsController managedObjectContext];
NSEntityDescription *entity = [[fetchedResultsController fetchRequest] entity];
NSManagedObject *newNoteBook = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];
[newNoteBook setValue:name forKey:@"name"];
[newNoteBook setValue:subName forKey:@"subName"];
//@update
return newNoteBook;
//@update: handled in NoteBook.m
//[newNoteBook setValue:[NSDate date] forKey:@"dateCreated"];
//[newNoteBook setValue:[NSDate date] forKey:@"dateUpdate"];
// Save the context
//[self saveContext];
}
--------------------------------------------------------------------------------------------------
3. 開啟 ManageNBViewController.m 檔案, 修改如下:
//@add
- (IBAction)save:(id)sender {
//@add: trim NSString
nameField.text = [nameField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
subNameField.text = [subNameField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
//@add
NSString *errorText = nil;
//@add: Create variables to store pre-change state, so we can back out of if validation errors occur
NSManagedObject *tempNoteBook = nil;
NSString *tempName = nil;
NSString *tempSubName = nil;
NSDate *tempUpdateDate = nil;
if (parenetController != nil) {
/* editing */
if (noteBook != nil) {
//@add: User is editing an existing noteBook. Store its current values
tempName = [NSString stringWithString:[noteBook valueForKey:@"name"]];
tempSubName = [NSString stringWithString:[noteBook valueForKey:@"subName"]];
tempUpdateDate = [noteBook valueForKey:@"dateUpdate"];
// Update with the new values
[noteBook setValue:nameField.text forKey:@"name"];
[noteBook setValue:subNameField.text forKey:@"subName"];
//now date
[noteBook setValue:[NSDate date] forKey:@"dateUpdate"];
// Save the context
//[parenetController saveContext];
}
/* creating a new noteBook */
else {
//[parenetController insertNoteBookWithName:nameField.text subName:subNameField.text];
//@update: User is adding a new noteBook. Create the new managed object but keep a pointer to it
tempNoteBook = [parenetController insertNoteBookWithName:nameField.text subName:subNameField.text];
}
//@update: Save the context and gather any validation errors
errorText = [parenetController saveContext];
}
//@add
if (errorText != nil) {
//@add: Validation error occurred. Show an alert
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error!" message:errorText delegate:nil cancelButtonTitle:nil otherButtonTitles:@"OK", nil];
[alert show];
[alert release];
// Because we had errors and the context didn't save, undo any changes this method made
if (tempNoteBook != nil) {
//@add: We added an object, so delete it
[[parenetController.fetchedResultsController managedObjectContext] deleteObject:tempNoteBook];
}
else {
//@add: We edited an object, so restore it to how it was
[noteBook setValue:tempName forKey:@"name"];
[noteBook setValue:tempSubName forKey:@"subName"];
[noteBook setValue:tempUpdateDate forKey:@"dateUpdate"];
}
}
else {
//@add: Successful save! Dismiss the modal only on success
[self dismissModalViewControllerAnimated:YES];
}
//[self dismissModalViewControllerAnimated:YES];
}
--------------------------------------------------------------------------------------------------
1. 開啟 ArticleListViewController.h 檔案, 修改如下:
//- (void)saveContext;
//@update: it will return error message
- (NSString *)saveContext;
//- (void)insertArticleWithTitle:(NSString *)title subTitle:(NSString *)subTitle content:(NSString *)content;
//@update: When we added an object, if save failure, we can delete it by keeping a pointer to it
- (NSManagedObject *)insertArticleWithTitle:(NSString *)title subTitle:(NSString *)subTitle content:(NSString *)content;
--------------------------------------------------------------------------------------------------
2. 開啟 ArticleListViewController.m 檔案, 修改如下:
//- (void)saveContext {
//@update: it will return error message
- (NSString *)saveContext {
NSManagedObjectContext *context = [fetchedResultsController managedObjectContext];
NSError *error = nil;
//@add
NSString *errorText = nil;
if (![context save:&error]) {
//NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
//abort();
//@add
errorText = [Common validationErrorText:error];
}
//@add
return errorText;
}
--------------------------------------------------------------------------------------------------
//- (void)insertArticleWithTitle:(NSString *)title subTitle:(NSString *)subTitle content:(NSString *)content {
//@update: When we added an object, if save failure, we can delete it by keeping a pointer to it
- (NSManagedObject *)insertArticleWithTitle:(NSString *)title subTitle:(NSString *)subTitle content:(NSString *)content {
NSManagedObjectContext *context = [fetchedResultsController managedObjectContext];
NSEntityDescription *entity = [[fetchedResultsController fetchRequest] entity];
NSManagedObject *newArticle = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];
[newArticle setValue:title forKey:@"title"];
[newArticle setValue:subTitle forKey:@"subTitle"];
[newArticle setValue:content forKey:@"content"];
//@update: use NSSet to handle to-many relationship
NSMutableSet *articles = [self.noteBook mutableSetValueForKey:@"articles"];
[articles addObject:newArticle];
//@add
return newArticle;
//@update: handled in NoteArticle.m
//Date
//[newArticle setValue:[NSDate date] forKey:@"dateCreated"];
//[newArticle setValue:[NSDate date] forKey:@"dateUpdate"];
//[self.noteBook setValue:newArticle forKey:@"articles"];
// 'NSInvalidArgumentException', reason: 'Unacceptable type of value for to-many relationship: property = "articles"; desired type = NSSet; given type = NSManagedObject;
//@use NSSet to handle to-many relationship
/*
NSMutableSet *articles = [self.noteBook mutableSetValueForKey:@"articles"];
[articles addObject:newArticle];
// Save the context
[self saveContext];
*/
}
--------------------------------------------------------------------------------------------------
3. 開啟 ManageArticleViewController.m 檔案, 修改如下:
//@add
- (IBAction)save:(id)sender {
//@add: trim NSString
titleField.text = [titleField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
subTitleField.text = [subTitleField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
contentText.text = [contentText.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
//@update
NSString *errorText = nil;
//@add: Create variables to store pre-change state, so we can back out of if validation errors occur
NSManagedObject *tempArticle = nil;
NSString *tempTitle = nil;
NSString *tempSubTitle = nil;
NSString *tempContent = nil;
NSDate *tempUpdateDate = nil;
if (parenetController != nil) {
/* editing */
if (article != nil) {
//@add: User is editing an existing article. Store its current values
tempTitle = [NSString stringWithString:[article valueForKey:@"title"]];
tempSubTitle = [NSString stringWithString:[article valueForKey:@"subTitle"]];
tempContent = [NSString stringWithString:[article valueForKey:@"content"]];
tempUpdateDate = [article valueForKey:@"dateUpdate"];
// Update with the new values
[article setValue:titleField.text forKey:@"title"];
[article setValue:subTitleField.text forKey:@"subTitle"];
[article setValue:contentText.text forKey:@"content"];
//now date
[article setValue:[NSDate date] forKey:@"dateUpdate"];
// Save the context
//[parenetController saveContext];
}
/* creating a new article */
else {
//[parenetController insertArticleWithTitle:titleField.text subTitle:subTitleField.text content:contentText.text];
//@add: User is adding a new article. Create the new managed object but keep a pointer to it
tempArticle = [parenetController insertArticleWithTitle:titleField.text subTitle:subTitleField.text content:contentText.text];
}
//@update: Save the context and gather any validation errors
errorText = [parenetController saveContext];
}
//@add
if (errorText != nil) {
//@add: Validation error occurred. Show an alert
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error!" message:errorText delegate:nil cancelButtonTitle:nil otherButtonTitles:@"OK", nil];
[alert show];
[alert release];
//@add: Because we had errors and the context didn't save, undo any changes this method made
if (tempArticle != nil) {
//@add: We added an object, so delete it
[[parenetController.fetchedResultsController managedObjectContext] deleteObject:tempArticle];
}
else {
//@add: We edited an object, so restore it to how it was
[article setValue:tempTitle forKey:@"title"];
[article setValue:tempSubTitle forKey:@"subTitle"];
[article setValue:tempContent forKey:@"content"];
[article setValue:tempUpdateDate forKey:@"dateUpdate"];
}
}
else {
//@add: Successful save! Dismiss the modal only on success
[self dismissModalViewControllerAnimated:YES];
}
//[self dismissModalViewControllerAnimated:YES];
}
--------------------------------------------------------------------------------------------------
E. 結果如下:
沒有留言:
張貼留言
注意:只有此網誌的成員可以留言。