Saltar al contenido
K
iOS con SwiftUIPaso a paso35 min de lectura · 20 min de máquina Básico

Tu primera app de iOS

Al terminar: Crear la app de iOS, enlazar el paquete Torneo, entender App, Scene y scenePhase y escribir tus primeras pruebas.

Al terminar tendrás en ~/Marcador-Swift/MarcadorApp una app de iOS recién creada que arranca en el simulador con la pantalla de la plantilla, con el nombre «Marcador», el identificador com.sebastiannarvaez.marcador y la versión mínima de iOS puesta en 18.0 en el proyecto y en la app. Todavía no enseña nada del partido: eso llega en las dos lecciones siguientes, cuando le enlaces el paquete Torneo y escribas sus primeras pruebas.

Importa porque un proyecto de Xcode tiene piezas que un paquete no tiene (targets, schemes, ajustes de compilación, firma) y casi todo error de «no compila y no sé por qué» de aquí en adelante vive en una de ellas.

Es la primera vez que abres un proyecto de Xcode, así que la lección va despacio: primero las palabras, después un ejercicio corto en tu Playground y, al final, el proyecto de verdad. Temas: proyecto, target, scheme, firma, identificador y versión mínima.

Qué es un proyecto de Xcode

Hasta ahora trabajaste con un paquete: una carpeta con un Package.swift que dice qué código tiene y qué pruebas. Una app de iPhone necesita más: un nombre y un icono, una versión mínima de iOS, una firma y varias formas de compilarse. Todo eso vive en un proyecto de Xcode, una carpeta con extensión .xcodeproj y el código de la app a su lado.

Target, scheme y ajustes

Tres palabras describen un proyecto de Xcode:

PalabraQué esEn Marcador
Objetivo (target)Una receta para producir un resultado: la app, un grupo de pruebasMarcadorApp, MarcadorAppTests, MarcadorAppUITests
Esquema (scheme)Qué targets se compilan al pulsar Run o Test, y con qué configuraciónUno llamado MarcadorApp
Ajustes de compilación (build settings)Las opciones de cada target: nombre, versión mínima de iOS, modo de Swift…Se editan en Xcode y se guardan en el fichero project.pbxproj

project.pbxproj es un texto largo que Xcode escribe por ti dentro de MarcadorApp.xcodeproj. No se edita a mano: se cambia desde la interfaz.

Qué es firmar

Firmar una app es adjuntarle un certificado y un perfil que dicen «esta app la publica este desarrollador y puede correr en estos dispositivos». iOS no instala en un iPhone real nada que no esté firmado. En el simulador no hace falta: Xcode firma «para ejecutar en local», sin cuenta de por medio.

Un Team (equipo) es la cuenta de desarrollador con la que Xcode firma. Si guardaste tu Apple Account en Xcode (Instalar Xcode y el simulador, paso 6), tienes un Personal Team gratuito. Para este curso en el simulador, cualquier opción vale: tu Personal Team o ninguno. Solo importa con un iPhone real como destino: sin Team, Xcode se detiene con

Signing for "MarcadorApp" requires a development team. Select a development team in the Signing & Capabilities editor.

y la solución es elegir ahí tu Personal Team.

El identificador y la versión mínima

El identificador del paquete (bundle identifier) es el nombre único de la app instalada, escrito al revés como un dominio. Es el applicationId de Android. En el simulador no se registra en ningún sitio, así que usa el de la guía: com.sebastiannarvaez.marcador. Las órdenes de Terminal y el registro de las lecciones lo usan tal cual; si algún día pones el tuyo, cámbialo también en esas órdenes y en el Logger que escribirás en la lección siguiente.

La versión mínima de iOS (Minimum Deployments) es el sistema más antiguo donde la app se puede instalar. El asistente pone la de tu Xcode (una muy reciente, p. ej. 26.2), y la pone en dos niveles: en el proyecto entero, que heredan todos sus targets, y en la app. La bajarás a 18.0 en los dos sitios por una regla: una app no puede pedir menos que lo que exigen sus paquetes. Torneo declara .iOS(.v18) en su Package.swift (la línea platforms de async/await y Task), así que la app tiene que pedir 18 o más. Si pidiera 17, Xcode se negaría a compilar, porque el paquete no se puede usar en un iOS 17. Se ve en la lección siguiente, donde enlazas Torneo.

