# Validation and Errors Handling

## VGSTextField Input Validation

VGSCollectSDK provides built-in validation for input data and files. Validation is based on defined validation rules set for each specific `VGSTextField`. These rule sets are defined by default for each `FieldType`, but you still have possibility to customize them.

`VGSTextField` will be validating after each textfield's editing event. You can check validation results through textfield's state fields: `textfield.state.isValid` & `textfield.state.validationErrors`.

### Default Validation

Here are few examples how default validation works for specific `FieldType`:

| **FieldType**       | **Validation Details**                                                                                                                                                                                                                                                             |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **.none**           | Any input will be valid if no other validation rules will be applied                                                                                                                                                                                                               |
| **.ssn**            | US social security number, should be 9 digits, don't start with \[000,666,9] or contain sequences of the same digits. Ignores some SSNs used in advertising                                                                                                                        |
| **.cardNumer**      | Card number validation with checkSum algorithm(Luhn algorithm), available card lengths for defined card types                                                                                                                                                                      |
| **.date**           | Any date that match the selected format. By default valid date should be in `mm-dd-yyyy`                                                                                                                                                                                           |
| **.expDate**        | Any date starting from current month. By default valid expiration date should be in short year format - `MM/yy`                                                                                                                                                                    |
| **.cardHolderName** | Name, should be 2 or more symbols, valid characters shold match pattern: `[a-zA-Z0-9]`                                                                                                                                                                                             |
| **.cvc**            | card cvc number, should be 3-4 digits. If in same `VGSCollect` instance there is associated `.cardNumer` field, **cvc** validation will be dynamic. It depends on input Card Brand type. For example: for **AMEX** valid cvc only with 4 digits, while for **Visa** 3 digits only. |

If you didn't find specific `FieldType` for your use case, you can always configure `VGSTextField` with `VGSConfiguration.fieldType = .none`. Then assign any specific rule set to the field and use other configuration attributes. Check more on our code examples below.

> • To get full details about how validation rules work for each specific fields you can check `VGSCollectSDK` source code.

### Available Validation Rules

VGSCollect SDK provide pre-build validation rules that you can apply to `VGSTextField` object through it's configuration.

Here are some examples of the available rules:

| **VGSValidationRule**               | **Description**                                                                                                                                                                              |
| ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| VGSValidationRuleLength             | Validate input in scope of `min` & `max` lengths                                                                                                                                             |
| VGSValidationRulePattern            | Validate input in scope of matching the regex `pattern`                                                                                                                                      |
| VGSValidationRuleDateRange          | Validate input in scope of matching date range. It also validate dates is in specific range. Supports `VGSDateFormat` date formats validation                                                |
| VGSValidationRuleCardExpirationDate | Validate input in scope of matching card expiration date format and time range. Supports `VGSCardExpDateFormat` date formats validation                                                      |
| VGSValidationRulePaymentCard        | Validate input in scope of matching supported card brands, available lengths and checkSum algorithms. Support optional validation of cards that are not defined in SDK - `CardBrand.unknown` |
| VGSValidationRuleLuhnCheck          | Validate input in scope of matching Luhn algorithm                                                                                                                                           |
| VGSValidationRuleLengthMatch        | Validate input in scope of multiple lengths, e.x.: \[16, 19]                                                                                                                                 |
| VGSValidationRuleABARoutingNumber   | Validate input against ABA (American Bankers Association) routing number requirements.                                                                                                       |

When you don't want to send to your organization vault data that is not valid, you can also set up additional `VGSConfiguration` attributes:

* **.isRequired** - if `true`, then the SDK will check if the input data in the field is **not empty** or **nil** on `VGSCollect.sendData(_:)` request. When validation failed, the DK will return a specific `VGSError` with `extraInfo` field that contains info about not valid fields.
* **.isRequiredValidOnly** - if `true`, then the SDK will check if the input data in the field is **valid** on `VGSCollect.sendData(_:)` request. When validation failed, Sthe DK will return specific `VGSError` with `extraInfo` field that contains info about not valid fields.

### How to customize VGSTextField Validation?

Here are a few examples of how to create your own validation rules set or edit existing rules that will validate input in `VGSTextField`.

Create a textfield that should be valid when input is **6-9 digits** long and starts with **555**

```swift
  let configuration = VGSConfiguration(collector: collector, fieldName: "secretNumber")
  configuration.type = .none
  configuration.formatPattern = "XXX-XXX-XXX"
  configuration.validationRules = VGSValidationRuleSet(rules: [
    VGSValidationRuleLength.init(min: 6, max: 9, error: "WRONG LENGTH"),
    VGSValidationRulePattern.init(pattern: "^555\\d*$", error: "PATTERN FAILED")
  ])
  textField.configuration = configuration
  textField.placeholder = "XXX-XXX-XXX"
```

Validate payment cards that are not supported by the SDK. In this case, all `.unknown` card types will go through `Luhn` check validation and possible card lengths.

