# Collect and Send Data

### Collect and Send Data

With the VGS Collect SDK, you can tokenize data via the **Vault API** or securely send it to your server using the **VGS Proxy**.

### Send Data to Your Server via VGS Proxy

Using the `sendData(_:)` method, sensitive data from `VGSTextField` will be collected, stored in your organization's Vault, and sent to the Upstream URL specified in the Inbound Route.

```swift
/**
  Send data from VGSTextFields to your organization vault.
  
  - Parameters:
    - path: Inbound rout path for your organization vault.
    - method: HTTPMethod, default is `.post`.
    - routeId: id of VGS Proxy Route, default is `nil`.
    - extraData: Any data you want to send together with data from VGSTextFields , default is `nil`.
    - requestOptions: `VGSCollectRequestOptions` object, holds additional request options. Default options are `.nestedJSON`.
    - completion: response completion block, returns `VGSResponse`.
  
  - Note:
    Errors can be returned in the `NSURLErrorDomain` and `VGSCollectSDKErrorDomain`.
*/
public func sendData(path: String,
                    method: HTTPMethod = .post,
                  routeId: String? = nil,
                extraData: [String: Any]? = nil,
            requestOptions: VGSCollectRequestOptions = VGSCollectRequestOptions(),
          completion block: @escaping (VGSResponse) -> Void)

/**
  Asynchronously send data from VGSTextFields to your organization vault.
*/
public func sendData(path: String,
                    method: HTTPMethod = .post,
                  routeId: String? = nil,
                extraData: [String: Any]? = nil,
            requestOptions: VGSCollectRequestOptions = VGSCollectRequestOptions()) async throws -> VGSResponse

/**
  Send data from VGSTextFields to your organization vault using the Combine framework.
*/
public func sendDataPublisher(path: String,
                    method: HTTPMethod = .post,
                  routeId: String? = nil,
                extraData: [String: Any]? = nil,
            requestOptions: VGSCollectRequestOptions = VGSCollectRequestOptions()) -> Future<VGSResponse, Never> 
```

The send data request returns `VGSResponse.failure(_:)` in the following cases:\
• The response status code is invalid (i.e., not in the range **\[200..<300]**).\
• The request is not possible due to iOS system conditions (e.g., no internet connection).\
• The data being sent is invalid according to `VGSConfiguration`.\\

Errors can be returned in the `NSURLErrorDomain` and `VGSCollectSDKErrorDomain`.\
For more details, check the [SDK Reference Docs](https://verygoodsecurity.github.io/vgs-collect-ios/Errors.html).

> • Data will be collected from all text fields that registered to current `VGSCollect`instance and send to your organization vault id.\
> • By default `aliases` are not a part of response data for `sendData(_:)` request. It's your responsibility to configure a response for the client after the data goes through the Inbound Proxy to your backend. However, if you test `sendData(_:)` request with our echo server, you will get a response with the same body as in the request, where the raw data will be redacted to aliases.

#### Code example

*Send data to your organization vault:*

```swift
func sendData(_ sender: UIButton) {

    /// extra information can be sent together with all sensitive data from VGSTextFields
    var extraData = [String: Any]()
    extraData["cardHolderName"] = "Joe Business"

    /// send data to your Vault
    vgsCollect.sendData(path: "/post", method: .post, extraData: extraData) { [weak self](../../response) in
      switch response {
        case .success(let code, let data, let response):
          // parse data
        case .failure(let code, let data, let response, let error):
          // handle failed request
          switch code {
            // handle error codes
          }
      }
    }
}
```

*Asynchronously send data from VGSTextFields to your organization vault:*

```swift
func sendData() async {
    do {
      let response = try await vgsCollect.sendData(path: "/post", method: .post)
      switch respose {
        case .success(let code, let data, let response):
          // parse data
        case .failure(let code, let data, let response, let error):
          // handle failed request
          switch code {
            // handle error codes
          }
      }
    } catch {
      // handle error
    }
}
```

*Send data from VGSTextFields to your organization vault using the Combine framework:*

```swift
  vgsCollect.sendDataPublisher(path: "/post", method: .post)
    .sink { completion in
      switch completion {
        case .finished:
          // handle finished
        case .failure(let error):
          // handle error
      }
    } receiveValue: { response in
      // parse data
    }
    .store(in: &cancellables)
```

> • If you need to send files data, check **VGSFilePickerController**.

### Sending custom data

To send additional custom data, include it in the **extraData** parameter of the `sendData(_:`)\` request.

#### Code example

```swift
// ...

