-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #75 from boostcampwm-2024/feature/build-core-data
CoreData 구축 및 테스트
- Loading branch information
Showing
13 changed files
with
546 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,21 @@ | ||
import Foundation | ||
|
||
public enum MHError: Error, CustomStringConvertible { | ||
public enum MHError: Error, CustomStringConvertible, Equatable { | ||
case DIContainerResolveFailure(key: String) | ||
case convertDTOFailure | ||
case findEntityFailure | ||
case saveContextFailure | ||
|
||
public var description: String { | ||
switch self { | ||
case .DIContainerResolveFailure(let key): | ||
"\(key)에 대한 dependency resolve 실패" | ||
case .convertDTOFailure: | ||
"Entity에 대한 DTO 변환 실패" | ||
case .findEntityFailure: | ||
"Entity 찾기 실패" | ||
case .saveContextFailure: | ||
"Update된 Context 저장 실패" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import MHFoundation | ||
import MHDomain | ||
|
||
public struct BookCoverDTO { | ||
let identifier: UUID | ||
let title: String | ||
let imageURL: String? | ||
let color: String | ||
let category: String? | ||
let favorite: Bool | ||
|
||
func toBookCover() -> BookCover? { | ||
guard let color = BookColor(rawValue: self.color) else { return nil } | ||
|
||
return BookCover( | ||
identifier: self.identifier, | ||
title: self.title, | ||
imageURL: self.imageURL, | ||
color: color, | ||
category: self.category, | ||
favorite: self.favorite | ||
) | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
MemorialHouse/MHData/MHData/LocalStorage/BookCoverStorage.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import MHFoundation | ||
import MHCore | ||
|
||
public protocol BookCoverStorage { | ||
func create(data: BookCoverDTO) async -> Result<Void, MHError> | ||
func fetch() async -> Result<[BookCoverDTO], MHError> | ||
func update(with id: UUID, data: BookCoverDTO) async -> Result<Void, MHError> | ||
func delete(with id: UUID) async -> Result<Void, MHError> | ||
} |
105 changes: 105 additions & 0 deletions
105
MemorialHouse/MHData/MHData/LocalStorage/CoreData/CoreDataBookCoverStorage.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
import MHFoundation | ||
import MHCore | ||
import CoreData | ||
|
||
final class CoreDataBookCoverStorage { | ||
private let coreDataStorage: CoreDataStorage | ||
|
||
init(coreDataStorage: CoreDataStorage) { | ||
self.coreDataStorage = coreDataStorage | ||
} | ||
} | ||
|
||
extension CoreDataBookCoverStorage: BookCoverStorage { | ||
func create(data: BookCoverDTO) async -> Result<Void, MHError> { | ||
let context = coreDataStorage.persistentContainer.viewContext | ||
guard let entity = NSEntityDescription.entity(forEntityName: "BookCoverEntity", in: context) else { | ||
return .failure(.DIContainerResolveFailure(key: "BookCoverEntity")) | ||
} | ||
let bookCover = NSManagedObject(entity: entity, insertInto: context) | ||
bookCover.setValue(data.identifier, forKey: "identifier") | ||
bookCover.setValue(data.title, forKey: "title") | ||
bookCover.setValue(data.category, forKey: "category") | ||
bookCover.setValue(data.color, forKey: "color") | ||
bookCover.setValue(data.imageURL, forKey: "imageURL") | ||
bookCover.setValue(data.favorite, forKey: "favorite") | ||
|
||
await coreDataStorage.saveContext() | ||
return .success(()) | ||
} | ||
|
||
func fetch() async -> Result<[BookCoverDTO], MHError> { | ||
let context = coreDataStorage.persistentContainer.viewContext | ||
let request = BookCoverEntity.fetchRequest() | ||
|
||
do { | ||
let bookCoverEntities = try context.fetch(request) | ||
let result = bookCoverEntities.compactMap { $0.toBookCoverDTO() } | ||
|
||
return .success(result) | ||
} catch { | ||
MHLogger.debug("Error fetching book covers: \(error.localizedDescription)") | ||
return .failure(.convertDTOFailure) | ||
} | ||
} | ||
|
||
func update(with id: UUID, data: BookCoverDTO) async -> Result<Void, MHError> { | ||
do { | ||
let context = coreDataStorage.persistentContainer.viewContext | ||
guard let newEntity = try getEntityByIdentifier(in: context, with: id) else { | ||
return .failure(.findEntityFailure) | ||
} | ||
newEntity.setValue(data.identifier, forKey: "identifier") | ||
newEntity.setValue(data.title, forKey: "title") | ||
newEntity.setValue(data.category, forKey: "category") | ||
newEntity.setValue(data.color, forKey: "color") | ||
newEntity.setValue(data.imageURL, forKey: "imageURL") | ||
newEntity.setValue(data.favorite, forKey: "favorite") | ||
|
||
await coreDataStorage.saveContext() | ||
return .success(()) | ||
} catch { | ||
return .failure(.findEntityFailure) | ||
} | ||
} | ||
|
||
func delete(with id: UUID) async -> Result<Void, MHError> { | ||
do { | ||
let context = coreDataStorage.persistentContainer.viewContext | ||
guard let entity = try getEntityByIdentifier(in: context, with: id) else { | ||
return .failure(.findEntityFailure) | ||
} | ||
|
||
context.delete(entity) | ||
|
||
await coreDataStorage.saveContext() | ||
return .success(()) | ||
} catch { | ||
return .failure(.findEntityFailure) | ||
} | ||
} | ||
|
||
private func getEntityByIdentifier(in context: NSManagedObjectContext, with id: UUID) throws -> BookCoverEntity? { | ||
let request = BookCoverEntity.fetchRequest() | ||
|
||
return try context.fetch(request).first(where: { $0.identifier == id }) | ||
} | ||
} | ||
|
||
// MARK: - BookCoverEntity Extension | ||
extension BookCoverEntity { | ||
func toBookCoverDTO() -> BookCoverDTO? { | ||
guard let identifier = self.identifier, | ||
let title = self.title, | ||
let color = self.color else { return nil } | ||
|
||
return BookCoverDTO( | ||
identifier: identifier, | ||
title: title, | ||
imageURL: self.imageURL, | ||
color: color, | ||
category: self.category, | ||
favorite: self.favorite | ||
) | ||
} | ||
} |
Oops, something went wrong.