Data Models

iOS SDK: Data Models

Reference guide for the core data structures used throughout the Digital Card Engine SDK.

CardEntity

The primary model representing a complete card with all associated data.

public class CardEntity: Codable {
    public let receivedCard: ReceivedCard
    public let cardArt: String?
    public let isReceiver: Bool?
    public let transactionHistory: [Transaction]?
    public let events: [CardEvent]?
    public let assetType: AssetType?
    public let metadata: CardMetadata?
}

Properties

PropertyTypeOptionalDescription
receivedCardReceivedCardNoCore card information (ID, balance, PAN, etc.)
cardArtString?YesBase64-encoded image of the card front
isReceiverBool?YesWhether the current user is the card receiver
transactionHistory[Transaction]?YesList of transactions (fetched separately via getTransactionHistory)
events[CardEvent]?YesCard lifecycle events (fetched separately)
assetTypeAssetType?YesType categorization for the card
metadataCardMetadata?YesAdditional extensible metadata

Usage

// Fetch cards
Task {
    do {
        let cards: [CardEntity] = try await sdk.getCards()
        
        // Access card details
        if let card = cards.first {
            let label = card.receivedCard.cardLabel
            let balance = card.receivedCard.balance
            let last4 = card.receivedCard.last4
        }
    } catch {
        print("Error fetching cards: \(error)")
    }
}

ReceivedCard

Core card information including identifiers, balance, and cardholder details.

public class ReceivedCard: Codable {
    public let giftCardId: String
    public let expiryDate: String
    public let cardArtId: String
    public let balance: Double?
    public let name: String?
    public let lastName: String?
    public let last4: String?
    public let message: CardMessage?
    public let status: ActivationStatus?
    public let cardLabel: String?
}

Properties

PropertyTypeOptionalDescription
giftCardIdStringNoUnique card identifier
expiryDateStringNoExpiration date (ISO 8601 format)
cardArtIdStringNoID to fetch card artwork
balanceDouble?YesCurrent card balance
nameString?YesCardholder first name
lastNameString?YesCardholder last name
last4String?YesLast 4 digits of PAN
messageCardMessage?YesOptional card message/greeting
statusActivationStatus?YesCard activation status
cardLabelString?YesDisplay name for the card

Usage

let receivedCard = cardEntity.receivedCard

// Display card label
let displayName = receivedCard.cardLabel ?? receivedCard.giftCardId

// Format masked PAN
let maskedPan = receivedCard.last4.map { "•••• \($0)" } ?? "N/A"

// Format balance
let formattedBalance = receivedCard.balance.map { 
    String(format: "$%.2f", $0)
} ?? "N/A"

Transaction

Represents a single card transaction.

public class Transaction: Codable {
    public let transactionType: TransactionType
    public let merchantCategoryCode: String?
    public let transactionDate: String
    public let transactionAmount: Double
    public let transactionId: String?
    public let transactionDescription: String
}

Properties

PropertyTypeOptionalDescription
transactionTypeTransactionTypeNoTransaction type enum (see below)
merchantCategoryCodeString?YesMerchant category code (MCC)
transactionDateStringNoTransaction date/time (ISO date-time format)
transactionAmountDoubleNoAbsolute transaction amount
transactionIdString?YesUnique transaction identifier
transactionDescriptionStringNoDescription or merchant name

TransactionType

public enum TransactionType: String, Codable {
    case load = "LOAD"
    case deduction = "DEDUCTION"
    case authorisation = "AUTHORISATION"
}

Usage

// Fetch transactions
Task {
    do {
        let result = try await sdk.getTransactionHistory(card: card)
        
        let transactions = result?.transactionHistory ?? []
        
        // Display transaction
        transactions.forEach { txn in
            print("\(txn.transactionAmount) on \(txn.transactionDate): \(txn.transactionDescription)")
        }
        
        // Filter by type
        let loads = transactions.filter { $0.transactionType == .load }
        let deductions = transactions.filter { $0.transactionType == .deduction }
    } catch {
        print("Error: \(error)")
    }
}

CardEvent

Represents a lifecycle or activity event for a card.

public class CardEvent: Codable {
    public let eventTime: String
    public let eventType: CardEventType
    public let initialBalance: Double
}

Properties

PropertyTypeOptionalDescription
eventTimeStringNoEvent date/time (ISO 8601)
eventTypeCardEventTypeNoType enum (see below)
initialBalanceDoubleNoCard balance at the time of the event

CardEventType

public enum CardEventType: String, Codable {
    case purchased = "PURCHASED"
    case fundsLoaded = "FUNDS_LOADED"
    case sent = "SENT"
    case activated = "ACTIVATED"
    case declined = "DECLINED"
    case expired = "EXPIRED"
}

Usage

// Fetch events (events come with transaction history)
Task {
    do {
        let cardWithEvents = try await sdk.getTransactionHistory(card: card)
        let events = cardWithEvents?.events ?? []
        
        // Display recent events
        events.prefix(5).forEach { event in
            print("\(event.eventType.rawValue) at \(event.eventTime): balance=\(event.initialBalance)")
        }
        
        // Filter by type
        let fundsLoaded = events.filter { $0.eventType == .fundsLoaded }
        let activations = events.filter { $0.eventType == .activated }
    } catch {
        print("Error: \(error)")
    }
}

CardMessage

Optional message attached to a card (e.g., gift card greeting).

