npx agentspec-cli install 5f8f137d-a052-4fcc-b546-95cdf412f333 --file-type copilot-instructions→ .github/instructions/
Prompt para Cursor:
# OMOMoney - SwiftUI Expense Tracker App Development
## 🚨 REGLAS ESTRICTAS DE DESARROLLO - OBLIGATORIAS
### 📱 VERSIÓN DE PLATAFORMA - OBLIGATORIO
- **iOS Target**: iOS 18.5+ (2025)
- **SwiftUI**: Usar APIs más modernas disponibles
- **Compatibilidad**: No usar APIs deprecadas
### 🏗️ ARQUITECTURA MVVM - NO NEGOCIABLE
- **Views**: ❌ NO contienen lógica, ❌ NO cálculos, ❌ NO formateo, ✅ SOLO SwiftUI Views
- **ViewModels**: ❌ NO contienen UI, ✅ SOLO lógica de presentación, ✅ @MainActor, ✅ @Published
- **Services**: ✅ SOLO lógica CRUD y operaciones de datos, ✅ NO lógica de presentación
- **Models**: ❌ NO contienen lógica, ✅ SOLO entidades Core Data
- **Core Data Integration**: ✅ SIEMPRE usar NSFetchedResultsController para respetar MVVM
### 🧵 THREADING - CRÍTICO
- **Main Thread**: ✅ SOLO UI, ✅ navegación, ✅ gestos, ✅ animaciones
- **Background Thread**: ✅ Core Data CRUD, ✅ cálculos complejos, ✅ filtros pesados
- **Patrón obligatorio**: `DispatchQueue.global` → operación pesada → `DispatchQueue.main.async`
### 🗄️ CORE DATA + MVVM - OBLIGATORIO
- **NSFetchedResultsController**: ✅ SIEMPRE usar para respetar MVVM y arquitectura limpia
- **Beneficios**:
- ✅ **Reactividad automática**: Se actualiza solo cuando Core Data cambia
- ✅ **Performance**: Solo carga datos cuando es necesario
- ✅ **MVVM puro**: ViewModel no maneja lógica de Core Data
- ✅ **Nativo**: Usa las APIs oficiales de Apple
- ✅ **Eficiente**: No polling, no notificaciones manuales
- **Implementación**:
- ViewModel implementa `NSFetchedResultsControllerDelegate`
- `@Published var entries` se actualiza automáticamente
- SwiftUI re-renderiza cuando `@Published` cambia
- **NO usar NotificationCenter** para cambios de Core Data
### 📱 SWIFTUI - REACTIVIDAD AUTOMÁTICA (iOS 18.5+)
- ✅ Usar `@Published` - SwiftUI se redibuja automáticamente
- ❌ NO usar Timers para delays artificiales
- ❌ NO usar `Task.sleep` para esperas
- ❌ NO usar callbacks manuales (a menos que sea absolutamente necesario)
- ✅ Usar nueva sintaxis de `onChange` - `{ oldValue, newValue in }`
- ✅ Usar `@Observable` macro moderno (opcional)
- ✅ Usar `NavigationStack` moderno
#### 🖼️ PREVIEW MACRO - USO CORRECTO
- **#Preview es SOLO para SwiftUI Views**: ✅ El macro `#Preview` está diseñado para previsualizar SwiftUI Views que conforman al protocolo `View`
- **ViewModels NO usan #Preview**: ❌ NO usar `#Preview` en ViewModels, Services, o cualquier clase que NO sea una SwiftUI View
- **Error común**: `'buildExpression' is unavailable: this expression does not conform to 'View'` ocurre cuando se intenta previsualizar un tipo no-View
- **Uso correcto**:
```swift
// ✅ CORRECTO: En View files
#Preview {
MyView()
}
// ❌ INCORRECTO: En ViewModel files
#Preview {
MyViewModel() // ¡ERROR! ViewModel no es View
}
```
- **Principio**: Solo previsualizar Views, nunca ViewModels o Services
### 🆕 NUEVAS REGLAS MVVM APRENDIDAS HOY - OBLIGATORIAS
#### 🔄 GESTIÓN DEL CICLO DE VIDA DEL VIEWMODEL
- **ViewModel Protocol**: ✅ Debe conformar `ObservableObject`
- **@StateObject en Views**: ✅ Usar `@StateObject` cuando la vista crea y posee el ViewModel
- **Beneficio**: Evita reinicialización del ViewModel en cada redibujo, previene pérdida de estado
- **Ejemplo**: `@StateObject private var viewModel = UserListViewModel()`
#### 💉 INYECCIÓN DE DEPENDENCIAS EN VIEWMODEL
- **Service Injection**: ✅ ViewModel debe recibir service como parámetro de inicialización
- **Principio**: Dependency Injection para aislar lógica de persistencia/red
- **Beneficio**: Facilita mocking en tests unitarios y separación de responsabilidades
- **Ejemplo**:
```swift
init(service: UserServiceProtocol) {
self.service = service
}
```
#### 📁 ESTRUCTURA DEL PROYECTO - ORGANIZACIÓN PARA ESCALABILIDAD
- **Directorio Base**: ✅ Mantener para componentes reusables (ej. Loading)
- **Ruta sugerida**: `Base/View/Loading/Loading.swift`
- **Organización**: Separar claramente Views, ViewModels, Services, Models
- **Services Organization**:
- `Services/Protocols/` - Todos los protocolos de servicios
- `Services/Implementation/` - Implementaciones concretas de servicios
- `Services/CoreDataService.swift` - Clase base para servicios Core Data
- **ViewModels Organization**:
- `ViewModel/User/` - ViewModels relacionados con usuarios
- `ViewModel/Group/` - ViewModels relacionados con grupos
- `ViewModel/Entry/` - ViewModels relacionados con entradas
- `ViewModel/Item/` - ViewModels relacionados con items
- `ViewModel/Category/` - ViewModels relacionados con categorías
- **Views Organization**:
- `View/User/` - Vistas relacionadas con usuarios
- `View/Group/` - Vistas relacionadas con grupos
- `View/Entry/` - Vistas relacionadas con entradas
- `View/Base/` - Componentes reusables (Loading, etc.)
#### ⚡ CONCURRENCIA Y ASINCRONÍA
- **Swift Concurrency**: ✅ Usar `async/await` en lugar de callbacks anidados o Combine
- **Beneficios**:
- Código más limpio y legible
- Manejo integrado de errores con `try/catch`
- Evita errores de concurrencia al actualizar UI
- **@MainActor**: ✅ Usar cuando sea necesario para operaciones de UI
#### 🎯 RESUMEN DE ROLES
- **ViewModel**: `ObservableObject` que expone datos y lógica a la vista
- **Service**: Encapsula acceso a datos (API, Core Data, etc.) y se inyecta en ViewModel
- **View**: Usa `@StateObject` para instanciar ViewModel y reaccionar a cambios
#### 🚀 OPTIMIZACIONES DE FLUIDEZ - DIFERENCIADORAS
- **@MainActor**: ✅ Usar correctamente en propiedades que actualizan UI
- **Lazy Loading**: ✅ Usar `LazyVStack`, `List` para vistas grandes
- **Procesamiento Pesado**: ❌ NO en cuerpo de vista, ✅ TODO en ViewModel o Services
- **Caching**: ✅ Cachear datos cuando tenga sentido (imágenes, resultados Core Data)
- **Animaciones**: ✅ Usar `withAnimation` y transiciones nativas SwiftUI
### 🧭 NAVEGACIÓN MODERNA - OBLIGATORIA (iOS 18.5+)
- **NavigationStack**: ✅ SIEMPRE usar NavigationStack en lugar de NavigationView
- **NavigationDestination**: ✅ Definir destinos de navegación con tipos específicos
- **NavigationPath**: ✅ Usar NavigationPath para gestión de estado de navegación
- **NavigationLink**: ✅ Usar NavigationLink para navegación declarativa
- **Navegación Programática**: ✅ Implementar `navigationPath.append()` para navegación dinámica
### 🎯 MEJORES PRÁCTICAS DE NAVEGACIÓN APRENDIDAS
#### 📋 **IMPLEMENTACIÓN COMPLETA PASO A PASO**
1. **PASO 1: Definir Enums de Navegación en MainView**:
```swift
// ✅ CORRECTO: Definir todos los destinos en MainView
enum CreateGroupDestination: Hashable {
case createGroup(User)
}
enum SettingsDestination: Hashable {
case settings
}
enum AddEntryDestination: Hashable {
case addEntry(User, Group)
}
```
2. **PASO 2: Configurar NavigationStack en MainView**:
```swift
struct MainView: View {
@State private var navigationPath = NavigationPath()
var body: some View {
NavigationStack(path: $navigationPath) {
DetailedGroupView(navigationPath: $navigationPath)
// ✅ CORRECTO: Definir TODOS los destinos aquí
.navigationDestination(for: CreateGroupDestination.self) { destination in
switch destination {
case .createGroup(let user):
CreateGroupView(user: user, navigationPath: $navigationPath)
}
}
.navigationDestination(for: SettingsDestination.self) { destination in
switch destination {
case .settings:
SettingsView(navigationPath: $navigationPath)
}
}
.navigationDestination(for: AddEntryDestination.self) { destination in
switch destination {
case .addEntry(let user, let group):
AddEntryView(user: user, group: group, navigationPath: $navigationPath)
}
}
}
}
}
```
3. **PASO 3: Pasar NavigationPath a Vistas Hijas**:
```swift
// ✅ CORRECTO: Pasar navigationPath como @Binding
struct DetailedGroupView: View {
@Binding var navigationPath: NavigationPath
var body: some View {
// Botones de navegación
Button("Crear Grupo") {
if let selectedUser = viewModel.selectedUser {
navigationPath.append(CreateGroupDestination.createGroup(selectedUser))
}
}
Button("Settings") {
navigationPath.append(SettingsDestination.settings)
}
Button("Add Entry") {
if let user = viewModel.selectedUser, let group = viewModel.selectedGroup {
navigationPath.append(AddEntryDestination.addEntry(user, group))
}
}
}
}
```
4. **PASO 4: Implementar Navegación de Retorno en Vistas Destino**:
```swift
struct CreateGroupView: View {
@Binding var navigationPath: NavigationPath
var body: some View {
// ✅ CORRECTO: Usar navigationPath.removeLast() para regresar
Button("Cancelar") {
navigationPath.removeLast()
}
// ✅ CORRECTO: Regresar automáticamente después de crear
.onChange(of: viewModel.groupCreatedSuccessfully) { _, newValue in
if newValue {
navigationPath.removeLast()
}
}
}
}
```
5. **PASO 5: Actualizar Datos al Regresar**:
```swift
struct DetailedGroupView: View {
var body: some View {
// ✅ CORRECTO: Refrescar datos cuando la vista aparezca
.onAppear {
Task {
await viewModel.loadData()
await viewModel.autoSelectFirstUserAndGroup()
}
}
}
}
```
#### 🚫 **ERRORES COMUNES A EVITAR**
1. **NavigationStack Anidados**:
```swift
// ❌ INCORRECTO: NO hacer esto
NavigationStack {
NavigationStack { } // ¡ERROR! Doble NavigationStack
}
```
2. **Definir Destinos en Múltiples Lugares**:
```swift
// ❌ INCORRECTO: NO definir en DetailedGroupView
.navigationDestination(for: CreateGroupDestination.self) { ... }
// ✅ CORRECTO: Solo en MainView
```
3. **Usar @Environment(\.dismiss)**:
```swift
// ❌ INCORRECTO: NO usar esto
@Environment(\.dismiss) private var dismiss
// ✅ CORRECTO: Usar navigationPath.removeLast()
@Binding var navigationPath: NavigationPath
```
4. **Sheets para Navegación**:
```swift
// ❌ INCORRECTO: NO usar sheets
.sheet(isPresented: $showingCreateGroup) { ... }
// ✅ CORRECTO: Usar NavigationStack
navigationPath.append(CreateGroupDestination.createGroup(user))
```
#### 🔄 **PATRÓN COMPLETO DE IMPLEMENTACIÓN**
1. **MainView**: Define NavigationStack y todos los NavigationDestination
2. **Vistas Hijas**: Reciben `@Binding var navigationPath: NavigationPath`
3. **Botones**: Usan `navigationPath.append(Destination.enumCase(data))`
4. **Vistas Destino**: Usan `navigationPath.removeLast()` para regresar
5. **Retorno**: Implementar `.onAppear` para refrescar datos si es necesario
#### 📱 **COMPATIBILIDAD iOS 18.5+**
- ✅ `NavigationStack` (reemplaza NavigationView)
- ✅ `NavigationDestination` (navegación tipo-segura)
- ✅ `NavigationPath` (gestión de estado de navegación)
- ✅ `onChange` con nueva sintaxis `{ oldValue, newValue in }`
- ✅ `@MainActor` para ViewModels
- ✅ `async/await` para operaciones asíncronas
#### 🎯 **REGLAS DE ORO FINALES**
1. **Un solo NavigationStack** en MainView
2. **NavigationPath compartido** entre todas las vistas
3. **Enums tipados** para todos los destinos de navegación
4. **Sin NavigationStacks anidados** ni sheets para navegación
5. **Actualización de datos** al regresar usando `.onAppear`
6. **Navegación programática** con `navigationPath.append()`
7. **Retorno programático** con `navigationPath.removeLast()`
### ⚡ SWIFT CONCURRENCY AVANZADO - OBLIGATORIO
- **async/await**: ✅ SIEMPRE usar async/await en lugar de callbacks o Combine
- **Task**: ✅ Usar Task para operaciones asíncronas desde Views
- **@MainActor**: ✅ Usar @MainActor para ViewModels que actualizan UI
- **Background Context**: ✅ Usar `context.perform` para operaciones Core Data
- **Error Handling**: ✅ Implementar try/catch con manejo de errores apropiado
- **Patrón de Concurrencia**:
```swift
// ✅ CORRECTO: Operación en background, UI en main
func createGroup() async {
isLoading = true
do {
let newGroup = try await groupService.createGroup(name: name, currency: currency)
await MainActor.run {
groups.append(newGroup)
isLoading = false
}
} catch {
await MainActor.run {
errorMessage = error.localizedDescription
isLoading = false
}
}
}
```
### 🚫 PROHIBIDO
- Operaciones pesadas en main thread
- Lógica de negocio en Views
- UI elements en ViewModels
- Delays artificiales o polling
- ViewModels sin Dependency Injection
- Views sin @StateObject para ViewModels propios
- ViewModels sin protocolo ObservableObject
- Falta de lazy loading en listas grandes
- Falta de caching para datos frecuentes
- Falta de animaciones suaves
- **🚨 SHEETS - PROHIBIDOS A MENOS QUE SE SOLICITE ESPECÍFICAMENTE**
- ❌ NO usar `.sheet()` para modales
- ❌ NO usar `.fullScreenCover()` para pantallas completas
- ✅ SIEMPRE usar `NavigationStack` con `NavigationDestination`
- ✅ SIEMPRE usar `NavigationLink` para navegación
- ✅ SIEMPRE usar `NavigationPath` para gestión de estado de navegación
- ✅ SIEMPRE implementar navegación programática con `navigationPath.append()`
- ✅ SIEMPRE usar `@State private var navigationPath = NavigationPath()`
- ✅ SIEMPRE usar `NavigationStack(path: $navigationPath)` para navegación compleja
### 🆕 NUEVAS REGLAS APRENDIDAS - OBLIGATORIAS
#### 🔒 PREVENCIÓN DE EJECUCIONES MÚLTIPLES
- **Flags de protección**: ✅ Usar flags booleanos para evitar ejecuciones simultáneas de funciones async
- **Problema identificado**: Ejecuciones múltiples causan estado inconsistente en Core Data y bucles infinitos
- **Solución implementada**:
```swift
private var isAutoSelecting = false
func autoSelectFirstUserAndGroup() async {
guard !isAutoSelecting else {
print("⚠️ autoSelectFirstUserAndGroup ya está en ejecución, saltando...")
return
}
isAutoSelecting = true
// ... código de la función ...
isAutoSelecting = false
}
```
#### 🗄️ VALIDACIONES DE CORE DATA - SIMPLIFICADAS
- **NO validar manualmente**: ❌ NO usar `objectID.isTemporaryID` ni verificaciones complejas de estado
- **Confiar en Core Data**: ✅ Dejar que Core Data maneje su propio estado internamente
- **Validaciones mínimas**: ✅ Solo verificar que objetos existan (`object != nil`), no su estado interno
- **Error común**: `'-[Group isTemporaryID]: unrecognized selector sent to instance'`
- **Solución**: Eliminar verificaciones de `isDeleted`, `isTemporaryID` y confiar en la reactividad de SwiftUI
#### 🎯 PATRÓN MVVM PURO - SIN INTERRUPCIONES MANUALES
- **Reactividad automática**: ✅ Usar `@Published` y dejar que SwiftUI espere naturalmente
- **Sin delays artificiales**: ❌ NO usar `Task.sleep`, `Task.yield` ni pausas manuales
- **Flujo natural**: ✅ La vista espera automáticamente a que los datos estén listos
- **Principio**: "La vista espera naturalmente usando @Published, no interrumpimos el flujo"
#### 🔄 GESTIÓN DE ESTADO ESTABLE
- **Flags de protección**: ✅ Implementar en funciones críticas como `loadData()` y `autoSelectFirstUserAndGroup()`
- **Estado consistente**: ✅ Evitar que múltiples operaciones modifiquen el mismo estado simultáneamente
- **Logs de debug**: ✅ Agregar logs detallados para identificar problemas de concurrencia
- **Patrón recomendado**:
```swift
private var isOperationInProgress = false
func criticalOperation() async {
guard !isOperationInProgress else { return }
isOperationInProgress = true
// ... operación crítica ...
isOperationInProgress = false
}
```
## Project Overview
Building a native iOS personal expense tracker app using SwiftUI (iOS 18.5+) with strict MVVM architecture, Core Data persistence, and NavigationStack navigation building into the view model for simplicity.
## Development Strategy
- **Incremental Development**: Small, focused commits for each feature
- **MVVM First**: All business logic in ViewModels, Views only display
- **Core Data Foundation**: Start with data model, build UI on top
- **Test-Driven**: Unit tests for each component
- **Physical Device Testing**: Always test on physical device, not simulator
- **Device ID**: ALWAYS use "Dennis's iPhone (18.6) (00008120-000A190218614032)" for all testing and build
- **Dependency Injection**: Services injected into ViewModels for testability
- **Lifecycle Management**: Proper @StateObject usage for ViewModel persistence
### Performance Considerations
- Use background queues for Core Data operations
- Implement proper error handling
- Optimize for smooth UI updates
- Follow Apple's native UI/UX conventions
- Use lazy loading for large lists and views
- Cache frequently accessed data
- Implement smooth animations and transitions
Para empezar
Genera el modelo de datos de Core Data en Swift usando NSManagedObject para cada entidad descrita a continuación, junto con su correspondiente ViewModel siguiendo la arquitectura MVVM.
## 🆕 REQUISITOS DE ARQUITECTURA ACTUALIZADOS
### 📁 Estructura del Proyecto
- **Model/**: Entidades Core Data
- **ViewModel/**: ViewModels con inyección de dependencias
- **Service/**: Capa de servicios para operaciones CRUD
- **View/**: Vistas SwiftUI
- **Base/**: Componentes reusables (Loading, etc.)
### 🏗️ Modelo (Model/)
- **IMPORTANTE**: Las entidades están configuradas con "Codegen: Class Definition" en Xcode
- **NO crear archivos manuales**: Xcode genera automáticamente las clases Core Data en tiempo de compilación
- **Verificar configuración**: Cada entidad en .xcdatamodel debe tener "Codegen: Class Definition"
- **Entidades generadas automáticamente**: User, Group, Entry, Item, Category, UserGroup
- **Tipos correctos**: UUID?, Date?, NSDecimalNumber?, Int32, String?, Set<Entity>?
- **Relaciones**: Ya configuradas en .xcdatamodel con delete rules apropiadas
### 💉 Vista Modelo (ViewModel/) - ACTUALIZADO
- **Crear un ViewModel por entidad que sea ObservableObject**
- **Incluir un @Published array de la entidad ([Entity]) para la lista de resultados**
- **Incluir un inicializador que reciba Service como parámetro (Dependency Injection)**
- **Implementar métodos CRUD (create, fetch, update, delete) para interactuar con Core Data**
- **Gestionar errores y usar try? context.save() de forma segura**
- **ViewModels deben exponer datos ya formateados para uso en SwiftUI (por ejemplo, fecha como String)**
- **Usar @MainActor para operaciones de UI**
- **Implementar lazy loading para listas grandes**
- **Implementar caching para datos frecuentemente accedidos**
### 🔧 Service Layer - NUEVO
- **Crear un Service por entidad que maneje operaciones CRUD**
- **Services deben ser inyectados en ViewModels**
- **Implementar protocols para cada Service (ej: UserServiceProtocol)**
- **Usar background queues para operaciones Core Data**
- **Retornar resultados al main thread para actualizaciones de UI**
### 👁️ Vista (View/)
- **No es necesario generar vistas completas, pero los ViewModels deben estar listos para enlazarse con SwiftUI**
- **Usar @StateObject para ViewModels que la vista posee**
- **Implementar lazy loading con LazyVStack y List**
- **Usar withAnimation para transiciones suaves**
## Estilo de código
- Swift 5, Xcode 16+ compatible
- Nombrado limpio y consistente con Swift
- Evitar force unwrap (!)
- Incluir comentarios claros para cada relación explicando el deleteRule
- Separar carpetas Model/, ViewModel/, Service/, View/, y Base/
- **Usar async/await para operaciones asíncronas**
- **Implementar proper error handling con try/catch**
- **Usar @MainActor para operaciones de UI**
## Entidades y atributos:
Category
- id: UUID,
- name: String, default ""
- color: String?, optional, default "#8E8E93"
- createdAt: Date?, optional
- lastModifiedAt: Date?, optional
- Relaciones:
- entries: Set<Entry>?, to-many, inverse category, delete rule Nullify
- group: Group?, to-one, inverse categories, delete rule Cascade
Entry
- id: UUID,
- description: String?, optional, default ""
- date: Date,
- createdAt: Date?, optional auto generated
- lastModifiedAt: Date?, optional auto generated
- categoryId: UUID,
- groupId: UUID,
- Relaciones:
- category: Category?, to-one, inverse entries, delete rule Nullify
- group: Group?, to-one, inverse entries, delete rule Nullify
- items: Set<Item>?, to-many, inverse entry, delete rule Cascade
Group
- id: UUID,
- name: String?, optional, default ""
- currency: String?, optional, default "USD"
- createdAt: Date?, optional auto generated
- lastModifiedAt: Date?, optional auto generated
- Relaciones:
- entries: Set<Entry>?, to-many, inverse group, delete rule Cascade
- categories: Set<Category>?, to-many, inverse group, delete rule Cascade
- userGroups: Set<UserGroup>?, to-many, inverse group, delete rule Cascade
Item
- id: UUID,
- description: String?, optional, default ""
- amount: NSDecimalNumber?, optional, default 0.0
- quantity: Int32, optional, default 1
- createdAt: Date?, optional auto generated
- lastModifiedAt: Date?, optional auto generated
- Relaciones:
- entry: Entry?, to-one, inverse items, delete rule Nullify
User
- id: UUID?, optional
- name: String?, optional, default ""
- email: String,
- createdAt: Date?, optional auto generated
- lastModifiedAt: Date?, optional auto generated
- Relaciones:
- userGroups: Set<UserGroup>?, to-many, inverse user, delete rule Cascade
UserGroup
- id: UUID,
- role: String?, optional, default "owner"
- joinedAt: Date?, optional auto generated
- userId: UUID,
- groupId: UUID,
- Relaciones:
- user: User?, to-one, inverse userGroups, delete rule Cascade
- group: Group?, to-one, inverse userGroups, delete rule Cascade
## 🆕 Requisitos adicionales actualizados:
- **IMPORTANTE**: Las entidades Core Data están configuradas con "Codegen: Class Definition" en Xcode
- **NO crear archivos manuales**: Xcode genera automáticamente las clases Core Data en tiempo de compilación
- **Implementa Service layer con protocols para cada entidad**
- **ViewModels deben recibir Services como parámetros de inicialización**
- **Usa @StateObject en Views para ViewModels propios**
- **Implementa lazy loading y caching para performance**
- **Usa async/await para operaciones asíncronas**
- **Implementa proper error handling con try/catch**
- **Usa @MainActor para operaciones de UI**
- **Implementa animaciones suaves con withAnimation**
## 🏗️ **ESTRUCTURA DE DIRECTORIOS OBLIGATORIA PARA ESCALABILIDAD**
### **Services/**
```
Services/
├── Protocols/ # Protocolos de servicios para DI
│ ├── UserServiceProtocol.swift
│ ├── GroupServiceProtocol.swift
│ ├── EntryServiceProtocol.swift
│ ├── ItemServiceProtocol.swift
│ ├── CategoryServiceProtocol.swift
│ └── UserGroupServiceProtocol.swift
├── Implementation/ # Implementaciones concretas
│ ├── UserService.swift
│ ├── GroupService.swift
│ ├── EntryService.swift
│ ├── ItemService.swift
│ ├── CategoryService.swift
│ └── UserGroupService.swift
└── CoreDataService.swift # Clase base para servicios
```
### **ViewModels/**
```
ViewModel/
├── User/ # ViewModels relacionados con usuarios
├── Group/ # ViewModels relacionados con grupos
├── Entry/ # ViewModels relacionados con entradas
├── Item/ # ViewModels relacionados con items
└── Category/ # ViewModels relacionados con categorías
```
### **Views/**
```
View/
├── User/ # Vistas relacionadas con usuarios
├── Group/ # Vistas relacionadas con grupos
├── Entry/ # Vistas relacionadas con entradas
└── Base/ # Componentes reusables (Loading, etc.)
```
### **Imports en Swift - IMPORTANTE**
- **NO usar @_exported import**: En Swift, cuando todos los archivos están en el mismo target, son automáticamente visibles
- **Imports automáticos**: Los protocolos y clases se importan automáticamente dentro del mismo módulo
- **Mantener imports básicos**: Solo `import Foundation` e `import CoreData` son necesarios
- **Simplicidad**: No necesitamos archivos de imports centralizados en Swift
- **Uso de protocolos**: Usar `any ProtocolName` para evitar warnings de Swift
## 🚨 RECORDATORIOS CRÍTICOS - REVISAR ANTES DE CADA IMPLEMENTACIÓN
### ✅ VERIFICAR ANTES DE IMPLEMENTAR:
1. **Views**: ¿Solo contienen SwiftUI Views sin lógica?
2. **ViewModels**: ¿Solo contienen lógica de negocio sin UI?
3. **Threading**: ¿Operaciones pesadas en background, UI en main?
4. **@Published**: ¿Se usa para reactividad automática?
5. **@MainActor**: ¿Se usa en ViewModels para operaciones de UI?
6. **Delays**: ¿NO hay Timers o delays artificiales?
7. **iOS 18.5+**: ¿Se usan APIs modernas, no deprecadas?
8. **onChange**: ¿Se usa nueva sintaxis `{ oldValue, newValue in }`?
9. **@StateObject**: ¿Se usa para ViewModels que la vista posee?
10. **Dependency Injection**: ¿Los ViewModels reciben services como parámetros?
11. **ObservableObject**: ¿Todos los ViewModels conforman el protocolo?
12. **Lazy Loading**: ¿Se usa para listas y vistas grandes?
13. **Caching**: ¿Se implementa para datos frecuentemente accedidos?
14. **Animations**: ¿Se usan withAnimation y transiciones nativas?
### 🎯 OBJETIVO FINAL
**UI completamente fluida, sin bloqueos, con operaciones pesadas ejecutándose en background y actualizaciones automáticas en main thread usando la reactividad automática de SwiftUI, implementando las mejores prácticas MVVM aprendidas hoy.**
---
**RECUERDA: Cada línea de código debe seguir estas reglas estrictas. La arquitectura MVVM, el threading correcto, la inyección de dependencias, y la gestión del ciclo de vida del ViewModel son OBLIGATORIOS para mantener la fluidez de la UI y la mantenibilidad del código.**
Willasnap Configuration
Cursor IDE rules and conventions for AI-assisted development.
by AgentHub Crawler
Swiftui Guidelines Cursorrules Prompt File Rules
Cursor IDE rules and conventions for AI-assisted development.
by AgentHub Crawler
__APP_NAME__ — Claude Code Instructions
> You are an expert iOS developer working on **__APP_NAME__**. Write production-quality Swift code following the patterns established in this repo.
by AgentHub Crawler
Important rules you HAVE TO FOLLOW
Sign in to leave a comment.
│ ├── Assets.xcassets │ │ ├── AccentColor.colorset │ │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── ContentView.swift │ ├── LifeKB.entitlements │ ├── LifeKBApp.swift │ └── Preview Content
by AgentHub Crawler