# BlinkCard Module

You can add a card scanning solution to your application by adding **VGSCollect/BlinkCard** module. It provides a fast and easy way to scan payment cards and import them to VGS Collect. This tutorial is aimed to help you to adapt [BlinkCard SDK](https://github.com/blinkcard/blinkcard-ios) with VGS Collect iOS SDK.

Using `BlinkCard` in your app requires a valid license key. To get your license key you should contact [MicroBlink](https://microblink.com/).

You can start by watching our step-by-step tutorial, in which you’ll find out how to make `BlinkCard` SDK a part of your iOS app.

To see `VGSCollect/BlinkCard` in action, check our [Demo app](https://github.com/verygoodsecurity/vgs-collect-ios).

### Pricing

`VGSCollect/BlinkCard` module is a paid integration. Please [contact](https://microblink.com/contact-us/) Microblink for more details.

### Before you start

Using `VGSCollect/BlinkCard` API in your app you should obtain a valid **license key**. You can obtain it from [Microblink dashboard](https://commerce-platform-staging.blinkreceipt.com/login).

### Integration

The module can be deployed in iOS 13.0 or later.

#### Integrate with Swift Package Manager

To integrate `VGSCollect/BlinkCard` module via Swift Package Manager, in Xcode add the <https://github.com/verygoodsecurity/vgs-collect-ios.git> repo and choose the exact [version](https://github.com/verygoodsecurity/vgs-collect-ios/releases).

Select both `VGSCollectSDK` and `VGSBlinkCardCollector` packages.

Follow the official Apple SPM guide [instructions](https://developer.apple.com/documentation/xcode/adding_package_dependencies_to_your_app) for more details.<br>

#### Integrate with Cocoapods

Add **BlinkCard** module alongside with core **VGSCollectSDK** module into your App Podfile:

```swift
pod 'VGSCollectSDK'
pod 'VGSCollectSDK/BlinkCard'
```

Then in Terminal run:

```ruby
pod install
```

## UIKit integration

### VGSBlinkCardController

An object you use to manage `BlinkCard` scan ViewController.

#### Declaration

```swift
class VGSBlinkCardController
```

#### Creating a VGSBlinkCardController

```swift
/// - Parameters:
///   - licenseKey: key required for BlinkCard SDK usage.
///   - delegate: `VGSBlinkCardControllerDelegate`. Default is `nil`.
///   - errorCallback: Error callback with Int error code(represents `MBCLicenseError` enum), triggers only when error occures.
public required init(licenseKey: String, delegate: VGSBlinkCardControllerDelegate? = nil, onError errorCallback: @escaping ((NSInteger) -> Void))
```

#### VGSBlinkCardController Attributes and Methods

```swift
/// `VGSBlinkCardControllerDelegate` - handle `BlinkCard` states.
public var delegate: VGSBlinkCardControllerDelegate?

/// Present `BlinkCard` scanner.
/// - Parameters:
///   - viewController: `UIViewController` that will present card scanner.
///   - animated: pass `true` to animate the presentation; otherwise, pass `false`.
///   - completion: the block to execute after the presentation finishes.
public func presentCardScanner(on viewController: UIViewController, animated: Bool, completion: (() -> Void)?)

/// Dismiss `BlinkCard` scanner.
/// - Parameters:
///   - animated: pass `true` to animate the dismiss of presented viewcontroller; otherwise, pass `false`.
///   - completion: the block to execute after the dismiss finishes.
public func dismissCardScanner(animated: Bool, completion: (() -> Void)?)

/// Set custom localization fileName.
public static func setCustomLocalization(fileName: String)
```

### VGSBlinkCardControllerDelegate

#### Declaration

```swift
protocol VGSBlinkCardControllerDelegate
```

#### Managing Controller States

```swift
/// On user confirm scanned data by selecting Done button on `BlinkCard` screen.
@objc func userDidFinishScan()

/// On user press Cancel button on `BlinkCard` screen.
@objc func userDidCancelScan()
```

#### Providing VGSTextFields for setting Scanned Data

```swift
/// Asks `VGSTextField` where scanned data with `VGSConfiguration.FieldType` need to be set.
/// Called after user select Done button, just before userDidFinishScan() delegate.
@objc func textFieldForScannedData(type: VGSBlinkCardDataType) -> VGSTextField?
```

If scanned data is valid, it will be set in your `VGSTextFields` automatically after user confirmation.

### VGSBlinkCardDataType

**VGSBlinkCardDataType** is an Enum of supported scan data types by BlinkCard module.

| **VGSBlinkCardDataType**          | **Type**, **Value**          |
| --------------------------------- | ---------------------------- |
| .cardNumber                       | **String**, Digits string    |
| .name                             | **String**, Cardholder name  |
| .cvc                              | **String**, 3-4 digits       |
| .expirationDate                   | **String**, "mm/yy" digits   |
| .expirationDateLong               | **String**, "mm/yyyy" digits |
| .expirationDateShortYearThenMonth | **String**, "yy/mm" digits   |
| .expirationDateLongYearThenMonth  | **String**, "yyyy/mm" digits |
| .expirationMonth                  | **String**, "mm" digits      |
| .expirationYear                   | **String**, "yy" digits      |
| .expirationYearLong               | **String**, "yyyy" digits    |

### Usage

Create UI Form with `VGSTextfields`, if not done yet.\
Inside your ViewController create and configure `VGSBlinkCardController` instance:

```swift
class ViewController: UIViewController {

    // VGSBlinkCardController instance
    var scanController: VGSBlinkCardController?

    // Scan card button
    var scanButton = UIButton()

    /// Initialize VGSCollect instance
    var vgsCollect = VGSCollect(id: "<VAULT_ID>", environment: .sandbox)

    /// VGS UI Elements
    var cardNumberField = VGSCardTextField()
    var cardHolderNameField = VGSTextField()
    var expCardDateField = VGSTextField()
    var cvcField = VGSTextField()


    override func viewDidLoad() {
       super.viewDidLoad()

       /// Init scan controller with license key.
       scanController = VGSBlinkCardController(licenseKey: <licenseKey>, delegate: self, onError: { errorCode in
          print("BlinkCard license error, code: \(errorCode)")
        })

       /// Add action to handle scan button touch
       scanButton.addTarget(self, action: #selector(scanData(_:)), for: .touchUpInside)

       /// Setup Fields Layout and Configuration
       ...
    }

     // Present scan ViewController
     @objc func scanData(_ sender: UIButton) {
         scanButton.presentCardScanner(on: self, animated: true, completion: nil)
     }
}
```

Implement `VGSBlinkCardControllerDelegate` methods.\
To setup scanned data into specific `VGSTextFields` implement `textFieldForScannedData:`. If scanned data is valid it will be set in your `VGSTextFields` automatically. Check `VGSBlinkCardDataType` to get available scanned data types.

```swift
extension CardsDataCollectingViewController: VGSBlinkCardControllerDelegate {
  func textFieldForScannedData(type: VGSBlinkCardDataType) -> VGSTextField? {
      // match VGSTextField with scanned data
      switch type {
      case .expirationDateLong:
          return expCardDate
      case .cardNumber:
          return cardNumber
      case .cvc:
        return cvcCardNum
      case .name:
        return cardHolderName
      default:
          return nil
      }
  }
  
  func userDidFinishScan() {
      scanController?.dismissCardScanner(animated: true, completion: {
          // add actions on scan controller dismiss completion
      })
  }
  
  func userDidCancelScan() {
      scanController?.dismissCardScanner(animated: true, completion: {
          // add actions on scan controller dismiss completion
      })
  }
}
```

## SwiftUI integration

VGS Collect iOS SDK provide support for integration with apps that are buid with SwiftUI toolkit by providing SwiftUI wrappers. VGS Collect SwiftUI wrappers are designed to make integration easier and more straight forward by taking care of all needed state and editing events.

### VGSBlinkCardControllerRepresentable

#### Declaration

```swift
struct VGSBlinkCardControllerRepresentable: UIViewControllerRepresentable
```

#### Creating a VGSBlinkCardControllerRepresentable

```swift
/// Initialization
/// - Parameters:
///   - licenseKey: key required for BlinkCard  SDK usage.
///   - dataCoordinators: `[VGSBlinkCardDataType: VGSCardScanCoordinator]` represents connection between scanned data and VGSTextFields.
///   - errorCallback: Error callback with Int error code(represents `MBCLicenseError` enum), triggered only when error occured.
public init(licenseKey: String, dataCoordinators: [VGSBlinkCardDataType: VGSCardScanCoordinator], errorCallback: @escaping ((NSInteger) -> Void))
```

#### Instance Methods

VGSBlinkCardControllerRepresentable is a SwiftUI wrapper around **BlinkCard** card scanner controller and have similar attributes and functionality represented in SwiftUI way.

#### VGSBlinkCardControllerRepresentable card recognition modifier

You can find available attributes and their description in **BlinkCard** [documentation](https://blinkcard.github.io/blinkcard-ios/Classes/MBCBlinkCardRecognizer.html).

```swift
  /// Should extract the card owner information.
  public var extractOwner: Bool = true
  /// Should extract the payment card’s month of expiry.
  public var extractExpiryDate: Bool = true
  /// Should extract CVV.
  public var extractCvv: Bool = true
  /// Should extract the payment card’s IBAN.
  public var extractIban: Bool = true
  /// Whether invalid card number is accepted.
  public var allowInvalidCardNumber: Bool = false
```

#### VGSBlinkCardControllerRepresentable Event modifier

```swift
/// On Scaner finish card recognition.
public var onCardScanned: (() -> Void)?
/// On card scanning canceled  by user.
public var onCardScanCanceled: (() -> Void)?
```

### Code examples

Create UI Form with `VGSTextfieldRepresentable`, if not done yet.\
Inside your View create and configure `VGSBlinkCardControllerRepresentable` instance:

```swift
struct CardDataCollectionSwiftUI: View {
    /// Track BlinkCard visibility status
    @State private var showingBlinkCardScanner = false
    /// Match scanned data with apropriate text fields.
    @State private var scanedDataCoordinators: [VGSBlinkCardDataType: VGSCardScanCoordinator] = [
            .cardNumber: VGSCardScanCoordinator(),
            .name: VGSCardScanCoordinator(),
            .cvc: VGSCardScanCoordinator(),
            .expirationDate: VGSCardScanCoordinator()
        ]
         
    // MARK: - Build View
    var body: some View {
        /// setup VGSConfiguration for fields 
        /// ...
      return VStack(spacing: 8) {
        VGSTextFieldRepresentable(configuration: holderNameConfiguration)
          .placeholder("Cardholder Name")
          .cardScanCoordinator(scanedDataCoordinators[.name]!)
        VGSCardTextFieldRepresentable(configuration: cardNumConfiguration)
          .placeholder("4111 1111 1111 1111")
          .cardScanCoordinator(scanedDataCoordinators[.cardNumber]!)
        HStack(spacing: 20) {
          VGSExpDateTextFieldRepresentable(configuration: expDateConfiguration)
            .placeholder("MM/YY")
            .cardScanCoordinator(scanedDataCoordinators[.expirationDate]!)
          VGSCVCTextFieldRepresentable(configuration: cvcConfiguration)
            .placeholder("CVC")
            .cardScanCoordinator(scanedDataCoordinators[.cvc]!)
            .setSecureTextEntry(true)
        }
        HStack(spacing: 20) {
          Button(action: {
            showingBlinkCardScanner = true
          }) {
              Text("SCAN")
                  .padding()
                  .cornerRadius(8)
                  .overlay(
                      RoundedRectangle(cornerRadius: 10)
                          .stroke(Color.blue, lineWidth: 2)
                  )
          }
          .fullScreenCover(isPresented: $showingBlinkCardScanner) {
            VGSBlinkCardControllerRepresentable(licenseKey: AppCollectorConfiguration.shared.blinkCardLicenseKey!, dataCoordinators: scanedDataCoordinators) { (errorCode) in
              print(errorCode)
            }.allowInvalidCardNumber(true)
            .onCardScanned({
              showingBlinkCardScanner = false
            })
            .onCardScanCanceled({
              showingBlinkCardScanner = false
            })
          }
        }
      }
    }
}
```


---

# 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/blinkcard-module.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.
