Kermit the Frog
Kermit the Frog

Reputation: 3969

How do you use Key Value Validation?

Can someone walk me through an example of how to use key value validation in IOS? I am confused.

I am writing a Payments SDK, where people pass in credit card number, security code, etc to a Card class, and I need to validate these values. For example, make sure the credit card number is valid.

Is it possible to do automatic validation?

Also, can we call all of the validators at once?

Like if I have a Card class, can I call if([card isValid]) to call all of the validate functions at once without doing so myself? like:

Card * card = [[Card alloc] init];
card.number = @"424242...";
card.securityCode = @"455";
card.expirationMonth = @"";
card.expirationYear = @"";
if([card isValid]) {

Thanks for your help!

Upvotes: 10

Views: 2540

Answers (3)

Holly
Holly

Reputation: 5300

The link provided by Susan has all the detail you should need. An example implementation would be like this:

- (BOOL)validateSecurityCode:(id *)ioValue 
                       error:(NSError * __autoreleasing *)outError 
{
    // The securityCode must be a numeric value exactly 3 digits long
    NSString *testValue = (NSString *)*ioValue;
    if (([testValue length]!=3) || ![testValue isInteger])) {
        if (outError != NULL) {
            NSString *errorString = NSLocalizedString(
                    @"A Security Code must be exactly 3 characters long.",
                    @"validation: Security Code, invalid value");
            NSDictionary *userInfoDict = @{ NSLocalizedDescriptionKey : errorString };
            *outError = [[NSError alloc] initWithDomain:SECURITYCODE_ERROR_DOMAIN
                                                   code:SECURITYCODE_INVALID_NAME_CODE
                                               userInfo:userInfoDict];
        }
        return NO;
    }
    return YES;
}

Note: I used NSString -isInteger from this post.

The manual says

You can call validation methods directly, or by invoking validateValue:forKey:error: and specifying the key.

The advantage of this is that your - (BOOL)isValid method can be very simply.

- (BOOL)isValid
{
    static NSArray *keys = nil;
    static dispatch_once_t once;
    dispatch_once(&once, ^{
        keys = @[@"securityCode", @"number", @"expirationMonth", @"expirationYear"];
    });

    NSError *error = nil;
    for (NSString *aProperty in keys) {
        BOOL valid = [self validateValue:[self valueForKey:aProperty]
                                  forKey:aProperty
                                   error:&error];
        if (!valid) {
            NSLog("Validation Error: %@", error);
            return NO;
        }
    }
    return YES;
}

Upvotes: 6

William Falcon
William Falcon

Reputation: 9813

Here's an example of key-value validation.

According to Apple:

Key-value coding provides a consistent API for validating a property value. The validation infrastructure provides a class the opportunity to accept a value, provide an alternate value, or deny the new value for a property and give a reason for the error.

https://developer.apple.com/library/mac/documentation/cocoa/conceptual/KeyValueCoding/Articles/Validation.html

Method Signature

-(BOOL)validateName:(id *)ioValue error:(NSError * __autoreleasing *)outError {
    // Implementation specific code.
    return ...;
}

Properly calling the method

Apple: You can call validation methods directly, or by invoking validateValue:forKey:error: and specifying the key.

Ours:

//Shows random use of this
    -(void)myRandomMethod{

        NSError *error;

        BOOL validCreditCard = [self validateCreditCard:myCreditCard error:error];
    }

Our test implementation of your request

    //Validate credit card
    -(BOOL)validateCreditCard:(id *)ioValue error:(NSError * )outError{

        Card *card = (Card*)ioValue;

        //Validate different parts
        BOOL validNumber = [self validateCardNumber:card.number error:outError];
        BOOL validExpiration = [self validateExpiration:card.expiration error:outError];
        BOOL validSecurityCode = [self validateSecurityCode:card.securityCode error:outError];

        //If all are valid, success
        if (validNumber && validExpiration && validSecurityCode) {
            return YES;

            //No success
        }else{
            return NO;
        }
    }

    -(BOOL)validateExpiration:(id *)ioValue error:(NSError * )outError{

        BOOL isExpired = false;

        //Implement expiration

        return isExpired;
    }

    -(BOOL)validateSecurityCode:(id *)ioValue error:(NSError * )outError{

    //card security code should not be nil and more than 3 characters long
    if ((*ioValue == nil) || ([(NSString *)*ioValue length] < 3)) {

        //Make sure error us not null
        if (outError != NULL) {

            //Localized string
            NSString *errorString = NSLocalizedString(
                                                      @"A card's security code must be at least three digits long",
                                                      @"validation: Card, too short expiration error");

            //Place into dict so we can add it to the error
            NSDictionary *userInfoDict = @{ NSLocalizedDescriptionKey : errorString };

            //Error
            *outError = [[NSError alloc] initWithDomain:CARD_ERROR_DOMAIN
                                                   code:CARD_INVALID_SECURITY_CODE
                                               userInfo:userInfoDict];
        }
        return NO;
    }
    return YES;
}

    -(BOOL)validateCardNumber:(id *)ioValue error:(NSError * )outError{

        BOOL isValid = false;

        //Implement card number verification

        return isValid;
    }

Upvotes: 2

RyanR
RyanR

Reputation: 7758

Key Value Coding (KVC) Validation is for validating a single value that you want to put in a specific property. It is not automatic and you should never invoke a validation method within the -set accessor for a property. The intended use (at least the way I've seen it used in Apple samples or used it myself) is to validate input from the user before mutating your object. On Mac OSX applications with Cocoa Bindings this is very useful, but on iOS not so much.

I don't believe KVC Validation is what you are looking for since you want to check that all of an objects property values are valid. As others have posted you could make it work, but it would add complexity without any significant benefit.

Upvotes: 0

Related Questions