🙆
SwiftDataでCRUD
SwiftDataでCRUDのコード例です。
コード例を検索しても、SwiftUI(View)の中でmodelContext
を宣言したりするのではなく、ViewModelやServiceから使いたいときの例があまりなかったので残しておきます。
あまり詳しく調べてはいないので、まずい書き方もあるかもしれません。
開発でデバッグしている限りでは動いてると思います。
参考文献
コード例
SwiftDataCrudApp.swift
import SwiftUI
import SwiftData
@main
struct SwiftDataCrudApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(Persistance.sharedModelContainer)
}
}
Todo.swift
import Foundation
import SwiftData
@Model
final class Todo {
var id: UUID
var content: String
var dateModified: Date
init() {
self.id = UUID()
self.content = ""
self.dateModified = Date()
}
}
TodoService.swift
import Foundation
import SwiftData
class Persistance {
static var sharedModelContainer: ModelContainer = {
let schema = Schema([
Todo.self,
])
let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)
do {
return try ModelContainer(for: schema, configurations: [modelConfiguration])
} catch {
fatalError("error create sharedModelContainer: \(error)")
}
}()
}
actor PersistanceActor: ModelActor {
let modelContainer: ModelContainer
let modelExecutor: any ModelExecutor
init(modelContainer: ModelContainer) {
self.modelContainer = modelContainer
let context = ModelContext(modelContainer)
modelExecutor = DefaultSerialModelExecutor(modelContext: context)
}
func save() {
do {
try modelContext.save()
}catch {
print("error save")
}
}
func insert<T:PersistentModel>(_ value:T) {
do {
modelContext.insert(value)
try modelContext.save()
}catch {
print("error insert")
}
}
func delete<T:PersistentModel>(_ value:T) {
do {
modelContext.delete(value)
try modelContext.save()
}catch {
print("error delete")
}
}
func get<T:PersistentModel>(_ descriptor:FetchDescriptor<T>)->[T]? {
var fetched:[T]?
do {
fetched = try modelContext.fetch(descriptor)
}catch {
print("error get")
}
return fetched
}
}
final class TodoService {
static let shared = TodoService()
lazy var actor = {
return PersistanceActor(modelContainer: Persistance.sharedModelContainer)
}()
func createTodo() async -> Todo {
let todo = Todo()
await actor.insert(todo)
return todo
}
func searchTodos(keyword: String) async -> [Todo] {
let predicate = #Predicate<Todo> { todo in
todo.content.contains(keyword)
}
let descriptor = FetchDescriptor(predicate: predicate)
return await actor.get(descriptor) ?? []
}
func getTodoById(id: UUID) async -> Todo? {
let predicate = #Predicate<Todo> { todo in
todo.id == id
}
let descriptor = FetchDescriptor(predicate: predicate)
let todos = await actor.get(descriptor)
guard let todos = todos,
let todo = todos.first
else {
return nil
}
return todo
}
func deleteTodo(id: UUID) async -> Bool {
guard let todo = await getTodoById(id: id) else { return false }
await actor.delete(todo)
return true
}
func updateTodo(id: UUID, content: String? = nil) async -> Todo? {
guard var todo = await getTodoById(id: id) else { return nil }
if let content = content {
todo.content = content
todo.dateModified = Date()
}
await actor.save()
return todo
}
func getAllTodos() async -> [Todo] {
let predicate = #Predicate<Todo> { todo in
return true
}
let descriptor = FetchDescriptor(predicate: predicate)
return await actor.get(descriptor) ?? []
}
}
ViewModelからの利用例
TodoViewModel.swift
extension TodoViewModel {
func search() async {
if (searchWord == "") {
displayItems = await TodoService.shared.getAllTodos()
return
}
displayItems = await TodoService.shared.searchTodos(keyword: searchWord)
}
}
Discussion