Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose model container, refactor throwing functions. #7

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 29 additions & 13 deletions SwiftDataTCA/Service/Database.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,37 +16,53 @@ extension DependencyValues {
}
}

fileprivate let appContext: ModelContext = {
fileprivate let liveContext: ModelContext = {
ModelContext(liveContainer)
}()

fileprivate let previewContext: ModelContext = {
ModelContext(previewContainer)
}()

fileprivate let liveContainer: ModelContainer = {
do {

let url = URL.applicationSupportDirectory.appending(path: "Model.sqlite")
let config = ModelConfiguration(url: url)

let container = try ModelContainer(for: Movie.self, migrationPlan: MovieMigrationPlan.self, configurations: config)
return ModelContext(container)
return try ModelContainer(for: Movie.self, migrationPlan: MovieMigrationPlan.self, configurations: config)
} catch {
fatalError("Failed to create container.")
fatalError("Failed to create live container.")
}
}()

fileprivate let previewContainer: ModelContainer = {
do {
let config = ModelConfiguration(isStoredInMemoryOnly: true)
return try ModelContainer(for: Movie.self, migrationPlan: MovieMigrationPlan.self, configurations: config)
} catch {
fatalError("Failed to create preview container.")
}
}()

struct Database {
var context: () throws -> ModelContext
var context: @Sendable () -> ModelContext
var container: @Sendable () -> ModelContainer
}

extension Database: DependencyKey {
public static let liveValue = Self(
context: { appContext }
context: { liveContext },
container: { liveContainer }
)
}

extension Database: TestDependencyKey {
public static var previewValue = Self.noop
public static var previewValue = Self.inMemory

public static let testValue = Self(
context: unimplemented("\(Self.self).context")
)
public static let testValue = Self.inMemory

static let noop = Self(
context: unimplemented("\(Self.self).context")
private static let inMemory = Self(
context: { previewContext },
container: { previewContainer }
)
}
37 changes: 11 additions & 26 deletions SwiftDataTCA/Service/MovieDatabase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ extension DependencyValues {
struct MovieDatabase {
var fetchAll: @Sendable () throws -> [Movie]
var fetch: @Sendable (FetchDescriptor<Movie>) throws -> [Movie]
var add: @Sendable (Movie) throws -> Void
var delete: @Sendable (Movie) throws -> Void
var add: @Sendable (Movie) -> Void
var delete: @Sendable (Movie) -> Void

enum MovieError: Error {
case add
Expand All @@ -33,7 +33,7 @@ extension MovieDatabase: DependencyKey {
fetchAll: {
do {
@Dependency(\.databaseService.context) var context
let movieContext = try context()
let movieContext = context()

let descriptor = FetchDescriptor<Movie>(sortBy: [SortDescriptor(\.title)])
return try movieContext.fetch(descriptor)
Expand All @@ -44,38 +44,29 @@ extension MovieDatabase: DependencyKey {
fetch: { descriptor in
do {
@Dependency(\.databaseService.context) var context
let movieContext = try context()
let movieContext = context()
return try movieContext.fetch(descriptor)
} catch {
return []
}
},
add: { model in
do {
@Dependency(\.databaseService.context) var context
let movieContext = try context()
@Dependency(\.databaseService.context) var context
let movieContext = context()

movieContext.insert(model)
} catch {
throw MovieError.add
}
movieContext.insert(model)
},
delete: { model in
do {
@Dependency(\.databaseService.context) var context
let movieContext = try context()
@Dependency(\.databaseService.context) var context
let movieContext = context()

let modelToBeDelete = model
movieContext.delete(modelToBeDelete)
} catch {
throw MovieError.delete
}
let modelToBeDelete = model
movieContext.delete(modelToBeDelete)
}
)
}

extension MovieDatabase: TestDependencyKey {
public static var previewValue = Self.noop

public static let testValue = Self(
fetchAll: unimplemented("\(Self.self).fetch"),
Expand All @@ -84,10 +75,4 @@ extension MovieDatabase: TestDependencyKey {
delete: unimplemented("\(Self.self).delete")
)

static let noop = Self(
fetchAll: { [] },
fetch: { _ in [] },
add: { _ in },
delete: { _ in }
)
}
8 changes: 1 addition & 7 deletions SwiftDataTCA/SwiftDataTCAApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,6 @@ import Dependencies
@main
struct SwiftDataTCAApp: App {
@Dependency(\.databaseService) var databaseService
var modelContext: ModelContext {
guard let modelContext = try? self.databaseService.context() else {
fatalError("Could not find modelcontext")
}
return modelContext
}

var body: some Scene {
WindowGroup {
Expand All @@ -33,7 +27,7 @@ struct SwiftDataTCAApp: App {
QueryView(store: .init(initialState: .init(), reducer: {
QueryReducer()._printChanges()
}))
.modelContext(self.modelContext)
.modelContainer(databaseService.container())
.tabItem {
Label("QueryView", systemImage: "2.circle")
}
Expand Down
23 changes: 10 additions & 13 deletions SwiftDataTCA/View/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -190,19 +190,13 @@ extension TCAContentView {
state.refetchMovies()
return .none
case .add:
do {
let randomMovieName = ["Star Wars", "Harry Potter", "Hunger Games", "Lord of the Rings"].randomElement()!
try context.add(.init(title: randomMovieName, cast: ["Sam Worthington", "Zoe Saldaña", "Stephen Lang", "Michelle Rodriguez"]))
} catch { }
let randomMovieName = ["Star Wars", "Harry Potter", "Hunger Games", "Lord of the Rings"].randomElement()!
context.add(.init(title: randomMovieName, cast: ["Sam Worthington", "Zoe Saldaña", "Stephen Lang", "Michelle Rodriguez"]))
return .run { @MainActor send in
send(.onAppear, animation: .default)
}
case .delete(let movie):
do {
try context.delete(movie)
} catch {

}
context.delete(movie)

return .run { @MainActor send in
send(.onAppear, animation: .default)
Expand Down Expand Up @@ -241,8 +235,11 @@ extension TCAContentView {
}

#Preview {
TCAContentView(
store: .init(initialState: .init(),
reducer: TCAContentView.Feature.init)
)
@Dependency(\.databaseService) var databaseService
let container = databaseService.container()

return TCAContentView(store: Store(initialState: TCAContentView.Feature.State(), reducer: {
TCAContentView.Feature()
}))
.modelContainer(container)
}
30 changes: 8 additions & 22 deletions SwiftDataTCA/View/QueryView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,7 @@ struct QueryReducer: Reducer {
case favorite(Movie)
}

@Dependency(\.swiftData) var context
@Dependency(\.databaseService) var databaseService
@Dependency(\.swiftData) var database

var body: some ReducerOf<Self> {
Reduce { state, action in
Expand All @@ -74,28 +73,15 @@ struct QueryReducer: Reducer {
state.movies = newMovies
return .none
case .addMovie:
do {
let randomMovieName = ["Star Wars", "Harry Potter", "Hunger Games", "Lord of the Rings"].randomElement()!
try context.add(.init(title: randomMovieName, cast: ["Sam Worthington", "Zoe Saldaña", "Stephen Lang", "Michelle Rodriguez"]))
} catch { }
return .none
let randomMovieName = ["Star Wars", "Harry Potter", "Hunger Games", "Lord of the Rings"].randomElement()!
database.add(.init(title: randomMovieName, cast: ["Sam Worthington", "Zoe Saldaña", "Stephen Lang", "Michelle Rodriguez"]))
return .none
case .delete(let movieToDelete):
do {
try context.delete(movieToDelete)
} catch { }
return .none
database.delete(movieToDelete)
return .none
case .favorite(let movieToFavorite):
movieToFavorite.favorite.toggle()
guard let context = try? self.databaseService.context() else {
print("Failed to find context")
return .none
}
do {
try context.save()
} catch {
print("Failed to save")
}
return .none
movieToFavorite.favorite.toggle()
return .none
}
}
}
Expand Down