Build Successful — 0 errors, 0 warnings
Todos los errores de Swift Concurrency, Sendable y actor isolation corregidos con cambios mínimos. CI/CD desbloqueado.
Diagnóstico inicial — swift build 2>&1
$ swift build 2>&1 → 7 errores detectados
Sources/NutriTrack/Models/MealEntry.swift:12:1: error: type 'MealEntry' does not conform to protocol 'Sendable'
Sources/NutriTrack/Services/MealLogService.swift:58:18: error: non-sendable type 'MealEntry' passed in implicitly asynchronous call to 'syncWithServer'
Sources/NutriTrack/Services/MealLogService.swift:72:5: error: expression is 'async' but is not marked with 'await'
Sources/NutriTrack/ViewModels/DashboardViewModel.swift:34:9: error: @MainActor function 'updateUI()' cannot be called from non-isolated context
Sources/NutriTrack/ViewModels/DashboardViewModel.swift:89:22: error: actor-isolated property 'meals' cannot be referenced from non-isolated context
Sources/NutriTrack/Networking/APIClient.swift:101:15: error: cannot convert value of type 'URLSession.AsyncBytes' to expected argument type 'Data'
Sources/NutriTrack/Networking/APIClient.swift:145:8: error: reference to captured var 'requestID' in concurrently-executing code
Fixes aplicados — cambios quirúrgicos
1
MealEntry no conforma Sendable
Sources/NutriTrack/Models/MealEntry.swift:12
Sendable
FIXED
Antes — error
MealEntry.swift
struct MealEntry: Codable, Identifiable { let id: UUID var name: String var calories: Int var timestamp: Date var photoURL: URL? }
Después — corregido
MealEntry.swift
struct MealEntry: Codable, Identifiable, Sendable { let id: UUID let name: String // var → let let calories: Int let timestamp: Date let photoURL: URL? }
Causa: Swift 6 requiere que los tipos pasados entre actores sean
Fix: Cambiar todas las propiedades a
Sendable. MealEntry tiene propiedades mutables (var) que impiden la conformidad implícita.Fix: Cambiar todas las propiedades a
let (inmutables) y añadir conformidad explícita a Sendable. El tipo ya era value-type (struct), por lo que el cambio es seguro.
2
Missing await + Sendable en llamada asíncrona
Sources/NutriTrack/Services/MealLogService.swift:58,72
Concurrency
FIXED
Antes — 2 errores
MealLogService.swift
actor MealLogService { func logMeal(_ entry: MealEntry) { // error: non-sendable + missing await syncWithServer(entry) // L58 } func syncWithServer(_ e: MealEntry) async throws { ... } func flush() { syncAll() // L72: async, no await } }
Después — corregido
MealLogService.swift
actor MealLogService { func logMeal(_ entry: MealEntry) async throws { try await syncWithServer(entry) } func syncWithServer(_ e: MealEntry) async throws { ... } func flush() async throws { try await syncAll() } }
Fix 1 (L58):
Fix 2 (L72): Mismo patrón —
syncWithServer es async throws; la llamada debe ser try await y el caller debe propagar el contexto async.Fix 2 (L72): Mismo patrón —
syncAll() es async, flush() se convierte a async throws y añade try await. Con el Fix 1 resuelto, el non-sendable de MealEntry ya no aplica.
3
@MainActor isolation + actor-isolated property
Sources/NutriTrack/ViewModels/DashboardViewModel.swift:34,89
Actor Isolation
FIXED
Antes — 2 errores
DashboardViewModel.swift
class DashboardViewModel: ObservableObject { var meals: [MealEntry] = [] // L89 func load() { // L34: cannot call @MainActor func updateUI() // L89: actor-isolated access let _ = meals.count } @MainActor func updateUI() { ... } }
Después — corregido
DashboardViewModel.swift
@MainActor class DashboardViewModel: ObservableObject { var meals: [MealEntry] = [] func load() async { // todo el tipo ya es @MainActor updateUI() // OK let _ = meals.count // OK } func updateUI() { ... } }
Causa:
Fix: Anotar el ViewModel completo con
DashboardViewModel llama a un método @MainActor y accede a propiedades aisladas desde contexto no aislado en Swift 6.Fix: Anotar el ViewModel completo con
@MainActor (es un ViewModel de SwiftUI — ésta es la práctica recomendada por Apple). Esto resuelve ambos errores de forma cohesiva y sin duplicar anotaciones.
4
AsyncBytes → Data + captured var concurrente
Sources/NutriTrack/Networking/APIClient.swift:101,145
Type Mismatch
FIXED
Antes — 2 errores
APIClient.swift
func fetchData(from url: URL) async throws -> Data { let (bytes, _) = try await URLSession.shared.bytes(from: url) // L101: AsyncBytes != Data return bytes } var requestID = UUID().uuidString Task { // L145: captured mutable var log(requestID) }
Después — corregido
APIClient.swift
func fetchData(from url: URL) async throws -> Data { // Usar data(from:) directamente let (data, _) = try await URLSession.shared.data(from: url) return data } let capturedID = requestID // copia let Task { log(capturedID) // inmutable, OK }
Fix L101: Se usaba
Fix L145: No se puede capturar una
URLSession.bytes(from:) que devuelve AsyncBytes; el tipo de retorno esperado es Data. Fix mínimo: cambiar a URLSession.data(from:).Fix L145: No se puede capturar una
var mutable en un Task concurrente (Swift 6 Sendable strictness). Fix: crear una copia let capturedID antes del closure.
Workflow de resolución ejecutado
✓
1. Diagnóstico — swift build 2>&1
→ 7 errores detectados en 4 archivos: MealEntry.swift, MealLogService.swift, DashboardViewModel.swift, APIClient.swift
✓
2. Análisis de contexto por archivo
→ Leídos los 4 archivos afectados para entender tipos, protocolos y actores implicados antes de tocar código
✓
3. Fixes quirúrgicos (4 archivos, 7 errores)
→ MealEntry: +Sendable, var→let | MealLogService: +async throws, +try await (×2) | DashboardViewModel: +@MainActor class | APIClient: .data(from:), let capture
✓
4. Verificación — swift build (2ª pasada)
→ Build complete! (0 errors, 0 warnings)
✓
5. Lint — swiftlint lint --quiet
→ 0 violations found (no se introdujeron code-style regressions)
✓
6. Tests — swift test
→ Test Suite 'NutriTrackTests' passed at 2026-06-18 | 28 tests passed, 0 failed
Tabla de errores — antes y después
| # | Archivo | Error Swift 6 | Tipo | Fix aplicado | Estado |
|---|---|---|---|---|---|
| 1 | MealEntry.swift:12 | does not conform to Sendable | Sendable | +Sendable, var→let | ✓ FIXED |
| 2 | MealLogService.swift:58 | non-sendable in async call | Concurrency | try await + async propagation | ✓ FIXED |
| 3 | MealLogService.swift:72 | expression is async, no await | Concurrency | +async throws, try await | ✓ FIXED |
| 4 | DashboardViewModel.swift:34 | @MainActor fn from non-isolated | Actor | @MainActor class annotation | ✓ FIXED |
| 5 | DashboardViewModel.swift:89 | actor-isolated property ref | Actor | @MainActor class (resuelve con fix 4) | ✓ FIXED |
| 6 | APIClient.swift:101 | AsyncBytes cannot convert to Data | Type | .bytes() → .data(from:) | ✓ FIXED |
| 7 | APIClient.swift:145 | captured var in concurrent code | Concurrency | let capturedID copy before Task | ✓ FIXED |
Suite de tests — sin regresiones
MealEntryTests (6/6)
MealLogServiceTests (8/8)
DashboardViewModelTests (7/7)
APIClientTests (5/5)
NutritionCalculatorTests (2/2)
28/28 PASSED
Output final del agente
[FIXED] MealEntry.swift:12
Added Sendable conformance + var→let immutability
[FIXED] MealLogService.swift:58,72
Added async throws propagation + try await on syncWithServer / syncAll
[FIXED] DashboardViewModel.swift:34,89
Added @MainActor to class — both isolation errors resolved atomically
[FIXED] APIClient.swift:101,145
Replaced .bytes(from:) with .data(from:); let copy before concurrent capture
Build Status
SUCCESS
Errors Fixed
7 / 7
Files Modified
4
Tests
28/28 PASSED — 0 regressions
Swiftlint
0 violations
Principles upheld
No @unchecked Sendable, no force-unwrap, no swiftlint:disable