```swift
  let cardConfiguration = VGSConfiguration(collector: collector, fieldName: "card_number")
  cardConfiguration.type = .cardNumber
  cardConfiguration.validationRules = VGSValidationRuleSet(rules:[
          VGSValidationRulePaymentCard(error: VGSValidationErrorType.cardNumber.rawValue, validateUnknownCardBrand: true)
        ])
  cardTextField.configuration = cardConfiguration
```

Validate date with format `mm/dd/yyyy` that is in a date range from `01/02/2010` and `01/02/2020`.

```swift
/// Define range validation
/// The validation will fails if the date is before 02/01/2010 or after 02/01/2020
let startDate = VGSDate(day: 2, month: 1, year: 2010)
let endDate = VGSDate(day: 2, month: 1, year: 2020)

/// Use `VGSDateConfiguration` when you need to set validation date range
let dateConfiguration = VGSDateConfiguration(
    collector: vgsCollect,
    fieldName: "date",
    datePickerStartDate: startDate,
    datePickerEndDate: endDate
)
dateConfiguration.inputDateFormat = .mmddyyyy

/// Update configuration in the text field
dateField.configuration = dateFieldConfiguration

/// Update validation rules, `start` and `end` should be the same used in the configuration `datePickerStartDate` and `datePickerEndDate`
dateConfiguration.validationRules = VGSValidationRuleSet(
    rules: [
        VGSValidationRuleDateRange(
            dateFormat: .mmddyyyy,
            error: VGSValidationErrorType.date.rawValue,
            start: startDate
            end: endDate
        )
    ]
)
```

Validate expiration date with long year format `MM/yyyy`.

```swift
  let expDateConfiguration = VGSConfiguration(collector: collector, fieldName: "exp_date")
  expDateConfiguration.type = .expDate
  expDateConfiguration.validationRules = VGSValidationRuleSet(rules: [
          VGSValidationRuleCardExpirationDate(dateFormat: .longYear, error:  VGSValidationErrorType.expDate.rawValue)
        ])
  expDateTextField.configuration = expDateConfiguration
```

Use the Input Validation results

```swift
extension ViewController: VGSTextFieldDelegate {

  func vgsTextFieldDidChange(_ textField: VGSTextField) {

    /// update textfield color on validation state changes
    textField.borderColor = textField.state.isValid ? .lightGray : .red

    /// show the validation error message
    validationLabel.text = textField.state.isValid ? "All is OK!" : textField.state.validationErrors.first
  }
}
```

### File Data validation

Before sending file data to your organization vault, `VGSCollectSDK` validates selected files.

Here are a few examples of file data validation rules:

* File source should be available for the App.
* It should be possible to convert a file into **base64**.
* File size doesn't exceed the limit of **24mb** and its size is not **0**.
* File not removed from the directory where it was selected before uploading to your organization's vault.

For each of these errors `VGSCollectSDK` will produce specific `VGSError` on `VGSCollect.sendFile(_:)`.

## Errors Handling

This guide covers error handling features that will help you make your App more reliable and recover from errors produced by `VGSCollectSDK`.

### VGSError

An object `VGSCollectSDK` used to produce errors inside the SDK. All `VGSCollectSDK` errors are produced under `VGSCollectSDKErrorDomain` domain.

#### Declaration

```swift
class VGSError: NSError
```

#### Attributes

| **Attribute**             | **Type/Description**                                     |
| ------------------------- | -------------------------------------------------------- |
| key                       | **String**, error key defined in `VGSErrorInfoKey.swift` |
| NSLocalizedDescriptionKey | **String**, error description for developer              |
| extraInfo                 | **Dictionary**, can contain additional error details     |

`VGSError` inherits iOS native `NSError` class and works similar. `VGSError` populates error codes, declared in `VGSErrorType`, and error keys, declared in [VGSCollectSDK Reference docs](https://verygoodsecurity.github.io/vgs-collect-ios/Error%20Keys.html).

#### Error codes

List of Error codes produced by VGS Collect iOS SDK locally.

| **Code** | **Key**                        | **Description**                                        |
| -------- | ------------------------------ | ------------------------------------------------------ |
| 1001     | `inputDataIsNotValid`          | When input data is not valid, but required to be valid |
| 1101     | `inputFileNotFound`            | When can't find file on device                         |
| 1102     | `inputFileTypeIsNotSupported`  | When selected file type not supported                  |
| 1103     | `inputFileSizeExceedsTheLimit` | When file size is larger then allowed limit            |
| 1150     | `sourceNotAvailable`           | When can't get access to file source                   |
| 1400     | `unexpectedResponseType`       | When response type is not supported                    |
| 1401     | `unexpectedResponseDataFormat` | When response data format is not supported             |
| 1480     | `invalidConfigurationURL`      | When VGS configuration URL is not valid                |
| 1300     | `invalidAccessToken`           | When access token is nil or empty                      |

#### Code example

```swift
vgsCollect.sendData(path: "/post", extraData: nil) { [weak self](../../response) in
  
  switch response {
  case .failure(let code, _, _, let error):
    switch code {
    /// Hande not valid fields data error
    case VGSErrorType.inputDataIsNotValid.rawValue:
      /// handle error
    /// ...
    return
  }
}
```
