8.9 KiB
AI Implementation Context
This file summarizes project-specific context, architecture, and conventions to speed up future AI work.
Project Summary
BusinessCard is a SwiftUI app for building and sharing digital business cards with QR codes. It includes iOS screens for cards, sharing, customization, contact tracking, and widget previews, plus a watchOS companion to show a default card QR code.
Key Constraints
- iOS 26+, watchOS 12+, Swift 6.2.
- SwiftUI with
@Observableclasses and@MainActor. - Protocol‑oriented architecture is prioritized.
- Clean Architecture with separation of concerns.
- One public type per file, keep files under 300 lines.
- No UIKit unless explicitly requested.
- String Catalogs only (
.xcstrings). - No magic numbers in views; use Bedrock's
Designconstants. - Uses Bedrock package for shared design system and utilities.
Core Data Flow
AppStateowns:CardStore(cards and selection)ContactsStore(contact list + search)ShareLinkService(share URLs)
- SwiftData with CloudKit for persistence and sync.
- App Groups for iOS-Watch data sharing.
- Views read state via environment and render UI only.
Dependencies
Bedrock Package
Located at /Frameworks/Bedrock (local package). Provides:
Design.Spacing,Design.CornerRadius,Design.Opacity, etc.QRCodeGeneratorandQRCodeImageViewfor QR codes- Reusable settings components
App-specific extensions are in Design/DesignConstants.swift:
Design.CardSize- card dimensions, avatar, QR sizesDesign.Shadow.offsetNone- zero offset extensionColor.AppBackground,Color.CardPalette,Color.AppAccent,Color.AppText
Important Files
Models
-
Models/BusinessCard.swift— SwiftData model with:- Name fields: prefix, firstName, middleName, lastName, maidenName, suffix, preferredName
- Basic fields: role, company, email, phone, website, location
- Rich fields: pronouns, bio, headline, accreditations (comma-separated tags)
- Dynamic contact fields:
@Relationshipto array ofContactFieldobjects - Legacy fields: social links (LinkedIn, Twitter, etc.) for backward compatibility
- Photo:
photoDataandlogoDatastored with@Attribute(.externalStorage) - Computed:
theme,layoutStyle,vCardPayload,hasSocialLinks,orderedContactFields - Helper methods:
addContactField,removeContactField,reorderContactFields
-
Models/ContactField.swift— SwiftData model for dynamic contact fields:- Properties:
typeId,value,title,orderIndex - Relationship:
card(inverse to BusinessCard) - Computed:
fieldType,displayName,iconImage(),iconColor,buildURL() - Supports multiple fields of same type, drag-to-reorder
- Properties:
-
Models/ContactFieldType.swift— Struct defining 30+ field types:- Categories: contact, social, developer, messaging, payment, creator, scheduling, other
- Properties:
id,displayName,systemImage,isCustomSymbol,iconColor - Properties:
valueLabel,valuePlaceholder,titleSuggestions,keyboardType - Method:
iconImage()— returns correct Image (asset vs SF Symbol) - Method:
urlBuilder— closure to build deep link URLs - Static instances:
.email,.phone,.linkedIn,.twitter,.instagram, etc. - Uses custom assets from
Assets.xcassets/SocialSymbols/
-
Models/Contact.swift— SwiftData model with:- Basic fields: name, role, company
- Annotations: notes, tags (comma-separated), followUpDate, whereYouMet
- Received cards: isReceivedCard, email, phone
- Photo:
photoData - Computed:
tagList,hasFollowUp,isFollowUpOverdue - Static:
fromVCard(_:)parser
-
Models/CardTheme.swift— card theme palette -
Models/CardLayoutStyle.swift— stacked/split/photo -
Models/AppTab.swift— tab bar enum
Protocols (POP)
Protocols/BusinessCardProviding.swift— card selection interfaceProtocols/ContactTracking.swift— contact management interfaceProtocols/ShareLinkProviding.swift— share URL generation interface
State
State/AppState.swift— central state containerState/CardStore.swift— card CRUD, selection, watch syncState/ContactsStore.swift— contacts, search, received cards
Services
Services/ShareLinkService.swift— share URL helpersServices/WatchSyncService.swift— App Group sync to watch
Views
Main screens:
Views/RootTabView.swift— tabbed shell (4 tabs: My Cards, Share, Contacts, Widgets)Views/CardsHomeView.swift— full-screen swipeable card view with edit buttonViews/ShareCardView.swift— QR + share actions + track shareViews/ContactsView.swift— contact list with sectionsViews/WidgetsView.swift— widget preview mockups
Feature views:
Views/BusinessCardView.swift— card display with layoutsViews/CardEditorView.swift— create/edit cards with PhotosPickerViews/ContactDetailView.swift— full contact view with annotationsViews/QRScannerView.swift— camera-based QR scannerViews/QRCodeView.swift— QR code image generator
Reusable components (in Views/Components/):
AvatarBadgeView.swift— circular avatar with photo or iconIconRowView.swift— icon + text row for detailsLabelBadgeView.swift— small badge labelsActionRowView.swift— generic action row with chevronContactFieldPickerView.swift— grid picker for selecting contact field typesContactFieldsManagerView.swift— orchestrates picker + added fields listAddedContactFieldsView.swift— displays added fields with drag-to-reorder
Sheets (in Views/Sheets/):
RecordContactSheet.swift— track share recipientContactFieldEditorSheet.swift— add/edit contact field with type-specific UI
Small utilities:
Views/EmptyStateView.swift— empty state placeholderViews/PrimaryActionButton.swift— styled action button
Assets
Assets.xcassets/SocialSymbols/— custom brand icons as.symbolsetfiles:- Social: linkedin, x-twitter, instagram, facebook, tiktok, threads, bluesky, mastodon, reddit, twitch
- Developer: github.fill
- Messaging: telegram, discord.fill, slack, matrix
- Creator: patreon.fill, ko-fi
Icon Rendering:
- Custom symbols use
Image("symbolName")(asset catalog) - SF Symbols use
Image(systemName: "symbolName") ContactFieldType.isCustomSymbolflag determines which to useContactFieldType.iconImage()returns the correct Image type
Design + Localization
Design/DesignConstants.swift— extends BedrockResources/Localizable.xcstrings— string catalogLocalization/String+Localization.swift— string helpers
watchOS
BusinessCardWatch/BusinessCardWatchApp.swiftBusinessCardWatch/Views/WatchContentView.swiftBusinessCardWatch/State/WatchCardStore.swiftBusinessCardWatch/Models/WatchCard.swiftBusinessCardWatch/Resources/Localizable.xcstrings
File Guidelines
Size Limits
- Main views: aim for under 300 lines
- Extract reusable sub-views to
Components/ - Extract sheets/modals to
Sheets/ - Private structs in same file OK if under 50 lines
Current File Sizes
| File | Lines | Status |
|---|---|---|
| ContactFieldType | ~650 | 30+ field definitions, acceptable |
| CardEditorView | ~520 | Complex form + field manager, acceptable |
| BusinessCardView | ~320 | Clickable fields + legacy fallback, acceptable |
| QRScannerView | ~310 | Camera + parsing, acceptable |
| ShareCardView | ~235 | Good |
| ContactDetailView | ~235 | Good |
| ContactsView | ~220 | Good |
| CardsHomeView | ~150 | Full-screen swipeable cards, good |
| AddedContactFieldsView | ~135 | Drag-to-reorder, good |
| ContactFieldPickerView | ~100 | Grid picker, good |
| All others | <110 | Good |
Localization
- All user-facing strings are in
.xcstrings. - Supported locales: en, es‑MX, fr‑CA.
- Use
String.localized("Key")for non-Text strings.
Testing
BusinessCardTests/BusinessCardTests.swiftcovers:- vCard payload formatting
- Card CRUD operations
- Contact search and filtering
- Social links detection
- Contact notes/tags
- Follow-up status
- vCard parsing for received cards
Known Stubs / TODOs
- Apple Wallet and NFC flows are alert-only placeholders.
- Share URLs are sample placeholders.
- Widget previews are not WidgetKit extensions.
- See
ROADMAP.mdfor full feature status.
If You Extend The App
- Add new strings to the String Catalogs.
- Use
Design.*from Bedrock for spacing, opacity, etc. - Add app-specific constants to
DesignConstants.swift. - Keep view logic UI-only; push business logic to state classes.
- Prefer protocols for new capabilities.
- Add unit tests for new model logic.
- Update
ROADMAP.mdwhen adding features. - Keep files under 300 lines — extract components when needed.
- No duplicate code — check for existing components first.
- One public type per file — private helpers OK if small.