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

CoreData에서 Page 데이터 읽어오기 및 보여주기 (멀티미디어 포함 X) #101

Merged
merged 14 commits into from
Nov 30, 2024
Merged
Show file tree
Hide file tree
Changes from 13 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
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@ final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
BookCategoryStorage.self,
object: CoreDataBookCategoryStorage(coreDataStorage: coreDataStorage)
)
DIContainer.shared.register(
BookCoverStorage.self,
object: CoreDataBookCoverStorage(coreDataStorage: coreDataStorage)
)
DIContainer.shared.register(
BookStorage.self,
object: CoreDataBookStorage(coreDataStorage: coreDataStorage)
)
}

private func registerRepositoryDependency() throws {
Expand All @@ -67,6 +75,16 @@ final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
BookCategoryRepository.self,
object: LocalBookCategoryRepository(storage: bookCategoryStorage)
)
let bookCoverStorage = try DIContainer.shared.resolve(BookCoverStorage.self)
DIContainer.shared.register(
BookCoverRepository.self,
object: LocalBookCoverRepository(storage: bookCoverStorage)
)
let bookStorage = try DIContainer.shared.resolve(BookStorage.self)
DIContainer.shared.register(
BookRepository.self,
object: LocalBookRepository(storage: bookStorage)
)
}

private func registerUseCaseDependency() throws {
Expand Down Expand Up @@ -95,6 +113,25 @@ final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
DeleteBookCategoryUseCase.self,
object: DefaultDeleteBookCategoryUseCase(repository: bookCategoryRepository)
)

// MARK: - Book UseCase
let bookRepository = try DIContainer.shared.resolve(BookRepository.self)
DIContainer.shared.register(
CreateBookUseCase.self,
object: DefaultCreateBookUseCase(repository: bookRepository)
)
DIContainer.shared.register(
FetchBookUseCase.self,
object: DefaultFetchBookUseCase(repository: bookRepository)
)
DIContainer.shared.register(
UpdateBookUseCase.self,
object: DefaultUpdateBookUseCase(repository: bookRepository)
)
DIContainer.shared.register(
DeleteBookUseCase.self,
object: DefaultDeleteBookUseCase(repository: bookRepository)
)
}

private func registerViewModelFactoryDependency() throws {
Expand Down Expand Up @@ -123,5 +160,18 @@ final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
deleteBookCategoryUseCase: deleteBookCategoryUseCase
)
)

// MARK: - Book ViewModel
let fetchBookUseCase = try DIContainer.shared.resolve(FetchBookUseCase.self)
DIContainer.shared.register(
BookViewModelFactory.self,
object: BookViewModelFactory(fetchBookUseCase: fetchBookUseCase)
)

// MARK: - Page ViewModel
DIContainer.shared.register(
ReadPageViewModelFactory.self,
object: ReadPageViewModelFactory()
)
}
}
15 changes: 11 additions & 4 deletions MemorialHouse/MHData/MHData/DTO/BookCoverDTO.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,22 @@ import MHFoundation
import MHDomain

