Skip to main content
Volver al blogCopiar Markdown
·8 min de lectura
·iosswiftuiproductuxtesting

WealthSim: calcular bien no bastaba para usar la app

WealthSim mostraba un saldo en el preset y otro al abrirlo. Anuncios al explorar, resultados fuera de pantalla y el paso de calcular a revisar un plan. →

Óscar Gallego

Óscar Gallego

Ingeniero de Software

WealthSim: proyección de saldo en la app para iPhone
En este artículo

Una tarjeta de WealthSim mostraba un saldo. La tocabas y la calculadora te daba otro, más bajo. Con suerte, además, aparecía un anuncio a pantalla completa para darte tiempo a pensar cuál de los dos era el bueno.

WealthSim es mi calculadora de interés compuesto, con versión web y app nativa de iOS. Metes un capital inicial, una aportación mensual y unas hipótesis de rentabilidad; te devuelve una proyección. En la revisión del 5 de octubre encontré que la tarjeta del preset calculaba sin comisiones, pero al aplicarlo conservaba las de la calculadora. El mismo motor, dos entradas distintas que la pantalla presentaba como si fueran la misma.

La revisión también señalaba que el resultado desaparecía al bajar a los controles. Yo podía explicar cada número mirando el código. La persona con el móvil tenía que fiarse de una cifra que cambiaba al tocarla y hacer scroll para ver qué había hecho su último ajuste.

El test que faltaba estaba entre la tarjeta y el resultado

La vista previa del preset llamaba a InvestmentProjector con comisión e inflación a cero. CalculatorSession.applyPreset conservaba las que ya tenía el usuario: por defecto, una comisión anual del 0,25 % y una inflación del 2 %.

La comisión explicaba la diferencia en el saldo nominal. La inflación también tenía que viajar con el mismo contexto, aunque afecte a la lectura en dinero de hoy. En 0f507ae pasé ambos valores a PresetStrip.preview, de modo que la tarjeta usara las mismas hipótesis que quedarían activas al tocarla.

La propiedad que quería proteger era la igualdad entre lo que la tarjeta enseña y lo que ocurre al aplicarla. Tener tests de la fórmula no comprueba esa relación.

En FirstSessionUXTests.swift quedó un test parametrizado con los presets del catálogo. Este es el fragmento que hace la comparación, usando los valores por defecto de la sesión:

@Test(arguments: PresetCatalog.presets)
func presetPreviewMatchesAppliedBalance(preset: Preset) throws {
    let session = CalculatorSession()
    let preview = PresetStrip.preview(
        for: preset,
        managementFee: try #require(Decimal(string: "0.25")),
        inflation: 2,
        currencyCode: session.currencyCode
    )
    session.applyPreset(preset)
    #expect(preview.finalBalance == session.presentation?.finalBalance)
}

Ese test tiene un límite: cubre la configuración inicial. Si mañana quiero proteger todas las combinaciones de comisiones editables, tendré que ampliar las entradas. Pero ya compara dos caminos de la app que antes podían discrepar sin que el test de la fórmula se enterara.

La misma comprobación sirve para una tarifa que cambia al abrir el checkout o una estimación que cambia al guardarla. Antes de añadir otro caso al motor, compara la vista previa con el resultado de la acción que ofrece.

Aplicar un preset contaba como ocasión para enseñar publicidad

El coordinador de anuncios tenía actionsPerAd = 1. Aplicar un preset emitía onSignificantAction, igual que guardar o reabrir un escenario. Si había consentimiento, un anuncio preparado y se cumplían las demás condiciones, el primer gesto de exploración podía acabar en un interstitial.

Para el contador eran acciones equivalentes. Para quien está probando la app, tocar un preset apenas es averiguar qué hace.

En el mismo commit quité esa llamada de applyPreset. Añadí protección durante la primera sesión y las primeras 24 horas desde el primer arranque, hasta que terminen ambas. Después, el contador exige tres tareas completadas y permite como máximo dos interstitials por sesión. El contador de tareas se conserva entre arranques.

También quité el banner que estaba encima del resultado. Sigue habiendo publicidad en la versión gratuita. Elegí reducir las interrupciones mientras alguien está probando valores, y acepté que eso elimina oportunidades de mostrar anuncios. No tengo una comparativa de ingresos que permita ponerle un precio a la decisión.

El saldo tenía que seguir visible mientras movía el slider

El resultado estaba encima de los controles dentro de un scroll largo. Podía actualizarlo en cada cambio y, aun así, obligar al usuario a subir para verlo. El cálculo era inmediato; comprobarlo no.