La regla es una comparación, y una comparación se escribe en Swift. Un ejemplo con otros datos: una atracción de feria pide una altura mínima, y sube quien mide lo mismo o más.

ejemplo mínimo: quién puede subir a la atracción (con comentarios)
// Una atracción de feria pide una altura mínima: sube quien mide lo mismo o más.
func puedeSubir(alturaDeLaPersona: Int, alturaMinima: Int) -> Bool {
    alturaDeLaPersona >= alturaMinima   // `>=` significa «mayor o igual»: da true o false
}
 
let alturas = [110, 120, 150]           // tres personas, en centímetros
for altura in alturas {                 // una vuelta por cada persona
    // `condición ? uno : otro` elige entre dos textos según sea true o false
    let resultado = puedeSubir(alturaDeLaPersona: altura, alturaMinima: 120) ? "sube" : "no sube"
    print("Mide \(altura) cm, mínimo 120 cm: \(resultado)")
}

Al ejecutarlo en el Playground, la consola imprime:

Mide 110 cm, mínimo 120 cm: no sube
Mide 120 cm, mínimo 120 cm: sube
Mide 150 cm, mínimo 120 cm: sube

Reto · Ejercicio: quién puede pedir menos

En tu Playground, escribe una función, compila, que reciba la versión mínima de iOS de una app y la de un paquete que esa app usa, y diga si la app compila: solo si la de la app es igual o mayor que la del paquete. Pruébala con tres apps, de iOS 17, 18 y 26, que usan un paquete de iOS 18, e imprime una línea por cada una. Lo que debe salir:

App iOS 17, paquete iOS 18: no compila
App iOS 18, paquete iOS 18: compila
App iOS 26, paquete iOS 18: compila
Ver solución
solución: quién puede pedir menos
// La versión mínima de iOS de una app no puede ser más baja que la de sus paquetes.
func compila(minimoDeLaApp: Int, minimoDelPaquete: Int) -> Bool {
    minimoDeLaApp >= minimoDelPaquete
}
 
let casos = [17, 18, 26]
for app in casos {
    let resultado = compila(minimoDeLaApp: app, minimoDelPaquete: 18) ? "compila" : "no compila"
    print("App iOS \(app), paquete iOS 18: \(resultado)")
}

Solo la app de iOS 17 pide menos de lo que exige el paquete. Los nombres de la función y de los parámetros son libres: lo que importa es la salida.

Paso 1 · Crea el proyecto con el asistente

Objetivo: un proyecto de Xcode llamado MarcadorApp, dentro de ~/Marcador-Swift, al lado del paquete Torneo.

Marcador-Swift/tu carpeta de trabajo, en ~
Torneo/ya existe; más adelante se enlaza, no se toca
MarcadorApp/
MarcadorApp.xcodeprojse crea con el asistente
MarcadorApp/el código de la app
MarcadorAppTests/pruebas unitarias
MarcadorAppUITests/pruebas de interfaz
La app vive junto al paquete, no dentro de él.

El asistente de proyectos es el formulario con el que Xcode crea un proyecto nuevo, con los ficheros mínimos para que compile.

  1. Abre Xcode. En la ventana de bienvenida pulsa Create New Project…; si ya hay un proyecto abierto, usa File → New → Project….
  2. Arriba elige la pestaña iOS, la plantilla App y pulsa Next.
  3. Rellena el formulario así:
CampoQué ponerPara qué sirve
Product NameMarcadorAppEl nombre del proyecto y de la app
TeamDéjalo como estéCon quién se firma la app; en el paso 4 eliges el tuyo (o ninguno)
Organization Identifiercom.sebastiannarvaezLa primera mitad del identificador único de la app; el asistente le pega el nombre del producto
InterfaceSwiftUILa forma moderna de dibujar pantallas, que aprenderás en las próximas lecciones
LanguageSwift
Testing SystemSwift Testing with XCTest UI TestsCrea dos grupos de pruebas: unitarias con Swift Testing y de interfaz con XCTest (otro marco de pruebas de Apple; lo verás en Tus primeras pruebas en iOS)
StorageNoneSin base de datos
  1. Pulsa Next. Xcode abre un selector de carpetas: entra en Marcador-Swift dentro de tu carpeta personal (la que creaste en Tu primer paquete de Swift) y pulsa Create.

