# VGSCollect and States

### VGSCollect

An object you use for observing `VGSTextField` states and send data

#### Declaration

```swift
class VGSCollect
```

#### Creating a VGSCollect

```swift

/// Initialzation.
///
/// - Parameters:
///   - id: your organization vault id.
///   - environment: your organization vault environment with data region.(e.g. "live", "live-eu1", "sanbox").
///   - hostname: Custom Hostname, if not set, data will be sent to Vault Url
init(id: String, environment: String, hostname: String? = nil)

/// Initialzation.
///
/// - Parameters:
///   - id: your organization vault id.
///   - environment: your organization vault environment. By default `Environment.sandbox`.
///   - dataRegion: id of data storage region (e.g. "eu-123").
///   - hostname: Custom Hostname, if not set, data will be sent to Vault Url
init(id: String, environment: Environment = .sandbox, dataRegion: String? = nil, hostname: String? = nil)
```

> • All **VGSCollect** instances in the app will be working independently and only with fields that have them in `VGSConfiguration` settings.\
> • All **VGSCollect** instances can be configured differently.

`VGSCollect` instance grab the data from `VGSTextFields` during `sendData(_:)` request. By default, it has strong reference with all textfield and files related with to it. If you need to remove reference from textfield or file before `VGSCollect` instance is deallocated, you can do it manually whenever you need it.

#### Code example

```swift

/// unsubscribe single VGSTextField
vgsCollect.unsubscribeTextField(cardNumber)

/// unsubscribe all VGSTextFields related to VGSCollect instance
vgsCollect.unsubscribeAllTextFields()

```

#### Multiple Data Regions

Please use the following configuration to set up a specific data region if needed.

```swift
/// Check your data region, it should look like: "live-eu-1"
/// In your code follow the VGSCollect initialization pattern
var vgsCollect = VGSCollect(id: "<VAULT_ID>", environment: .live, dataRegion: "eu-1")

///When you set enivronment as String, you can point region directly in environment string
var vgsCollect = VGSCollect(id: "<VAULT_ID>", environment: "live-eu-1")
```

#### Add a Custom Hostname

When integrated with VGS, by default, the traffic is passed via VGS proxy, which has `tntxxxxx.sandbox.verygoodproxy.com` format, where `tntxxxxx` is your Vault identifier. Collect SDK allows you to use your custom hostname and make requests to a non-VGS domain name or localhost (on your local machine).\
Before adding a new Hostname you should create a CNAME record for your existing domain in your DNS provider’s account that should point to `<VAULT_ID>.<ENVIRONMENT>.verygoodproxy.com`.

