Guia sobre SwiftData — Models

Umas das novidades do Swift e da Apple, sabe que ela lançou o SwiftData para ajudar a simplificar o uso do CoreData junto com o SwiftUI

Bruno Faganello
Code With Coffee

--

— Post publicado em: https://blog.faganello.dev.br/guia-sobre-swiftdata-models

O que é o SwiftData

Primeiro de tudo é que não podemos confundir o SwiftData com um Banco de Dados, o SwiftData é basicamente um ORM (Mapeamento objeto-relacional (Object–relational mapping)). Para curiosidade dos demais, o banco de dados padrão do iOS é o SQLite.

SwiftData é uma framework lançada para substituir a antiga framework chamada CoreData que vem da era do Objective-C (Saudades colchetes). Mesmo seu projeto sendo em Swift/SwftUI você conseguia integrar o CoreData, mas não é uma solução nativa para essas ferramentas.

SwiftData foi introduzido no iOS 17 e não funciona nas versões abaixo, uma pena para projeto que ainda não podem migrar a versão minima.

Models

Vamos falar sobre as Models, que basicamente seriam nossas tabelas em um banco de dados relacional.

@Model class Book {
var title: String
var pages: Double
var theme: String
}

Primeiro de tudo que precisamos adicionar a Macro @Model e não podemos trabalhar com Struct, temos que obrigatóriamente com as class. Basicamente esse é um modelo da classe Livro que será salvo no nosso banco de dados.

Atributos

Você deve estar se perguntando, mas se eu quiser adicionar um atributo unico, pode ser o title ou talvez um id. Nesse caso vamos adicionar outra macro na frente do nosso atributo.

@Model class Book {
@Attribute(.unique) var title: String
var pages: Double
var theme: String
}

Dessa forma não conseguiremos salvar dois livros com o mesmo título.

Vou deixar uma lista aqui de atributos que podemos ser usados:

/// Ensures the property's value is unique across all models of the same type.
public static var unique: Schema.Attribute.Option { get }Atributos Transitórios (transient attributes)

/// Transforms the property's value between an in-memory form and a persisted form.
public static func transformable(by transformerType: ValueTransformer.Type) -> Schema.Attribute.Option
public static func transformable(by transformerName: String) -> Schema.Attribute.Option

/// Stores the property's value as binary data adjacent to the model storage.
public static var externalStorage: Schema.Attribute.Option { get }

/// Stores the property's value in an encrypted form.
public static var allowsCloudEncryption: Schema.Attribute.Option { get }

/// Preserves the property's value in the persistent history when the context deletes the owning model.
public static var preserveValueOnDeletion: Schema.Attribute.Option { get }

/// Track changes to this property but do not persist
public static var ephemeral: Schema.Attribute.Option { get }

/// Indexes the property's value so it can appear in Spotlight search results.
public static var spotlight: Schema.Attribute.Option { get }

Atributos Transitórios (transient attributes)

São atributos que fazem parte da nossa model, mas que não queremos salvar no banco de dados por algum motivo específico.

@Model class Book {
@Attribute(.unique) var title: String
var pages: Double
var theme: String

// Will not save this two properties
@Transient var readingSpeedPerPage = 0
var fullTitle: String {
"\(title) - \(theme)"
}
}

SwiftData vai automaticamente salvar somente as Stored Properties do seu modelo, qualquer Computed Properties será tratada como transient, caso você queria que alguma Stored Property seja temporário basta colocar a macro @Transient na frente.

Relationship

@Model class Book {
@Attribute(.unique) var title: String
var pages: Double
var theme: String
// Will not save this two properties
@Transient var readingSpeedPerPage = 0
var fullTitle: String {
"\(title) - \(theme)"
}
}
@Model class Author {
@Attribute(.unique) var name: String
@Relationship(deleteRule: .cascade) var books: [Books]
}

Para criar uma relação entre duas classes precisamos criar um atributo, nesse caso eu estou dizendo que o Author conhece seus livros mas o livro não sabe quem é seu Author, e adicionar a Macro @Relationship e adicionar o tipo de regra de deleção. Segue uma lista das regras disponíveis:

case cascade
A rule that deletes any related models.

case deny
A rule that prevents the deletion of a model because it contains one or more references to other models.

case noAction
A rule that doesn't make changes to any related models.

case nullify
A rule that nullifies the related model's reference to the deleted model.

Basicamente:

  • Cascade — Se apagarmos o Author todos os livros que ele tem como referencia vão ser apagados.
  • Deny — Só vai deixar apagar o Author depois que apagarmos todos os livros
  • noAction — Apaga o Author mas deixa os livros salvos
  • nullify — Se nossa classe Book conhecesse a classe Author essa propriedade ficaria com valor nil se apagarmos o Author desses livros.

Por padrão o SwiftData a regra de deleção é nullify

Para terminar esse artigo vamos fazer nossa classe livro conhecer o autor, porque existem dois jeito de fazer isso.

Uma maneira é deixar explicito na macro de relação

@Model class Book {
@Attribute(.unique) var title: String
var pages: Double
var theme: String
var author: Author
// Will not save this two properties
@Transient var readingSpeedPerPage = 0
var fullTitle: String {
"\(title) - \(theme)"
}
}

@Model class Author {
@Attribute(.unique) var name: String
@Relationship(deleteRule: .cascade, inverse: \Book.author) var books: [Books]
}

A outra é deixar o atributo Author como Optional

@Model class Book {
@Attribute(.unique) var title: String
var pages: Double
var theme: String
var author: Author?

// Will not save this two properties
@Transient var readingSpeedPerPage = 0
var fullTitle: String {
"\(title) - \(theme)"
}
}
@Model class Author {
@Attribute(.unique) var name: String
@Relationship(deleteRule: .cascade) var books: [Books]
}

--

--

Bruno Faganello
Code With Coffee

Engenheiro de Software Mobile. Fico constantemente atualizado com relação a tecnologia para que isso possa mudar a vida das pessoas. 💻