Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/100.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 如何通过核心数据验证将有意义的错误消息返回给视图控制器?_Ios_Objective C_Validation_Core Data - Fatal编程技术网

Ios 如何通过核心数据验证将有意义的错误消息返回给视图控制器?

Ios 如何通过核心数据验证将有意义的错误消息返回给视图控制器?,ios,objective-c,validation,core-data,Ios,Objective C,Validation,Core Data,编辑:我想让大家知道,在我发布这篇文章的一个月里,我一直在解决这个问题。现在有一个演示如何轻松实现这一点并保持与KVC兼容的示例。没有理由避免在iOS上进行核心数据验证。它可能与MacOSX不同,但并不困难 我正在视图控制器中编辑Person对象的属性。Person是NSManagedObject子类。我正在对核心数据进行早期(保存前)验证。我使用的是有文档记录的validateValue:forKey:error:方法,如下所示 NSError *error; BOOL isValid =

编辑:我想让大家知道,在我发布这篇文章的一个月里,我一直在解决这个问题。现在有一个演示如何轻松实现这一点并保持与KVC兼容的示例。没有理由避免在iOS上进行核心数据验证。它可能与MacOSX不同,但并不困难


我正在视图控制器中编辑Person对象的属性。Person是NSManagedObject子类。我正在对核心数据进行早期(保存前)验证。我使用的是有文档记录的
validateValue:forKey:error:
方法,如下所示

NSError *error;
BOOL isValid = [person validateValue:&firstNameString forKey:@"firstName" error:&error];
if (!isValid) {
    ...
}
Error Domain=NSCocoaErrorDomain Code=1670 "The operation couldn’t be completed. (Cocoa error 1670.)" UserInfo=0x8f44a90 {NSValidationErrorObject=<Event: 0xcb41a60> (entity: Event; id: 0xcb40d70 <x-coredata://ADB90708-BAD9-47D8-B722-E3B368598E94/Event/p1> ; data: {
firstName = B;
}), NSValidationErrorKey=firstName, NSLocalizedDescription=The operation couldn’t be completed. (Cocoa error 1670.), NSValidationErrorValue=B}
    switch ([error code]) {

        case NSValidationStringTooShortError:
            errorMsg = @"First name must be at least two characters.";
            break;

        case NSValidationStringTooLongError:
            errorMsg = @"First name is too long.";
            break;

    // of course, for real, these would be localized strings, not just hardcoded like this
    }
我在Xcode的核心数据模型编辑器中设置了最小值和最大值。当我验证
firstName
时,它太短,我会得到这样一个错误

NSError *error;
BOOL isValid = [person validateValue:&firstNameString forKey:@"firstName" error:&error];
if (!isValid) {
    ...
}
Error Domain=NSCocoaErrorDomain Code=1670 "The operation couldn’t be completed. (Cocoa error 1670.)" UserInfo=0x8f44a90 {NSValidationErrorObject=<Event: 0xcb41a60> (entity: Event; id: 0xcb40d70 <x-coredata://ADB90708-BAD9-47D8-B722-E3B368598E94/Event/p1> ; data: {
firstName = B;
}), NSValidationErrorKey=firstName, NSLocalizedDescription=The operation couldn’t be completed. (Cocoa error 1670.), NSValidationErrorValue=B}
    switch ([error code]) {

        case NSValidationStringTooShortError:
            errorMsg = @"First name must be at least two characters.";
            break;

        case NSValidationStringTooLongError:
            errorMsg = @"First name is too long.";
            break;

    // of course, for real, these would be localized strings, not just hardcoded like this
    }
这在概念上是好的,但其他视图控制器上的
firstName
和其他
Person
属性是可编辑的,因此必须在任何视图控制器编辑的
firstName
上再次实现切换。一定有更好的办法

查看用于属性级验证的核心数据文档可以发现

If you want to implement logic in addition to the constraints 
you provide in the managed object model, you should not override 
validateValue:forKey:error:. Instead you should implement methods 
of the form validate<Key>:error:. 
如果要实现约束之外的逻辑
如果在托管对象模型中提供,则不应重写
validateValue:forKey:错误:。相反,您应该实现方法
表单的验证:错误:。
所以我亲自实现了
validateFirstName:error:
。而且很方便,它通过视图控制器中现有的
validateValue:forKey:error:
方法执行,正如文档所说的那样

但是在
validateFirstName:error:
内部,
error
仍然为零,即使
firstName
太短。当我继续并将控制返回到视图控制器时,会出现一个错误,如问题顶部所示,但再次说明,为时已晚。我希望在控件到达
validateFirstName:error:
时,
firstName
将根据模型编辑器中指定的约束进行验证,并且填充的error对象将通过error参数传入。但事实并非如此