Si en ese selector ves la casilla Create Git repository on my Mac, desmárcala: este curso no usa control de versiones.

Xcode crea la carpeta ~/Marcador-Swift/MarcadorApp y abre el proyecto.

Paso 2 · Recorre lo que ha generado

Objetivo: saber qué es cada cosa del panel de la izquierda antes de tocar nada.

A la izquierda está el Project navigator (el panel con la lista de ficheros; se muestra y se oculta con Cmd+1). Es lo que ves ahora:

MarcadorApp (proyecto)el icono azul: el proyecto entero
MarcadorAppcarpeta del código de la app
MarcadorAppApp.swiftel punto de entrada
ContentView.swiftla pantalla que dibuja la plantilla
Assetsiconos, colores e imágenes
MarcadorAppTestspruebas unitarias
MarcadorAppTests.swift
MarcadorAppUITestspruebas de interfaz
MarcadorAppUITests.swift
MarcadorAppUITestsLaunchTests.swift
El Project navigator tras crear el proyecto.
  • El icono azul MarcadorApp es el proyecto: al seleccionarlo, el editor de la derecha enseña sus ajustes (lo usarás en el paso 4). Hay tres cosas llamadas MarcadorApp: el icono azul, la carpeta de debajo y, más adelante, un fichero .swift; las lecciones te dirán cuál con «el icono azul» o «la carpeta».
  • La carpeta MarcadorApp guarda el código de la app. Es una carpeta sincronizada: Xcode la vigila, y cualquier .swift que pongas dentro entra en la app sin que tengas que registrarlo en ningún sitio.
  • MarcadorAppTests y MarcadorAppUITests son otras dos carpetas sincronizadas, una por cada grupo de pruebas.

Lo que Xcode genera se queda directamente en la carpeta del código. Casi todo lo que escribas tú irá en carpetas con nombre, cada una con lo que va junto; en la raíz solo queda lo que une la app entera.

Ahora, la barra de herramientas de arriba: en el centro tiene el selector del scheme (MarcadorApp) y, justo al lado, el del destino (dónde se ejecuta la app: un simulador, un iPhone conectado…). A la izquierda de los selectores está el botón de Run, el triángulo.

Los tres paneles que vas a usar más: el Project navigator (Cmd+1), el Issue navigator (Cmd+5), que lista errores y avisos de compilación, y el Report navigator (Cmd+9), que guarda el registro de cada compilación y de cada ejecución de pruebas. La consola de la app sale en el Debug area, el panel de abajo: View → Debug Area → Activate Console (Cmd+Shift+Y).

Paso 3 · Ejecuta la plantilla tal como viene

Objetivo: ver la app de la plantilla en el simulador antes de cambiar nada, para saber que tu Xcode funciona.

  1. En el selector de destino (junto a MarcadorApp), despliega la lista y elige un iPhone de la sección de simuladores: el que tengas (la guía usa iPhone 17, que pide iOS 26; con un runtime anterior te saldrá, por ejemplo, un iPhone 16). Esa lista sale de los simuladores que instalaste en Instalar Xcode y el simulador; desde la Terminal puedes verla con xcrun simctl list devices available. Si la lista está vacía y ves un botón Get, falta el runtime de iOS: vuelve a esa lección.
  2. Pulsa el botón Run (el triángulo) o Cmd+R. La primera compilación tarda un rato (de uno a varios minutos); abajo, en el centro de la barra de herramientas, Xcode va diciendo qué hace. Termina con Build Succeeded.
  3. Se abre la app Simulator con un iPhone y, dentro, la app: un globo terráqueo y el texto «Hello, world!».
  4. Para pararla, el botón cuadrado de la barra de herramientas o Cmd+. (Cmd y punto).

La pantalla que ves está en ContentView.swift. Ábrela (clic en el Project navigator): es un struct ContentView: View con un VStack, una Image y un Text("Hello, world!"). Aún no hace falta entenderlo: lo reemplazarás en la lección siguiente y SwiftUI: primeros pasos lo explica a fondo.

