Guia sobre SwiftData - Models

Bruno FaganelloBruno Faganello
4 min read

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 atríbuto.

@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 atríbutos 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 }

/// 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 exclusã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]
}
1
Subscribe to my newsletter

Read articles from Bruno Faganello directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Bruno Faganello
Bruno Faganello