Swift ๐ฆ
The official Swift client for the CardScan API provides a native Swift interface with async/await support.
Installation
Swift Package Manager
Add the following to your Package.swift
:
dependencies: [
.package(url: "https://github.com/CardScan-ai/api-clients.git", from: "1.0.0")
]
Or in Xcode:
File โ Add Package Dependencies
Enter:
https://github.com/CardScan-ai/api-clients.git
Select version rule and add to your project
Basic Usage
import CardScanClient
// Initialize with your API key
let apiKey = "sk_test_cardscan_ai_..."
let client = CardScanAPI(apiKey: apiKey)
// Generate a session token for a user
do {
let tokenResponse = try await client.getAccessToken(userId: "unique-user-123")
let sessionToken = tokenResponse.Token
let identityId = tokenResponse.IdentityId
let sessionId = tokenResponse.session_id
// Initialize client with session token for frontend operations
let userClient = CardScanAPI(sessionToken: sessionToken, live: false)
} catch {
print("Error creating access token: \(error)")
}
Card Scanning Workflow
1. Create a Card
struct CardCreationExample {
let client: CardScanAPI
func createCard() async throws -> CardApiResponse {
let request = CreateCardRequest(
enableBacksideScan: false,
enableLivescan: false,
metadata: [
"patient_id": "12345",
"visit_id": "v-67890"
]
)
let card = try await client.createCard(request: request)
print("Card ID: \(card.cardId)")
print("State: \(card.state)") // .pending
return card
}
}
2. Generate Upload URL
func generateUploadUrl(for cardId: String) async throws -> GenerateCardUploadUrlResponse {
let request = GenerateCardUploadUrlRequest(
orientation: .front,
captureType: .manual
)
let uploadData = try await client.generateCardUploadUrl(
cardId: cardId,
request: request
)
return uploadData
}
3. Upload Image
import Foundation
func uploadImage(_ image: UIImage, using uploadData: GenerateCardUploadUrlResponse) async throws {
guard let imageData = image.jpegData(compressionQuality: 0.8) else {
throw CardScanError.invalidImage
}
var request = URLRequest(url: uploadData.uploadUrl)
request.httpMethod = "POST"
// Create multipart form data
let boundary = UUID().uuidString
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
var body = Data()
// Add upload parameters
for (key, value) in uploadData.uploadParameters {
body.append("--\(boundary)\r\n".data(using: .utf8)!)
body.append("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n".data(using: .utf8)!)
body.append("\(value)\r\n".data(using: .utf8)!)
}
// Add image data
body.append("--\(boundary)\r\n".data(using: .utf8)!)
body.append("Content-Disposition: form-data; name=\"file\"; filename=\"card.jpg\"\r\n".data(using: .utf8)!)
body.append("Content-Type: image/jpeg\r\n\r\n".data(using: .utf8)!)
body.append(imageData)
body.append("\r\n--\(boundary)--\r\n".data(using: .utf8)!)
request.httpBody = body
let (_, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
throw CardScanError.uploadFailed
}
}
4. Poll for Results
func waitForCompletion(cardId: String, timeout: TimeInterval = 300) async throws -> CardApiResponse {
let startTime = Date()
while Date().timeIntervalSince(startTime) < timeout {
let card = try await client.getCard(cardId: cardId)
switch card.state {
case .completed, .error:
return card
default:
// Wait 2 seconds before polling again
try await Task.sleep(nanoseconds: 2_000_000_000)
}
}
throw CardScanError.timeout
}
// Usage
let completedCard = try await waitForCompletion(cardId: card.cardId)
if let details = completedCard.details {
print("Member ID: \(details.memberId ?? "N/A")")
print("Group Number: \(details.groupNumber ?? "N/A")")
}
Error Handling
enum CardScanError: Error {
case invalidImage
case uploadFailed
case timeout
}
do {
let card = try await client.getCard(cardId: "invalid-id")
} catch let error as ApiError {
switch error.statusCode {
case 401:
print("Invalid API key or session token")
case 404:
print("Card not found")
case 429:
print("Rate limit exceeded")
default:
print("API Error: \(error.statusCode) - \(error.message)")
}
} catch {
print("Unexpected error: \(error)")
}
WebSocket Support
For real-time updates:
import Foundation
class CardScanWebSocket: NSObject {
private var webSocket: URLSessionWebSocketTask?
private let session = URLSession(configuration: .default)
private let token: String
init(token: String) {
self.token = token
super.init()
}
func connect() {
let url = URL(string: "wss://sandbox.cardscan.ai/v1/ws?token=\(token)")!
webSocket = session.webSocketTask(with: url)
webSocket?.resume()
receiveMessage()
}
private func receiveMessage() {
webSocket?.receive { [weak self] result in
switch result {
case .success(let message):
switch message {
case .string(let text):
self?.handleMessage(text)
default:
break
}
self?.receiveMessage()
case .failure(let error):
print("WebSocket error: \(error)")
}
}
}
private func handleMessage(_ text: String) {
guard let data = text.data(using: .utf8),
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
let type = json["type"] as? String else { return }
switch type {
case "card.processing":
print("Card processing started")
case "card.completed":
if let cardId = json["card_id"] as? String {
print("Card completed: \(cardId)")
}
case "card.error":
if let error = json["error"] as? String {
print("Card error: \(error)")
}
default:
break
}
}
}
Eligibility Verification
func verifyEligibility(for card: CardApiResponse) async throws -> EligibilityApiResponse {
let request = CreateEligibilityRequest(
eligibility: EligibilityRequest(
provider: ProviderDto(
firstName: "John",
lastName: "Smith",
npi: "1234567890"
),
subscriber: SubscriberDto(
firstName: card.details?.memberName ?? "",
lastName: card.details?.memberName ?? "",
dateOfBirth: "1980-01-01"
)
)
)
let eligibility = try await client.createEligibility(
cardId: card.cardId,
request: request
)
// Poll for results
return try await waitForEligibilityCompletion(
eligibilityId: eligibility.eligibilityId
)
}
SwiftUI Integration
import SwiftUI
import CardScanClient
struct CardScannerView: View {
@State private var isScanning = false
@State private var scannedCard: CardApiResponse?
@State private var error: Error?
let client: CardScanAPI
var body: some View {
VStack {
if let card = scannedCard {
CardDetailsView(card: card)
} else {
Button("Scan Insurance Card") {
Task {
await scanCard()
}
}
.disabled(isScanning)
}
if isScanning {
ProgressView("Processing...")
}
if let error = error {
Text("Error: \(error.localizedDescription)")
.foregroundColor(.red)
}
}
.padding()
}
@MainActor
private func scanCard() async {
isScanning = true
error = nil
do {
// Create card
let card = try await client.createCard(
request: CreateCardRequest(enableBacksideScan: false)
)
// In a real app, capture/select image here
// For demo, assume we have an image
// Process and wait for results
scannedCard = try await waitForCompletion(cardId: card.cardId)
} catch {
self.error = error
}
isScanning = false
}
}
Combine Support
For reactive programming with Combine:
import Combine
extension CardScanAPI {
func createCardPublisher(request: CreateCardRequest) -> AnyPublisher<CardApiResponse, Error> {
Future { promise in
Task {
do {
let card = try await self.createCard(request: request)
promise(.success(card))
} catch {
promise(.failure(error))
}
}
}
.eraseToAnyPublisher()
}
}
// Usage
let cancellable = client.createCardPublisher(request: request)
.sink(
receiveCompletion: { completion in
if case .failure(let error) = completion {
print("Error: \(error)")
}
},
receiveValue: { card in
print("Created card: \(card.cardId)")
}
)
Configuration
// Custom configuration
let configuration = CardScanConfiguration(
baseURL: "https://sandbox.cardscan.ai/v1",
timeout: 30,
retryCount: 3,
retryDelay: 1.0
)
let client = CardScanAPI(
apiKey: "sk_test_cardscan_ai_...",
configuration: configuration
)
Source Code
View the source code and contribute: GitHub
Last updated
Was this helpful?