Skip to content

Latest commit

 

History

History
261 lines (204 loc) · 9.54 KB

README.md

File metadata and controls

261 lines (204 loc) · 9.54 KB

DataThespian

SwiftPM Twitter GitHub GitHub issues GitHub Workflow Status

Codecov CodeFactor Grade codebeat badge Code Climate maintainability Code Climate technical debt Code Climate issues

Table of Contents

Introduction

DataThespian is a thread-safe SwiftData implementation that uses the power of ModelActors to provide an optimized and type-safe database interface. It offers a clean API for common database operations while maintaining concurrency safety and preventing common SwiftData pitfalls.

Key features:

  • Thread-safe database operations using ModelActor
  • Type-safe query interface with Selectors
  • SwiftUI integration via Environment
  • Support for monitoring database changes
  • Collection synchronization utilities

Requirements

Apple Platforms

  • Xcode 16.0 or later
  • Swift 6.0 or later
  • iOS 17 / watchOS 10.0 / tvOS 17 / macOS 14 or later deployment targets

Linux

  • Ubuntu 20.04 or later
  • Swift 6.0 or later

Installation

To integrate DataThespian into your app using SPM, specify it in your Package.swift file:

let package = Package(
  ...
  dependencies: [
    .package(url: "https://github.com/brightdigit/DataThespian.git", from: "1.0.0")
  ],
  targets: [
      .target(
          name: "YourApps",
          dependencies: [
            .product(name: "DataThespian", package: "DataThespian"), ...
          ]),
      ...
  ]
)

Usage

Setting up Database

When working with SwiftData, it's crucial to use a single ModelContext throughout your app. There are two ways to create your database:

Using Built-in ModelActorDatabase

// Create a database using the built-in ModelActorDatabase
let database = ModelActorDatabase(modelContainer: container)

Creating Your Own Database Type

You can also create your own database type by implementing the Database protocol:

@ModelActor
actor CustomDatabase: Database {
}

Using SharedDatabase

To avoid issues with multiple ModelContexts being created each time SwiftUI redraws views, use SharedDatabase to ensure a single shared context:

public struct SharedDatabase {
    public static let shared: SharedDatabase = .init()

    public let schemas: [any PersistentModel.Type]
    public let modelContainer: ModelContainer
    public let database: any Database

    private init(
        schemas: [any PersistentModel.Type] = .all,
        modelContainer: ModelContainer? = nil,
        database: (any Database)? = nil
    ) {
        self.schemas = schemas
        let modelContainer = modelContainer ?? .forTypes(schemas)
        self.modelContainer = modelContainer
        self.database = database ?? ModelActorDatabase(modelContainer: modelContainer)
    }
}

Then set up the database in your SwiftUI app:

var body: some Scene {
    WindowGroup {
        RootView()
    }
    .database(SharedDatabase.shared.database)
    /* If you need @Query support
    .modelContainer(SharedDatabase.shared.modelContainer)
    */
}

Access the database in your views using the environment:

@Environment(\.database) private var database

Making Queries

DataThespian provides a type-safe way to query your data:

// Fetch a single item
let item = try await database.get(for: .predicate(#Predicate<Item> { 
    $0.name == "Test" 
}))

// Fetch multiple items with sorting
let items = await database.fetch(for: .descriptor(
    predicate: #Predicate<Item> { $0.isActive },
    sortBy: [SortDescriptor(\Item.timestamp, order: .reverse)]
))

// Insert new item
let timestamp = Date()
let newItem = await database.insert { 
    Item(name: "Test", timestamp: timestamp) 
}

// Save changes
try await database.save()

// Re-query after save using a unique field
let savedItem = try await database.getOptional(for: .predicate(#Predicate<Item> { 
    $0.timestamp == timestamp 
}))

Documentation

To learn more, check out the full documentation.

License

This code is distributed under the MIT license. See the LICENSE file for more info.