Skip to content

Latest commit

 

History

History
314 lines (243 loc) · 12.8 KB

File metadata and controls

314 lines (243 loc) · 12.8 KB

AGENTS.md

This file provides guidance to AI agents like Claude Code (claude.ai/code) when working with code in this repository.

This file is symlinked for cross-agents compatibility to the following paths:

  • CLAUDE.md

Project Overview

Bitkit iOS is a native Swift implementation of a Bitcoin and Lightning Network wallet. This is a work-in-progress repository that is NOT the live production app. The production app uses React Native and is at github.com/synonymdev/bitkit.

This app integrates with:

  • LDK Node (Lightning Development Kit) for Lightning Network functionality
  • BitkitCore (Rust-based core library) for Bitcoin operations
  • Electrum/Esplora for blockchain data
  • Blocktank for Lightning channel services

Build & Development Commands

Building

# Standard build - Open Bitkit.xcodeproj in Xcode and build

# E2E test build (uses local Electrum backend by default)
xcodebuild -workspace Bitkit.xcodeproj/project.xcworkspace \
  -scheme Bitkit \
  -configuration Debug \
  SWIFT_ACTIVE_COMPILATION_CONDITIONS='$(inherited) E2E_BUILD' \
  build

# E2E test build with network Electrum and regtest (Info.plist build setting)
E2E_BACKEND=network E2E_NETWORK=regtest \
  xcodebuild -workspace Bitkit.xcodeproj/project.xcworkspace \
  -scheme Bitkit \
  -configuration Debug \
  SWIFT_ACTIVE_COMPILATION_CONDITIONS='$(inherited) E2E_BUILD' \
  build

Running on Physical Device

From Terminal:

# List connected devices
xcrun devicectl list devices

# Set device ID from the list above
DEVICE_ID="<device-identifier-from-list>"

# Build for device
xcodebuild -project Bitkit.xcodeproj -scheme Bitkit -configuration Debug \
  -destination 'generic/platform=iOS' -derivedDataPath build build

# Install app on device
xcrun devicectl device install app --device $DEVICE_ID build/Build/Products/Debug-iphoneos/Bitkit.app

# Launch app with console output
xcrun devicectl device process launch --device $DEVICE_ID to.bitkit --console

From Xcode:

  1. Open Bitkit.xcodeproj
  2. Select your device from the destination dropdown
  3. Press Cmd+R to build and run

Note: The project includes a "Remove Static Framework Stubs" build phase that removes empty LDKNodeFFI.framework from the app bundle. This is needed because LDKNodeFFI is a static library (linked at compile time), not a dynamic framework.

Code Formatting

# Install SwiftFormat
brew install swiftformat

# Format all Swift code
swiftformat .

# Setup git hooks for automatic formatting on commits
npm install -g git-format-staged
./scripts/setup-hooks.sh

Localization

# Validate translations (checks for missing translations and validates translation keys)
node scripts/validate-translations.js

Note: Localization files are synced from Transifex using bitkit-transifex-sync.

Testing

# Run tests via Xcode Test Navigator or:
# Cmd+U in Xcode

Architecture

SwiftUI Patterns (CRITICAL)

This project follows modern SwiftUI patterns and explicitly AVOIDS traditional MVVM with ViewModels. The architecture uses:

  1. @Observable Objects for Business Logic

    • Use @Observable class for shared business logic instead of ViewModels
    • Inject via .environment(businessLogic)
    • Retrieve with @Environment(BusinessLogic.self)
    • Example: @Observable class UserManager { var users: [User] = []; func loadUsers() async { } }
  2. Native SwiftUI Data Flow

    • @State for local view state only
    • @Binding for two-way data flow between parent/child views
    • @Observable for shared business logic objects
    • All state mutations must happen on @MainActor
  3. Lifecycle Management

    • Use .task modifier for async operations (NOT .onAppear)
    • .task automatically cancels when view disappears
    • Async operations should delegate to @Observable business logic objects
  4. Component Design

    • Decompose views into small, focused, single-purpose components
    • Use descriptive names (e.g., UserProfileCard not Card)
    • Prefer composition over deep view hierarchies
    • Components should be independent and reusable with generic data types

