Programación en Swift y SwiftUI para iOS Developers

Cómo formatear números en SwiftUI

Cualquier iOS Developer que haya trabajado en aplicaciones del mundo real sabe que presentar los datos de manera clara y comprensible es tan importante como la lógica detrás de ellos. Ya sea que estés mostrando el saldo de una cuenta bancaria, el progreso de descarga de un archivo o la distancia recorrida en un entrenamiento, la forma en que los números se presentan al usuario final puede definir la calidad de la experiencia de usuario (UX).

En el ecosistema moderno de desarrollo de Apple, formatear numeros en SwiftUI se ha convertido en una tarea sorprendentemente elegante. Atrás quedaron los días en los que dependíamos exclusivamente de complejas configuraciones con NumberFormatter. Gracias a las recientes actualizaciones en la programación Swift, Apple ha introducido una API declarativa, segura y altamente optimizada para manejar formatos directamente en tus vistas.

En este tutorial extenso y detallado, exploraremos a fondo cómo trabajar con números en Swift, utilizando Xcode y SwiftUI para crear experiencias impecables en iOS, macOS y watchOS.

1. La Evolución del Formateo en la Programación Swift

Antes de la llegada de iOS 15 y macOS 12, la forma estándar de convertir un Double o un Int en una cadena de texto legible era utilizando la clase NumberFormatter de Foundation. Aunque poderosa y altamente personalizable, requería mucho código repetitivo (boilerplate). A menudo, tenías que crear instancias estáticas de formateadores para no penalizar el rendimiento de la aplicación.

Con la maduración de SwiftUI, Apple introdujo la API FormatStyle. Esta API es un cambio de paradigma: es declarativa, se integra directamente con los tipos de datos nativos de Swift y está fuertemente tipada. Esto significa que el compilador de Xcode te ayudará a evitar errores en tiempo de ejecución al formatear tus datos.

2. El Poder de la API FormatStyle y el método .formatted()

La forma más directa de aplicar formato a un número hoy en día es utilizando el método .formatted() que está disponible en la mayoría de los tipos numéricos estándar (Int, Double, Float, etc.).

Ejemplo Básico

let vistasDePantalla = 14500
let vistasFormateadas = vistasDePantalla.formatted()
// Resultado en una región de habla inglesa: "14,500"
// Resultado en España: "14.500"

El verdadero poder de este enfoque es la localización automática. Como iOS Developer, no tienes que preocuparte por saber si el usuario prefiere comas o puntos para separar los miles; el sistema operativo, ya sea en iOS, macOS o watchOS, lo determina automáticamente basándose en la configuración regional (Locale) del dispositivo del usuario.

3. Formatear Números en SwiftUI Directamente en las Vistas

SwiftUI lleva la API FormatStyle un paso más allá al integrarla directamente en sus vistas, especialmente en la vista Text. En lugar de formatear el número antes de pasarlo a la vista, puedes inyectar el valor numérico y el estilo de formato directamente en el inicializador de Text.

Formato Numérico Estándar

Para mostrar un número con formato estándar, utilizamos el parámetro format dentro de la vista Text:

import SwiftUI

struct EstadisticasView: View {
    let puntuacion: Double = 12345.678
    
    var body: some View {
        VStack(spacing: 20) {
            Text("Puntuación del Jugador")
                .font(.headline)
            
            // Formatear numeros en SwiftUI es tan sencillo como esto:
            Text(puntuacion, format: .number)
                .font(.largeTitle)
                .bold()
        }
        .padding()
    }
}

Al compilar esto en Xcode, el sistema renderizará “12,345.678” o “12.345,678” dependiendo de la región, aplicando la separación de miles y decimales de manera nativa.

4. Controlando la Precisión y los Decimales

Uno de los requerimientos más comunes en la programación Swift es limitar la cantidad de decimales que se muestran en pantalla. La API nos permite encadenar modificadores al formato base .number.

Modificador .precision

Puedes usar .precision(.fractionLength(x...y)) para definir el número mínimo y máximo de decimales permitidos.

import SwiftUI

struct DetallesFinancierosView: View {
    let tasaDeInteres: Double = 4.123456
    
    var body: some View {
        VStack {
            // Muestra exactamente 2 decimales: 4.12
            Text(tasaDeInteres, format: .number.precision(.fractionLength(2)))
            
            // Muestra entre 1 y 3 decimales dependiendo del valor: 4.123
            Text(tasaDeInteres, format: .number.precision(.fractionLength(1...3)))
        }
    }
}

Agrupación y Signos

Si necesitas ocultar la separación de miles (por ejemplo, al mostrar un número de ID o un año), puedes desactivar el agrupamiento. También puedes forzar la visualización del signo positivo para indicar un crecimiento.

let año = 2026
let crecimiento = 15.5

// Sin separador de miles (ej. 2026 en lugar de 2,026)
Text(año, format: .number.grouping(.never))

