Getting Started with SwiftData: Data Handling Simplified

·

3 min read

SwiftData, making its debut at WWDC 2023, emerges as a new framework designed to provide a Swift-centric approach to persistence within your iOS applications. Dubbed 'Swift native,' this framework offers an intuitive API, aligning seamlessly with the principles of the Swift programming language.

How to create Models with Swift?

SwiftData introduces the Model macro. This vital component provides a declarative way to define data models, streamlining compatibility with SwiftData's persistence mechanisms.

Annotating a class with Model signals to SwiftData that it's a data model, triggering the framework to take charge of its lifecycle and persistence tasks ranging from creation and reading to updating, deleting instances, and persisting data across app launches. All this happens at compile time.

Here is a simple example:

import SwiftData

@Model
class User{
      var id: UUID()
    var name: String
    var email: String

    init(name: String, email: String) {
        self.name = name
        self.email = email
        self.subscriptions: [Subscription]? = []
    }

}

SwiftData supports primitive types (Bool, Int, String). It also supports complex types (structs, enums and all other value types that conform to the Codable protocol.

Fine Tuning The Model

If we want to add uniqueness to the model, we can annotate a property in our model with Attribute(_:originalName:hashModifier:) macro.

@Attribute(.unique) var id = UUID()

To add relationship in the model we can use Relationship(_:_:originalName:inverse:hashModifier:) macro.

@Relationship(.nullify) var subscriptions: [Subscription]? = []

Setting Up Default Storage with SwiftData

With your data model defined, the next crucial step is establishing the default storage configuration. In SwiftData, the concept of a “container” refers to the database. This is achieved through the modelContainer view modifier.

// Initialize with only a schema
let container = try ModelContainer([User.self])

// Initialize with configurations
let container = try ModelContainer(
    for: [User.self],
    configurations: ModelConfiguration(url: URL("path"))
)

The container is then integrated into a SwiftUI view as follows:

import SwiftUI

@main
struct HelloApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(
            for: [User.self]
        )
    }
}

By integrating the container within the SwiftUI view hierarchy, SwiftData seamlessly provides access to the model throughout the application.

Managing the Model

To insert, update, or delete instances of your model, you’ll use a ModelContext. It is responsible for the in-memory data and coordination with the model container to successfuly persist that data. Context is fetched through enviroment variable:

@Environment(\.modelContext) private  var modelContext

let user1 = User(name: "John Doe" , email: "profile@domain.com")

//to insert new instance
// swift data can throw errors, do-catch to handle any error
do {
    let user2 = User(name: "User2" , email: "user2@domain.com")
    try modelContext.insert(user2)
} catch {
    print("Failed to insert task: \(error)")
}

//to save all changes
modelContext.save()

//to fetch all subscriptions for a user
let subscriptions = user.subscriptions

//to delete the user
modelContext.delete(user)

//query that user whose name starts with "R"
//query is supposed to be used within view
@Query(where : \User.name.starts(with: "R"))
var filterUser:[User]

SwiftData marks a revolutionary stride in persistent storage for Swift applications. It leverages the robust underlying storage architecture of Core Data. In essence, SwiftData provides developers with a more user-friendly syntax while retaining the power of Core Data beneath the surface.

For a deeper exploration of advanced features and usage details, consult the official SwiftData documentation available on the Apple Developer website.

Reference: developer.apple.com/documentation/swiftdata