Skip to content

Commit

Permalink
improve-api
Browse files Browse the repository at this point in the history
  • Loading branch information
leogdion committed Oct 11, 2024
1 parent 18d86a6 commit 7fdf52a
Show file tree
Hide file tree
Showing 16 changed files with 195 additions and 205 deletions.
1 change: 0 additions & 1 deletion Sources/DataThespian/BackgroundDatabase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
#if canImport(SwiftData)
import Foundation
public import SwiftData
import SwiftUI

public final class BackgroundDatabase: Database {
private actor DatabaseContainer {
Expand Down
2 changes: 0 additions & 2 deletions Sources/DataThespian/DataMonitor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,7 @@
#if canImport(Combine) && canImport(SwiftData) && canImport(CoreData)

import Combine

import CoreData

import Foundation
import SwiftData

Expand Down
186 changes: 94 additions & 92 deletions Sources/DataThespian/Database+Extras.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,117 +27,119 @@
// OTHER DEALINGS IN THE SOFTWARE.
//

public import Foundation
public import SwiftData

extension Database {
public func insert<PersistentModelType: PersistentModel>(
_ closuer: @Sendable @escaping () -> PersistentModelType
) async -> Model<PersistentModelType> {
let id: PersistentIdentifier = await self.insert(closuer)
return .init(persistentIdentifier: id)
}
#if canImport(SwiftData)
public import Foundation
public import SwiftData

extension Database {
public func insert<PersistentModelType: PersistentModel>(
_ closuer: @Sendable @escaping () -> PersistentModelType
) async -> Model<PersistentModelType> {
let id: PersistentIdentifier = await self.insert(closuer)
return .init(persistentIdentifier: id)
}

public func with<PersistentModelType: PersistentModel, U: Sendable>(
_ id: Model<PersistentModelType>,
_ closure: @escaping @Sendable (PersistentModelType) throws -> U
) async rethrows -> U {
try await self.get(for: id.persistentIdentifier) { (model: PersistentModelType?) -> U in
guard let model else {
throw Model<PersistentModelType>.NotFoundError(
persistentIdentifier: id.persistentIdentifier
)
public func with<PersistentModelType: PersistentModel, U: Sendable>(
_ id: Model<PersistentModelType>,
_ closure: @escaping @Sendable (PersistentModelType) throws -> U
) async rethrows -> U {
try await self.get(for: id.persistentIdentifier) { (model: PersistentModelType?) -> U in
guard let model else {
throw Model<PersistentModelType>.NotFoundError(
persistentIdentifier: id.persistentIdentifier
)
}
return try closure(model)
}
return try closure(model)
}
}

public func first<T: PersistentModel>(_ selectPredicate: Predicate<T>) async throws -> Model<T>? {
try await self.first(selectPredicate, with: Model.ifMap)
}

public func first<T: PersistentModel, U: Sendable>(
_ selectPredicate: Predicate<T>, with closure: @escaping @Sendable (T?) throws -> U
) async throws -> U {
try await self.fetch {
.init(predicate: selectPredicate, fetchLimit: 1)
} with: { models in
try closure(models.first)
public func first<T: PersistentModel>(_ selectPredicate: Predicate<T>) async throws -> Model<T>? {
try await self.first(selectPredicate, with: Model.ifMap)
}
}

public func first<T: PersistentModel, U: Sendable>(
fetchWith selectPredicate: Predicate<T>,
otherwiseInsertBy insert: @Sendable @escaping () -> T,
with closure: @escaping @Sendable (T) throws -> U
) async throws -> U {
let value = try await self.fetch {
.init(predicate: selectPredicate, fetchLimit: 1)
} with: { models in
try models.first.map(closure)
}

if let value {
return value
public func first<T: PersistentModel, U: Sendable>(
_ selectPredicate: Predicate<T>, with closure: @escaping @Sendable (T?) throws -> U
) async throws -> U {
try await self.fetch {
.init(predicate: selectPredicate, fetchLimit: 1)
} with: { models in
try closure(models.first)
}
}

let inserted: Model = await self.insert(insert)
public func first<T: PersistentModel, U: Sendable>(
fetchWith selectPredicate: Predicate<T>,
otherwiseInsertBy insert: @Sendable @escaping () -> T,
with closure: @escaping @Sendable (T) throws -> U
) async throws -> U {
let value = try await self.fetch {
.init(predicate: selectPredicate, fetchLimit: 1)
} with: { models in
try models.first.map(closure)
}

return try await self.with(inserted, closure)
}
if let value {
return value
}

public func delete<T: PersistentModel>(model _: T.Type, where predicate: Predicate<T>? = nil)
async throws
{ try await self.delete(where: predicate) }
let inserted: Model = await self.insert(insert)

public func delete<T: PersistentModel>(_ model: Model<T>) async {
await self.delete(T.self, withID: model.persistentIdentifier)
}
return try await self.with(inserted, closure)
}

public func deleteAll(of types: [any PersistentModel.Type]) async throws {
try await self.transaction { context in for type in types { try context.delete(model: type) } }
}
public func delete<T: PersistentModel>(model _: T.Type, where predicate: Predicate<T>? = nil)
async throws
{ try await self.delete(where: predicate) }

public func fetch<T: PersistentModel, U: Sendable>(
_: T.Type, with closure: @escaping @Sendable ([T]) throws -> U
) async throws -> U {
try await self.fetch {
FetchDescriptor<T>()
} with: { models in
try closure(models)
public func delete<T: PersistentModel>(_ model: Model<T>) async {
await self.delete(T.self, withID: model.persistentIdentifier)
}
}

public func fetch<T: PersistentModel>(_: T.Type) async throws -> [Model<T>] {
try await self.fetch(T.self) { models in models.map(Model.init) }
}
public func fetch<T: PersistentModel>(
_: T.Type, _ selectDescriptor: @escaping @Sendable () -> FetchDescriptor<T>
) async throws -> [Model<T>] {
await self.fetch(selectDescriptor) { models in models.map(Model.init) }
}
public func deleteAll(of types: [any PersistentModel.Type]) async throws {
try await self.transaction { context in for type in types { try context.delete(model: type) } }
}

public func fetch<T, U: Sendable>(
of _: T.Type,
for objectIDs: [PersistentIdentifier],
with closure: @escaping @Sendable (T) throws -> U
) async throws -> [U] where T: PersistentModel {
try await withThrowingTaskGroup(of: U?.self, returning: [U].self) { group in
for id in objectIDs {
group.addTask { try await self.get(for: id) { model in try model.map(closure) } }
public func fetch<T: PersistentModel, U: Sendable>(
_: T.Type, with closure: @escaping @Sendable ([T]) throws -> U
) async throws -> U {
try await self.fetch {
FetchDescriptor<T>()
} with: { models in
try closure(models)
}
}

public func fetch<T: PersistentModel>(_: T.Type) async throws -> [Model<T>] {
try await self.fetch(T.self) { models in models.map(Model.init) }
}
public func fetch<T: PersistentModel>(
_: T.Type, _ selectDescriptor: @escaping @Sendable () -> FetchDescriptor<T>
) async throws -> [Model<T>] {
await self.fetch(selectDescriptor) { models in models.map(Model.init) }
}

return try await group.reduce(into: []) { partialResult, item in
if let item { partialResult.append(item) }
public func fetch<T, U: Sendable>(
of _: T.Type,
for objectIDs: [PersistentIdentifier],
with closure: @escaping @Sendable (T) throws -> U
) async throws -> [U] where T: PersistentModel {
try await withThrowingTaskGroup(of: U?.self, returning: [U].self) { group in
for id in objectIDs {
group.addTask { try await self.get(for: id) { model in try model.map(closure) } }
}

return try await group.reduce(into: []) { partialResult, item in
if let item { partialResult.append(item) }
}
}
}
}

public func get<T, U: Sendable>(
of _: T.Type,
for objectID: PersistentIdentifier,
with closure: @escaping @Sendable (T?) throws -> U
) async throws -> U where T: PersistentModel {
try await self.get(for: objectID) { model in try closure(model) }
public func get<T, U: Sendable>(
of _: T.Type,
for objectID: PersistentIdentifier,
with closure: @escaping @Sendable (T?) throws -> U
) async throws -> U where T: PersistentModel {
try await self.get(for: objectID) { model in try closure(model) }
}
}
}
#endif
3 changes: 0 additions & 3 deletions Sources/DataThespian/DatabaseKey.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,8 @@
//

#if canImport(SwiftUI)

import Foundation

import SwiftData

public import SwiftUI

private struct DefaultDatabase: Database {
Expand Down
1 change: 0 additions & 1 deletion Sources/DataThespian/FetchDescriptor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@

#if canImport(SwiftData)
public import Foundation

public import SwiftData

extension FetchDescriptor {
Expand Down
1 change: 0 additions & 1 deletion Sources/DataThespian/ManagedObjectMetadata.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
//

#if canImport(SwiftData)

public import SwiftData

public struct ManagedObjectMetadata: Sendable, Hashable {
Expand Down
2 changes: 0 additions & 2 deletions Sources/DataThespian/Model.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@
//

#if canImport(SwiftData)

import Foundation

public import SwiftData

@available(*, deprecated, renamed: "Model")
Expand Down
22 changes: 12 additions & 10 deletions Sources/DataThespian/ModelActor+Database.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,18 @@
// OTHER DEALINGS IN THE SOFTWARE.
//

public import SwiftData
#if canImport(SwiftData)
public import SwiftData

extension ModelActor where Self: Database {
public static var assertIsBackground: Bool { false }
extension ModelActor where Self: Database {
public static var assertIsBackground: Bool { false }

public func withModelContext<T: Sendable>(
_ closure: @Sendable @escaping (ModelContext) throws -> T
) async rethrows -> T {
assert(isMainThread: true, if: Self.assertIsBackground)
let modelContext = self.modelContext
return try closure(modelContext)
public func withModelContext<T: Sendable>(
_ closure: @Sendable @escaping (ModelContext) throws -> T
) async rethrows -> T {
assert(isMainThread: true, if: Self.assertIsBackground)
let modelContext = self.modelContext
return try closure(modelContext)
}
}
}
#endif
84 changes: 43 additions & 41 deletions Sources/DataThespian/ModelActorDatabase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,54 +27,56 @@
// OTHER DEALINGS IN THE SOFTWARE.
//

public import SwiftData
#if canImport(SwiftData)
public import SwiftData

// @ModelActor
// public actor ModelActorDatabase: Database {}
// @ModelActor
// public actor ModelActorDatabase: Database {}

public actor ModelActorDatabase: Database, ModelActor {
public nonisolated let modelExecutor: any SwiftData.ModelExecutor
public nonisolated let modelContainer: SwiftData.ModelContainer
public actor ModelActorDatabase: Database, ModelActor {
public nonisolated let modelExecutor: any SwiftData.ModelExecutor
public nonisolated let modelContainer: SwiftData.ModelContainer

private init(modelExecutor: any ModelExecutor, modelContainer: ModelContainer) {
self.modelExecutor = modelExecutor
self.modelContainer = modelContainer
}
private init(modelExecutor: any ModelExecutor, modelContainer: ModelContainer) {
self.modelExecutor = modelExecutor
self.modelContainer = modelContainer
}

public init(modelContainer: SwiftData.ModelContainer) {
self.init(
modelContainer: modelContainer,
modelContext: ModelContext.init
)
}
public init(modelContainer: SwiftData.ModelContainer) {
self.init(
modelContainer: modelContainer,
modelContext: ModelContext.init
)
}

public init(
modelContainer: SwiftData.ModelContainer,
modelContext closure: @Sendable @escaping (ModelContainer) -> ModelContext
) {
self.init(
modelContainer: modelContainer,
modelExecutor: DefaultSerialModelExecutor.create(from: closure)
)
}
public init(
modelContainer: SwiftData.ModelContainer,
modelContext closure: @Sendable @escaping (ModelContainer) -> ModelContext
) {
self.init(
modelContainer: modelContainer,
modelExecutor: DefaultSerialModelExecutor.create(from: closure)
)
}

public init(
modelContainer: SwiftData.ModelContainer,
modelExecutor closure: @Sendable @escaping (ModelContainer) -> any ModelExecutor
) {
self.init(
modelExecutor: closure(modelContainer),
modelContainer: modelContainer
)
public init(
modelContainer: SwiftData.ModelContainer,
modelExecutor closure: @Sendable @escaping (ModelContainer) -> any ModelExecutor
) {
self.init(
modelExecutor: closure(modelContainer),
modelContainer: modelContainer
)
}
}
}

extension DefaultSerialModelExecutor {
fileprivate static func create(
from closure: @Sendable @escaping (ModelContainer) -> ModelContext
) -> @Sendable (ModelContainer) -> any ModelExecutor {
{
DefaultSerialModelExecutor(modelContext: closure($0))
extension DefaultSerialModelExecutor {
fileprivate static func create(
from closure: @Sendable @escaping (ModelContainer) -> ModelContext
) -> @Sendable (ModelContainer) -> any ModelExecutor {
{
DefaultSerialModelExecutor(modelContext: closure($0))
}
}
}
}
#endif
Loading

0 comments on commit 7fdf52a

Please sign in to comment.