A native iOS app for organizing Secret Santa draws — simple, offline, and private.
- Groups — create multiple groups with custom name, icon, color theme, description, and rules
- Participants — add members with name, email, phone, avatar (emoji), and notes; supports iOS Contacts picker
- Exclusion Rules — define pairs that cannot draw each other (couples, family members)
- Intelligent Draw — backtracking algorithm with up to 200 attempts, fully respects exclusion rules
- Wish Lists — each participant can add wishes with category, price range (BRL), and store suggestions
- Secure Reveal — dedicated screen to reveal a participant's assigned friend privately
- Purchase Confirmation — participants mark when they have bought the gift
- Dashboard — post-draw metrics: total participants, confirmed purchases, progress bar
- History — full record of all past draws per group
- Statistics — aggregated data across all groups (participants, draws, average wish price, etc.)
- Backup & Restore — export and import all data as a JSON file
- Sharing — notify participants via WhatsApp, SMS, or Email with personalized pre-written messages
- PDF Export — generate a full PDF report with draw results and wish lists
- QR Code — generate and share QR codes for each participant's reveal
- Local Notifications — configurable reminders X days before the event date
- Advanced Group Settings — price range, event date/location, deadline, custom rules, privacy flags
- Onboarding — 6-page welcome guide for new users
| Layer | Technology |
|---|---|
| UI | SwiftUI |
| Persistence | SwiftData |
| Minimum iOS | iOS 17+ |
| Language | Swift 5.9+ |
| Architecture | MVVM (implicit via SwiftData + @Query) |
| Dependencies | None (native frameworks only) |
secretsanta/
├── secretsantaApp.swift # App entry point — ModelContainer registration
├── ContentView.swift # Unused wrapper (redirects to GruposView)
│
├── Models/
│ ├── Grupo.swift # Group entity (@Model)
│ ├── Participante.swift # Participant entity with exclusions and confirmation status
│ ├── Desejo.swift # Wish item entity
│ ├── Sorteio.swift # Draw record entity (pairs stored as JSON Data)
│ ├── DashboardSorteioView.swift # Post-draw dashboard screen
│ ├── GrupoConfiguracoesView.swift # Group settings screen
│ └── NotificacaoHelper.swift # Sharing logic via WhatsApp/SMS/Email + sequential send UI (located in Models/ for historical reasons)
│
├── Utils/
│ ├── SorteioEngine.swift # Draw algorithm — pure, no side effects
│ ├── BackupManager.swift # JSON backup serialization / deserialization
│ ├── CompartilharHelper.swift # Message generation + UIActivityViewController wrapper
│ ├── NotificacaoLocalManager.swift # Local notification scheduling (UNUserNotificationCenter)
│ ├── PDFGenerator.swift # PDF report generation (UIGraphicsPDFRenderer)
│ ├── QRCodeGenerator.swift # QR code image generation (Core Image)
│ └── ViewExtensions.swift # iOS compatibility helpers (.iOSTitleDisplayMode, etc.)
│
└── Views/
├── GruposView.swift # Groups list + create/edit form + GrupoCardView
├── ParticipantesView.swift # Participants list + form + iOS Contacts picker
├── ParticipanteDetalheView.swift # Participant profile, wish list, and exclusions
├── DesejoFormView.swift # Wish form with BRL currency live formatting
├── RevelarAmigoView.swift # Secure reveal screen with QR code + haptics
├── ConfirmacaoPresenteView.swift # Purchase confirmation + friend's wish list
├── HistoricoView.swift # Draw history per group
├── EstatisticasView.swift # Statistics dashboard
├── BackupView.swift # Export/import backup with preview
├── OnboardingView.swift # Welcome guide + tips (6 pages)
├── QRCodeView.swift # QR code full-screen display and sharing
└── ErrorHandling.swift # AlertState, input validation, BRL formatting helpers
secretsantaTests/
└── secretsantaTests.swift # 30+ unit tests (Swift Testing framework)
Grupo (1) ──→ (N) Participante ──→ (N) Desejo
Grupo (1) ──→ (N) Sorteio
All relationships use deleteRule: .cascade. Participant exclusions and draw pairs are stored as JSON Data fields to avoid SwiftData many-to-many limitations.
SorteioEngine.realizar(participantes:) implements randomized backtracking:
- Validates minimum 3 participants
- Shuffles participant list randomly
- For each participant, filters valid candidates (excludes self and defined exclusions)
- If no valid candidate is found, restarts the entire attempt
- Retries up to 200 times before throwing
SorteioError.impossivelRealizar
This guarantees random distribution while fully respecting all exclusion rules defined by the organizer.
{
"versao": 1,
"dataExportacao": "2026-03-21T00:00:00Z",
"grupos": [
{
"id": "...",
"nome": "Natal da Família",
"participantes": [ ... ]
}
]
}All fields are optional for forward compatibility. Import merges with existing data — nothing is deleted.
- Clone the repository
- Open
secretsanta.xcodeprojin Xcode 15+ - Select a simulator or physical device (iOS 17+)
- Press
Cmd + R
No external dependencies — uses only native Apple frameworks.
xcodebuild test \
-project secretsanta.xcodeproj \
-scheme secretsanta \
-destination 'platform=iOS Simulator,name=iPhone 16'Or press Cmd + U in Xcode.
Test suites:
SorteioEngineQuantidadeTests— minimum participant validation (0, 1, 2 fail; 3+ pass)SorteioEngineResultadoTests— draw integrity (no self-draws, all participants covered exactly once)SorteioEngineExclusoesTests— exclusion rules enforcement, including impossible scenariosSorteioErrorTests— error message correctnessBackupManagerExportacaoTests— JSON export structure and field validationBackupManagerImportacaoTests— import, data reconstruction, and relationship integrity
All data is stored locally on-device via SwiftData. No network requests, no cloud sync, no analytics or telemetry of any kind. Backups are JSON files generated and controlled entirely by the user. All data can be permanently deleted from within the app.
MIT