Core Architecture Layers

┌─────────────────────────────────────────────────┐
│              Views (SwiftUI)                    │
│  - MainNavView, Activity, Wallet, Settings     │
│  - Small, focused components                    │
└─────────────────┬───────────────────────────────┘
                  │
┌─────────────────▼───────────────────────────────┐
│        @Observable Business Logic               │
│  - AppViewModel, WalletViewModel, etc.          │
│  - Injected via .environment()                  │
└─────────────────┬───────────────────────────────┘
                  │
┌─────────────────▼───────────────────────────────┐
│              Services                           │
│  - CoreService (BitkitCore bridge)              │
│  - LightningService (LDK Node)                  │
│  - TransferService, CurrencyService             │
└─────────────────┬───────────────────────────────┘
                  │
┌─────────────────▼───────────────────────────────┐
│         External Dependencies                   │
│  - BitkitCore (Rust): Bitcoin operations        │
│  - LDKNode: Lightning Network operations        │
│  - Electrum/Esplora: Blockchain data            │
└─────────────────────────────────────────────────┘

Key Components

App Entry Point:

  • BitkitApp.swift: Main app entry, handles AppDelegate setup, push notifications, quick actions
  • AppScene.swift: Root scene coordinator, manages app-wide ViewModels and lifecycle
  • ContentView.swift: Root content view

Services Layer:

  • CoreService: Bridge to BitkitCore (Rust), handles Bitcoin operations and activity storage
  • LightningService: Manages LDK Node lifecycle, Lightning operations, channel management
  • TransferService: Orchestrates Bitcoin/Lightning transfers (send/receive)
  • TransferStorage: Persists pending transfer state
  • CurrencyService: Currency conversion and exchange rates
  • ElectrumConfigService, RgsConfigService: Backend configuration
  • ServiceQueue: Queue system for background operations (.core, .ldk queues)

Managers:

  • SessionManager: User session state
  • PushNotificationManager: Push notification handling for incoming payments
  • ScannerManager: QR code scanning for payments
  • ToastWindowManager: App-wide toast notifications
  • TransferTrackingManager: Tracks pending transfers (new feature)
  • TimedSheets/: Timed sheet management (backup reminders, high balance warnings)
  • SuggestionsManager, TagManager, LanguageManager, NetworkMonitor

ViewModels (Legacy): While the project is transitioning away from traditional ViewModels, these still exist but should follow @Observable patterns:

  • AppViewModel: App-wide state (toasts, errors)
  • WalletViewModel: Wallet state, balance, node lifecycle
  • ActivityListViewModel: Transaction/payment history
  • TransferViewModel: Transfer flows (send/receive)
  • NavigationViewModel, SheetViewModel: UI navigation state
  • BlocktankViewModel: Lightning channel ordering via Blocktank

Key Directories:

  • Components/: Reusable UI components (buttons, sliders, widgets)
  • Views/: Feature-specific views (Onboarding, Backup, Security, Wallets, Settings, Transfer)
  • Extensions/: Swift extensions for utilities and mock data
  • Utilities/: Helper utilities (Logger, Keychain, Crypto, Haptics, StateLocker)
  • Models/: Data models (Toast, ElectrumServer, NodeLifecycleState, etc.)
  • Styles/: Fonts and sheet styles

Service Queue Pattern

Operations that interact with CoreService or LightningService must use ServiceQueue:

// For BitkitCore operations
try await ServiceQueue.background(.core) {
    // Core operations here
}

// For LDK Node operations
try await ServiceQueue.background(.ldk) {
    // Lightning operations here
}

State Management Patterns