我剩下的两个想法可能会给switch语句带来一个好的归宿

  • 在Person.m中实现一个自定义方法,如
    firstnamevalizationforvalue:error:
    。视图控制器将调用该方法。在
    firstNameValidationForValue:error:
    中调用
    validateValue:forKey:error:
    。当返回错误时,使用开关构造一条有意义的错误消息,创建一个新的NSError对象,并将其返回给view controller以供使用。这是可行的,但它偏离了标准的KVC方法

  • 从Xcode中的核心数据模型编辑器中删除所有验证,并使用
    validateFirstName:error:
    等方法执行所有验证。根据结果,使用开关构造一条有意义的错误消息,创建一个新的NSError对象,并将其返回给视图控制器以供使用。这样做的优点是约束和错误消息位于同一方法中。而且,与第一个想法不同,它继续遵循标准的KVC方法

  • 你会怎么做?还有别的办法吗

    编辑:有关编辑周期的其他详细信息

    编辑周期从用户点击Add开始。将新的Person对象插入MOC。该视图显示一个用于编辑的表单,导航栏上显示“取消”和“完成”按钮。用户开始在字段A中输入数据,完成并点击字段B。假设fieldA在继续之前必须有效。在fieldB成为第一响应者之前,对fieldA进行验证。它失败了。视图控制器显示验证返回的错误消息,fieldA仍然是第一响应者。用户修复了问题,并再次点击fieldB。验证再次运行,这次通过。fieldB成为第一响应者。此“添加数据/点击另一个字段或点击下一个/验证/是否移动到下一个字段”过程将继续

    重要的是要知道,用户可以随时点击“取消”。在这种情况下,就MOC而言,我所要做的就是[myMOC回滚]

    @ImHuntingWabbits:如果调用
    save
    而不是
    validateValue:forKey:error:
    则该方法存在问题。假设用户正在fieldA中输入数据。用户点击fieldB并运行fieldA验证。此验证使用“保存并分析错误”方法。但是假设它通过了,所有其他字段也通过了。所以现在主运行中心已经保存。但是用户还没有点击Done,很可能会按Cancel。如果用户点击“取消”,则必须撤消保存。如果模型非常简单,但可能非常复杂,那么这可能相对容易。在我的特殊情况下,我不想采取这种方法

    另一次编辑

    我们可以在github再次开会吗:我在那里有一个示例应用程序,也许在自述中对这个问题有更好的描述。该示例应用程序表明,在iOS上进行验证通常并不困难。该应用程序演示了如何将有意义的错误消息返回给VC,并保持与KVC完全兼容


    它讨论了对核心数据的两种可能的增强,我希望在将它们放到雷达上之前得到一些反馈

    也许您已经考虑过这一点,但您似乎可以在
    Person
    类中重写NSManagedObject的
    -(BOOL)validateForUpdate:(NSError**)error
    方法,并调用
    super
    的实现来执行MOC验证(即,让核心数据确定您的属性是否符合数据模型的限制条件),如果返回错误,您可以进行一些快速逻辑,以精确缩小错误范围,并包括您自己的,m
    // ViewController
    -(void)checkPerson:(Person *)myPerson  { 
         NSError *error;
         if ([myPerson validateForUpdate:&error]) { 
               // all is well
               error = nil; // zap any previous error
         } else { 
               // validation failed
               if ([[error domain] isEqualToString:_kErrorDomain]) { // error is from our code
                  // display error 
                  NSLog(@"Error message received: %@", [error localizedDescription ]);
    
                 // handle the errorCode
                 if ([error errorCode] == -1)  // do something
                 else  // do something else
    
               } else {
                 // error is not our domain. If needed, then take care of the others, too
               }
         } // end if/then validateForUpdate
    } 
    
    User:
        name:        NSString    optional:no
        email:       NSString    optional:no, min_length:3
    
    @interface User : NSManagedObject
    
    + (User*) createWithParameters:(NSDictionary*)parameters
            inManagedObjectContext:(NSManagedObjectContext*)context;
    
    @property (nonatomic, retain) NSString * name;
    @property (nonatomic, retain, readonly) NSString * user_id;
    @property (nonatomic, retain) NSString * email;
    @end
    
    NSDictionary* user =   @{
        @"name":        @"John Appleseed",
        @"user_id":     @"634aa621-c63d-4085-aa27-bc3f3b02bcda",
        @"email":       @"e
    };
    
    NSError* error;
    if (![context save:&error]) {
        NSLog(@"ERROR: %@", error);
    }
    
    Error Domain=NSCocoaErrorDomain 
          Code=1670 
          "The operation couldn't be completed. (Cocoa error 1670.)" 
    UserInfo=0x2917570 {
        NSValidationErrorObject=<User: 0x28bbc20> (...), 
        NSValidationErrorKey=email, 
        NSLocalizedDescription=The operation couldn\U2019t be completed. (Cocoa error 1670.), 
        NSValidationErrorValue=e
      }"
    
    enum {
        NSManagedObjectValidationError                   = 1550,   // generic validation error
        NSValidationMultipleErrorsError                  = 1560,   // generic message for error containing multiple validation errors
        NSValidationMissingMandatoryPropertyError        = 1570,   // non-optional property with a nil value
        NSValidationRelationshipLacksMinimumCountError   = 1580,   // to-many relationship with too few destination objects
        NSValidationRelationshipExceedsMaximumCountError = 1590,   // bounded, to-many relationship with too many destination objects
        NSValidationRelationshipDeniedDeleteError        = 1600,   // some relationship with NSDeleteRuleDeny is non-empty
        NSValidationNumberTooLargeError                  = 1610,   // some numerical value is too large
        NSValidationNumberTooSmallError                  = 1620,   // some numerical value is too small
        NSValidationDateTooLateError                     = 1630,   // some date value is too late
        NSValidationDateTooSoonError                     = 1640,   // some date value is too soon
        NSValidationInvalidDateError                     = 1650,   // some date value fails to match date pattern
        NSValidationStringTooLongError                   = 1660,   // some string value is too long
        NSValidationStringTooShortError                  = 1670,   // some string value is too short
        NSValidationStringPatternMatchingError           = 1680,   // some string value fails to match some pattern
        ...
    }
    
    `@"The string for attribute '%@' is too short."`
    
    NSString* descFormat = [CoreDataValidationErrorHelper errorDescriptionForCode:error.code];
    NSString* desc = [NSString stringWithFormat:descFormat, error.user[NSValidationKeyErrorKey]];