Paso 4 · Firma, identificador y versión mínima

Objetivo: ajustar la versión mínima de iOS del proyecto, y el nombre, el identificador y la versión mínima del target de la app, y elegir un Team.

Selecciona el icono azul MarcadorApp en el Project navigator. En el editor aparecen dos listas: PROJECT y TARGETS. Elige, bajo TARGETS, el target MarcadorApp (el de la app, no los de pruebas). Arriba del editor hay pestañas: General, Signing & Capabilities, Info, Build Settings…

El Team. Dónde se elige:

  1. Pestaña Signing & Capabilities.
  2. Casilla Automatically manage signing marcada: Xcode crea y renueva los certificados por ti.
  3. Desplegable Team: la lista ofrece tu Personal Team, Add an Account… o None. Elige tu Personal Team o déjalo en None; las lecciones siguientes lo llaman «el Team que elegiste en el paso de firma (o ninguno)».

El nombre y el identificador: pestaña General, apartado Identity: Display Name Marcador (el nombre bajo el icono de la app) y Bundle Identifier com.sebastiannarvaez.marcador. El identificador también se ve en Signing & Capabilities: son el mismo ajuste en dos sitios.

La versión mínima, en dos sitios. Los dos de pruebas (MarcadorAppTests y MarcadorAppUITests) no la toman del ajuste de la app, sino del proyecto:

  1. Bajo PROJECT (no bajo TARGETS), elige MarcadorApp, pestaña Info, apartado Deployment Target: pon 18.0 en iOS Deployment Target. Los menús pueden cambiar un poco según tu versión de Xcode; si no lo encuentras, en la pestaña Build Settings del proyecto busca IPHONEOS_DEPLOYMENT_TARGET y pon 18.0.
  2. Bajo TARGETS, MarcadorApp, pestaña General, apartado Minimum Deployments: elige 18.0 en el desplegable.

Los tres ajustes de compilación que Xcode ya dejó puestos, y que verás en lecciones posteriores, están en la pestaña Build Settings (escribe el nombre en el buscador de arriba a la derecha):

Ajuste (nombre en Build Settings)ValorQué hace
Swift Language Version (SWIFT_VERSION)5Modo de lenguaje Swift 5 para la app; Torneo compila en Swift 6 y los dos modos conviven
Default Actor Isolation (SWIFT_DEFAULT_ACTOR_ISOLATION)MainActorTodo el código de la app es @MainActor sin escribirlo. Solo está en el target de la app: ni los de pruebas ni el paquete Torneo lo llevan
Generate Info.plist File (GENERATE_INFOPLIST_FILE)YesEl Info.plist es la ficha que describe la app al sistema (nombre, versión, permisos); aquí no hay fichero: se genera en cada compilación

Punto de control

  • Pulsa Cmd+R: la app de la plantilla arranca en el simulador, con el globo y «Hello, world!». Bajo su icono, al salir al inicio del simulador, pone «Marcador».
  • Selecciona el icono azul → target MarcadorApp → General: Bundle Identifier es com.sebastiannarvaez.marcador y Minimum Deployments, 18.0. Bajo PROJECT → MarcadorApp → Info, iOS Deployment Target también es 18.0.
  • En Build Settings ves SWIFT_DEFAULT_ACTOR_ISOLATION en MainActor.
  • Tu Playground muestra las tres líneas del ejercicio: la app de iOS 17 no compila y las de 18 y 26 sí.

Comprueba lo que entendiste

5 preguntas · necesitas 4 para completar la lección

  1. 1.Al pulsar Run o Test, ¿qué decide qué targets se compilan y con qué configuración?

  2. 2.¿Por qué puedes ejecutar la app en el simulador sin elegir un Team?

  3. 3.El paquete Torneo declara .iOS(.v18). ¿Qué versión mínima de iOS puede pedir la app que lo usa?

  4. 4.Con un simulador de iOS 18 y la versión mínima de iOS puesta solo en el target de la app, Cmd+U dice que el target de pruebas pide iOS 26.2. ¿Qué falta?

  5. 5.¿Qué es el bundle identifier de una app de iOS y a qué equivale en Android?

Faltan 5 de 5