/// extra information can be sent together with all sensitive data from VGSTextFields
var extraData = [String: Any]()
extraData["cardHolderName"] = "Joe Business"

/// send data to your Vault
vgsCollect.sendData(path: "/post", extraData: extraData) { [weak self](../../response) in
  switch response {
    // ...
  }
}
```

### Setting Custom API Headers

To include custom headers in your `sendData(_:)` request, assign them to:

```swift
var customHeaders: [String: String]?
```

#### Code example

```swift
// ...

/// set custom headers
vgsCollect.customHeaders = [
    "custom header": "custom data"
]
```

### VGSResponse

Represents the result of a VGS SDK request.

#### Declaration

```swift
@frozen enum VGSResponse

    /// Success response case
    case success(_ code:Int, _ data:Data?, _ response: URLResponse?)

    /// Failed response case
    case failure(_ code:Int, _ data:Data?, _ response: URLResponse?, _ error:Error?)
```

See [SDK Reference Docs](https://verygoodsecurity.github.io/vgs-collect-ios/Enums/VGSResponse.html) for more information.

## Advanced Settings

### Setting Custom JSON Structure

Use dot **(.)** notation in the `VGSConfiguration.fieldName` to define nested JSON structures. Each dot **(.)** in a **fiealdName** will create a new level of nesting.

#### Code example

```swift
/// Define field names
let userId = "userId"
let cardNumberFieldKey = "card_data.card_number"
let cardCVCFieldKey = "card_data.cvc"

/// Example of transformed JSON structure on sendData(_:)
{
  "userId": "id12345",
  “card_data: {
     “card_number”:  411111111111111”,
     “cvc”: “123”
  }”
}
```

> • If there are two fieldNames with equal names - they will be overwritten.\
> • If object structure and field name in **extraData** are same as defined in **VGSConfiguration** fieldName, value in **extraData** will be overwritten by data collected from **VGSCollect** elements with same name on **sendData(\_:)**.

Dot **(.)** notation does not apply to **extraData** keys. You must format **extraData** as desired before passing it to `sendData(_:)`.

#### Nested JSON with array

Use **\[index]** notation in **fieldName**888 to represent arrays. Only one key with an array index is supported per level. Multi-dimensional arrays are not supported.

*Field Mapping Policies:*

* **nestedJSON** - map fieldnames with dot notation to nested JSON. Arrays are not supported. Is default policy.
* **flatJSON** - map fieldNames to JSON. Flat JSON format uses **fieldname** as a single `key` and does not produce any nested JSON or array.
* **nestedJSONWithArrayOverwrite** - map fieldnames with dot notation to nested JSON and arrays. Replace `extraData` the array with the VGS Collect array.
* **nestedJSONWithArrayMerge** - map fieldnames with dot notation to nested JSON and arrays. Merge VGS Collect and `extraData` array.

Specify `fieldName` mapping policy with `VGSCollectRequestOptions` on `sendData(_:)`.

```swift
/// Define field names
let cardNumberFieldKey = "card_data[0].number"
let cardCVCFieldKey = "card_data[1].cvc"

let vgsCollect = VGSCollect(id: "<VAULT_ID>", environment: .sandbox, dataRegion: "eu-1")
var options = VGSCollectRequestOptions()
options.fieldNameMappingPolicy = .nestedJSONArrayOvewrite
// Or
options.fieldNameMappingPolicy = .nestedJSONArrayMerge