// Forzar la visualización del signo (ej. +15.5)
Text(crecimiento, format: .number.sign(strategy: .always()))

5. El Mundo del Dinero: Formateo de Monedas

Manejar divisas es una tarea crítica. Un error común es intentar agregar el símbolo de la moneda manualmente (ej. Text("$\(monto)")). Esto rompe la localización y es una mala práctica de accesibilidad. Para formatear numeros en SwiftUI como moneda, utilizamos .currency.

import SwiftUI

struct CarritoDeComprasView: View {
    let total: Double = 1499.99
    
    var body: some View {
        HStack {
            Text("Total a pagar:")
            Spacer()
            // Formato de moneda usando el código ISO 4217
            Text(total, format: .currency(code: "USD"))
                .font(.title2)
                .foregroundColor(.green)
        }
        .padding()
    }
}

El formato .currency es extremadamente inteligente. Si la configuración regional del dispositivo es España y el código es “EUR”, mostrará 1.499,99 €. Si la región es Estados Unidos y el código es “EUR”, mostrará €1,499.99. Esto garantiza que tus aplicaciones se sientan nativas y profesionales en cualquier mercado.

6. Porcentajes de Forma Sencilla

Al igual que con las monedas, no debes multiplicar por 100 y añadir un “%” a mano. SwiftUI maneja los porcentajes de forma elegante. Ten en cuenta que la API asume que el valor base es la fracción (donde 1.0 = 100%).

import SwiftUI

struct ProgresoView: View {
    let nivelDeCarga: Double = 0.854 // 85.4%
    
    var body: some View {
        // Muestra 85%
        Text(nivelDeCarga, format: .percent.rounded())
        
        // Muestra 85.4%
        Text(nivelDeCarga, format: .percent.precision(.fractionLength(1)))
    }
}

7. Trabajando con Unidades de Medida y Datos

Si estás desarrollando para watchOS, es muy probable que trabajes con datos de salud (distancia, calorías, frecuencia cardíaca). Si desarrollas aplicaciones de utilidades para macOS o iOS, puedes necesitar mostrar tamaños de archivos. Swift ofrece herramientas nativas para esto.

Formateando Bytes (Tamaños de Archivo)

La estructura ByteCountFormatStyle te ahorra la tarea de calcular si un archivo está en KB, MB, GB o TB.

import SwiftUI

struct AlmacenamientoView: View {
    let tamañoArchivoEnBytes: Int64 = 10_500_000_000 // Aprox 10.5 GB
    
    var body: some View {
        // Xcode compilará esto para mostrar "10.5 GB" (o equivalente regional)
        Text(tamañoArchivoEnBytes, format: .byteCount(style: .file))
    }
}

Unidades de Medida (Measurement)

Para distancias, pesos y temperaturas, utilizamos el tipo Measurement.

import SwiftUI

struct ClimaView: View {
    let temperatura = Measurement(value: 38, unit: UnitTemperature.celsius)
    let distancia = Measurement(value: 5.2, unit: UnitLength.kilometers)
    
    var body: some View {
        VStack {
            // El sistema lo convierte automáticamente a Fahrenheit si el usuario está en EE.UU.
            Text(temperatura, format: .measurement(width: .abbreviated))
            
            // Puede mostrar "5.2 km" o "3.2 mi" dependiendo de la región
            Text(distancia, format: .measurement(width: .wide))
        }
    }
}

8. Entrada de Usuario: TextFields con Formato y SwiftUI

Mostrar números es solo la mitad del trabajo de un iOS Developer. A menudo, necesitamos que los usuarios introduzcan números (por ejemplo, en una aplicación de finanzas). Aquí es donde TextField en SwiftUI brilla junto a la API de formato, parseando la entrada del usuario y convirtiéndola en un tipo de dato numérico automáticamente.

import SwiftUI

struct PagoView: View {
    @State private var montoIngresado: Double? = nil
    
    var body: some View {
        Form {
            Section(header: Text("Introduce el monto a transferir")) {
                TextField("Monto", value: $montoIngresado, format: .currency(code: "EUR"))
                    #if os(iOS)
                    .keyboardType(.decimalPad)
                    #endif
            }
            
            if let monto = montoIngresado {
                Text("Vas a transferir: \(monto, format: .currency(code: "EUR"))")
                    .foregroundColor(.blue)
            }
        }
    }
}

Consideraciones Multiplataforma (iOS, macOS, watchOS)

En el ejemplo anterior, notarás la directiva de compilación #if os(iOS). Esto es crucial cuando compartes código entre plataformas en Xcode.

  • En iOS, queremos invocar el teclado numérico (.decimalPad) para mejorar la UX.
  • En macOS, los usuarios tienen un teclado físico, por lo que .keyboardType no es necesario ni tiene el mismo impacto.
  • En watchOS, la entrada de texto es muy diferente (Scribble, dictado, o teclado numérico del sistema), y normalmente se maneja con vistas especializadas o controles de incremento/decremento (Stepper) en lugar de escribir libremente.

