# BusinessCard A SwiftUI iOS + watchOS app that creates and shares digital business cards with QR codes, quick share actions, customization, and contact tracking. Data syncs across devices via iCloud. ## Platforms - iOS 26+ - watchOS 12+ - Swift 6.2 ## Features ### My Cards - **Full-screen card display**: Swipe between your business cards in a full-screen view - Tap the **edit icon** (pencil) in the top right to edit the current card - Tap the **plus icon** to create a new card - Set a default card for sharing - **Modern card design**: Banner with optional cover photo, company logo, overlapping profile photo, clean contact rows - **Header layout picker**: Choose how profile, cover, and logo images are arranged in the card header - **Profile Banner**: Profile photo fills the entire banner (great for personal branding) - **Cover + Avatar**: Cover image as banner with profile photo overlapping at bottom - **Centered Logo**: Cover image with company logo centered and profile avatar smaller - **Logo Badge**: Cover with small logo badge in corner, profile avatar overlapping - **Side by Side**: Avatar and logo displayed together in the content area - **Smart layout suggestions**: The app suggests the best layout based on which images you've added - **Live layout preview**: See exactly how each layout looks before selecting in both the picker and editor - **Profile photos**: Add a headshot from library or camera with crop/zoom editor - **Cover photos**: Add a custom banner background from library or camera - **Company logos**: Upload a logo from library or camera - **3-step photo workflow**: Choose source (library/camera) → crop/position → save - **Rich profiles**: First/middle/last name, prefix, maiden name, preferred name, pronouns, headline, bio, accreditations - **Clickable contact fields**: Tap any field to call, email, open link, or launch app ### Share - **Floating share button**: Centered above the tab bar for quick access - **Dark-themed share sheet**: Sleek QR code display with prominent sharing options - Share options: copy link, SMS, email, WhatsApp, LinkedIn, and more - **Offline sharing toggle**: Share your card without internet connection - **Track shares**: Record who received your card and when - Button is hidden when no cards exist - Placeholder actions for Apple Wallet and NFC (alerts included) ### Card Editor Access via the edit (pencil) icon in the My Cards tab: - **Horizontal color picker**: Scrollable theme swatches for quick selection - Theme picker with multiple color palettes (Coral, Midnight, Ocean, Lime, Violet) - **Expandable name section**: Prefix, first, middle, last, maiden name, suffix, preferred name, pronouns - **Edit all card details**: Name, role, department, company, headline, bio - **Accreditations**: Tag-based input with inline editing #### Dynamic Contact Fields Add unlimited contact fields of any type, reorder by drag-and-drop: - **Contact**: Email, phone, website, address - **Social Media**: LinkedIn, X/Twitter, Instagram, Facebook, TikTok, Threads, Bluesky, Mastodon, Reddit, Twitch, YouTube, Snapchat, Pinterest - **Developer**: GitHub, GitLab, Stack Overflow, CodePen - **Messaging**: Telegram, WhatsApp, Discord, Slack, Matrix, Signal - **Support & Funding**: Patreon, Ko-fi, Buy Me a Coffee, GitHub Sponsors - **Payment**: Venmo, Cash App, PayPal, Zelle - **Scheduling**: Calendly - **Other**: Custom links with any URL Each field has: - **Custom icons**: Brand-colored icons from asset catalog (not generic SF Symbols) - **Title/Label**: Add context like "Work", "Personal", or custom call-to-action - **Deep linking**: Opens native apps when available (X, Instagram, etc.) - **Drag-to-reorder**: Long press and drag to change order - **Suggestion chips**: Quick-fill suggestions for field titles - **Live preview**: "Preview card" button to see changes before saving - **Delete cards** you no longer need ### Contacts - **Add contacts manually**: Tap + to create contacts with name, role, company, email, phone - **Profile photos**: Add or edit a photo for each contact - Track who you've shared your card with - **Scan QR codes** to save someone else's business card - **Notes & annotations**: Add notes about each contact - **Tags**: Organize contacts with comma-separated tags - **Follow-up reminders**: Set reminder dates with overdue indicators - **Where you met**: Record event or location where you connected - Search contacts using localized matching (searches name, company, role, tags, notes) - Shows last shared time and the card label used - Swipe to delete contacts ### Widgets (Preview Only) - Phone widget preview mock - Watch widget preview mock ### watchOS App - Shows the **default card QR code** full-screen (no card picker) - Default card is determined by the iPhone app - **Syncs with iPhone** via WatchConnectivity framework - QR codes are pre-generated on iPhone (CoreImage not available on watchOS) ## Data Sync ### iCloud Sync (iOS) Cards and contacts are stored using SwiftData with CloudKit sync enabled. Your data automatically syncs across all your iPhones and iPads signed into the same iCloud account. ### CloudKit Schema Safety Before changing any shipped SwiftData model schema, follow `CLOUDKIT_SCHEMA_GUARDRAILS.md`. This is the project checklist for safe additive changes and migration-friendly patterns. ### iPhone to Watch Sync The iPhone app syncs card data to the paired Apple Watch via WatchConnectivity framework using `updateApplicationContext`. When you create, edit, or delete cards on your iPhone, the changes are pushed to your watch automatically. **Note**: The watch receives data from the iPhone. To update cards on the watch, make changes on the iPhone first. QR codes are pre-generated on iPhone since CoreImage is not available on watchOS. **IMPORTANT**: Do NOT use App Groups for iPhone-Watch communication. App Groups only work between an app and its extensions on the SAME device. iPhone and Apple Watch are separate devices with separate file systems. ## Architecture - **Clean Architecture**: Clear separation between Views, State, Services, and Models - **Views are dumb**: Presentation only, no business logic - **State is smart**: `@Observable` classes with all business logic - **Centralized preferences**: `AppPreferencesStore` is the single source of truth for app-level settings (theme/debug flags) - **Protocol-oriented**: Interfaces for services and stores - **Protocol-based providers**: `AppMetadataProviding` and `VCardPayloadProviding` keep services/state decoupled - **SwiftData + CloudKit**: Persistent storage with iCloud sync - **WatchConnectivity**: iPhone to Apple Watch sync (NOT App Groups) - **One type per file**: Lean, maintainable files under 300 lines - **Reusable components**: Shared UI in `Views/Components/` - **Bedrock package**: Shared design system and utilities - **String Catalogs**: Localization for en, es-MX, fr-CA - **Single source of truth for names**: `BusinessCard.fullName` is computed from individual name fields (no stored displayName) ## Dependencies ### Bedrock (Local Package) The app uses the [Bedrock](ssh://git@192.168.1.128:220/mbrucedogs/Bedrock.git) package for: - **Design constants**: `Design.Spacing`, `Design.CornerRadius`, `Design.Opacity`, etc. - **QR code generation**: `QRCodeGenerator`, `QRCodeImageView` - **Reusable UI components**: Settings views, badges, effects App-specific extensions are in `Design/DesignConstants.swift`. ### Settings Layout Contract `SettingsCard` is the single owner of horizontal row inset in `SettingsView`. - Use `SettingsCardRow` for custom in-card rows (`HStack`, status/info rows, custom content). - Use `SettingsDivider` between in-card rows (instead of `Divider`/manual lines). - Use `SettingsNavigationRow(..., backgroundColor: .clear)` for standard in-card navigation rows. - Avoid child `.padding(.horizontal, ...)` inside `SettingsCard` unless intentional indentation is required. ## Project Structure ``` BusinessCard/ ├── Assets.xcassets/ │ └── SocialSymbols/ # Custom brand icons (LinkedIn, X, Instagram, etc.) ├── Configuration/ # App identifiers and configuration │ ├── Base.xcconfig # Source of truth for all identifiers │ ├── Debug.xcconfig # Debug configuration (imports Base) │ ├── Release.xcconfig # Release configuration (imports Base) │ └── AppIdentifiers.swift # Swift interface to configuration ├── Design/ # Design constants (extends Bedrock) ├── Localization/ # String helpers ├── Models/ │ ├── BusinessCard.swift # Main card model │ ├── CardHeaderLayout.swift # Header layout options (profile banner, cover+avatar, etc.) │ ├── Contact.swift # Received contacts │ ├── ContactField.swift # Dynamic contact fields (SwiftData) │ └── ContactFieldType.swift # Field type definitions with icons & URLs ├── Protocols/ # Protocol definitions ├── Resources/ # String Catalogs (.xcstrings) ├── Services/ # ShareLinkService, VCardFileService, WatchConnectivityService ├── State/ # Observable stores (CardStore, ContactsStore) └── Views/ ├── Components/ # Reusable UI (ContactFieldPickerView, HeaderLayoutPickerView, etc.) ├── Sheets/ # Modal sheets (ContactFieldEditorSheet, RecordContactSheet, etc.) ├── Widgets/ # Widgets feature view + feature-specific components └── [Feature].swift # Feature screens BusinessCardWatch Watch App/ # watchOS app target (WatchConnectivity sync) BusinessCardTests/ # Unit tests ``` ## Configuration ### Required Capabilities **iOS Target:** - iCloud (CloudKit enabled) - App Groups (`group.com.mbrucedogs.BusinessCard`) - for future widget access - Background Modes (Remote notifications) - Camera (for QR code scanning) **watchOS Target:** - WatchConnectivity (for receiving cards from iPhone) - Bundle ID must be prefixed with iOS bundle ID (e.g., `com.mbrucedogs.BusinessCard.watchkitapp`) ### Watch App Embedding (CRITICAL) The watch app must be embedded in the iOS app for deployment. In the iOS target's **Build Phases**: 1. **"Embed Watch Content"** phase must exist 2. **"Code Sign On Copy"** must be CHECKED ✓ Without this, the iOS app installs but the watch app does NOT install on the paired Apple Watch. See `Agents.md` for full troubleshooting guide. ### CloudKit Container `iCloud.com.mbrucedogs.BusinessCard` ### App Identifiers (xcconfig) All company-specific identifiers are centralized in `Configuration/Base.xcconfig`: ``` COMPANY_IDENTIFIER = com.mbrucedogs DEVELOPMENT_TEAM = 6R7KLBPBLZ ``` This flows through: - **Build settings**: Bundle IDs, Team ID (via xcconfig → project) - **Entitlements**: CloudKit container, App Group (via variable substitution) - **Swift code**: `AppIdentifiers.*` (via Info.plist → Bundle.main) **To migrate to a new developer account**: Update `Base.xcconfig` only. See `DevAccount-Migration.md`. ## Notes - Share URLs are sample placeholders - Wallet/NFC flows are stubs with alerts only - Widget UI is a visual preview (not a WidgetKit extension) ## Running 1. Open `BusinessCard.xcodeproj` in Xcode 2. Ensure Bedrock package is resolved (File → Packages → Resolve Package Versions) 3. Build and run on iOS Simulator or device ## Tests Unit tests cover: - vCard payload formatting - Default card selection - Contact search filtering - Create, update, delete cards - Contact tracking (new and existing contacts) - Theme and layout assignment - Social links detection - Contact notes and tags - Follow-up status and overdue detection - vCard parsing for received cards - Adding received cards via QR scan Run tests with `Cmd+U` in Xcode. ## Roadmap See [ROADMAP.md](ROADMAP.md) for planned features and implementation status. --- *Built with SwiftUI, SwiftData, and ❤️*