Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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!

like image 427
Kermit the Frog Avatar asked Oct 16 '13 21:10

Kermit the Frog


2 Answers

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;
    }
like image 28
William Falcon Avatar answered Sep 29 '22 14:09

William Falcon


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;
}
like image 155
Holly Avatar answered Sep 29 '22 12:09

Holly