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
— 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]
}