public struct BookCoverDTO {
let identifier: UUID
let id: UUID
Comment on lines 4 to +5
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Identifiable을 안쓰는데 id로 바꾸신 이유가 있나용 ??

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

다른 곳 들과의 통일성을 위해서 바꿨습니다 ! Entity와 DTO, CoreDataModel 각각의 attributes 이름이 같아야 헷갈리지 않을 것이라고 생각하여 바꿔줬습니다!

let title: String
let imageURL: String?
let color: String
let category: String?
let favorite: Bool

public init(identifier: UUID, title: String, imageURL: String?, color: String, category: String?, favorite: Bool) {
self.identifier = identifier
public init(
id: UUID,
title: String,
imageURL: String?,
color: String,
category: String?,
favorite: Bool
) {
self.id = id
self.title = title
self.imageURL = imageURL
self.color = color
Expand All @@ -22,7 +29,7 @@ public struct BookCoverDTO {
guard let color = BookColor(rawValue: self.color) else { return nil }

return BookCover(
identifier: self.identifier,
id: self.id,
title: self.title,
imageURL: self.imageURL,
color: color,
Expand Down
9 changes: 8 additions & 1 deletion MemorialHouse/MHData/MHData/DTO/BookDTO.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,23 @@ import MHDomain

public struct BookDTO {
let id: UUID
let title: String
let pages: [PageDTO]

public init(id: UUID, pages: [PageDTO]) {
public init(
id: UUID,
title: String,
pages: [PageDTO]
) {
self.id = id
self.title = title
self.pages = pages
}

func convertToBook() -> Book {
return Book(
id: self.id,
title: self.title,
pages: self.pages.map { $0.convertToPage() }
)
}
Expand Down
6 changes: 5 additions & 1 deletion MemorialHouse/MHData/MHData/DTO/MediaDescriptionDTO.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@
let type: String
let attributes: Data?

public init(id: UUID, type: String, attributes: Data?) {
public init(
id: UUID,
type: String,
attributes: Data?
) {
self.id = id
self.type = type
self.attributes = attributes
Expand All @@ -14,7 +18,7 @@

func convertToMediaDescription() -> MediaDescription? {
guard let type = MediaType(rawValue: self.type) else { return nil }
let attributes = try? JSONSerialization.jsonObject(with: attributes ?? Data(), options: []) as? [String: any Sendable]

Check warning on line 21 in MemorialHouse/MHData/MHData/DTO/MediaDescriptionDTO.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Line Length Violation: Line should be 120 characters or less; currently it has 126 characters (line_length)

Check warning on line 21 in MemorialHouse/MHData/MHData/DTO/MediaDescriptionDTO.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Line Length Violation: Line should be 120 characters or less; currently it has 126 characters (line_length)

return MediaDescription(
id: self.id,
Expand Down
6 changes: 5 additions & 1 deletion MemorialHouse/MHData/MHData/DTO/PageDTO.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ public struct PageDTO {
let metadata: [Int: MediaDescriptionDTO]
let text: String

public init(id: UUID, metadata: [Int: MediaDescriptionDTO], text: String) {
public init(
id: UUID,
metadata: [Int: MediaDescriptionDTO],
text: String
) {
self.id = id
self.metadata = metadata
self.text = text
Expand Down
2 changes: 1 addition & 1 deletion MemorialHouse/MHData/MHData/LocalStorage/BookStorage.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import MHFoundation
import MHCore

public protocol BookStorage {
public protocol BookStorage: Sendable {
func create(data: BookDTO) async -> Result<Void, MHDataError>
func fetch(with id: UUID) async -> Result<BookDTO, MHDataError>
func update(with id: UUID, data: BookDTO) async -> Result<Void, MHDataError>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,24 @@ import MHFoundation
import MHCore
import CoreData

final class CoreDataBookCoverStorage {
public final class CoreDataBookCoverStorage {
private let coreDataStorage: CoreDataStorage

init(coreDataStorage: CoreDataStorage) {
public init(coreDataStorage: CoreDataStorage) {
self.coreDataStorage = coreDataStorage
}
}

extension CoreDataBookCoverStorage: BookCoverStorage {
func create(data: BookCoverDTO) async -> Result<Void, MHDataError> {
public func create(data: BookCoverDTO) async -> Result<Void, MHDataError> {
let context = coreDataStorage.persistentContainer.viewContext
do {
try await context.perform {
guard let entity = NSEntityDescription.entity(forEntityName: "BookCoverEntity", in: context) else {
throw MHDataError.noSuchEntity(key: "BookCoverEntity")
}
let bookCover = NSManagedObject(entity: entity, insertInto: context)
bookCover.setValue(data.identifier, forKey: "identifier")
bookCover.setValue(data.id, forKey: "id")
bookCover.setValue(data.title, forKey: "title")
bookCover.setValue(data.category, forKey: "category")
bookCover.setValue(data.color, forKey: "color")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

코어데이터 CRUD 에 대한 메소드가 do-catch부터 entity 꺼내는 로직 등등 겹치는게 많아서
perform 처리하는 메소드를 지난 PR에서 만들어뒀습니다 !
이걸로 수정하는 건 어떨까요 ??

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

모두 수정했습니다 .ᐟ.ᐟ
추가로 perform 메소드는 CoreDataStorage 객체를 가지고 있는 모든 곳에서 공통적으로 사용되는 메서드라 판단하여 CoreDataStorage의 internal 메서드로 빼주어 추가적인 보일러 플레이트 코드를 방지하도록 코드 수정했습니당 !

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

역시.. 말 안 해도 알아서 해주는 빛영현

Expand All @@ -38,7 +38,7 @@ extension CoreDataBookCoverStorage: BookCoverStorage {
}
}

func fetch() async -> Result<[BookCoverDTO], MHDataError> {
public func fetch() async -> Result<[BookCoverDTO], MHDataError> {
let context = coreDataStorage.persistentContainer.viewContext
do {
var bookCoverEntities: [BookCoverEntity] = []
Expand All @@ -57,14 +57,14 @@ extension CoreDataBookCoverStorage: BookCoverStorage {
}
}

func update(with id: UUID, data: BookCoverDTO) async -> Result<Void, MHDataError> {
public func update(with id: UUID, data: BookCoverDTO) async -> Result<Void, MHDataError> {
let context = coreDataStorage.persistentContainer.viewContext
do {
try await context.perform { [weak self] in
guard let newEntity = try self?.getEntityByIdentifier(in: context, with: id) else {
throw MHDataError.findEntityFailure
}
newEntity.setValue(data.identifier, forKey: "identifier")
newEntity.setValue(data.id, forKey: "id")
newEntity.setValue(data.title, forKey: "title")
newEntity.setValue(data.category, forKey: "category")
newEntity.setValue(data.color, forKey: "color")
Expand All @@ -83,7 +83,7 @@ extension CoreDataBookCoverStorage: BookCoverStorage {
}
}

func delete(with id: UUID) async -> Result<Void, MHDataError> {
public func delete(with id: UUID) async -> Result<Void, MHDataError> {
let context = coreDataStorage.persistentContainer.viewContext
do {
try await context.perform { [weak self] in
Expand All @@ -108,20 +108,20 @@ extension CoreDataBookCoverStorage: BookCoverStorage {
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 })
return try context.fetch(request).first(where: { $0.id == id })
}
}

// MARK: - Mapper
extension CoreDataBookCoverStorage {
// MARK: - CoreToDTO
func coreBookCoverToDTO(_ bookCover: BookCoverEntity) -> BookCoverDTO? {
guard let identifier = bookCover.identifier,
guard let id = bookCover.id,
let title = bookCover.title,
let color = bookCover.color else { return nil }

return BookCoverDTO(
identifier: identifier,
id: id,
title: title,
imageURL: bookCover.imageURL,
color: color,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ import MHFoundation
import MHCore
import CoreData

final class CoreDataBookStorage {
public final class CoreDataBookStorage {
private let coreDataStorage: CoreDataStorage

init(coreDataStorage: CoreDataStorage) {
public init(coreDataStorage: CoreDataStorage) {
self.coreDataStorage = coreDataStorage
}
}

extension CoreDataBookStorage: BookStorage {
func create(data: BookDTO) async -> Result<Void, MHDataError> {
public func create(data: BookDTO) async -> Result<Void, MHDataError> {
let context = coreDataStorage.persistentContainer.viewContext
do {
try await context.perform { [weak self] in
Expand All @@ -21,7 +21,8 @@ extension CoreDataBookStorage: BookStorage {
}
let book = NSManagedObject(entity: entity, insertInto: context)
book.setValue(data.id, forKey: "id")
book.setValue(dtoPagesToCore(data.pages), forKey: "pages")
book.setValue(data.title, forKey: "title")
book.setValue(dtoPagesToCore(data.pages, in: context), forKey: "pages")
try context.save()
}

Expand All @@ -34,7 +35,8 @@ extension CoreDataBookStorage: BookStorage {
return .failure(.createEntityFailure)
}
}
func fetch(with id: UUID) async -> Result<BookDTO, MHDataError> {

public func fetch(with id: UUID) async -> Result<BookDTO, MHDataError> {
let context = coreDataStorage.persistentContainer.viewContext

do {
Expand All @@ -57,7 +59,8 @@ extension CoreDataBookStorage: BookStorage {
return .failure(.fetchEntityFaliure)
}
}
func update(with id: UUID, data: BookDTO) async -> Result<Void, MHDataError> {

public func update(with id: UUID, data: BookDTO) async -> Result<Void, MHDataError> {
do {
let context = coreDataStorage.persistentContainer.viewContext
try await context.perform { [weak self] in
Expand All @@ -67,7 +70,8 @@ extension CoreDataBookStorage: BookStorage {
}

newEntity.setValue(data.id, forKey: "id")
newEntity.setValue(dtoPagesToCore(data.pages), forKey: "pages")
newEntity.setValue(data.title, forKey: "title")
newEntity.setValue(dtoPagesToCore(data.pages, in: context), forKey: "pages")

try context.save()
}
Expand All @@ -80,7 +84,8 @@ extension CoreDataBookStorage: BookStorage {
return .failure(.updateEntityFailure)
}
}
func delete(with id: UUID) async -> Result<Void, MHDataError> {

public func delete(with id: UUID) async -> Result<Void, MHDataError> {
do {
let context = coreDataStorage.persistentContainer.viewContext
try await context.perform { [weak self] in
Expand Down Expand Up @@ -118,21 +123,25 @@ extension CoreDataBookStorage {
// MARK: - Core to DTO
private func coreBookToDTO(_ book: BookEntity) -> BookDTO? {
guard let id = book.id,
let title = book.title,
let corePages = book.pages
else { return nil }
let pages = corePagesToDTO(corePages)

return BookDTO(
id: id,
title: title,
pages: pages
)
}

private func corePagesToDTO(_ pages: NSOrderedSet) -> [PageDTO] {
return pages.compactMap {
guard let page = $0 as? PageEntity else { return nil }
return corePageToDTO(page)
}
}

private func corePageToDTO(_ page: PageEntity) -> PageDTO? {
guard let id = page.id,
let mediaDescriptions = page.mediaDescriptions,
Expand All @@ -151,6 +160,7 @@ extension CoreDataBookStorage {
text: text
)
}

private func coreMediaDescriptionToDTO(_ mediaDescription: MediaDescriptionEntity) -> (Int, MediaDescriptionDTO)? {
guard let id = mediaDescription.id,
let type = mediaDescription.type
Expand All @@ -164,11 +174,11 @@ extension CoreDataBookStorage {
}

// MARK: - DTO to Core
private func dtoPagesToCore(_ pages: [PageDTO]) -> NSOrderedSet? {
let context = coreDataStorage.persistentContainer.viewContext
private func dtoPagesToCore(_ pages: [PageDTO], in context: NSManagedObjectContext) -> NSOrderedSet? {
let pageEntities = pages.compactMap { dtoPageToCore($0, in: context) }
return NSOrderedSet(array: pageEntities)
}

private func dtoPageToCore(_ page: PageDTO, in context: NSManagedObjectContext) -> PageEntity? {
guard let entity = NSEntityDescription.insertNewObject(
forEntityName: "PageEntity",
Expand All @@ -183,6 +193,7 @@ extension CoreDataBookStorage {

return entity
}

private func dtoMediaDescriptionsToCore(
_ metadata: [Int: MediaDescriptionDTO],
in context: NSManagedObjectContext
Expand All @@ -192,6 +203,7 @@ extension CoreDataBookStorage {
}
return NSSet(array: mediaDescriptionEntities)
}

private func dtoMediaDescriptionToCore(
location: Int,
_ mediaDescription: MediaDescriptionDTO,
Expand Down
Loading
Loading