iOS SDK
Overview
The iOS SDK enables secure collection and reveal of sensitive data in native iOS applications, supporting both SwiftUI and UIKit.
Requirements
- iOS: 14.0+
- Swift: 5.9+
- Xcode: 14.0+
Installation
- Download and extract the SDK package (
SwiftPackage-0.0.1.zip) - Move the
secure-connect-sdk-iosfolder to your project root - In Xcode: File → Add Packages → Add Local
- Select the folder containing
Package.swift
Configuration
Initialize the SDK with your gateway URL:
import SecureConnectSDK
let config = SecureConnectConfig(gatewayUrl: "https://api.connect.financial")
SecureConnectSDK.shared.initialize(with: config, authToken: "your-auth-token") { result in
switch result {
case .success:
print("SDK initialized successfully")
case .failure(let error):
print("Initialization failed: \(error)")
}
}
Token Management
Update the authentication token without reinitializing:
SecureConnectSDK.shared.updateToken("new-auth-token")
SwiftUI Integration
Collecting Sensitive Data
import SwiftUI
import SecureConnectSDK
struct SSNCollectionView: View {
@State private var isValid = false
@State private var isSubmitting = false
@State private var errorMessage: String?
var body: some View {
VStack(spacing: 16) {
SecureTextField(
operation: .submitDocumentSSN,
placeholder: "Enter SSN",
onValidationChange: { valid in
isValid = valid
}
)
.frame(height: 44)
.overlay(
RoundedRectangle(cornerRadius: 8)
.stroke(isValid ? Color.gray : Color.red, lineWidth: 1)
)
if let error = errorMessage {
Text(error)
.foregroundColor(.red)
.font(.caption)
}
Button(action: submitSSN) {
if isSubmitting {
ProgressView()
.progressViewStyle(CircularProgressViewStyle())
} else {
Text("Submit")
}
}
.disabled(!isValid || isSubmitting)
.frame(maxWidth: .infinity)
.padding()
.background(isValid ? Color.blue : Color.gray)
.foregroundColor(.white)
.cornerRadius(8)
}
.padding()
}
private func submitSSN() {
isSubmitting = true
errorMessage = nil
SecureConnectSDK.shared.submit(
operation: .submitDocumentSSN,
customerId: "customer-uuid"
) { result in
isSubmitting = false
switch result {
case .success(let response):
print("Token: \(response.token)")
case .failure(let error):
errorMessage = error.localizedDescription
}
}
}
}
Revealing Card Data
struct CardRevealView: View {
let cardId: String
@State private var isRevealed = false
var body: some View {
VStack(spacing: 16) {
// Card Number
SecureLabel(
operation: .revealCardPAN,
cardId: cardId,
serializer: .panWithDashes,
placeholder: "•••• •••• •••• ••••"
)
.frame(height: 44)
.frame(maxWidth: .infinity)
.background(Color(.systemGray6))
.cornerRadius(8)
HStack(spacing: 16) {
// Expiry Date
VStack(alignment: .leading) {
Text("Expiry")
.font(.caption)
.foregroundColor(.gray)
SecureLabel(
operation: .revealCardExpiry,
cardId: cardId,
serializer: .expiryShort,
placeholder: "••/••"
)
.frame(height: 44)
}
// CVV
VStack(alignment: .leading) {
Text("CVV")
.font(.caption)
.foregroundColor(.gray)
SecureLabel(
operation: .revealCardCVV,
cardId: cardId,
placeholder: "•••"
)
.frame(height: 44)
}
}
Button(action: toggleReveal) {
Text(isRevealed ? "Hide" : "Reveal Card Details")
}
.frame(maxWidth: .infinity)
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(8)
}
.padding()
}
private func toggleReveal() {
if isRevealed {
SecureConnectSDK.shared.hideAll()
} else {
SecureConnectSDK.shared.revealAll(cardId: cardId)
}
isRevealed.toggle()
}
}
Complete Card View Example
struct CardDetailsView: View {
let cardId: String
@State private var isRevealed = false
var body: some View {
VStack(spacing: 24) {
// Card visual representation
ZStack {
RoundedRectangle(cornerRadius: 16)
.fill(LinearGradient(
colors: [.blue, .purple],
startPoint: .topLeading,
endPoint: .bottomTrailing
))
.frame(height: 200)
VStack(alignment: .leading, spacing: 20) {
Spacer()
// Card Number
SecureLabel(
operation: .revealCardPAN,
cardId: cardId,
serializer: .panWithSpaces,
placeholder: "•••• •••• •••• ••••"
)
.font(.system(size: 22, weight: .medium, design: .monospaced))
.foregroundColor(.white)
HStack {
VStack(alignment: .leading) {
Text("VALID THRU")
.font(.caption2)
.foregroundColor(.white.opacity(0.7))
SecureLabel(
operation: .revealCardExpiry,
cardId: cardId,
serializer: .expiryShort,
placeholder: "••/••"
)
.foregroundColor(.white)
}
Spacer()
VStack(alignment: .leading) {
Text("CVV")
.font(.caption2)
.foregroundColor(.white.opacity(0.7))
SecureLabel(
operation: .revealCardCVV,
cardId: cardId,
placeholder: "•••"
)
.foregroundColor(.white)
}
}
}
.padding(24)
}
Button(action: toggleReveal) {
Label(
isRevealed ? "Hide Details" : "Show Details",
systemImage: isRevealed ? "eye.slash" : "eye"
)
}
.buttonStyle(.borderedProminent)
}
.padding()
}
private func toggleReveal() {
if isRevealed {
SecureConnectSDK.shared.hideAll()
} else {
SecureConnectSDK.shared.revealAll(cardId: cardId)
}
isRevealed.toggle()
}
}
UIKit Integration
Collecting Sensitive Data
import UIKit
import SecureConnectSDK
class SSNViewController: UIViewController {
private let secureTextField = SecureTextFieldUIKit()
private let submitButton = UIButton(type: .system)
private let errorLabel = UILabel()
private var isValid = false
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
configureSecureField()
}
private func setupUI() {
view.backgroundColor = .systemBackground
// Configure secure text field
secureTextField.translatesAutoresizingMaskIntoConstraints = false
secureTextField.layer.borderWidth = 1
secureTextField.layer.borderColor = UIColor.systemGray4.cgColor
secureTextField.layer.cornerRadius = 8
// Configure error label
errorLabel.translatesAutoresizingMaskIntoConstraints = false
errorLabel.textColor = .systemRed
errorLabel.font = .preferredFont(forTextStyle: .caption1)
errorLabel.isHidden = true
// Configure submit button
submitButton.translatesAutoresizingMaskIntoConstraints = false
submitButton.setTitle("Submit", for: .normal)
submitButton.backgroundColor = .systemBlue
submitButton.setTitleColor(.white, for: .normal)
submitButton.layer.cornerRadius = 8
submitButton.addTarget(self, action: #selector(submitTapped), for: .touchUpInside)
submitButton.isEnabled = false
// Add subviews
view.addSubview(secureTextField)
view.addSubview(errorLabel)
view.addSubview(submitButton)
// Layout constraints
NSLayoutConstraint.activate([
secureTextField.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
secureTextField.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16),
secureTextField.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16),
secureTextField.heightAnchor.constraint(equalToConstant: 44),
errorLabel.topAnchor.constraint(equalTo: secureTextField.bottomAnchor, constant: 4),
errorLabel.leadingAnchor.constraint(equalTo: secureTextField.leadingAnchor),
submitButton.topAnchor.constraint(equalTo: errorLabel.bottomAnchor, constant: 16),
submitButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16),
submitButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16),
submitButton.heightAnchor.constraint(equalToConstant: 44)
])
}
private func configureSecureField() {
secureTextField.configure(
operation: .submitDocumentSSN,
placeholder: "Enter SSN"
)
secureTextField.onValidationChange = { [weak self] isValid in
self?.isValid = isValid
self?.submitButton.isEnabled = isValid
self?.submitButton.backgroundColor = isValid ? .systemBlue : .systemGray
self?.secureTextField.layer.borderColor = isValid ? UIColor.systemGray4.cgColor : UIColor.systemRed.cgColor
self?.errorLabel.isHidden = isValid
self?.errorLabel.text = isValid ? nil : "Invalid SSN format"
}
}
@objc private func submitTapped() {
submitButton.isEnabled = false
SecureConnectSDK.shared.submit(
operation: .submitDocumentSSN,
customerId: "customer-uuid"
) { [weak self] result in
DispatchQueue.main.async {
self?.submitButton.isEnabled = self?.isValid ?? false
switch result {
case .success(let response):
print("Token: \(response.token)")
case .failure(let error):
self?.showError(error.localizedDescription)
}
}
}
}
private func showError(_ message: String) {
let alert = UIAlertController(title: "Error", message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default))
present(alert, animated: true)
}
}
Revealing Card Data
class CardRevealViewController: UIViewController {
private let panLabel = SecureLabelUIKit()
private let cvvLabel = SecureLabelUIKit()
private let expiryLabel = SecureLabelUIKit()
private let revealButton = UIButton(type: .system)
private let cardId: String
private var isRevealed = false
init(cardId: String) {
self.cardId = cardId
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
configureSecureLabels()
}
private func configureSecureLabels() {
panLabel.configure(
operation: .revealCardPAN,
cardId: cardId,
serializer: .panWithDashes,
placeholder: "•••• •••• •••• ••••"
)
cvvLabel.configure(
operation: .revealCardCVV,
cardId: cardId,
placeholder: "•••"
)
expiryLabel.configure(
operation: .revealCardExpiry,
cardId: cardId,
serializer: .expiryShort,
placeholder: "••/••"
)
}
@objc private func revealTapped() {
if isRevealed {
SecureConnectSDK.shared.hideAll()
revealButton.setTitle("Reveal", for: .normal)
} else {
SecureConnectSDK.shared.revealAll(cardId: cardId)
revealButton.setTitle("Hide", for: .normal)
}
isRevealed.toggle()
}
}
Async/Await Support
The iOS SDK supports modern async/await patterns:
// Submit document
func submitDocument() async throws {
let response = try await SecureConnectSDK.shared.submit(
operation: .submitDocumentSSN,
customerId: "customer-uuid"
)
print("Token: \(response.token)")
}
// Reveal card details
func revealCardDetails() async throws {
try await SecureConnectSDK.shared.reveal(
operation: .revealCardPAN,
cardId: "card-uuid",
serializer: .panWithDashes
)
}
// Usage in SwiftUI
struct AsyncSubmitView: View {
@State private var isLoading = false
var body: some View {
Button("Submit") {
Task {
isLoading = true
do {
try await submitDocument()
} catch {
print("Error: \(error)")
}
isLoading = false
}
}
.disabled(isLoading)
}
}
Serializers (Formatting)
The SDK supports serializers for formatting revealed data:
| Serializer | Output Format | Example |
|---|---|---|
.panWithDashes | XXXX-XXXX-XXXX-XXXX | 4111-1111-1111-1111 |
.panWithSpaces | XXXX XXXX XXXX XXXX | 4111 1111 1111 1111 |
.expiryShort | MM/YY | 12/25 |
.expiryFull | MM/YYYY | 12/2025 |
Supported Operations
Collect Operations
| Operation | Description |
|---|---|
.submitDocumentID | Submit government-issued ID |
.submitDocumentSSN | Submit Social Security Number |
.submitDocumentDriversLicense | Submit driver's license |
.submitDocumentPassport | Submit passport |
Reveal Operations
| Operation | Description |
|---|---|
.revealDocumentID | Reveal government-issued ID |
.revealDocumentSSN | Reveal Social Security Number |
.revealDocumentDriversLicense | Reveal driver's license |
.revealDocumentPassport | Reveal passport |
.revealCardPAN | Reveal card number |
.revealCardCVV | Reveal card CVV |
.revealCardExpiry | Reveal card expiry date |
.revealCardData | Reveal all card data at once |