Node Lifecycle: The Lightning node has distinct lifecycle states tracked via NodeLifecycleState:

  • .notStarted.initializing.running.stopped
  • Error states: .errorStarting(String)

Transfer Tracking: New feature (TransferTrackingManager) tracks pending transfers to handle edge cases where transfers are initiated but not completed.

Important Development Notes

Security & Bitcoin/Lightning

  • Use proper Bitcoin/Lightning terminology in code and naming
  • All Bitcoin/Lightning operations belong in the service layer, never in views
  • The app uses StateLocker to prevent concurrent Lightning operations (.lightning lock)
  • Keychain is used for sensitive data (mnemonics, passphrases)

Network Configuration

  • The app currently runs on regtest only (see LightningService.swift:92 guard)
  • VSS (Versioned Storage Service) authentication is not yet implemented
  • Electrum/Esplora server URLs are configurable via Env
  • E2E builds use local Electrum backend via E2E_BUILD compilation flag (override with E2E_BACKEND/E2E_NETWORK build settings)

Error Handling

  • Use do-catch blocks for async operations
  • Provide user feedback via toasts: app.toast(type: .error, title: "...", description: "...")
  • Handle loading, error, and empty states comprehensively
  • Consider using enum LoadingState<T> { case idle, loading, loaded(T), error(Error) }

iOS Version Compatibility

  • Minimum deployment target is iOS 17.0.
  • Xcode previews work with the minimum target (iOS 17); they may not work on iOS 18+ due to Rust dependencies.
  • Use availability checks only for iOS 18+ features:
    if #available(iOS 18.0, *) {
        // Use iOS 18+ features
    } else {
        // Fallback
    }

Performance

  • Avoid expensive operations in view body
  • Move heavy computations to @Observable objects
  • Use proper state granularity to minimize view updates
  • Use @ViewBuilder for repetitive view code

Accessibility

Ensure accessibility modifiers and labels are added to custom components.

Code Style & Conventions

  • SwiftFormat configuration in .swiftformat
  • Max line width: 150 characters
  • Swift version: 5.10
  • Use descriptive names: isLoadingUsers not loading
  • Follow Apple's SwiftUI best practices

Changelog

  • ALWAYS add exactly ONE entry per PR under ## [Unreleased] in CHANGELOG.md for feat: and fix: PRs; skip for chore:, ci:, refactor:, test:, docs: unless the change is user-facing
  • NEVER add multiple changelog lines for the same PR — summarize all changes in a single concise entry
  • USE standard Keep a Changelog categories: ### Added, ### Changed, ### Deprecated, ### Removed, ### Fixed, ### Security
  • ALWAYS append #PR_NUMBER at the end of each changelog entry when the PR number is known
  • ALWAYS place new entries at the top of their category section (newest first)
  • NEVER modify released version sections — only edit ## [Unreleased]
  • ALWAYS create category headings on demand (don't add empty stubs)

Common Workflows

Adding a New Feature

  1. Identify if business logic should live in an @Observable object or existing ViewModel
  2. Create UI components in Components/ or feature-specific views in Views/
  3. Wire up via .environment() injection in AppScene.swift
  4. Use .task for async initialization
  5. Add error handling and user feedback (toasts)

Working with Lightning

  1. All Lightning operations go through LightningService.shared
  2. Lock the Lightning state with StateLocker.lock(.lightning) for critical operations
  3. Listen to LDK events via wallet.addOnEvent(id:) pattern
  4. Sync activity list after Lightning events

Working with Bitcoin

  1. Use CoreService for Bitcoin operations
  2. Activity tracking handles both on-chain and Lightning payments
  3. RBF (Replace-By-Fee) is tracked via ActivityService.replacementTransactions

Localization Changes

  1. Update translation keys in code
  2. Run node scripts/validate-translations.js to check for issues
  3. Sync with Transifex using bitkit-transifex-sync tool