From 28846e15a02ef8318b0cbc459c07830dc3f4609b Mon Sep 17 00:00:00 2001 From: Min Hyun Date: Mon, 18 Sep 2023 00:34:26 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20CoreDataManager=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Diary/App/AppDelegate.swift | 3 - Diary/App/SceneDelegate.swift | 5 +- .../DiaryDetailViewController.swift | 15 +++-- .../Controller/DiaryListViewController.swift | 34 ++++++++-- Diary/Model/CoreData/CoreDataManager.swift | 67 ++++++++++--------- 5 files changed, 76 insertions(+), 48 deletions(-) diff --git a/Diary/App/AppDelegate.swift b/Diary/App/AppDelegate.swift index 1961da3c5..0d4067146 100644 --- a/Diary/App/AppDelegate.swift +++ b/Diary/App/AppDelegate.swift @@ -9,9 +9,6 @@ import CoreData @main class AppDelegate: UIResponder, UIApplicationDelegate { - - - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. return true diff --git a/Diary/App/SceneDelegate.swift b/Diary/App/SceneDelegate.swift index eef2f17ef..be755170e 100644 --- a/Diary/App/SceneDelegate.swift +++ b/Diary/App/SceneDelegate.swift @@ -9,13 +9,14 @@ import UIKit class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? - + let coreDataManager = CoreDataManager(entityName: "Diary") func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = (scene as? UIWindowScene) else { return } window = UIWindow(windowScene: windowScene) let diaryListViewController = DiaryListViewController() + diaryListViewController.coreDataManager = coreDataManager let navigationController = UINavigationController(rootViewController: diaryListViewController) window?.rootViewController = navigationController window?.makeKeyAndVisible() @@ -50,7 +51,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { // Save changes in the application's managed object context when the application transitions to the background. do { - try CoreDataManager.shared.saveContext() + try coreDataManager.saveContext() } catch { fatalError(CoreDataError.saveFailure.message) } diff --git a/Diary/Controller/DiaryDetailViewController.swift b/Diary/Controller/DiaryDetailViewController.swift index d42ee80b9..d1d8c45b0 100644 --- a/Diary/Controller/DiaryDetailViewController.swift +++ b/Diary/Controller/DiaryDetailViewController.swift @@ -16,14 +16,15 @@ final class DiaryDetailViewController: UIViewController, ShareDisplayable { return textView }() - private let container = CoreDataManager.shared.persistentContainer + private let coreDataManager: CoreDataManager private var diary: Diary private var isNew: Bool private var latitude: Double? private var longitude: Double? - init(latitude: Double?, longitude: Double?) { - self.diary = CoreDataManager.shared.createDiary() + init(latitude: Double?, longitude: Double?, coreDataManager: CoreDataManager) { + self.coreDataManager = coreDataManager + self.diary = coreDataManager.createDiary() self.isNew = true self.latitude = latitude self.longitude = longitude @@ -32,9 +33,11 @@ final class DiaryDetailViewController: UIViewController, ShareDisplayable { fetchWeather() } - init(_ diary: Diary) { + init(_ diary: Diary, coreDataManager: CoreDataManager) { + self.coreDataManager = coreDataManager self.diary = diary self.isNew = false + super.init(nibName: nil, bundle: nil) } @@ -134,7 +137,7 @@ final class DiaryDetailViewController: UIViewController, ShareDisplayable { alertBuilder.addAction(.delete) { [weak self] _ in guard let self else { return } do { - try CoreDataManager.shared.deleteDiary(self.diary) + try coreDataManager.deleteEntity(self.diary) self.navigationController?.popViewController(animated: true) } catch CoreDataError.deleteFailure { let additionalAlertBuilder = AlertBuilder(viewController: self, prefferedStyle: .alert) @@ -191,7 +194,7 @@ extension DiaryDetailViewController: UITextViewDelegate { guard !contents.isEmpty else { return } do { - try CoreDataManager.shared.saveContext() + try coreDataManager.saveContext() } catch CoreDataError.saveFailure { let alertBulder = AlertBuilder(viewController: self, prefferedStyle: .alert) alertBulder.setType(.coreDataError(error: .saveFailure)) diff --git a/Diary/Controller/DiaryListViewController.swift b/Diary/Controller/DiaryListViewController.swift index 039974a49..07953f45f 100644 --- a/Diary/Controller/DiaryListViewController.swift +++ b/Diary/Controller/DiaryListViewController.swift @@ -25,7 +25,7 @@ final class DiaryListViewController: UIViewController { return formatter }() - private let container = CoreDataManager.shared.persistentContainer + var coreDataManager: CoreDataManager? private var diaryList = [Diary]() private var latitude: Double? @@ -62,8 +62,10 @@ final class DiaryListViewController: UIViewController { private func setupNavigationBarButton() { let addDiary = UIAction(image: UIImage(systemName: "plus")) { [weak self] _ in - guard let self else { return } - let createDiaryView = DiaryDetailViewController(latitude: self.latitude, longitude: self.longitude) + guard let self, let coreDataManager else { return } + let createDiaryView = DiaryDetailViewController(latitude: self.latitude, + longitude: self.longitude, + coreDataManager: coreDataManager) self.navigationController?.pushViewController(createDiaryView, animated: true) } @@ -77,8 +79,13 @@ final class DiaryListViewController: UIViewController { } private func readCoreData() { + guard let coreDataManager else { return } + do { - let fetchedDiaries = try CoreDataManager.shared.fetchDiary() + guard let fetchedDiaries = try coreDataManager.fetchEntity(sortBy: "createdAt") as? [Diary] else { + throw CoreDataError.unknown + } + diaryList = fetchedDiaries.filter { $0.title != nil } tableView.reloadData() } catch CoreDataError.dataNotFound { @@ -129,17 +136,22 @@ extension DiaryListViewController: UITableViewDelegate, ShareDisplayable { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { tableView.deselectRow(at: indexPath, animated: true) let diaryToEdit = diaryList[indexPath.row] - let createVC = DiaryDetailViewController(diaryToEdit) + + guard let coreDataManager else { return } + + let createVC = DiaryDetailViewController(diaryToEdit, coreDataManager: coreDataManager) navigationController?.pushViewController(createVC, animated: true) } func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { + guard let coreDataManager else { return nil } + let delete = UIContextualAction(style: .normal, title: "") { (_, _, success: @escaping (Bool) -> Void) in let selectedDiary = self.diaryList[indexPath.row] do { - try CoreDataManager.shared.deleteDiary(selectedDiary) + try coreDataManager.deleteEntity(selectedDiary) self.readCoreData() success(true) } catch CoreDataError.deleteFailure { @@ -196,9 +208,17 @@ extension DiaryListViewController: CLLocationManagerDelegate { extension DiaryListViewController: UISearchBarDelegate { func searchDiary(with keyword: String) { + guard let coreDataManager else { return } + if keyword.count > 0 { do { - let fetchedDiaries = try CoreDataManager.shared.filterDiary(keyword) + let predicate = "title CONTAINS[cd] %@ OR body CONTAINS[cd] %@" + guard let fetchedDiaries = try coreDataManager.filterEntity(keyword, + predicate: predicate, + sortBy: "createdAt") as? [Diary] else { + throw CoreDataError.unknown + } + diaryList = fetchedDiaries.filter { $0.title != nil } tableView.reloadData() } catch CoreDataError.dataNotFound { diff --git a/Diary/Model/CoreData/CoreDataManager.swift b/Diary/Model/CoreData/CoreDataManager.swift index c9e78202d..643a384fa 100644 --- a/Diary/Model/CoreData/CoreDataManager.swift +++ b/Diary/Model/CoreData/CoreDataManager.swift @@ -8,43 +8,50 @@ import CoreData class CoreDataManager { - static let shared = CoreDataManager() + var persistentContainer: NSPersistentContainer - lazy var persistentContainer: NSPersistentContainer = { - let container = NSPersistentContainer(name: "Diary") - container.loadPersistentStores(completionHandler: { (_, error) in - if let error = error as NSError? { - fatalError("Unresolved error \(error), \(error.userInfo)") - } - }) - return container - }() - - private init() {} + init(entityName: String) { + persistentContainer = { + let container = NSPersistentContainer(name: entityName) + container.loadPersistentStores(completionHandler: { (_, error) in + if let error = error as NSError? { + fatalError("Unresolved error \(error), \(error.userInfo)") + } + }) + return container + }() + } - func fetchDiary() throws -> [Diary] { - let request: NSFetchRequest = Diary.fetchRequest() - let sortByDate = NSSortDescriptor(key: "createdAt", ascending: false) - request.sortDescriptors = [sortByDate] + func fetchEntity(sortBy: String? = nil) throws -> [T] { + let request: NSFetchRequest = NSFetchRequest(entityName: persistentContainer.name) + if let sortBy { + let sorted = NSSortDescriptor(key: sortBy, ascending: false) + request.sortDescriptors = [sorted] + } + do { - let diaries = try persistentContainer.viewContext.fetch(request) - return diaries + let entities: [T] = try persistentContainer.viewContext.fetch(request) + return entities } catch { throw CoreDataError.dataNotFound } } - func filterDiary(_ keyword: String) throws -> [Diary] { - let request: NSFetchRequest = Diary.fetchRequest() - let predicate = NSPredicate(format: "title CONTAINS[cd] %@ OR body CONTAINS[cd] %@", keyword, keyword) - request.predicate = predicate - let sortByDate = NSSortDescriptor(key: "createdAt", ascending: false) - request.sortDescriptors = [sortByDate] + func filterEntity(_ keyword: String, predicate: String, sortBy: String? = nil) throws -> [T] { + let request: NSFetchRequest = NSFetchRequest(entityName: persistentContainer.name) + + let predicated = NSPredicate(format: predicate, keyword, keyword) + request.predicate = predicated + + if let sortBy { + let sorted = NSSortDescriptor(key: sortBy, ascending: false) + request.sortDescriptors = [sorted] + } do { - let diaries = try persistentContainer.viewContext.fetch(request) - return diaries + let entities = try persistentContainer.viewContext.fetch(request) + return entities } catch { throw CoreDataError.dataNotFound } @@ -58,15 +65,15 @@ class CoreDataManager { return newDiary } - func deleteDiary(_ diary: Diary?) throws { - guard let diary = diary else { + func deleteEntity(_ entity: T?) throws { + guard let entity else { throw CoreDataError.deleteFailure } - persistentContainer.viewContext.delete(diary) + persistentContainer.viewContext.delete(entity) try saveContext() } - func saveContext () throws { + func saveContext() throws { let context = persistentContainer.viewContext if context.hasChanges { do {