
financekit
Access eligible Wallet financial data using FinanceKit and FinanceKitUI. Use when querying transactions or balances, reading Apple Card, Apple Cash, Savings, or U.K. connected-account data, requesting financial-data authorization, using TransactionPicker, enabling iOS 26 background delivery, or saving and checking Wallet orders.
- Installs
- 3.1K
- Last 24h
- +16
- Trending rank
- #6793
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/dpearson2699/swift-ios-skills --skill financekit- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads financekit automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
FinanceKit
Access eligible financial data from Apple Wallet, including U.S. Apple Card, Apple Cash, Savings, and U.K. connected-account data. FinanceKit provides on-device access to accounts, balances, and transactions with user-controlled authorization. Targets Swift 6.3 / current Apple platforms; query APIs are available from iOS/iPadOS 17.4, TransactionPicker from iOS/iPadOS 18, and background delivery from iOS/iPadOS 26.
Keep FinanceKit guidance focused on financial-data access, Wallet order storage/querying, TransactionPicker, and background delivery. Route Apple Pay checkout to PassKit, widget UI/timeline work to WidgetKit, and Wallet order-tracking email or Apple Business Connect optimization outside this skill.
Contents
- Setup and Entitlements
- Data Availability
- Authorization
- Querying Accounts
- Account Balances
- Querying Transactions
- Long-Running Queries and History
- Transaction Picker
- Wallet Orders
- Background Delivery
- Common Mistakes
- Review Checklist
- References
Setup and Entitlements
Requirements
- Managed entitlement -- request
com.apple.developer.financekitfrom Apple via the FinanceKit entitlement request form. This is a managed capability; Apple reviews each application. - Organization-level Apple Developer account (individual accounts are not eligible).
- Account Holder role required to request the entitlement.
- Eligible App Store app -- the app must be in the Finance category, distributed through the App Store for iPhone in the United States or United Kingdom, and provide financial-management tools such as net-worth, spending, or budgeting features.
- Per-bundle-ID approval -- Apple assigns the entitlement to the approved bundle ID; do not assume it applies to sibling apps or extensions automatically.
- If the app offers financial products directly or through a regulated institution, it must allow customers to connect those accounts to Apple Wallet and share the data with FinanceKit.
Project Configuration
- Add the FinanceKit entitlement through Xcode managed capabilities after Apple approves the request.
- Add
NSFinancialDataUsageDescriptionto Info.plist -- this string is shown to the user during the authorization prompt. - For iOS 26 background delivery, add the FinanceKit entitlement to both the app and extension targets, then use App Groups for shared storage.
<key>NSFinancialDataUsageDescription</key>
<string>This app uses your financial data to track spending and provide budgeting insights.</string>
Data Availability
U.S. FinanceKit financial data requires iOS/iPadOS 17.4+ and currently covers eligible Apple Card, Apple Cash, and Savings data; Apple Card Family participants and Apple Cash Family children are excluded. U.K. support requires iOS/iPadOS 18.4+ and uses open banking for supported institutions. Orders APIs are available separately from financial-data query APIs.
Check whether the device supports FinanceKit before making any API calls. This value is constant across launches and iOS versions.
import FinanceKit
guard FinanceStore.isDataAvailable(.financialData) else {
// FinanceKit not available -- do not call any other financial data APIs.
// The framework terminates the app if called when unavailable.
return
}
For Wallet orders:
guard FinanceStore.isDataAvailable(.orders) else { return }
Data availability returning true does not guarantee data exists on the device. Data access can also become temporarily restricted (e.g., Wallet unavailable, MDM restrictions). Restricted access throws FinanceError.dataRestricted rather than terminating.
Authorization
Request authorization to access user-selected financial accounts. The system presents an account picker where the user chooses which accounts to share and the earliest transaction date to expose.
let store = FinanceStore.shared
let status = try await store.requestAuthorization()
switch status {
case .authorized: break // Proceed with queries
case .denied: break // User declined
case .notDetermined: break // No meaningful choice made
@unknown default: break
}
Checking Current Status
Query current authorization without prompting:
let currentStatus = try await store.authorizationStatus()
Once the user grants or denies access, requestAuthorization() returns the cached decision without showing the prompt again. Users can change access in Settings > Privacy & Security > Financial Data.
Querying Accounts
Accounts are modeled as an enum with two cases: .asset (e.g., Apple Cash, Savings) and .liability (e.g., Apple Card credit). Both share common properties (id, displayName, institutionName, currencyCode) while liability accounts add credit-specific fields.
func fetchAccounts() async throws -> [Account] {
let query = AccountQuery(
sortDescriptors: [SortDescriptor(\Account.displayName)],
predicate: nil,
limit: nil,
offset: nil
)
return try await store.accounts(query: query)
}
Working with Account Types
switch account {
case .asset(let asset):
print("Asset account, currency: \(asset.currencyCode)")
case .liability(let liability):
if let limit = liability.creditInformation.creditLimit {
print("Credit limit: \(limit.amount) \(limit.currencyCode)")
}
}
Account Balances
Balances represent the amount in an account at a point in time. A CurrentBalance is one of three cases: .available (includes pending), .booked (posted only), or .availableAndBooked.
func fetchBalances(for accountID: UUID) async throws -> [AccountBalance] {
let predicate = #Predicate<AccountBalance> { balance in
balance.accountID == accountID
}
let query = AccountBalanceQuery(
sortDescriptors: [SortDescriptor(\AccountBalance.id)],
predicate: predicate,
limit: nil,
offset: nil
)
return try await store.accountBalances(query: query)
}
Reading Balance Amounts
Amounts are always positive decimals. Use creditDebitIndicator to determine the sign:
func formatBalance(_ balance: Balance) -> String {
let sign = balance.creditDebitIndicator == .debit ? "-" : ""
return "\(sign)\(balance.amount.amount) \(balance.amount.currencyCode)"
}
// Extract from CurrentBalance enum:
switch balance.currentBalance {
case .available(let bal): formatBalance(bal)
case .booked(let bal): formatBalance(bal)
case .availableAndBooked(let available, _): formatBalance(available)
@unknown default: "Unknown"
}
Querying Transactions
Use TransactionQuery with Swift predicates, sort descriptors, limit, and offset.
let predicate = #Predicate<Transaction> { $0.accountID == accountID }
let query = TransactionQuery(
sortDescriptors: [SortDescriptor(\Transaction.transactionDate, order: .reverse)],
predicate: predicate,
limit: 50,
offset: nil
)
let transactions = try await store.transactions(query: query)
Reading Transaction Data
let amount = transaction.transactionAmount
let direction = transaction.creditDebitIndicator == .debit ? "spent" : "received"
print("\(transaction.transactionDescription): \(direction) \(amount.amount) \(amount.currencyCode)")
// merchantName, merchantCategoryCode, foreignCurrencyAmount are optional
Built-In Predicate Helpers
FinanceKit provides factory methods for common filters:
// Filter by transaction status
let bookedOnly = TransactionQuery.predicate(forStatuses: [.booked])
// Filter by transaction type
let purchases = TransactionQuery.predicate(forTransactionTypes: [.pointOfSale, .directDebit])
// Filter by merchant category
let groceries = TransactionQuery.predicate(forMerchantCategoryCodes: [
MerchantCategoryCode(rawValue: 5411) // Grocery stores
])
For a transaction field table and more query patterns, read references/financekit-patterns.md.
Long-Running Queries and History
Use AsyncSequence-based history APIs for catch-up sync, live updates, or resumable sync. These return inserted, updated, and deleted item IDs plus a HistoryToken.
func catchUpTransactions(for accountID: UUID) async throws {
let history = store.transactionHistory(
forAccountID: accountID,
since: loadSavedToken(),
isMonitoring: false // finish after saved-token catch-up
)
for try await changes in history {
removeLocalRecords(withIDs: changes.deleted)
upsert(changes.inserted + changes.updated)
saveToken(changes.newToken)
}
}
History Token Persistence
HistoryToken conforms to Codable. Persist it to resume queries without reprocessing data:
func saveToken(_ token: FinanceStore.HistoryToken) {
if let data = try? JSONEncoder().encode(token) {
UserDefaults.standard.set(data, forKey: "financeHistoryToken")
}
}
func loadSavedToken() -> FinanceStore.HistoryToken? {
guard let data = UserDefaults.standard.data(forKey: "financeHistoryToken") else { return nil }
return try? JSONDecoder().decode(FinanceStore.HistoryToken.self, from: data)
}
If a saved token points to compacted history, the framework throws FinanceError.historyTokenInvalid. Discard the token, then immediately run a fresh catch-up query for the affected account or balance stream so local state and the replacement token are rebuilt. Use isMonitoring: true only for a separate live monitor.
Account and Balance History
let accountChanges = store.accountHistory(since: nil, isMonitoring: true)
let balanceChanges = store.accountBalanceHistory(forAccountID: accountID, since: nil, isMonitoring: true)
Ongoing budgeting sync should cover the data model the user authorized: account objects for account additions/removals, account balances for trend and widget state, and transactions for spending detail. Use separate history tokens per stream or account so a compacted token only forces resync of the affected stream.
Transaction Picker
For apps that need selective, ephemeral access without full authorization, use TransactionPicker from FinanceKitUI. Access is not persisted -- transactions are passed directly for immediate use.
import FinanceKitUI
struct ExpenseImportView: View {
@State private var selectedTransactions: [Transaction] = []
var body: some View {
if FinanceStore.isDataAvailable(.financialData) {
TransactionPicker(selection: $selectedTransactions) {
Label("Import Transactions", systemImage: "creditcard")
}
}
}
}
Wallet Orders
FinanceKit supports saving and querying Wallet orders (e.g., purchase receipts, shipping tracking).
Saving an Order
let result = try await store.saveOrder(signedArchive: archiveData)
switch result {
case .added: break // Saved
case .cancelled: break // User cancelled
case .newerExisting: break // Newer version already in Wallet
@unknown default: break
}
Checking for an Existing Order
let orderID = FullyQualifiedOrderIdentifier(
orderTypeIdentifier: "com.merchant.order",
orderIdentifier: "ORDER-123"
)
let result = try await store.containsOrder(matching: orderID, updatedDate: lastKnownDate)
// result: .exists, .newerExists, .olderExists, or .notFound
Add Order to Wallet Button (FinanceKitUI)
import FinanceKitUI
AddOrderToWalletButton(signedArchive: orderData) { result in
// result: .success(SaveOrderResult) or .failure(Error)
}
Background Delivery
iOS 26+ supports background delivery extensions that notify your app of financial data changes outside its lifecycle. User authorization made in the main app is inherited by the extension. Both targets need the FinanceKit entitlement; use App Groups to share data between the app, extension, and related widgets.
Enabling Background Delivery
These registration methods are synchronous and nonthrowing; do not write try or await.
store.enableBackgroundDelivery(
for: [.accounts, .accountBalances, .transactions],
frequency: .daily
)
Available frequencies: .hourly, .daily, .weekly. These are expected minimum intervals between extension launches when data changes; longer frequencies give the extension a larger processing window.
Disable selectively or entirely:
store.disableBackgroundDelivery(for: [.transactions])
store.disableAllBackgroundDelivery()
Background Delivery Extension
Create a background delivery extension target in Xcode (Background Delivery Extension template). Implement the two async entry points directly on the extension type and return from didReceiveData(for:) only after essential work is saved.
import FinanceKit
@main
struct MyFinanceExtension: BackgroundDeliveryExtension {
func didReceiveData(for types: [FinanceStore.BackgroundDataType]) async {
if types.contains(.transactions) {
await processNewTransactions()
}
if types.contains(.accountBalances) {
await updateBalanceCache()
}
if types.contains(.accounts) {
await refreshAccountList()
}
}
func willTerminate() async { await savePartialWork() }
}
Common Mistakes
1. Calling APIs when data is unavailable
DON'T -- skip availability check:
let store = FinanceStore.shared
let status = try await store.requestAuthorization() // Terminates if unavailable
DO -- guard availability first:
guard FinanceStore.isDataAvailable(.financialData) else {
showUnavailableMessage()
return
}
let status = try await FinanceStore.shared.requestAuthorization()
2. Ignoring the credit/debit indicator
DON'T -- treat amounts as signed values:
let spent = transaction.transactionAmount.amount // Always positive
DO -- apply the indicator:
let amount = transaction.transactionAmount.amount
let signed = transaction.creditDebitIndicator == .debit ? -amount : amount
3. Not handling data restriction errors
DON'T -- assume authorized access persists:
let transactions = try await store.transactions(query: query) // Fails if Wallet restricted
DO -- catch FinanceError:
do {
let transactions = try await store.transactions(query: query)
} catch let error as FinanceError {
if case .dataRestricted = error { showDataRestrictedMessage() }
}
4. Replacing resumable history with snapshots
Use the canonical transaction-history loop above and persist each newToken only after local deletes and upserts commit. Keep separate tokens per stream/account; on invalid-token errors, resync only the affected scope.
5. Misinterpreting credit/debit on liability accounts
Both asset and liability accounts use .debit for outgoing money. But .credit means different things: on an asset account it means money received; on a liability account it means a payment or refund that increases available credit. See references/financekit-patterns.md for a full interpretation table.
Review Checklist
-
FinanceStore.isDataAvailable(.financialData)checked before any API call - App eligibility checked: Finance category, App Store iPhone distribution in the U.S. or U.K., financial-management feature set, organization account, Account Holder request
-
com.apple.developer.financekitentitlement requested and approved for the app bundle ID -
NSFinancialDataUsageDescriptionset in Info.plist with a clear, specific message - Authorization status handled for all cases (
.authorized,.denied,.notDetermined) -
FinanceError.dataRestrictedcaught and handled gracefully -
CreditDebitIndicatorapplied correctly to amounts (not treated as signed) - History tokens persisted for resumable queries
-
FinanceError.historyTokenInvalidhandled by discarding token and immediately resyncing the affected stream - Ongoing sync plan covers authorized accounts, balances, and transactions, not transactions alone
- Long-running queries use
isMonitoring: falsewhen live updates are not needed - Transaction picker used when full authorization is unnecessary
- Only data the app genuinely needs is queried
- Deleted IDs from history changes are explicitly removed from local account, balance, or transaction storage
- Background delivery calls use the synchronous iOS 26 APIs and the extension is in the same App Group as the main app
- Background delivery registers every needed data type:
.accounts,.accountBalances, and/or.transactions - FinanceKit entitlement added to both app and background delivery extension targets
- Financial data deleted when user revokes access
References
- Extended patterns (predicates, sorting, pagination, currency formatting, background updates): references/financekit-patterns.md
- Get started with FinanceKit
- FinanceKit framework
- FinanceKitUI framework
- FinanceStore
- Transaction
- Account
- AccountBalance
- FinanceKit entitlement
- Implementing a background delivery extension
- Meet FinanceKit (WWDC24)
- What's new in Apple Pay (WWDC25)
FAQ
What does the financekit skill do?
Access eligible Wallet financial data using FinanceKit and FinanceKitUI. Use when querying transactions or balances, reading Apple Card, Apple Cash, Savings, or U.K. connected-account data, requesting financial-data authorization, using TransactionPicker, enabling iOS 26 background delivery, or saving and checking Wallet orders.
How do I install financekit?
Run npx skills add https://github.com/dpearson2699/swift-ios-skills --skill financekit in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/financekit/ (personal) or .claude/skills/financekit/ (project).
Which AI agents can use financekit?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is financekit safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains financekit?
It is published by dpearson2699 in dpearson2699/swift-ios-skills. It has 3,078 recorded installs.
More from dpearson2699/swift-ios-skills
swiftui-animation
dpearson2699/swift-ios-skills
Implement, diagnose, or review SwiftUI motion using explicit and scoped implicit animations, springs, transitions, PhaseAnimator, KeyframeAnimator, matched geometry or navigation zoom, SF Symbol effects, and custom Animation types. Use when views should animate on state changes, insertion, removal, navigation, or multi-step choreography, or when motion must respect Reduce Motion and Swift concurrency.
ios-accessibility
dpearson2699/swift-ios-skills
Build and audit SwiftUI, UIKit, and AppKit accessibility for VoiceOver, Voice Control, Switch Control, Full Keyboard Access, Dynamic Type, focus restoration, labels/traits/actions, traversal, custom rotors, NSAccessibility, XCTest checks, adaptive system preferences, and App Store accessibility declarations. Use when implementing accessible UI, fixing an accessibility audit, testing assistive-technology behavior, or substantiating App Store Accessibility Nutrition Labels.
swiftui-performance
dpearson2699/swift-ios-skills
Profile, diagnose, and remediate SwiftUI runtime performance using code review, Instruments, and repeatable measurements. Use when a SwiftUI screen renders slowly, scrolling or animations hitch, view bodies update excessively, list identity churns, layout work spikes, or broad Observation dependencies raise CPU cost. Covers evidence-based triage, SwiftUI Instruments lanes, lazy-container guardrails, state lifetime, and before/after verification.
swiftui-patterns
dpearson2699/swift-ios-skills
Builds and reviews SwiftUI views with modern MV architecture, state, composition, isolated previews, and migration guidance. Covers @Observable ownership, @State/@Bindable/@Environment wiring, view decomposition, ViewModifiers, environment values, .task loading, iOS 26+ handoffs, Writing Tools, clipboard availability, and performance. Use when structuring SwiftUI state, managing @Observable, composing views, previewing meaningful UI states, or correcting SwiftUI patterns.
app-store-review
dpearson2699/swift-ios-skills
Audits App Store submission readiness and rejection risk across current review guidelines, PrivacyInfo.xcprivacy and required-reason APIs, privacy labels, ATT, StoreKit payments, metadata, entitlements, widgets, and Live Activities. Use when preparing a submission, responding to rejection, reconciling privacy evidence, or separating upload blockers from cleanup.
ios-networking
dpearson2699/swift-ios-skills
Build, review, or improve networking code in iOS/macOS apps using URLSession with async/await, structured concurrency, and modern Swift patterns. Use when working with REST APIs, downloading files, uploading data, WebSocket connections, pagination, retry logic, request middleware, caching, background transfers, or network reachability monitoring. Also use when handling HTTP requests, API clients, network error handling, or data fetching in Swift apps.
Popular mobile skills
design-mobile-apps
designed-by-ai/skills
Design mobile app screens with Sleek, edit Sleek projects, and implement their designs in React Native or HTML.
ios-design
uizze.sh
Design and review iOS UI/UX for iPhone apps in SwiftUI, UIKit, or React Native. Use for mobile app design, screen redesigns, navigation flows, typography, forms, keyboard and safe-area layout, and accessibility checks. Apply Apple Human Interface Guidelines and consult real product references through the optional Uizze connection.
vercel-react-native-skills
vercel-labs/agent-skills
React Native and Expo best practices for building performant mobile apps. Use when building React Native components, optimizing list performance, implementing animations, or working with native modules. Triggers on tasks involving React Native, Expo, mobile performance, or native platform APIs.
animate-expo
emilkowalski/skills
Build animations in React Native and Expo, making the decisions in the order that determines whether they feel right — should it animate, which thread it runs on, which properties, spring or timing, how the gesture hands off, how it degrades. Writes the implementation with Reanimated, Gesture Handler, Expo Router and expo-haptics. Use when animating anything in an Expo app, adding gestures, sheets, screen transitions, press feedback or haptics, or fixing motion that stutters on device. For web animation use animate.
xcode-project-setup
firebase/agent-skills
Safely modifies Xcode projects (.pbxproj) to add Swift Packages and link files. Use this skill whenever an iOS project needs dependencies installed (e.g. Firebase, Alamofire).
firebase-remote-config-basics
firebase/agent-skills
Manages Firebase Remote Config templates, feature flags, loading strategies, and SDKs (Android, iOS). Use when downloading/deploying remoteconfig JSON templates, managing version history/feature flags, setting in-app defaults, fetchAndActivate(), real-time listeners, or SDK setup. Don't use for Firebase Hosting, Auth, Firestore, Data Connect, Crashlytics, or App Hosting.