Novidades do SwiftData na WWDC 2024

Bruno FaganelloBruno Faganello
3 min read

Não é segredo para ninguém que eu sou fã do SwiftData pelo simples fato de que sofri muito usando o legado CoreData mas como toda boa tecnologia que é lançada algumas features estavam faltando e após a WWDC de 2024 queria trazer algumas novidades que fizeram meus olhos brilharem com o SwiftData

#Unique

A nova atualização trouxe para uma macro super interessante chamada #Unique que permite a criação de uma constraint (Não sei o melhor jeito de traduzir isso rs) para múltiplos atributos. Antigamente precisávamos fazer algum workaround para termos isso.

Antigamente:

@Model final class Song {
    @Attribute(.unique)
    var nameAndSinger: String    

    var name: String
    var singer: String

    init(name: String, singer: String) {
        self.name = name
        self.singer = singer
        nameAndSinger = name + singer
    }
}

Usando o #Unique:

@Model final class Song {
    #Unique<Song>([\.name, \.singer])
    var name: String
    var singer: String

    init(name: String, singer: String) {
        self.name = name
        self.singer = singer
    }
}

Dessa forma o SwiftData nos permite criar diversos objetos com tanto que a combinação de nome e singer seja única. Podemos criar Song com name A pro Faganello e podemos criar um Song com nome A pro cantor Bruno. Uma adição que eu já foi utilizar nos meus projetos porque eu fazia a gambiarra acima para ter o mesmo resultado.

@Attribute(.preserveValueOnDeletion)

As vezes precisamos criar a famosa exclusão lógica, quem nunca fez algo parecido com isDeleted: Bool. As vezes você queria deixar parcialmente excluido por x tempos caso o usuário tenha se arrependido de apagar e com isso você de fato não apagava o registro agora é possível fazer isso nativamente com o SwiftData

@Model
final class Song {
    @Attribute(.preserveValueOnDeletion)
    var album: Album
}

if let deletion = change as? History.DefaultDeleteChange<Song> {
   data = deletion.tombstone[\.album]
}

Pra saber mais sobre esse History: Apple Doc History

Custom Schemas

Podemos agora criar Custom Schemas para nosso Container do SwiftData, isso pode ser interessante se caso você precise por exemplo salvar algo em um formato de arquivo especifico para realizar uma exportação por exemplo.

// Modo Tradicional usando o proprio Schema da Apple
var container: ModelContainer = {
        do {
            let configuration = ModelConfiguration(schema: Schema([Trip.self]), url: fileURL)
            return try ModelContainer(for: Trip.self, configurations: configuration)
        }
        catch { ... }
    }()

// Seu Scheme personalizado, nesse caso algo voltado para json
var container: ModelContainer = {
        do {
            let configuration = JSONStoreConfiguration(schema: Schema([Song.self]), url: jsonFileURL)
            return try ModelContainer(for: Song.self, configurations: configuration)
        }
        catch { ... }

@Previewable

Uma ultima coisa que gostaria de destacar aqui é que aparentemente ficará mais fácil usar o SwiftData com o preview do SwiftUI sem muitas "gambiarras" para fazerem as coisas acontecerem, podemos inserir Mock ModelContainer utilizando um parâmetro na Preview do SwiftUI e para fazer alguma busca nos modelos podemos usar o @Previewable . Acabei brincando pouco com isso até o momento até porque eu já tenho minhas gambiarras funcionando e acabei não investindo tempo nessa mudança. Segue um exemplo de como utilizar.


struct SampleData: PreviewModifier {
    static func makeSharedContext() throws -> ModelContainer {
        let config = ModelConfiguration(isStoredInMemoryOnly: true)
        let container = try ModelContainer(for: Trip.self, configurations: config)
        Trip.makeSampleTrips(in: container)
        return container
    }

    func body(content: Content, context: ModelContainer) -> some View {
        content.modelContainer(context)
    }
}

extension PreviewTrait where T == Preview.ViewTraits {
    @MainActor static var sampleData: Self = .modifier(SampleData())
}

import SwiftUI
import SwiftData

struct ContentView: View {
    @Query
    var trips: [Trip]

    var body: some View {
        ...
    }
}

#Preview(traits: .sampleData) {
    ContentView()
}

import SwiftUI
import SwiftData

#Preview(traits: .sampleData) {
    @Previewable @Query var trips: [Trip]
    BucketListItemView(trip: trips.first)
}
0
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