Guía esencial para comenzar con SwiftData

SwiftData es una capa de persistencia diseñada para aplicaciones en los sistemas operativos de Apple, ofreciendo una alternativa simplificada a Core Data. Su enfoque declarativo la hace ideal para una integración fluida con SwiftUI.

Con SwiftData, puedes llevar a cabo todas las operaciones esenciales de persistencia de datos, incluyendo lectura, almacenamiento, actualización y eliminación de registros. Además, facilita la gestión de migraciones para adaptar los esquemas de datos según las necesidades.

El SwiftData Stack

Al igual que Core Data tenía su Core Data Stack, SwiftData cuenta con su propia estructura de SwiftData Stack. Esta estructura engloba distintos componentes fundamentales para su funcionamiento: el ModelContainer y el ModelContext.

El ModelContainer se encarga de las operaciones de persistencia de los modelos, mientras que el ModelContext registra los cambios realizados en los modelos, permitiendo reaccionar ante los mismos. Estas reacciones, como por ejemplo cuando eliminamos un registro, son inmediatamente manejadas por SwiftUI manteniendo las vistas actualizadas.

Configurando el Stack

Configurar tu ModelContainer con SwiftData es sencillo. Solo debes asegurarte de usar el modificador modelContainer(for:inMemory:isAutosaveEnabled:isUndoEnabled:onSetup:) en una escena de tu aplicación, preferiblemente en la escena principal. Por ejemplo:

import SwiftData
@main
struct StudentApp: App {
var body: some Scene {
WindowGroup {
StudentsView()
}
.modelContainer(for: [Student.self, Project.self])
}
}

Para acceder al ModelContext, utiliza la variable de entorno modelContext de la siguiente manera:

import SwiftUI
struct ContextView : View {
@Environment(\.modelContext) private var context
}

Modelando los datos

Para modelar nuestros datos o, dicho de otra forma, definir las entidades que conforman nuestro esquema podemos usar clases anotadas con macros. A continuación, un ejemplo:

import SwiftData
@Model
class Student {
@Attribute(.unique) var identification: String
var name: String
var age: Int
init(identification: String, name: String, age: Int) {
self.identification = identification
self.name = name
self.age = age
}
}

Hasta este momento es obligatorio crear el constructor (init) si queremos usar el macro @Model. Esto podría cambiar en el futuro.

En este fragmento de código, hemos definido la clase Student con tres propiedades: identification, name y age. La anotación @Model indica a SwiftData que esta clase es un modelo de nuestro esquema. Además, hemos empleado el macro @Attribute(.unique) para especificar que el campo identification debe ser único.

Además de @Attribute, puedes utilizar @Relationship y @Transient para proporcionar metadatos que guíen el comportamiento de SwiftData para una propiedad dada. Por ejemplo:

import SwiftData
@Model
class Student {
@Attribute(.unique) var identification: String
var name: String
var age: Int
@Transient
var alias: String = ""
@Relationship(deleteRule: .cascade)
var projects: [Project]
}

En este caso, hemos ampliado la clase Student con dos nuevas propiedades:

  • alias, marcada con @Transient, esto indica a SwiftData que esta propiedad debe ignorarse y no ser persistida.
  • projects, marcada con @Relationship(deleteRule: .cascade), establece una relación uno a muchos con la entidad Project, con la regla de eliminación cascade, lo que instruye a SwiftData a eliminar los proyectos asociados cuando se borre un estudiante.

Existen más opciones que puedes utilizar como deleteRule:

  • noAction. No hace nada.
  • nullify. Hace nula la referencia al modelo eliminado.
  • deny. No permite la eliminación de un registro si este tiene una relación con otro.

Leer datos

Para leer datos con SwiftData, utiliza el macro #Predicate. Veamos un ejemplo:

let age = 20
let studentPredicate = #Predicate<Student> {
$0.age < age
}

Para usar este macro asegúrate de importar Foundation.

En este código, definimos el criterio de búsqueda mediante la variable age, con un valor de 20. Luego, creamos un predicado usando el macro #Predicate, especificando el tipo de datos Student y una condición dentro del cierre: $0.age < age. Estas líneas bastan para realizar una lectura con SwiftData y obtener todos los estudiantes menores de 20 años.

Para consultas más avanzadas, puedes usar FetchDescriptor:

let descriptor = FetchDescriptor<Student>(
predicate: studentPredicate,
sortBy: [SortDescriptor(\Student.name)]
)
let students = try context.fetch(descriptor)

En este caso, hemos indicado que los resultados se ordenen según el campo name utilizando sortBy: SortDescriptor(\Student.name) en el descriptor. Luego, utilizamos try context.fetch(descriptor) para ejecutar la consulta.

SwiftData ❤️ SwiftUI

Para leer registros, utiliza el nuevo macro @Query(filter:sort:order:transaction:), que acepta varios parámetros para filtrar y ordenar los resultados. Aquí hay un ejemplo de cómo listar estudiantes en SwiftUI:

struct StudentsView: View {
// 1
@Environment(\.modelContext) private var context
// 2
@Query(sort: \Student.name) private var students: [Student]
var body: some View {
List {
// 3
ForEach(students, id: \Student.identification) { student in
VStack {
Text(student.name)
}
}
}
}
}

En este código:

  1. Obtenemos una referencia al contexto usando la variable de entorno modelContext.
  2. Con @Query, solicitamos todos los estudiantes ordenados por name y los almacenamos en la variable students.
  3. Utilizamos un ForEach para crear la lista de estudiantes, utilizando la propiedad identification como identificador.

Insertar datos

Para insertar un registro, podemos utilizar el siguiente código:

// 1
let student = Student(identification: "123", name: "John", age: 18)
// 2
context.insert(student)
// 3
do {
try context.save()
} catch {
print(error.localizedDescription)
}

Estos son los pasos:

  1. Creamos el objeto que deseamos insertar.
  2. Usamos el ModelContext para llamar a insert, pasando nuestro objeto.
  3. Finalmente, utilizamos try context.save() para guardar los cambios.

Eliminar datos

Para eliminar un registro, necesitamos una referencia al objeto y luego usamos context.delete(student):

context.delete(student)
try context.save()

Actualizar datos

Al igual que para eliminar necesitamos una referencia al objeto. Luego realizamos los cambios deseados y llamanos context.save():

student.name = "Luis"
try context.save()

Aunque para inserción y eliminación de datos hemos llamado a context.save() directamente, al utilizar SwiftUI, SwiftData guarda automáticamente los cambios en la base de datos. Sin embargo, es recomendable realizar esta operación directamente, ya que no se sabe con certeza cuándo SwiftData llevará a cabo estas acciones.

Otros datos interesantes sobre SwifData

  • Se integra con CloudKit, lo que permite compartir datos entre dispositivos.
  • Puedes utilizar Core Data y SwiftData en un mismo proyecto, ya que comparten la misma arquitectura.
  • Xcode proporciona la funcionalidad para convertir entidades de Core Data en clases, lo que te permite utilizarlas con SwiftData.
  • SwiftData admite tipos de datos complejos como struct, enum, Codables y Collections.

Comparte este artículo

Subscríbete a nuestro Newsletter

Mantente al día en el mundo de las aplicaciones móviles con nuestro blog especializado.

Artículos semanales

Todas las semanas artículos nuevos sobre el mundo de las aplicaciones móviles.

No spam

No te enviaremos spam, solo contenido de calidad. Puedes darte de baja cuando quieras.

Contenido de calidad

Nada de contenido generado de manera automática usando ChatGPT.

Recomendaciones

Tips indispensables sobre mejores prácticas y metodologías.

© 2024 AsyncLearn