El feedback #4 original apuntaba a la card 'Portfolio Evolution' del HOME, no
solo a la pestaña Charts (donde ya se hizo en build 60). Ahora la card del Home
se desliza entre páginas:
- Evolution (con su toggle Total/By Category intacto)
- Allocation (semicírculo + leyenda con %), datos de viewModel.categoryMetrics
Puntos indicadores, transición direccional, título dinámico. 'By Category' no
es página aparte porque ya es un toggle dentro de Evolution.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WoScpmHdVj1aUf4rAp6hbe
- Quick Update: checkClipboard ahora detecta también una IMAGEN copiada
(pb.hasImages) y hace OCR automático — te ofrece pegar el importe detectado
sin pulsar 'Escanear'. Gated por changeCount. El 'Pegar €X' del teclado
también usa el importe OCR'd de la imagen.
- Settings iCloud: botón 'Copiar diagnóstico de iCloud' SIEMPRE visible (el
desplegable de detalle solo salía con un error activo, difícil de encontrar).
Copia versión/build, contadores locales, últimas fechas import/export,
criticidad, hint y el error completo con códigos internos. Strings ×7.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WoScpmHdVj1aUf4rAp6hbe
#3 Quick Update paste/scan por campo:
- Barra de teclado con 'Pegar €X' (si hay importe en portapapeles) y 'Escanear'
(PhotosPicker → OCR on-device). Actúan sobre el campo enfocado.
- ImageAmountScanner nuevo (app target, Vision) espejo del scanner del Share
Extension: candidatos rankeados por prominencia, filtra % y años.
- 1 candidato → rellena directo; varios → confirmationDialog para elegir.
- Strings ×7.
#4 Swipe entre charts (iPhone):
- Gesto horizontal en el área del chart avanza al chart anterior/siguiente en
el orden del menú de título (respeta showForecast). Indicador de página con
puntos. Transición direccional. selectChart ya no bloquea premium (muestra
teaser), así que deslizar a uno premium funciona.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WoScpmHdVj1aUf4rAp6hbe
De 7 comentarios de TestFlight:
- Labels de línea ilegibles (puntos 5+7): ChartLabels.showsLabel thin-out
(máx 5 iPhone / 8 iPad, siempre primero y último). Charts multi-serie
(Compare, Period, Year vs Year) etiquetan solo el ENDPOINT de cada serie.
Prediction etiqueta solo el forecast final. Aplicado a Evolution, Rolling,
Drawdown, Volatility, Compare, Period, YoY, Prediction.
- 'Needs update' en meses pasados (punto 2): MonthlyCheckInView.snapshotForViewedMonth
— el estado es por el MES QUE SE VE (effective month de referenceDate), no
contra la última completion global. Diff usa el snapshot de ese mes.
- Selector de periodo Performance (punto 6): slider 1..60 → picker segmentado
consistente con el resto, opciones capadas a los meses de datos disponibles
(availableHistoryMonths). Ya no deja pedir ventanas sin datos.
- Home iPad/Mac (feedback previo): masonry con nº de columnas según ancho
(2 iPad / 3 Mac ancho >=1250), reparto por peso, propio ScrollView.
Pendiente de este lote: Goals no sincronizan (verificado modelo Goal CloudKit-OK
→ es el subset del partial-failure, no filtro ni esquema; necesita códigos
internos del error), paste/OCR por campo (punto 3) y swipe entre charts (punto 4).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WoScpmHdVj1aUf4rAp6hbe
Allocation:
- Semicírculo responsive que llena su mitad de la fila (grande en iPad, no
minúsculo en una esquina) + porcentaje dentro de cada segmento >=7%.
SemicircleArc acepta center explícito; labels en el ángulo medio del arco.
Home iPad:
- Ya no columna única estrecha a 720. Hero + check-in a ancho completo, resto
de cards en DOS columnas masonry independientes (sin acoplar alturas → sin
gaps del grid). Cap 1100pt.
Year vs Year KPIs:
- Retorno del año actual + diferencia (pp) vs año anterior + mejor/peor año
histórico. Usa forecastEndValue para el año en curso.
Fixes:
- 'View all achievements' en Journal no hacía nada: NavigationLink inline
(frágil según contexto de navegación) → sheet fiable en cualquier sitio.
- iCloud: CKErrorPartialFailure tras una sync que funciona ya NO se pinta en
rojo alarmante — nota calmada 'Sincronizando con iCloud' + detalle técnico
en disclosure. Rojo solo si nunca ha sincronizado y es fallo total.
syncErrorIsCritical + strings ×7.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WoScpmHdVj1aUf4rAp6hbe
Feedback del usuario en iPad:
- Allocation: gráfica de tarta → semicírculo estilo hemiciclo (parlamento).
SemicircleAllocation con arcos dibujados a mano (SemicircleArc Shape),
segmentos por cuota en 180°, total/selección en el centro, tap por segmento.
- Charts de Analyze (Compare, Period vs Period, Year vs Year) ahora SÍ muestran
KPIs en la cabecera de iPad como el resto: Compare (líder/rezagado por modo),
Period (retorno final por periodo), YoY (retorno final por año seleccionado).
- Momentum & Streaks en el sidebar del Journal (iPad, 320pt): variante compact
— tiles en fila sin subtítulos que rompían a 3 líneas, sin bloque de logros.
iCloud:
- CKErrorPartialFailure (CKErrorDomain code 2) mostraba un mensaje opaco.
CoreDataStack.hint(for:) recorre CKPartialErrorsByItemIDKey y clasifica el
código dominante → hint accionable (esquema/quota/red). Settings muestra el
hint legible sobre el detalle técnico. lastSyncErrorHint publicado. Strings ×7.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WoScpmHdVj1aUf4rAp6hbe
TipKit (Tips.configure con frecuencia diaria):
- QuickUpdateTip: popover en el '+' del Home — descubre Quick Update
- ScreenshotOCRTip: inline en Quick Update — descubre el OCR por captura
- SiriTip: en el cierre del check-in guiado — descubre el intent de voz
- ChartShareTip: popover en el compartir de Charts
- Se invalidan al ejecutar la acción; máx 1-2 impresiones cada uno
- Strings ×7 idiomas
Onboarding:
- Al completar el onboarding sin datos, se abre Add Source directamente
(notificación .openAddFirstSource) — el usuario nuevo no aterriza en un
dashboard vacío
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WoScpmHdVj1aUf4rAp6hbe
El modal estaba congelado en las features de 1.4.0 (hardcodeadas en la vista) y
el título decía 'What's New in 1.4' fijo. Rediseñado como catálogo por versión:
- WhatsNewCatalog: una entrada por release (1.4.0/1.4.1/1.4.2/1.5.0) con sus
features; AÑADIR ENTRADA CON CADA RELEASE (comentario en el código).
- Diff ACUMULATIVO desde lastSeenWhatsNewVersion: saltar 1.3.x → 1.4.2 muestra
1.4.0 + 1.4.1 + 1.4.2 agrupadas con badge de versión (headers solo si hay
más de una release pendiente). Comparación numérica de versiones (1.10 > 1.9).
- lastSeen vacío + onboarding ya completado (usuarios pre-1.4.0) = mostrar todo
el catálogo; instalación nueva = lastSeen sella a la versión actual al
completar onboarding (un usuario recién onboardeado no ve 'novedades').
- Sin entrada en el catálogo para el salto → no se muestra sheet.
- Título dinámico (whats_new_title_format) y contenido scrollable.
- Strings de 4 releases + título ×7 idiomas.
Verificado en simulador con -lastSeenWhatsNewVersion 1.4.0: pending
[1.5.0, 1.4.2, 1.4.1] y sheet agrupada correcta.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WoScpmHdVj1aUf4rAp6hbe
Home iPad/Mac (feedback del usuario: 'me gusta más el iPhone'):
- Fuera el grid de 2 columnas (acoplaba alturas de fila y dejaba huecos entre
cards de tamaños distintos) → columna única centrada a 720pt, patrón
Things/Day One. Eliminado el drag-drop del grid, columnSpan y el overlay
full-screen (~120 líneas); el orden/visibilidad sigue en Customize.
Goals:
- ProgressRing nuevo (anillo estilo Fitness con porcentaje centrado y color
semántico: verde=logrado, ámbar=retrasado, acento=en camino) en GoalRowView
y en la card de Goals del Home. GoalProgressBar queda solo en la share card.
Journal:
- Momentum & Streaks vive ahora en la cabecera del Journal (la historia del
hábito es narrativa, no dashboard).
- Filas timeline: círculo de mood como ancla visual (tint semántico por mood),
mes + estrellas + preview de nota.
Sources:
- Badge 'Needs update' como chip ámbar visible; valor en tipografía rounded.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WoScpmHdVj1aUf4rAp6hbe
El ritual mensual como flujo paso a paso en vez de formulario:
- Una fuente por pantalla: valor anterior a la vista, entrada grande (40pt
rounded), delta en vivo (chip verde/rojo con importe y %), sugerencia de
portapapeles, 'Igual que el mes pasado' y 'Omitir'
- Barra de progreso del ritual; cancelable solo al inicio (interactiveDismiss
bloqueado a mitad para no perder valores)
- Reflexión: mood (5 estados existentes) + nota de una línea
- Cierre: total nuevo, delta del mes, streak, compartir y haptic de éxito
- Persistencia por los MISMOS caminos que Quick Update (Snapshots) y el journal
(MonthlyCheckInStore → JournalEntry): coherente con el resto de entradas
- Entrada: CTA del check-in card en Home (fullScreenCover); la fila de
completado sigue navegando al journal
- Fix: referenceDate congelada en @State al presentar — el card la recalcula al
guardar y SwiftUI re-evaluaba el contenido del cover cambiando el mes a mitad
de flujo
- --pending-checkin en ScreenshotMode (revierte el seed a 'mes pendiente') +
UITest testGuidedCheckInFlow end-to-end (valor → skips → mood → cierre) ✓
- Strings ×7 idiomas
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WoScpmHdVj1aUf4rAp6hbe
Sistema:
- AppBackground: fuera gradiente + formas decorativas → systemGroupedBackground
plano en toda la app. La profundidad la da la jerarquía, no la decoración.
Home (3 zonas: estado / acción / contexto):
- TotalValueCard: hero tipográfico — número 44pt rounded sobre card limpia,
chip de delta semántico (verde/rojo), sparkline de evolución 56pt estilo
Stocks, fila secundaria YoY / inception / forecast. Fuera el gradiente azul.
- MonthlyCheckInCard state-driven: pendiente → protagonista con progreso real
de fuentes (N de M) y un CTA; completado → fila discreta con checkmark,
próximo check-in y streak integrado. En iPad va a ancho completo bajo el
hero (zona de acción), fuera del grid.
- InsightsRow: de carrusel de chips a un insight único rotatorio (tap para
ciclar, dots de posición).
- Eliminados del top: streak badge (vive en el check-in) y banner de pending
updates (el progreso del check-in lo comunica).
- Defaults nuevos: momentumStreaks/pendingUpdates/periodReturns/monthlySummary
ocultos por defecto (siguen disponibles en Customize; configs guardadas de
usuarios existentes se respetan).
- Strings nuevas en 7 idiomas (checkin_done_*, checkin_sources_progress).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WoScpmHdVj1aUf4rAp6hbe
El círculo decorativo de AppBackground (70% del ancho del panel, offset -35%)
se desborda de su panel por diseño y en layouts side-by-side queda flotando
SOBRE el sidebar de Charts. Las shapes de SwiftUI participan en hit-testing por
defecto y el panel derecho va después en el HStack → cada tap en la zona del
sidebar cubierta por el círculo moría en silencio. El patrón lo delató: fallaban
Overview + Analyze (arriba, bajo el círculo) y funcionaban Risk + Forecast
(abajo). Dependía de la geometría (orientación/tamaño), por eso no reproducía
en el simulador en portrait.
- AppBackground: .allowsHitTesting(false) — un fondo decorativo jamás debe
interceptar toques (fix global: aplica también a Sources/Journal en iPad)
- Panel de detalle de Charts: .clipped() para que la decoración tampoco PINTE
sobre el sidebar
- Selección premium nunca se bloquea: los charts premium se seleccionan y
muestran teaser de desbloqueo en el área del chart (chart_locked_* ×7 idiomas);
el paywall se presenta desde el botón (contexto fiable)
- UITests: testChartSidebarSelection ahora corre en landscape (geometría que
reproducía el bug) + testChartSelectionWithoutPremium nuevo; --no-premium
en ScreenshotMode para testear la experiencia free
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WoScpmHdVj1aUf4rAp6hbe
El bug de Allocation/Contributions que no respondían en iPad persistía tras dos
fixes de lógica — la causa estaba en la capa de interacción (Buttons custom en
LazyVGrid dentro de ScrollView dentro de NavigationSplitView). Rediseño completo
alineado con el HIG:
- iPad: List(selection:) nativa con estilo .sidebar y secciones agrupadas
(Overview/Analyze/Risk/Forecast) — hit-testing del sistema, navegación por
teclado, pointer effects y VoiceOver gratis. El gating premium sigue en
selectChart vía el binding de selección.
- Filtros (Group/Category/Sources) → Menu nativo en la toolbar con checkmarks
e icono con badge cuando hay filtros activos. Desaparecen las pills apiladas.
- iPhone: toolbarTitleMenu — el título es el selector de chart (patrón
Files/Freeform); fuera el carrusel de 14 chips. Periodo como segmented picker
estilo Stocks encima de la gráfica.
- Slider de Performance integrado en el toolbar del chart (iPad) y como card
compacta (iPhone).
- Eliminadas ~300 líneas de selectores custom (tiles, pills, chips).
Verificado con UITest nuevo (testChartSidebarSelection) en simulador iPad Pro:
tap en Allocation/Contributions/Rolling 12M carga cada chart, y el filtro de
periodo de Rolling reacciona. ScreenshotMode ahora activa debugPremiumOverride
para poder testear charts premium.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WoScpmHdVj1aUf4rAp6hbe
El extension acepta ahora imágenes además de texto: al compartir un screenshot
del banco/broker, Vision reconoce el texto EN EL DISPOSITIVO (la imagen nunca
sale del teléfono), extrae los importes candidatos y rellena el más prominente
visualmente (en las UIs bancarias el balance es el número más grande). El resto
de candidatos se ofrecen como chips de un toque. Percentajes y años se filtran.
- ExtImageAmountScanner.swift: VNRecognizeTextRequest + ranking por altura de
bounding box + dedup por valor
- ShareViewController: carga UIImage/URL/Data del provider, estado de escaneo,
sección de candidatos
- Info.plist: NSExtensionActivationSupportsImageWithMaxCount=1
- Strings nuevas en los 7 idiomas (ext_scanning_image, ext_detected_amounts,
ext_ocr_no_amounts)
- PredictionEngine: troceado ternario que agotaba el type-checker en Debug
- Bump a 1.4.2 build 48
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WoScpmHdVj1aUf4rAp6hbe
El archive fallaba en ValidateEmbeddedBinary con firma mixta (extension Manual
+ app/widget Automatic). Los tres targets usan ahora Apple Distribution con sus
perfiles App Store en Release; Debug sigue en Automatic.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WoScpmHdVj1aUf4rAp6hbe
El dolor: para actualizar cada source hay que saltar a la app del banco, copiar/
memorizar el valor y volver. iOS no permite ventanas flotantes editables (PiP es
solo vídeo), así que se resuelve con las dos vías sancionadas:
Share Extension (target nuevo PortfolioJournalQuickUpdate):
- Al compartir texto desde CUALQUIER app aparece "Portfolio Journal": mini-form
sobre la app anfitriona con el importe pre-parseado del texto compartido y la
siguiente source pendiente del mes preseleccionada. Guardar → sigues en el banco.
- Sin Core Data en la extensión: la app publica un espejo de sources activas en el
App Group (SharedQuickUpdateSync.refreshMirror) y la extensión encola
PendingQuickUpdate; la app los convierte en Snapshots reales al activarse
(ingestPending). ExtSharedBridge duplica el contrato (keep-in-sync comentado).
- Parser de importes tolerante a locales (12.345,67 € / $1,234.56 / 1234,5).
- UI localizada en los 7 idiomas (lproj propios de la extensión).
- pbxproj: target app-extension completo (sync group, embed, dependency, configs)
replicando el patrón del widget. Entitlements solo con App Group.
Clipboard auto-advance en Quick Update:
- Al volver a la app con un importe copiado: banner de un tap "Pegar X en <source>"
que rellena la siguiente source vacía y avanza el foco a la siguiente.
- Badge NEXT en la fila activa; no repite sugerencias del mismo clipboard.
Script de upload: -allowProvisioningUpdates + API key también en el archive para
que el bundle id nuevo de la extensión se registre automáticamente.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qUZrBusG82T37R7PeokqJ
El fetch del cache dependía del timeRange activo (monthsLimit = timeRange.months),
así que al reutilizarlo entre chart types/rangos (optimización del build 45) los
charts recibían historia truncada — Year vs Year o "All" con solo 12 meses, charts
vacíos o incompletos al cambiar de tipo.
Fix: el cache siempre guarda la historia completa (maxHistoryMonths) y el recorte
por rango se hace en memoria (el filtro por cutoffDate ya existía justo después).
Bonus: hiddenHistoryMonths ahora se calcula contra la ventana completa (teaser más
preciso).
Verificado en iPad Pro 13" simulador: Evolution 12M → All → Compare →
Period vs Period → Evolution, todos renderizan con datos.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qUZrBusG82T37R7PeokqJ
Incluye respecto a build 43:
- Fix crítico: QR viral y enlace de Settings apuntaban a ids inexistentes (404)
- Paywall multi-plan (sub anual con trial + lifetime), 6 benefits, teaser de historia
- Rating prompt acelerado, sample data por defecto, streak protection, App Intents
- Instrumentación de onboarding/shares/backups
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qUZrBusG82T37R7PeokqJ
Incluye respecto a build 42:
- Migración de MonthlyContributionStore y AllocationTargetStore a CoreData (sync iCloud)
- Versionado por variables en Info.plist, fix bug .p8 del script de upload, .gitignore
NOTA: los campos CloudKit CD_monthlyContribution / CD_allocationTarget requieren deploy
del schema a producción para sincronizar en TestFlight.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qUZrBusG82T37R7PeokqJ
- Info.plist (app + widget): CFBundleShortVersionString -> $(MARKETING_VERSION),
CFBundleVersion -> $(CURRENT_PROJECT_VERSION). Antes estaban hardcodeados, lo que
obligaba a editar 3 sitios por release y causó el rebote de build (subía 41).
- archive_and_upload_appstore.sh: find_p8_file ahora asigna la variable global P8_PATH
en vez de devolverla por $(...); el trap EXIT que limpia el .p8 temporal ya no corre
en un subshell, así que el fichero sobrevive hasta el upload (antes xcodebuild fallaba
en -exportArchive con "-authenticationKeyPath ... existing file").
- .gitignore nuevo: build artifacts, credenciales (.p8/.mobileprovision), basura macOS
y documentos personales. Destrackeados .DS_Store y UserInterfaceState.xcuserstate.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qUZrBusG82T37R7PeokqJ
- 151: contribución editable por snapshot en cualquier modo (quitado gate detailed)
con diálogo de propagación al editar: solo este / adelante / atrás / todos
(SnapshotRepository.propagateContribution, SnapshotFormViewModel.contributionChanged)
- 148: Period vs Period mostraba mal el último mes del period B — la agrupación usaba
chartMonth(for:) que aplicaba el grace-period del check-in a snapshots históricos;
ahora agrupa por mes calendario crudo
- 152: iPad Sources no saltaba entre fuentes — añadido .id() al SourceDetailView para
recrear el StateObject al cambiar de selección
- 146/147: Year vs Year con forecast del año en curso (asterisco de estimado) y KPIs
arriba / detalle debajo
- 145: card de contribución mensual en SourceDetailView
- Nuevas claves snapshot_contribution_propagate_* en los 7 idiomas
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qUZrBusG82T37R7PeokqJ
- Compare (free): multi-source overlay chart, Base 100 / Return % / Value modes,
chip selector, reactive to time range filter
- What If (premium): allocation simulator with sliders per source, dual-line chart
actual vs simulated indexed to 100, orange warning when weights ≠ 100%
- Period vs Period (free): date-picker-driven comparison of any two custom periods,
return % normalized from month 1 = 0%, color-coded legend with period labels
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- completedMonthKeys: remove UserDefaults-based MonthlyCheckInStore check (not synced via iCloud)
- MonthlyCheckInCard.effectiveLastCheckInDate: use max() instead of ?? to prefer newer synced snapshot date
- MonthlyCheckInView.lastCompletionDate: use max() of local + latest CoreData snapshot via @FetchRequest
- FirebaseService.logPaywallShown: fix parameter key to paywall_trigger (matches GA4 custom dimension)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 1A: Notificación mensual de resumen de portfolio (día 5 de cada mes)
- 1B: Badge de racha mensual en Dashboard (streak counter)
- 1C: Empty states mejorados en Goals y Journal con CTAs claros
- 2A: Quick Update sheet — actualiza todas las fuentes desde una pantalla
- 2B: Notificación batch update redirige al Quick Update sheet
- 2C: Widget deep link portfoliojournal://quickupdate abre Quick Update
- 3A: App Store version check banner en Settings
- 3C: What's New sheet en primer launch de versión nueva
- Localización completa en 7 idiomas para todas las nuevas strings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Crashlytics: add FirebaseCrashlytics framework + dSYM upload build phase
- Onboarding: useSampleData off by default, Add First Investment as primary CTA
- AddSourceView: contextual placeholder and footer explaining what a source is
- Charts: CompactPaywallBanner visible to free users on every visit
- Notifications: re-engagement (7d) and monthly check-in local notifications
- Paywall: decorative chart preview replacing static crown icon
- Localization: all new strings in en + es-ES
iCloud sync:
- Force viewContext.refreshAllObjects() on remote change notifications so
data from other devices is picked up immediately without app restart
- Call refreshFromCloudKit() on foreground to merge any changes made while
the device was inactive
- Wait up to 10s for initial CloudKit sync on launch before showing onboarding
(shows "Checking iCloud..." during the wait)
- New OnboardingICloudCheckView: shown on fresh installs with iCloud available,
lets user restore from iCloud before starting onboarding from scratch
Localization:
- Added de, fr, it, ja, pt-BR lproj folders
- New iCloud onboarding strings in en + es-ES (+ button literals)
ASO metadata (fastlane):
- Updated en-US: new subtitle, keywords, description (fixed "no cloud sync"
claim), promotional text, release notes
- Added full metadata for es-ES, de-DE, fr-FR, it, ja, pt-BR (63 files total)
- All keyword fields validated ≤100 Unicode chars
Infrastructure:
- Gemfile + Gemfile.lock for fastlane
- Scripts/archive_and_upload_appstore.sh for CI/CD
Version: 1.2.1 (build 7)
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>