vgsCollect.sendData(path: "/post", requestOptions: options) { requestResult in }
```

Example of transformed JSON structure on `sendData(_:)`

```json
{
  "card_data" :
  [
    { "number" :  "411111111111111"},
    {"cvc": "123"}
  ]
}
```

> Check [more examples](https://github.com/verygoodsecurity/docs-content-vault/blob/update-content/vgs-collect/custom-json-structure-examples/README.md) how to customize JSON structure.

If you need to change the output value format, you can read more about available functionality in [VGSConfiguration](/vault/developer-tools/vgs-collect/ios-sdk/configuration.md) section.

## Tokenize data

VGS offers two Vault API versions for data tokenization:\
• Vault API v1: Uses `tokenizeData(_:)`. [Learn more](/vault/developer-tools/apis/vault-api-v1.md).\
• Vault API v2: Uses `createAliases(_:)` and requires **Authorization**. [Learn more](/vault/developer-tools/apis/vault-api.md).\
\
Each API version requires an appropriate *Upstream URL* specified in your Vault's Route configuration.\\

> • Vault API v2 is our newest Vault API and is recommended for integrations.\
> • If you already use API v1 you can find migration guide to API v2 below.

{% tabs %}
{% tab title="Vault API v2" %}
Makes a tokenization request to Vault API v2 with data from VGSTextFields:

```swift
public func createAliases(routeId: String? = nil, completion block: @escaping (VGSTokenizationResponse) -> Void)
```

Asynchronously makes a tokenization request with data from VGSTextFields:

```swift
public func createAliases(routeId: String? = nil) async throws -> VGSTokenizationResponse
```

Makes a tokenization request with data from VGSTextFields using the Combine framework:

```swift
public func createAliasesDataPublisher(routeId: String? = nil) -> Future<VGSTokenizationResponse, Never>
```

{% endtab %}

{% tab title="Vault API v1" %}
Makes a tokenization request to Vault API v1 with data from VGSTextFields:

```swift
public func tokenizeData(routeId: String? = nil, completion block: @escaping (VGSTokenizationResponse) -> Void)
```

Asynchronously makes a tokenization request with data from VGSTextFields:

```swift
public func tokenizeData(routeId: String? = nil) async throws -> VGSTokenizationResponse
```

Makes a tokenization request with data from VGSTextFields using the Combine framework:

```swift
public func tokenizeDataPublisher(routeId: String? = nil) -> Future<VGSTokenizationResponse, Never>
```

{% endtab %}
{% endtabs %}

### VGSTokenizationResponse

An object you use to handle SDK tokenization request completion.

#### Declaration

```swift
@frozen public enum VGSTokenizationResponse {
    /**
     Success response case

     - Parameters:
        - code: response status code.
        - body: response **JsonData** object.
        - response: URLResponse object represents a URL load response.
    */
  case success(_ code: Int, _ body: JsonData?, _ response: URLResponse?)

    /**
     Failed response case

     - Parameters:
        - code: response status code.
        - data: response **Data** object.
        - response: `URLResponse` object represents a URL load response.
        - error: `Error` object.
    */
    case failure(_ code: Int, _ data: Data?, _ response: URLResponse?, _ error: Error?)
}
```

#### Code example

Send tokenization request with data from VGSTextFields to your organization vault:

{% tabs %}
{% tab title="Vault API v2" %}

You should set authToken into VGSCollect customHeaders before calling the createAliases(:) function:

```swift
vgsCollect.customHeaders = ["Authorization": "Bearer (authToken)"]
vgsCollect.createAliases{ [weak self](response) in
  switch response {
  case .success(_, let resultBody, _):
    let response = (String(data: try! JSONSerialization.data(withJSONObject: resultBody!, options: .prettyPrinted), encoding: .utf8)!)
      print(response)
      return
  case .failure(let code, _, _, let error):
    switch code {
    case 400..<499:
      // Wrong request. This also can happend when your Routs not setup yet or your <vaultId> is wrong
    case VGSErrorType.inputDataIsNotValid.rawValue:
      if let error = error as? VGSError {
        self?.consoleLabel.text = "Error: Input data is not valid. Details:
 (error)"
      }
    default:
      self?.consoleLabel.text = "Error: Something went wrong. Code: (code)"
    }
    print("Submit request error: (code), (String(describing: error))")
    return
  }
}
```

{% endtab %}

{% tab title="Vault API v1" %}

```swift
vgsCollect.tokenizeData { (response) in
  switch response {
  case .success(_, let resultBody, _):
    let response = (String(data: try! JSONSerialization.data(withJSONObject: resultBody!, options: .prettyPrinted), encoding: .utf8)!)
    print("Success: (response)")
    return
  case .failure(let code, _, _, let error):
    switch code {
    case 400..<499:
      // Wrong request. This also can happend when your Routs not setup yet or your <vaultId> is wrong
      print("Error: Wrong Request, code: (code)")
    case VGSErrorType.inputDataIsNotValid.rawValue:
      // When input is not valid according to configuration
      if let error = error as? VGSError {
        print("Error: Input data is not valid. Details: (error)")
      }
  default:
    print("Error: Something went wrong. Code: (code)")
  }
  print("Tokenize request error: (code), (String(describing: error))")
  return
  }
}
```

{% endtab %}
{% endtabs %}

\
Send tokenization request with data from VGSTextFields to your organization vault using the async/await:

{% tabs %}
{% tab title="Vault API v2" %}

```swift