\*\* Learn more about [Custom Hostnames](https://docs.verygoodsecurity.com/vault/http-proxy/inbound-connection/custom-hostnames)\*\*.

Then, in your Application create a `VGSCollect` instance with the Custom Hostname:

```swift

var vgsCollect = VGSCollect(id: "<VAULT_ID>", environment: <ENVIRONMENT>, hostname: "www.customdomain.com")

```

> • When the hostname is not valid or not registered in VGS system, it will be ignored by SDK and all the traffic will be passed via VGS proxy directly.\
> • Only **https** scheme is supported.\\

## Observing States

There are three types of `VGSTextField` states available that depends on choosen `FieldType` in `VGSConfiguration`: **State**, **VGSCardState** and **VGSSSNState**.

### State

An object you use for observing `VGSTextField` parameters that depends on input and `VGSConfiguration`.

#### Declaration

```swift
class State
```

You should not create **State** object by itself. Instead, get access to it from `VGSTextField.state` attribute. `State` object and it's attributes are **read-only**.

#### Some of State attributes

| **Attribute** | **Type/Default value**                                      |
| ------------- | ----------------------------------------------------------- |
| `fieldName`   | `VGSConfiguration.fieldName` associated with `VGSTextField` |
| `isValid`     | Contains `VGSTextField` current validation state            |
| `inputLength` | Contains user input characters count in `VGSTextField`      |

To get a full list of `State` attributes check [VGSCollectSDK Reference docs](https://verygoodsecurity.github.io/vgs-collect-ios/Classes/State.html)

### VGSCardState

An object you use for observing `VGSTextField` parameters when `VGSConfiguration.type` defined as `FieldType.cardNumber`.

#### Declaration

```swift
class VGSCardState: State
```

**VGSCardState** extends default `State` with more specific attributes related to credit card numbers

#### VGSCardState attributes

| **Attribute** | **Type/Default value**                                            |
| ------------- | ----------------------------------------------------------------- |
| `last4`       | Last 4 digits of the valid card number                            |
| `bin`         | Bin digits of the valid card number                               |
| `cardBrand`   | `CardType` object describing Credit Card Brand of the card number |

> * **bin** and **last4** attributes will be empty until the input card number is valid.

To get a full list of supported card brands check [VGSCollectSDK Reference docs](https://docs.verygoodsecurity.com/vault/developer-tools/test-data#test-credit-and-debit-cards)

### VGSSSNState

An object you use for observing `VGSTextField` parameters when `VGSConfiguration.type` defined as `FieldType.ssn`.

#### Declaration

```swift
class VGSSSNState: State
```

**VGSSSNState** extends default `State` with more specific attributes related to social security numbers

#### VGSSSNState attributes

| **Attribute** | **Type/Default value**                 |
| ------------- | -------------------------------------- |
| `last4`       | Last 4 digits of the valid card number |

> * **last4** attributes will be empty until the input social security number is valid.

#### Code example

```swift
extension ViewController: VGSTextFieldDelegate {
  /// Check
  func vgsTextFieldDidChange(_ textField: VGSTextField) {

    /// VGSCardState is extended State for textfields with type .cardNumber
    if let cardState = textField.state as? VGSCardState {
      if cardState.cardBrand.cardLengths.contains(cardState.inputLength) {
        print("User add full card number")
      }
      if cardState.isValid {
        print("\(cardState.cardBrand.stringValue): \(cardState.bin)******\(cardState.last4)")
      }
    }

    /// VGSSSNState is extended State for textfields with type .ssn
    else if let ssnState = textField.state as? VGSSSNState {
      if ssnState.isValid {
        print("\(ssnState.last4)")
      }
    }
  }
}
```

### State Publisher

If you preffere to use Combine framework, you can observe state changes with `.statePublisher` attribure. It will be triggered on each `VGSTextField`'s interaction event, similar to `VGSTextFieldDelegate` events.

#### Declaration

```swift
/// `VGSTextFieldStatePublisher` publisher that emits the `State` of a given `VGSTextField`.
var statePublisher: VGSTextFieldStatePublisher
```

#### Code example

```swift
/// Track textfield state changes
cardHolderName.statePublisher.sink { [weak self] state in
  self?.cardHolderName.borderColor = state.isValid ? .lightGray : .red
}.store(in: &cancellables)

/// Map State to VGSCardState to get access for card attributes
cardNumber.statePublisher.compactMap { state -> VGSCardState? in
  return state as? VGSCardState
}.sink { [weak self] cardState in
  self?.cardNumber.borderColor = cardState.isValid ? .lightGray : .red
}.store(in: &cancellables)
```

### Observe VGSTextField states

You can observe states every time `VGSTextField` input is changed. Observer callback will be called on each `VGSTextField`'s interaction event. For more granular editing events handler, check [VGSTextFieldDelegate](https://docs.verygoodsecurity.com/vault/developer-tools/vgs-collect/ui-components#vgstextfielddelegate) methods.

```swift
/// Observe only focused text field
var observeFieldState: ((_ textField: VGSTextField) -> Void)?

/// Observe all text fields registered in a VGSCollect instance
var observeStates: ((_ form: [VGSTextField]) -> Void)?
```

#### Code example

```swift
// ...

/// Observe all textfields states on editing
vgsCollect.observeStates = { textFields in

    textFields.forEach({ textField in
         print(textField.state.description)

         /// VGSCardState is extended state for textfields with type .cardNumber
         if let cardState = textField.state as? VGSCardState, cardState.isValid {
              print("\(cardState.cardBrand.stringValue): \(cardState.bin)******\(cardState.last4)")
         }
    })
}
```

For more details about available attributes and functionality check [VGSCollectSDK Reference docs](https://verygoodsecurity.github.io/vgs-collect-ios/)