9. Personalización Avanzada y Creación de Formatos Propios

Aunque los formatos predeterminados cubren el 95% de los casos de uso en la programación Swift, habrá momentos en los que necesites algo específico (por ejemplo, formatear un número de teléfono o un número de tarjeta de crédito).

En SwiftUI, puedes crear tu propio FormatStyle adoptando el protocolo correspondiente.

import Foundation

struct NumeroTarjetaFormatStyle: ParseableFormatStyle {
    var parseStrategy: NumeroTarjetaParseStrategy = .init()
    
    func format(_ value: String) -> String {
        // Lógica simple: insertar un espacio cada 4 caracteres
        let cleanNumber = value.replacingOccurrences(of: " ", with: "")
        var formatted = ""
        for (index, character) in cleanNumber.enumerated() {
            if index != 0 && index % 4 == 0 {
                formatted.append(" ")
            }
            formatted.append(character)
        }
        return formatted
    }
}

struct NumeroTarjetaParseStrategy: ParseStrategy {
    func parse(_ value: String) throws -> String {
        return value.replacingOccurrences(of: " ", with: "")
    }
}

extension FormatStyle where Self == NumeroTarjetaFormatStyle {
    static var tarjetaDeCredito: NumeroTarjetaFormatStyle { .init() }
}

Uso en SwiftUI:

TextField("Número de Tarjeta", value: $numeroTarjeta, format: .tarjetaDeCredito)

Este enfoque te permite encapsular lógicas complejas de formateo, manteniendo tus vistas de SwiftUI limpias y declarativas.

10. Compatibilidad y Manejo de Versiones Antiguas (iOS 14 y anteriores)

Como iOS Developer, la realidad comercial a menudo dicta que debemos soportar versiones anteriores del sistema operativo. La API FormatStyle introducida en Swift requiere iOS 15, macOS 12 o watchOS 8 como mínimo.

Si tu proyecto en Xcode tiene un “Minimum Deployment Target” inferior a estas versiones, deberás utilizar la clase tradicional NumberFormatter.

¿Cómo integrar NumberFormatter en SwiftUI?

La forma más eficiente es crear un formateador estático y pasarlo al inicializador de Text o TextField.

import SwiftUI

struct LegacyFormatView: View {
    let balance: Double = 10500.50
    
    // Crear el formateador una sola vez es vital para el rendimiento
    static let currencyFormatter: NumberFormatter = {
        let formatter = NumberFormatter()
        formatter.numberStyle = .currency
        formatter.currencyCode = "USD"
        return formatter
    }()
    
    var body: some View {
        // Uso en iOS 14 e inferiores
        Text(NSNumber(value: balance), formatter: Self.currencyFormatter)
    }
}

Si bien es más verboso que la nueva API, es una técnica de programación Swift completamente válida y necesaria para proyectos “legacy”. Si estás desarrollando un proyecto verde (desde cero) hoy en día, puedes y debes optar exclusivamente por la API FormatStyle.

11. Rendimiento y Buenas Prácticas en Xcode

Al formatear numeros en SwiftUI, es fundamental tener en cuenta el ciclo de vida de la vista. SwiftUI redibuja las vistas de manera muy agresiva cuando cambia el estado.

  1. Evita el procesamiento pesado en el body: La API .formatted() está altamente optimizada en Swift, pero si estás realizando cálculos numéricos complejos antes de formatear, hazlo en un ViewModel o utiliza .task o .onAppear para preparar los datos.
  2. Aprovecha la inferencia de tipos: No especifiques el tipo de dato si Xcode puede inferirlo. Mantiene el código limpio.
  3. Localización primero: Nunca asumas que tus usuarios leen los números igual que tú. Apóyate siempre en .currency, .number y .measurement en lugar de concatenar strings manualmente.
  4. Pruebas Unitarias (Unit Testing): Cuando escribas lógica de formato personalizada (como el ejemplo de la tarjeta de crédito), asegúrate de escribir pruebas en Xcode comprobando diferentes “Locales” para asegurar que tu código no se rompa si el dispositivo se configura en alemán o japonés.

Conclusión

Dominar la forma en que presentamos los datos numéricos es un sello distintivo de un iOS Developer experimentado. La evolución de la programación Swift ha traído herramientas increíbles a nuestro entorno de Xcode. Formatear numeros en SwiftUI ha pasado de ser una tarea tediosa y propensa a errores a ser un proceso elegante, seguro y completamente declarativo.

Ya sea que estés construyendo una aplicación financiera para macOS, un tracker de fitness para watchOS o una tienda de comercio electrónico para iOS, aprovechar las capacidades nativas de FormatStyle, .currency, .percent y Measurement garantizará que tu aplicación ofrezca una experiencia premium y localizada para cada usuario alrededor del mundo.

Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Article

LabeledContent en SwiftUI

Related Posts