El rediseño de 80238e9 añadió PinnedResultBar: cuando la tarjeta grande sale por arriba, una barra compacta conserva el año y el saldo proyectado mientras editas. El gráfico también pasó a mostrar barras con aportaciones e intereses separados.

Esto no necesitó una fórmula nueva. Necesitó mantener la respuesta cerca del gesto que la cambia. La captura bonita de la primera pantalla no enseñaba ese problema: había que bajar hasta un control y usarlo.

La revisión de primera sesión me dejó esta tabla. El orden va de una inconsistencia concreta a una decisión de producto bastante más cara:

Lo que veía la personaCambio que hiceQué puedo comprobar
El preset y el resultado daban saldos distintosPasar las comisiones e inflación activas a la vista previaComparar preview y resultado tras aplicar el preset
Explorar podía disparar un anuncioSacar los presets del contador y proteger el primer usoComprobar que ese gesto no habilita un interstitial
Editar dejaba el resultado fuera de pantallaAñadir la barra compacta del saldoMover un control con la tarjeta grande fuera de la vista
Guardar una proyección no daba una tarea para el mes siguienteAñadir «Mi plan» y el registro de saldoVerificar el flujo; saber si alguien vuelve requiere observar uso real

Mi plan añade un saldo real a una curva hipotética

Una calculadora puede cumplir su función en una sola visita. No considero que alguien que hace una cuenta y se va haya usado mal WealthSim. Para justificar otra visita necesitaba ofrecerle otra tarea.

Con «Mi plan» puedes convertir una proyección en un objetivo con importe y fecha. Después registras tu saldo real cada mes y lo comparas con la trayectoria del plan. La versión gratuita permite un plan activo. El recordatorio mensual es opcional y pide permiso al activarlo.

El cambio de modelo aparece en GoalPlan y PlanCheckIn. Una proyección guardada conserva hipótesis. Un registro mensual añade una observación de lo que ha pasado. PlanStore.logCheckIn actualiza la entrada del mes si ya existe: corregir el saldo de octubre no debe dibujar un segundo octubre.

Hay una limitación que conviene explicar junto al gráfico. PlanRoute usa los resultados anuales de InvestmentProjector e interpola entre ellos para colocar los meses. La trayectoria es una referencia calculada con las hipótesis del plan; no representa cómo se moverá el mercado. Y registrar el saldo requiere trabajo manual. Si esa comparación no te sirve, un recordatorio solo añade una molestia.

Por eso la pregunta que me interesa ahora es si alguien registra un saldo y vuelve otro mes a compararlo. Abrir una notificación, por sí solo, no me diría que el plan le resulta útil.

Construí más de lo que todavía puedo validar

La revisión del 5 de octubre dejó constancia de que no había datos de retención disponibles en App Store Connect y de que la app no tenía analítica de producto para responder a estas preguntas. A 6 de octubre, no puedo presentar este rediseño como una mejora medida de retención.

Además, 80238e9 metió «Mi plan», el cambio visual y funciones Pro en un mismo commit. Aunque después vea más uso, ese paquete no permite atribuírselo a una sola decisión. Fue bastante más trabajo que arreglar el preset y retirar el banner superior.

Si repitiera el proceso, empezaría por esas correcciones pequeñas, comprobaría el recorrido con alguien que no conozca la app y dejaría «Mi plan» como una hipótesis separada. Tiene un coste de desarrollo mayor y depende de que la persona quiera llevar ese seguimiento. El anuncio temprano y la discrepancia del preset se podían corregir sin apostar por un hábito nuevo.

Coge una sola tarjeta de tu producto que anticipe un resultado. Anota el valor, ejecuta la acción y compáralos manteniendo la configuración del usuario. Si no coinciden, ya tienes un arreglo concreto que hacer antes de abrir el roadmap.

Fuentes y alcance

Esta historia sale de la revisión de UI del 5 de octubre y del historial de mi repositorio privado de WealthSim: 0f507ae para la primera sesión y 80238e9 para el rediseño y «Mi plan». El test reproducido está en FirstSessionUXTests.swift; el registro mensual y la trayectoria, en PlanStore.swift y PlanMath.swift. Son cambios de código, no resultados de un experimento de retención ni una confirmación de disponibilidad de cada función en la App Store.

Lectura relacionada: al guardar estos datos también aparecen decisiones de persistencia. En mi otra app conté cómo SwiftData y CloudKit condicionan el modelo antes de sincronizar nada.


P.D. ¿Has encontrado una vista previa que prometía algo distinto a lo que hacía el botón? Cuéntame el caso en Twitter/X.

Comparte este artículo