public class CardMessage: Codable {
    public let receiver: String
    public let text: String
}

Properties

PropertyTypeOptionalDescription
receiverStringNoReceiver name/greeting
textStringNoMessage text

Usage

if let message = cardEntity.receivedCard.message {
    print("To: \(message.receiver)")
    print(message.text)
}

ActivationStatus

Card activation/lifecycle status.

public enum ActivationStatus: String, Codable {
    case sent = "sent"
    case activated = "ACTIVE"
    case declined = "declined"
    case expired = "expired"
    case inActivation = "IN_ACTIVATION"
    case notActivated = "NOT_ACTIVATED"
    case rejected = "REJECTED"
    case inProgress = "IN_PROGRESS"
    case failed = "FAILED"
    case blocked = "BLOCKED"
}

Usage

let status = cardEntity.receivedCard.status

switch status {
case .activated:
    showCard()
case .blocked:
    showLockedState()
case .expired:
    showExpiredMessage()
case .declined, .rejected, .failed:
    showErrorState()
default:
    showPendingState()
}

AssetType

Categorization for different card types.

public enum AssetType: String, Codable {
    case order = "ORDER"
    case giftCard = "GIFT_CARD"
}

CardMetadata

Pagination metadata for card transactions and events.

public class CardMetadata: Codable {
    public let page: Int
    public let totalPages: Int
    public let totalElements: Int
    public let hasNext: Bool
}

Properties

PropertyTypeOptionalDescription
pageIntNoCurrent page number
totalPagesIntNoTotal number of pages available
totalElementsIntNoTotal number of elements across pages
hasNextBoolNoWhether more pages are available

Usage

if let metadata = cardEntity.metadata {
    print("Page \(metadata.page) of \(metadata.totalPages)")
    print("Total elements: \(metadata.totalElements)")
    
    if metadata.hasNext {
        // Load next page
    }
}

Complete Example

import SwiftUI

struct CardDetailsExample: View {
    let sdk: DigitalCardEngine
    @State private var cards: [CardEntity] = []
    @State private var isLoading = false
    @State private var errorMessage: String?
    
    var body: some View {
        VStack {
            if isLoading {
                ProgressView("Loading cards...")
            } else if let error = errorMessage {
                Text("Error: \(error)")
                    .foregroundColor(.red)
            } else {
                List(cards, id: \.receivedCard.giftCardId) { card in
                    CardRow(card: card)
                }
            }
        }
        .task {
            await loadCards()
        }
    }
    
    func loadCards() async {
        isLoading = true
        errorMessage = nil
        
        do {
            // Fetch all cards
            cards = try await sdk.getCards()
            
            // Process first card
            if let card = cards.first {
                let receivedCard = card.receivedCard
                print("Card: \(receivedCard.cardLabel ?? "Unknown")")
                print("Balance: \(receivedCard.balance ?? 0)")
                print("Last 4: \(receivedCard.last4 ?? "N/A")")
                print("Status: \(receivedCard.status?.rawValue ?? "Unknown")")
                
                // Fetch transactions and events (they come together)
                let result = try await sdk.getTransactionHistory(card: card)
                
                result?.transactionHistory?.forEach { txn in
                    print("\(txn.transactionType.rawValue): \(txn.transactionAmount) on \(txn.transactionDate)")
                }
                
                // Fetch events (included in transaction history result)
                result?.events?.forEach { event in
                    print("\(event.eventType.rawValue) at \(event.eventTime): balance=\(event.initialBalance)")
                }
            }
        } catch {
            errorMessage = error.localizedDescription
        }
        
        isLoading = false
    }
}

struct CardRow: View {
    let card: CardEntity
    
    var body: some View {
        VStack(alignment: .leading) {
            Text(card.receivedCard.cardLabel ?? card.receivedCard.giftCardId)
                .font(.headline)
            if let balance = card.receivedCard.balance {
                Text(String(format: "$%.2f", balance))
                    .font(.subheadline)
            }
            if let last4 = card.receivedCard.last4 {
                Text("•••• \(last4)")
                    .font(.caption)
                    .foregroundColor(.secondary)
            }
        }
    }
}

Nullability Guidelines

Many properties are optional to accommodate:

  • Partial API responses: Backend may not always return all fields
  • Progressive loading: Data fetched incrementally (e.g., transactions loaded separately)
  • Optional features: Not all cards have messages, metadata, etc.

Always use optional binding (if let, guard let) or optional chaining (?.) when accessing optional properties.


Type Conversions

Date Formatting

import Foundation

func formatTransactionDate(_ isoDateTime: String) -> String {
    let formatter = ISO8601DateFormatter()
    guard let date = formatter.date(from: isoDateTime) else {
        return isoDateTime
    }
    
    let displayFormatter = DateFormatter()
    displayFormatter.dateFormat = "MMM dd, yyyy HH:mm"
    return displayFormatter.string(from: date)
}

// Usage
let displayDate = formatTransactionDate(transaction.transactionDate)

Currency Formatting

func formatAmount(_ amount: Double, currencyCode: String = "USD") -> String {
    let formatter = NumberFormatter()
    formatter.numberStyle = .currency
    formatter.currencyCode = currencyCode
    return formatter.string(from: NSNumber(value: amount)) ?? "$0.00"
}

// Usage
let displayAmount = formatAmount(transaction.transactionAmount)