do {
  let response = try await vgsCollect.createAliases()
  switch respose {
    case .success(let code, let data, let response):
      // parse data
    case .failure(let code, let data, let response, let error):
      // handle failed request
      switch code {
      // handle error codes
      }
    }
} catch {
// handle error
}

```

{% endtab %}

{% tab title="Vault API v1" %}

```swift

do {
  let response = try await vgsCollect.tokenizeData()
  switch respose {
    case .success(let code, let data, let response):
      // parse data
    case .failure(let code, let data, let response, let error):
      // handle failed request
      switch code {
      // handle error codes
      }
  }
} catch {
// handle error
}
  
```

{% endtab %}
{% endtabs %}

\
Send toa kenization request with data from VGSTextFields to your organization's vault using the Combine framework:

{% tabs %}
{% tab title="Vault API v2" %}

```swift

  vgsCollect.createAliasesDataPublisher()
    .sink { completion in
      switch completion {
        case .finished:
          // handle finished
        case .failure(let error):
          // handle error
      }
    } receiveValue: { response in
      // parse data
    }
    .store(in: &cancellables)

```

{% endtab %}

{% tab title="Vault API v1" %}

```swift

  vgsCollect.tokenizeDataPublisher()
    .sink { completion in
      switch completion {
        case .finished:
          // handle finished
        case .failure(let error):
          // handle error
      }
    } receiveValue: { response in
      // parse data
    }
    .store(in: &cancellables)

```

{% endtab %}
{% endtabs %}

> * Data will be collected from all `VGSTextFields` registered to current `VGSCollect`instance.\
>   \- Only data from `VGSTextFields` configured with one of available [tokenization configurations](/vault/developer-tools/vgs-collect/ios-sdk/tokenization-configuration.md) will be tokenized and stored in your's organization vault.\
>   \- Data from `VGSTextFields` configured with other configuration types will be returned as *raw value* along with tokenized data in `VGSTokenizationResponse`.\
>   \- Data from fields with type `.cvc` and `.cardNumber` will not be included to `VGSTokenizationResponse` if [tokenization configurations](/vault/developer-tools/vgs-collect/ios-sdk/tokenization-configuration.md) not set for appropriate fields.

#### RouteId

The route identifier specifies how data should be routed in your organization's vault. You should follow the next rules when setting `routeId`:

* You should set specific `routeId` when you use multiple routes in your organization's vault.
* RouteId should be a valid **UUID** string; you will receive an error in `VGSTokenizationResponse` in case if it's not valid.
* If you have only one Inbound Route in your organization's vault, you can leave `routeId` as **nil**.

### Migrating from Vault API v1 to v2

Migration from Vault **API v1** to **API v2** will require you to implement the next steps:

* Update your organization's Vault Route with a new **Upstream Host**: use `https://<your-vault-id>.sandbox.vault-api.verygoodvault.com` for **sandbox** or `https://<your-vault-id>.live.vault-api.verygoodvault.com` for a **live** environment.
* Receive Tokenization **authToken** from your backend and set it in **VGSCollect** headers:\
  `vgsCollect.customHeaders = ["Authorization": "Bearer \(authToken)"]`.
* Use `-createAliases(:)` function instead of `-tokenize(:)` creating aliases.

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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.verygoodsecurity.com/vault/developer-tools/vgs-collect/ios-sdk/submit-data.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
