From fecc10be9b59efb5fdd0347ad56cb46bc8f9b2ab Mon Sep 17 00:00:00 2001 From: MaryJo-github Date: Tue, 5 Sep 2023 18:52:49 +0900 Subject: [PATCH 01/26] =?UTF-8?q?:sparkles:=20Feat:=20Core=20Data=20?= =?UTF-8?q?=EB=AA=A8=EB=8D=B8=20=EC=83=9D=EC=84=B1=20=EB=B0=8F=20CRUD=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Diary+CoreDataClass.swift | 15 +++ Diary+CoreDataProperties.swift | 27 +++++ Diary.xcodeproj/project.pbxproj | 12 +++ Diary/Model/ContainerManager.swift | 98 +++++++++++++++++++ Diary/Model/DiaryContent.swift | 1 + .../Diary.xcdatamodel/contents | 9 +- 6 files changed, 160 insertions(+), 2 deletions(-) create mode 100644 Diary+CoreDataClass.swift create mode 100644 Diary+CoreDataProperties.swift create mode 100644 Diary/Model/ContainerManager.swift diff --git a/Diary+CoreDataClass.swift b/Diary+CoreDataClass.swift new file mode 100644 index 000000000..1b6d38998 --- /dev/null +++ b/Diary+CoreDataClass.swift @@ -0,0 +1,15 @@ +// +// Diary+CoreDataClass.swift +// Diary +// +// Created by Mary & Whales on 2023/09/05. +// +// + +import Foundation +import CoreData + +@objc(Diary) +public class Diary: NSManagedObject { + +} diff --git a/Diary+CoreDataProperties.swift b/Diary+CoreDataProperties.swift new file mode 100644 index 000000000..4a645c136 --- /dev/null +++ b/Diary+CoreDataProperties.swift @@ -0,0 +1,27 @@ +// +// Diary+CoreDataProperties.swift +// Diary +// +// Created by Mary & Whales on 2023/09/05. +// +// + +import Foundation +import CoreData + +extension Diary { + + @nonobjc public class func fetchRequest() -> NSFetchRequest { + return NSFetchRequest(entityName: "Diary") + } + + @NSManaged public var title: String? + @NSManaged public var body: String? + @NSManaged public var timeInterval: Double + @NSManaged public var id: UUID? + +} + +extension Diary: Identifiable { + +} diff --git a/Diary.xcodeproj/project.pbxproj b/Diary.xcodeproj/project.pbxproj index 330407466..04fdb1828 100644 --- a/Diary.xcodeproj/project.pbxproj +++ b/Diary.xcodeproj/project.pbxproj @@ -20,6 +20,9 @@ 44A761C12A9F7A6100C10AE2 /* EditingDiaryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44A761C02A9F7A6100C10AE2 /* EditingDiaryViewController.swift */; }; 44A762012AA5F96700C10AE2 /* UITableViewCell+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44A762002AA5F96700C10AE2 /* UITableViewCell+.swift */; }; 44A762032AA6031800C10AE2 /* ReuseIdentifiable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44A762022AA6031800C10AE2 /* ReuseIdentifiable.swift */; }; + 44A7622D2AA710F000C10AE2 /* Diary+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44A7622B2AA710F000C10AE2 /* Diary+CoreDataClass.swift */; }; + 44A7622E2AA710F000C10AE2 /* Diary+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44A7622C2AA710F000C10AE2 /* Diary+CoreDataProperties.swift */; }; + 44A762302AA712A200C10AE2 /* ContainerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44A7622F2AA712A200C10AE2 /* ContainerManager.swift */; }; C739AE25284DF28600741E8F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C739AE24284DF28600741E8F /* AppDelegate.swift */; }; C739AE27284DF28600741E8F /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C739AE26284DF28600741E8F /* SceneDelegate.swift */; }; C739AE29284DF28600741E8F /* DiaryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C739AE28284DF28600741E8F /* DiaryViewController.swift */; }; @@ -43,6 +46,9 @@ 44A761C02A9F7A6100C10AE2 /* EditingDiaryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditingDiaryViewController.swift; sourceTree = ""; }; 44A762002AA5F96700C10AE2 /* UITableViewCell+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableViewCell+.swift"; sourceTree = ""; }; 44A762022AA6031800C10AE2 /* ReuseIdentifiable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReuseIdentifiable.swift; sourceTree = ""; }; + 44A7622B2AA710F000C10AE2 /* Diary+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Diary+CoreDataClass.swift"; sourceTree = ""; }; + 44A7622C2AA710F000C10AE2 /* Diary+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Diary+CoreDataProperties.swift"; sourceTree = ""; }; + 44A7622F2AA712A200C10AE2 /* ContainerManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerManager.swift; sourceTree = ""; }; C739AE21284DF28600741E8F /* Diary.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Diary.app; sourceTree = BUILT_PRODUCTS_DIR; }; C739AE24284DF28600741E8F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; C739AE26284DF28600741E8F /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -80,6 +86,7 @@ children = ( 3CCA1E782A9F08C8008683C3 /* DiaryContent.swift */, 44A761BE2A9F163600C10AE2 /* DiaryManager.swift */, + 44A7622F2AA712A200C10AE2 /* ContainerManager.swift */, ); path = Model; sourceTree = ""; @@ -141,6 +148,8 @@ C739AE18284DF28600741E8F = { isa = PBXGroup; children = ( + 44A7622B2AA710F000C10AE2 /* Diary+CoreDataClass.swift */, + 44A7622C2AA710F000C10AE2 /* Diary+CoreDataProperties.swift */, 3C3C086B2AA1DD8D00C8D4CF /* Localizable.strings */, 3CCA1E762A9DBF56008683C3 /* .swiftlint.yml */, C739AE23284DF28600741E8F /* Diary */, @@ -268,6 +277,7 @@ buildActionMask = 2147483647; files = ( 44A761BA2A9F100900C10AE2 /* DecodingManager.swift in Sources */, + 44A7622D2AA710F000C10AE2 /* Diary+CoreDataClass.swift in Sources */, 44A762032AA6031800C10AE2 /* ReuseIdentifiable.swift in Sources */, C739AE29284DF28600741E8F /* DiaryViewController.swift in Sources */, 4495F2EF2A9CCF62007D5278 /* DiaryTableViewCell.swift in Sources */, @@ -277,7 +287,9 @@ C739AE27284DF28600741E8F /* SceneDelegate.swift in Sources */, 44A761BD2A9F142B00C10AE2 /* DataError.swift in Sources */, 44A762012AA5F96700C10AE2 /* UITableViewCell+.swift in Sources */, + 44A762302AA712A200C10AE2 /* ContainerManager.swift in Sources */, 3C3C08902AA3AB6C00C8D4CF /* UIViewController+.swift in Sources */, + 44A7622E2AA710F000C10AE2 /* Diary+CoreDataProperties.swift in Sources */, 3C3C08F02AA6241D00C8D4CF /* DateFormatter+.swift in Sources */, C739AE2F284DF28600741E8F /* Diary.xcdatamodeld in Sources */, 44A761C12A9F7A6100C10AE2 /* EditingDiaryViewController.swift in Sources */, diff --git a/Diary/Model/ContainerManager.swift b/Diary/Model/ContainerManager.swift new file mode 100644 index 000000000..5cd7f3376 --- /dev/null +++ b/Diary/Model/ContainerManager.swift @@ -0,0 +1,98 @@ +// +// ContainerManager.swift +// Diary +// +// Created by Mary & Whales on 2023/09/05. +// + +import CoreData +import OSLog + +final class ContainerManager { + static let shared = ContainerManager() + + private init() { } + + private let persistentContainer: NSPersistentContainer = { + let container = NSPersistentContainer(name: "Diary") + container.loadPersistentStores { _, error in + if let error = error { + fatalError("Unable to load persistent stores: \(error)") + } + } + + return container + }() + + private var context: NSManagedObjectContext { + return persistentContainer.viewContext + } + + func insert(diaryContent: DiaryContent) { + let entity = NSEntityDescription.entity(forEntityName: "Diary", in: context) + + if let entity = entity { + let managedObject = NSManagedObject(entity: entity, insertInto: context) + + managedObject.setValue(diaryContent.title, forKey: "title") + managedObject.setValue(diaryContent.body, forKey: "body") + managedObject.setValue(diaryContent.timeInterval, forKey: "timeInterval") + managedObject.setValue(diaryContent.id, forKey: "id") + + save() + } + } + + func save() { + do { + try context.save() + } catch { + os_log("%@", error.localizedDescription) + } + } + + func update(diaryContent: DiaryContent) { + let request = Diary.fetchRequest() + + request.predicate = NSPredicate(format: "id == %@", diaryContent.id as CVarArg) + + do { + let diaries = try context.fetch(request) + guard let diary = diaries.first else { return } + let diaryObject = diary as NSManagedObject + + diaryObject.setValue(diaryContent.title, forKey: "title") + diaryObject.setValue(diaryContent.body, forKey: "body") + + save() + } catch { + os_log("%@", error.localizedDescription) + } + } + + func fetchAll() -> [Diary]? { + do { + let diaries = try context.fetch(Diary.fetchRequest()) + return diaries + } catch { + os_log("%@", error.localizedDescription) + } + + return nil + } + + func delete(id: UUID) { + let request = Diary.fetchRequest() + + request.predicate = NSPredicate(format: "id == %@", id as CVarArg) + + do { + let diaries = try context.fetch(request) + guard let diary = diaries.first else { return } + context.delete(diary) + try context.save() + } catch { + os_log("%@", error.localizedDescription) + } + } +} diff --git a/Diary/Model/DiaryContent.swift b/Diary/Model/DiaryContent.swift index 0fc49c32e..6922f8b43 100644 --- a/Diary/Model/DiaryContent.swift +++ b/Diary/Model/DiaryContent.swift @@ -8,6 +8,7 @@ import Foundation struct DiaryContent: Decodable { + let id = UUID() var title: String var body: String let timeInterval: Double diff --git a/Diary/Resource/Diary.xcdatamodeld/Diary.xcdatamodel/contents b/Diary/Resource/Diary.xcdatamodeld/Diary.xcdatamodel/contents index 50d2514e8..082120e3d 100644 --- a/Diary/Resource/Diary.xcdatamodeld/Diary.xcdatamodel/contents +++ b/Diary/Resource/Diary.xcdatamodeld/Diary.xcdatamodel/contents @@ -1,4 +1,9 @@ - - + + + + + + + \ No newline at end of file From 971a07b728c1376e11ef6317c9d4e63879401c8d Mon Sep 17 00:00:00 2001 From: Whales Date: Tue, 5 Sep 2023 20:56:46 +0900 Subject: [PATCH 02/26] =?UTF-8?q?=E2=9C=A8=20Feat:=20=EC=83=88=20=EC=9D=BC?= =?UTF-8?q?=EA=B8=B0=20=EC=B6=94=EA=B0=80=20=ED=99=94=EB=A9=B4=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=9E=90=EB=8F=99=EC=9C=BC=EB=A1=9C=20=ED=82=A4?= =?UTF-8?q?=EB=B3=B4=EB=93=9C=20=EB=B3=B4=EC=97=AC=EC=A3=BC=EB=8A=94=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Diary/Controller/DiaryViewController.swift | 4 ++-- Diary/Controller/EditingDiaryViewController.swift | 10 ++++++---- Diary/View/DiaryTableViewCell.swift | 4 ++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Diary/Controller/DiaryViewController.swift b/Diary/Controller/DiaryViewController.swift index 001a51178..d15956349 100644 --- a/Diary/Controller/DiaryViewController.swift +++ b/Diary/Controller/DiaryViewController.swift @@ -31,7 +31,7 @@ final class DiaryViewController: UIViewController { configureView() configureTableView() - setUpConstraints() + setupConstraints() fetchDiaryContents() } @@ -70,7 +70,7 @@ final class DiaryViewController: UIViewController { tableView.register(DiaryTableViewCell.self, forCellReuseIdentifier: DiaryTableViewCell.reuseIdentifier) } - private func setUpConstraints() { + private func setupConstraints() { NSLayoutConstraint.activate([ tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), tableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor), diff --git a/Diary/Controller/EditingDiaryViewController.swift b/Diary/Controller/EditingDiaryViewController.swift index 6a5e85aed..f728c3de1 100644 --- a/Diary/Controller/EditingDiaryViewController.swift +++ b/Diary/Controller/EditingDiaryViewController.swift @@ -33,8 +33,8 @@ final class EditingDiaryViewController: UIViewController { super.viewDidLoad() configureView() - setUpConstraints() - fillDiaryTextView() + setupConstraints() + setupDiaryTextView() } private func configureView() { @@ -48,7 +48,7 @@ final class EditingDiaryViewController: UIViewController { navigationItem.title = diaryContent.date } - private func setUpConstraints() { + private func setupConstraints() { NSLayoutConstraint.activate([ diaryTextView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), diaryTextView.bottomAnchor.constraint(equalTo: view.keyboardLayoutGuide.topAnchor), @@ -57,9 +57,11 @@ final class EditingDiaryViewController: UIViewController { ]) } - private func fillDiaryTextView() { + private func setupDiaryTextView() { if diaryContent.title.isEmpty == false { diaryTextView.text = String(format: "%@\n\n%@", diaryContent.title, diaryContent.body) + } else { + diaryTextView.becomeFirstResponder() } } } diff --git a/Diary/View/DiaryTableViewCell.swift b/Diary/View/DiaryTableViewCell.swift index ab377f636..18081f518 100644 --- a/Diary/View/DiaryTableViewCell.swift +++ b/Diary/View/DiaryTableViewCell.swift @@ -75,10 +75,10 @@ final class DiaryTableViewCell: UITableViewCell { contentView.addSubview(titleStackView) accessoryType = .disclosureIndicator - setUpConstraints() + setupConstraints() } - private func setUpConstraints() { + private func setupConstraints() { NSLayoutConstraint.activate([ titleStackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 5), titleStackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -5), From 03b05c150357fa532d27f7bdb893b812688a03ff Mon Sep 17 00:00:00 2001 From: Whales Date: Tue, 5 Sep 2023 22:26:26 +0900 Subject: [PATCH 03/26] =?UTF-8?q?=E2=9C=A8=20Feat:=20=EC=9D=BC=EA=B8=B0=20?= =?UTF-8?q?=ED=8E=B8=EC=A7=91=20=ED=99=94=EB=A9=B4=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=ED=82=A4=EB=B3=B4=EB=93=9C=EA=B0=80=20=EC=88=A8=EA=B9=80/?= =?UTF-8?q?=ED=95=B4=EC=A0=9C=20=EB=90=98=EB=8F=84=EB=A1=9D=20TapGesture?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../EditingDiaryViewController.swift | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/Diary/Controller/EditingDiaryViewController.swift b/Diary/Controller/EditingDiaryViewController.swift index f728c3de1..e2ebe18fe 100644 --- a/Diary/Controller/EditingDiaryViewController.swift +++ b/Diary/Controller/EditingDiaryViewController.swift @@ -31,7 +31,7 @@ final class EditingDiaryViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - + configureView() setupConstraints() setupDiaryTextView() @@ -63,5 +63,21 @@ final class EditingDiaryViewController: UIViewController { } else { diaryTextView.becomeFirstResponder() } + + addGesture() + } + + private func addGesture() { + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(didTapTextView(_:))) + diaryTextView.addGestureRecognizer(tapGesture) + } + + @objc + private func didTapTextView(_ sender: Any) { + if diaryTextView.isFirstResponder { + diaryTextView.resignFirstResponder() + } else { + diaryTextView.becomeFirstResponder() + } } } From eee7407b37e2ff22e47eb00fcce52245c48fd19e Mon Sep 17 00:00:00 2001 From: MaryJo-github Date: Wed, 6 Sep 2023 16:18:19 +0900 Subject: [PATCH 04/26] =?UTF-8?q?:sparkles:=20Feat:=20=EC=9D=BC=EA=B8=B0?= =?UTF-8?q?=20=EC=9E=90=EB=8F=99=20=EC=A0=80=EC=9E=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 사용자가 입력을 멈추는 경우(키보드가 사라지는 경우) --- .../EditingDiaryViewController.swift | 46 +++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/Diary/Controller/EditingDiaryViewController.swift b/Diary/Controller/EditingDiaryViewController.swift index e2ebe18fe..c14063545 100644 --- a/Diary/Controller/EditingDiaryViewController.swift +++ b/Diary/Controller/EditingDiaryViewController.swift @@ -35,6 +35,13 @@ final class EditingDiaryViewController: UIViewController { configureView() setupConstraints() setupDiaryTextView() + setObserver() + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + + save() } private func configureView() { @@ -59,12 +66,14 @@ final class EditingDiaryViewController: UIViewController { private func setupDiaryTextView() { if diaryContent.title.isEmpty == false { - diaryTextView.text = String(format: "%@\n\n%@", diaryContent.title, diaryContent.body) + diaryTextView.text = diaryContent.title + diaryContent.body } else { diaryTextView.becomeFirstResponder() + ContainerManager.shared.insert(diaryContent: diaryContent) } addGesture() + diaryTextView.delegate = self } private func addGesture() { @@ -72,12 +81,43 @@ final class EditingDiaryViewController: UIViewController { diaryTextView.addGestureRecognizer(tapGesture) } - @objc - private func didTapTextView(_ sender: Any) { + @objc private func didTapTextView(_ sender: Any) { if diaryTextView.isFirstResponder { diaryTextView.resignFirstResponder() } else { diaryTextView.becomeFirstResponder() } } + + private func setObserver() { + NotificationCenter.default.addObserver( + self, + selector: #selector(enterBackground), + name: UIWindowScene.didEnterBackgroundNotification, + object: nil + ) + } + + @objc private func enterBackground() { + save() + } + + private func save() { + guard let text = diaryTextView.text else { return } + + if let index = text.firstIndex(of: "\n") { + diaryContent.title = String(text[text.startIndex ..< index]) + diaryContent.body = String(text[index ..< text.endIndex]) + } else { + diaryContent.title = text + } + + ContainerManager.shared.update(diaryContent: diaryContent) + } +} + +extension EditingDiaryViewController: UITextViewDelegate { + func textViewDidEndEditing(_ textView: UITextView) { + save() + } } From a200cfa40abee1e28c8d21b556169ac4199a1329 Mon Sep 17 00:00:00 2001 From: MaryJo-github Date: Wed, 6 Sep 2023 16:19:56 +0900 Subject: [PATCH 05/26] =?UTF-8?q?Revert=20":sparkles:=20Feat:=20=EC=9D=BC?= =?UTF-8?q?=EA=B8=B0=20=EC=9E=90=EB=8F=99=20=EC=A0=80=EC=9E=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit eee7407b37e2ff22e47eb00fcce52245c48fd19e. --- .../EditingDiaryViewController.swift | 46 ++----------------- 1 file changed, 3 insertions(+), 43 deletions(-) diff --git a/Diary/Controller/EditingDiaryViewController.swift b/Diary/Controller/EditingDiaryViewController.swift index c14063545..e2ebe18fe 100644 --- a/Diary/Controller/EditingDiaryViewController.swift +++ b/Diary/Controller/EditingDiaryViewController.swift @@ -35,13 +35,6 @@ final class EditingDiaryViewController: UIViewController { configureView() setupConstraints() setupDiaryTextView() - setObserver() - } - - override func viewWillDisappear(_ animated: Bool) { - super.viewWillDisappear(animated) - - save() } private func configureView() { @@ -66,14 +59,12 @@ final class EditingDiaryViewController: UIViewController { private func setupDiaryTextView() { if diaryContent.title.isEmpty == false { - diaryTextView.text = diaryContent.title + diaryContent.body + diaryTextView.text = String(format: "%@\n\n%@", diaryContent.title, diaryContent.body) } else { diaryTextView.becomeFirstResponder() - ContainerManager.shared.insert(diaryContent: diaryContent) } addGesture() - diaryTextView.delegate = self } private func addGesture() { @@ -81,43 +72,12 @@ final class EditingDiaryViewController: UIViewController { diaryTextView.addGestureRecognizer(tapGesture) } - @objc private func didTapTextView(_ sender: Any) { + @objc + private func didTapTextView(_ sender: Any) { if diaryTextView.isFirstResponder { diaryTextView.resignFirstResponder() } else { diaryTextView.becomeFirstResponder() } } - - private func setObserver() { - NotificationCenter.default.addObserver( - self, - selector: #selector(enterBackground), - name: UIWindowScene.didEnterBackgroundNotification, - object: nil - ) - } - - @objc private func enterBackground() { - save() - } - - private func save() { - guard let text = diaryTextView.text else { return } - - if let index = text.firstIndex(of: "\n") { - diaryContent.title = String(text[text.startIndex ..< index]) - diaryContent.body = String(text[index ..< text.endIndex]) - } else { - diaryContent.title = text - } - - ContainerManager.shared.update(diaryContent: diaryContent) - } -} - -extension EditingDiaryViewController: UITextViewDelegate { - func textViewDidEndEditing(_ textView: UITextView) { - save() - } } From 5248c7bb5760f3f2176b8bac6c8629c9633df2ec Mon Sep 17 00:00:00 2001 From: MaryJo-github Date: Wed, 6 Sep 2023 16:27:04 +0900 Subject: [PATCH 06/26] =?UTF-8?q?:sparkles:=20Feat:=20=EC=9D=BC=EA=B8=B0?= =?UTF-8?q?=20=EC=9E=90=EB=8F=99=20=EC=A0=80=EC=9E=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 사용자가 입력을 멈추는 경우(키보드가 사라지는 경우) - 앱이 백그라운드로 진입하는 경우 - 이전화면(리스트 화면)으로 이동하는 경우 --- .../EditingDiaryViewController.swift | 46 +++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/Diary/Controller/EditingDiaryViewController.swift b/Diary/Controller/EditingDiaryViewController.swift index e2ebe18fe..c14063545 100644 --- a/Diary/Controller/EditingDiaryViewController.swift +++ b/Diary/Controller/EditingDiaryViewController.swift @@ -35,6 +35,13 @@ final class EditingDiaryViewController: UIViewController { configureView() setupConstraints() setupDiaryTextView() + setObserver() + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + + save() } private func configureView() { @@ -59,12 +66,14 @@ final class EditingDiaryViewController: UIViewController { private func setupDiaryTextView() { if diaryContent.title.isEmpty == false { - diaryTextView.text = String(format: "%@\n\n%@", diaryContent.title, diaryContent.body) + diaryTextView.text = diaryContent.title + diaryContent.body } else { diaryTextView.becomeFirstResponder() + ContainerManager.shared.insert(diaryContent: diaryContent) } addGesture() + diaryTextView.delegate = self } private func addGesture() { @@ -72,12 +81,43 @@ final class EditingDiaryViewController: UIViewController { diaryTextView.addGestureRecognizer(tapGesture) } - @objc - private func didTapTextView(_ sender: Any) { + @objc private func didTapTextView(_ sender: Any) { if diaryTextView.isFirstResponder { diaryTextView.resignFirstResponder() } else { diaryTextView.becomeFirstResponder() } } + + private func setObserver() { + NotificationCenter.default.addObserver( + self, + selector: #selector(enterBackground), + name: UIWindowScene.didEnterBackgroundNotification, + object: nil + ) + } + + @objc private func enterBackground() { + save() + } + + private func save() { + guard let text = diaryTextView.text else { return } + + if let index = text.firstIndex(of: "\n") { + diaryContent.title = String(text[text.startIndex ..< index]) + diaryContent.body = String(text[index ..< text.endIndex]) + } else { + diaryContent.title = text + } + + ContainerManager.shared.update(diaryContent: diaryContent) + } +} + +extension EditingDiaryViewController: UITextViewDelegate { + func textViewDidEndEditing(_ textView: UITextView) { + save() + } } From 5f793263ac637d120af10880e27d905f75473cdf Mon Sep 17 00:00:00 2001 From: Whales Date: Wed, 6 Sep 2023 20:48:46 +0900 Subject: [PATCH 07/26] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20CoreData?= =?UTF-8?q?=EC=97=90=20=EC=A0=80=EC=9E=A5=EB=90=9C=20DiaryContent=20?= =?UTF-8?q?=EB=B6=88=EB=9F=AC=EC=98=A4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Diary+CoreDataProperties.swift | 6 +- Diary/Controller/DiaryViewController.swift | 7 +- Diary/Model/DiaryContent.swift | 12 +-- Diary/Model/DiaryManager.swift | 15 +++- .../sample.dataset/Contents.json | 12 --- .../sample.dataset/sample.json | 77 ------------------- 6 files changed, 28 insertions(+), 101 deletions(-) delete mode 100644 Diary/Resource/Assets.xcassets/sample.dataset/Contents.json delete mode 100644 Diary/Resource/Assets.xcassets/sample.dataset/sample.json diff --git a/Diary+CoreDataProperties.swift b/Diary+CoreDataProperties.swift index 4a645c136..4217e98d7 100644 --- a/Diary+CoreDataProperties.swift +++ b/Diary+CoreDataProperties.swift @@ -15,10 +15,10 @@ extension Diary { return NSFetchRequest(entityName: "Diary") } - @NSManaged public var title: String? - @NSManaged public var body: String? + @NSManaged public var title: String + @NSManaged public var body: String @NSManaged public var timeInterval: Double - @NSManaged public var id: UUID? + @NSManaged public var id: UUID } diff --git a/Diary/Controller/DiaryViewController.swift b/Diary/Controller/DiaryViewController.swift index d15956349..8f68869cd 100644 --- a/Diary/Controller/DiaryViewController.swift +++ b/Diary/Controller/DiaryViewController.swift @@ -33,6 +33,11 @@ final class DiaryViewController: UIViewController { configureTableView() setupConstraints() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + fetchDiaryContents() } @@ -81,7 +86,7 @@ final class DiaryViewController: UIViewController { private func fetchDiaryContents() { do { - try diaryManager.fetchDiaryContents(name: "sample") + try diaryManager.fetchDiaryContents() } catch { print(error.localizedDescription) presentAlertWith(title: "데이터 불러오기 실패", message: "앱을 다시 실행해주십시오.", actionConfigs: ("확인", .default, nil)) diff --git a/Diary/Model/DiaryContent.swift b/Diary/Model/DiaryContent.swift index 6922f8b43..ee5a9f9d9 100644 --- a/Diary/Model/DiaryContent.swift +++ b/Diary/Model/DiaryContent.swift @@ -7,8 +7,8 @@ import Foundation -struct DiaryContent: Decodable { - let id = UUID() +struct DiaryContent { + let id: UUID var title: String var body: String let timeInterval: Double @@ -19,8 +19,10 @@ struct DiaryContent: Decodable { return formattedDate } - private enum CodingKeys: String, CodingKey { - case title, body - case timeInterval = "created_at" + init(id: UUID = UUID(), title: String, body: String, timeInterval: Double) { + self.id = id + self.title = title + self.body = body + self.timeInterval = timeInterval } } diff --git a/Diary/Model/DiaryManager.swift b/Diary/Model/DiaryManager.swift index 3c1321e6a..3a24f2620 100644 --- a/Diary/Model/DiaryManager.swift +++ b/Diary/Model/DiaryManager.swift @@ -10,9 +10,18 @@ import OSLog struct DiaryManager { var diaryContents: [DiaryContent]? - mutating func fetchDiaryContents(name: String) throws { - let data: [DiaryContent] = try DecodingManager.decodeJSON(fileName: name, by: JSONDecoder()) + mutating func fetchDiaryContents() throws { + guard let data: [Diary] = ContainerManager.shared.fetchAll() else { return } - diaryContents = data + var contents: [DiaryContent] = [] + + data.forEach { element in + contents.append(DiaryContent(id: element.id, + title: element.title, + body: element.body, + timeInterval: element.timeInterval)) + } + + diaryContents = contents } } diff --git a/Diary/Resource/Assets.xcassets/sample.dataset/Contents.json b/Diary/Resource/Assets.xcassets/sample.dataset/Contents.json deleted file mode 100644 index 4a27eac56..000000000 --- a/Diary/Resource/Assets.xcassets/sample.dataset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "data" : [ - { - "filename" : "sample.json", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Diary/Resource/Assets.xcassets/sample.dataset/sample.json b/Diary/Resource/Assets.xcassets/sample.dataset/sample.json deleted file mode 100644 index 6746d8848..000000000 --- a/Diary/Resource/Assets.xcassets/sample.dataset/sample.json +++ /dev/null @@ -1,77 +0,0 @@ -[ - { - "title": "똘기떵이호치새초미자축인묘", - "body": "A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine.\n\nI am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now.\n\nWhen, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath", - "created_at": 1608651333 - }, - { - "title": "드라고요롱이마초미미진사오미", - "body": "A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine.\n\nI am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now.\n\nWhen, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath\n\nI am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now.\n\nWhen, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath\n\nI am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now.\n\nWhen, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath", - "created_at": 1608651333 - }, - { - "title": "몽키키키강달찡찡신유술해", - "body": "A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine.\n\nI am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now.\n\nWhen, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath", - "created_at": 1608651333 - }, - { - "title": "네번째", - "body": "A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine.\n\nI am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now.\n\nWhen, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath\n\nI am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now.\n\nWhen, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath\n\nI am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now.\n\nWhen, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath\n\nI am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now.\n\nWhen, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath\n\nI am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now.\n\nWhen, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath\n\nI am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now.\n\nWhen, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath", - "created_at": 1608651333 - }, - { - "title": "승리는 우리의 것", - "body": "A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine.\n\nI am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now.\n\nWhen, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath", - "created_at": 1608651333 - }, - { - "title": "호롤ㄹ로롤롤로로로롤롤로롤롤ㄹ롤롤 나방이 홓ㄹ로롤롤ㄹ로로로ㅗ롤로롤", - "body": "A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine.\n\nI am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now.\n\nWhen, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath\n\nI am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now.\n\nWhen, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath\n\nI am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now.\n\nWhen, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath", - "created_at": 1608651333 - }, - { - "title": "이것은 리스트의 아이템입니다. 쪼매 제목이 길쥬? 그렇습니다. 그래도 뭐... 해야죠 뭐", - "body": "A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine.\n\nI am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now.\n\nWhen, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath", - "created_at": 1608651333 - }, - { - "title": "우주 외계인 그는 무서운 암흑대왕", - "body": "A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine.\n\nI am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now.\n\nWhen, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath\n\nI am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now.\n\nWhen, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath\n\nI am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now.\n\nWhen, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath", - "created_at": 1608651333 - }, - { - "title": "이것은 리스트의 아이템입니다. 쪼매 제목이 길쥬? 그렇습니다. 그래도 뭐... 해야죠 뭐", - "body": "A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine.\n\nI am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now.\n\nWhen, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath", - "created_at": 1608651333 - }, - { - "title": "하나면 하나지 둘이겠느냐~", - "body": "A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine.\n\nI am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now.\n\nWhen, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath", - "created_at": 1608651333 - }, - { - "title": "더 내려가봐유?", - "body": "A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine.\n\nI am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now.\n\nWhen, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath", - "created_at": 1608651333 - }, - { - "title": "이것은 리스트의 아이템입니다. 쪼매 제목이 길쥬? 그렇습니다. 그래도 뭐... 해야죠 뭐", - "body": "A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine.\n\nI am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now.\n\nWhen, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath\n\nI am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now.\n\nWhen, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath\n\nI am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now.\n\nWhen, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath", - "created_at": 1608651333 - }, - { - "title": "이것은 리스트의 아이템입니다. 쪼매 제목이 길쥬? 그렇습니다. 그래도 뭐... 해야죠 뭐", - "body": "A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine.\n\nI am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now.\n\nWhen, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath", - "created_at": 1608651333 - }, - { - "title": "이것은 리스트의 아이템입니다. 쪼매 제목이 길쥬? 그렇습니다. 그래도 뭐... 해야죠 뭐", - "body": "A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine.\n\nI am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now.\n\nWhen, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath", - "created_at": 1608651333 - }, - { - "title": "이것은 리스트의 아이템입니다. 쪼매 제목이 길쥬? 그렇습니다. 그래도 뭐... 해야죠 뭐", - "body": "A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot, which was created for the bliss of souls like mine.\n\nI am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now.\n\nWhen, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath\n\nI am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now.\n\nWhen, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath\n\nI am so happy, my dear friend, so absorbed in the exquisite sense of mere tranquil existence, that I neglect my talents. I should be incapable of drawing a single stroke at the present moment; and yet I feel that I never was a greater artist than now.\n\nWhen, while the lovely valley teems with vapour around me, and the meridian sun strikes the upper surface of the impenetrable foliage of my trees, and but a few stray gleams steal into the inner sanctuary, I throw myself down among the tall grass by the trickling stream; and, as I lie close to the earth, a thousand unknown plants are noticed by me: when I hear the buzz of the little world among the stalks, and grow familiar with the countless indescribable forms of the insects and flies, then I feel the presence of the Almighty, who formed us in his own image, and the breath", - "created_at": 1608651333 - } -] \ No newline at end of file From ef67611ca0401a673617383ed39b6ae275b4a358 Mon Sep 17 00:00:00 2001 From: Whales Date: Wed, 6 Sep 2023 23:28:17 +0900 Subject: [PATCH 08/26] =?UTF-8?q?=E2=9C=A8=20Feat:=20=EC=9D=BC=EA=B8=B0=20?= =?UTF-8?q?=ED=8E=B8=EC=A7=91=20=ED=99=94=EB=A9=B4=EC=97=90=20=EB=8D=94?= =?UTF-8?q?=EB=B3=B4=EA=B8=B0=20=EB=B2=84=ED=8A=BC=20=EB=B0=8F=20=EC=95=A1?= =?UTF-8?q?=EC=85=98=20=EC=8B=9C=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Diary/Controller/DiaryViewController.swift | 6 +++- .../EditingDiaryViewController.swift | 30 +++++++++++++++---- Diary/Extension/UIViewController+.swift | 4 ++- Diary/View/DiaryTableViewCell.swift | 4 ++- 4 files changed, 35 insertions(+), 9 deletions(-) diff --git a/Diary/Controller/DiaryViewController.swift b/Diary/Controller/DiaryViewController.swift index 8f68869cd..9ec88f8da 100644 --- a/Diary/Controller/DiaryViewController.swift +++ b/Diary/Controller/DiaryViewController.swift @@ -39,6 +39,7 @@ final class DiaryViewController: UIViewController { super.viewWillAppear(animated) fetchDiaryContents() + tableView.reloadData() } private func configureView() { @@ -89,7 +90,10 @@ final class DiaryViewController: UIViewController { try diaryManager.fetchDiaryContents() } catch { print(error.localizedDescription) - presentAlertWith(title: "데이터 불러오기 실패", message: "앱을 다시 실행해주십시오.", actionConfigs: ("확인", .default, nil)) + presentAlertWith(title: "데이터 불러오기 실패", + message: "앱을 다시 실행해주십시오.", + preferredStyle: .alert, + actionConfigs: ("확인", .default, nil)) } } } diff --git a/Diary/Controller/EditingDiaryViewController.swift b/Diary/Controller/EditingDiaryViewController.swift index c14063545..6e712ecd7 100644 --- a/Diary/Controller/EditingDiaryViewController.swift +++ b/Diary/Controller/EditingDiaryViewController.swift @@ -52,9 +52,29 @@ final class EditingDiaryViewController: UIViewController { } private func configureNavigationItem() { + let othersDiaryBarButtonItem = UIBarButtonItem(image: UIImage(systemName: "ellipsis.bubble"), + style: .plain, + target: self, + action: #selector(tappedOthersButton)) + + navigationItem.rightBarButtonItem = othersDiaryBarButtonItem navigationItem.title = diaryContent.date } + @objc private func tappedOthersButton() { + let deleteHandler: (UIAlertAction) -> Void = { _ in + ContainerManager.shared.delete(id: self.diaryContent.id) + self.navigationController?.popViewController(animated: true) + } + + presentAlertWith(title: nil, + message: nil, + preferredStyle: .actionSheet, + actionConfigs: ("Share...", .default, nil), + ("Delete", .destructive, deleteHandler), + ("Cancel", .cancel, nil)) + } + private func setupConstraints() { NSLayoutConstraint.activate([ diaryTextView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), @@ -90,12 +110,10 @@ final class EditingDiaryViewController: UIViewController { } private func setObserver() { - NotificationCenter.default.addObserver( - self, - selector: #selector(enterBackground), - name: UIWindowScene.didEnterBackgroundNotification, - object: nil - ) + NotificationCenter.default.addObserver(self, + selector: #selector(enterBackground), + name: UIWindowScene.didEnterBackgroundNotification, + object: nil) } @objc private func enterBackground() { diff --git a/Diary/Extension/UIViewController+.swift b/Diary/Extension/UIViewController+.swift index d6c8f61df..f748ed65e 100644 --- a/Diary/Extension/UIViewController+.swift +++ b/Diary/Extension/UIViewController+.swift @@ -10,12 +10,14 @@ import UIKit extension UIViewController { func presentAlertWith(title: String?, message: String?, + preferredStyle: UIAlertController.Style, actionConfigs: (title: String?, style: UIAlertAction.Style, handler: ((UIAlertAction) -> Void)?)...) { let alertController = UIAlertController(title: title, message: message, - preferredStyle: .alert) + preferredStyle: preferredStyle) + for config in actionConfigs { let action = UIAlertAction(title: config.title, style: config.style, diff --git a/Diary/View/DiaryTableViewCell.swift b/Diary/View/DiaryTableViewCell.swift index 18081f518..757dfa0aa 100644 --- a/Diary/View/DiaryTableViewCell.swift +++ b/Diary/View/DiaryTableViewCell.swift @@ -29,6 +29,7 @@ final class DiaryTableViewCell: UITableViewCell { let label = UILabel() label.font = .preferredFont(forTextStyle: .caption1) label.adjustsFontForContentSizeCategory = true + label.setContentHuggingPriority( .init(100), for: .horizontal) return label }() @@ -45,6 +46,7 @@ final class DiaryTableViewCell: UITableViewCell { private let descriptionStackView: UIStackView = { let stackView = UIStackView() stackView.axis = .horizontal + stackView.alignment = .center stackView.spacing = 5 return stackView @@ -63,7 +65,7 @@ final class DiaryTableViewCell: UITableViewCell { func configureCell(data: DiaryContent) { titleLabel.text = data.title dateLabel.text = data.date - previewLabel.text = data.body + previewLabel.text = data.body.trimmingCharacters(in: .whitespacesAndNewlines) } private func configureUI() { From fbc2d8615bc6fbb5c02c509e3526a6e6e4d318b9 Mon Sep 17 00:00:00 2001 From: MaryJo-github Date: Thu, 7 Sep 2023 20:34:28 +0900 Subject: [PATCH 09/26] =?UTF-8?q?:recycle:=20Refactor:=20DiaryManager=20st?= =?UTF-8?q?ruct=20->=20class=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - DiaryViewController 오류 print -> os_log 변경 --- Diary/Controller/DiaryViewController.swift | 5 +++-- Diary/Model/DiaryManager.swift | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Diary/Controller/DiaryViewController.swift b/Diary/Controller/DiaryViewController.swift index 9ec88f8da..bcf8cceb2 100644 --- a/Diary/Controller/DiaryViewController.swift +++ b/Diary/Controller/DiaryViewController.swift @@ -5,9 +5,10 @@ // last modified by Mary & Whales import UIKit +import OSLog final class DiaryViewController: UIViewController { - private var diaryManager: DiaryManager + private let diaryManager: DiaryManager private var tableView: UITableView = { let tableView = UITableView() @@ -89,7 +90,7 @@ final class DiaryViewController: UIViewController { do { try diaryManager.fetchDiaryContents() } catch { - print(error.localizedDescription) + os_log("%@", error.localizedDescription) presentAlertWith(title: "데이터 불러오기 실패", message: "앱을 다시 실행해주십시오.", preferredStyle: .alert, diff --git a/Diary/Model/DiaryManager.swift b/Diary/Model/DiaryManager.swift index 3a24f2620..fc2db7639 100644 --- a/Diary/Model/DiaryManager.swift +++ b/Diary/Model/DiaryManager.swift @@ -7,10 +7,10 @@ import OSLog -struct DiaryManager { +final class DiaryManager { var diaryContents: [DiaryContent]? - mutating func fetchDiaryContents() throws { + func fetchDiaryContents() throws { guard let data: [Diary] = ContainerManager.shared.fetchAll() else { return } var contents: [DiaryContent] = [] From a1800437c07f7e368dc9452e4ffddfd01721b668 Mon Sep 17 00:00:00 2001 From: MaryJo-github Date: Thu, 7 Sep 2023 22:57:42 +0900 Subject: [PATCH 10/26] =?UTF-8?q?:sparkles:=20Feat:=20ActivityView=20?= =?UTF-8?q?=EB=B0=8F=20CheckDeleteAlert=20=EA=B5=AC=ED=98=84=20=EB=B0=8F?= =?UTF-8?q?=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - presentCheckDeleteAlert 및 presentActivityView 메서드를 UIViewController extension에 구현 --- .../EditingDiaryViewController.swift | 17 +++++++++++----- Diary/Extension/UIViewController+.swift | 20 +++++++++++++++++++ Diary/Model/ContainerManager.swift | 8 ++++---- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/Diary/Controller/EditingDiaryViewController.swift b/Diary/Controller/EditingDiaryViewController.swift index 6e712ecd7..a0511021b 100644 --- a/Diary/Controller/EditingDiaryViewController.swift +++ b/Diary/Controller/EditingDiaryViewController.swift @@ -62,15 +62,22 @@ final class EditingDiaryViewController: UIViewController { } @objc private func tappedOthersButton() { + let shareHandler: (UIAlertAction) -> Void = { _ in + let diaryContentItem = self.diaryContent.title + self.diaryContent.body + self.presentActivityView(shareItem: diaryContentItem) + } + let deleteHandler: (UIAlertAction) -> Void = { _ in - ContainerManager.shared.delete(id: self.diaryContent.id) - self.navigationController?.popViewController(animated: true) + self.presentCheckDeleteAlert { + ContainerManager.shared.delete(id: self.diaryContent.id) + self.navigationController?.popViewController(animated: true) + } } presentAlertWith(title: nil, message: nil, preferredStyle: .actionSheet, - actionConfigs: ("Share...", .default, nil), + actionConfigs: ("Share...", .default, shareHandler), ("Delete", .destructive, deleteHandler), ("Cancel", .cancel, nil)) } @@ -97,11 +104,11 @@ final class EditingDiaryViewController: UIViewController { } private func addGesture() { - let tapGesture = UITapGestureRecognizer(target: self, action: #selector(didTapTextView(_:))) + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(tappedTextView(_:))) diaryTextView.addGestureRecognizer(tapGesture) } - @objc private func didTapTextView(_ sender: Any) { + @objc private func tappedTextView(_ sender: Any) { if diaryTextView.isFirstResponder { diaryTextView.resignFirstResponder() } else { diff --git a/Diary/Extension/UIViewController+.swift b/Diary/Extension/UIViewController+.swift index f748ed65e..644b91d35 100644 --- a/Diary/Extension/UIViewController+.swift +++ b/Diary/Extension/UIViewController+.swift @@ -27,4 +27,24 @@ extension UIViewController { present(alertController, animated: true) } + + func presentCheckDeleteAlert(completion: @escaping () -> Void) { + let deleteHandler: (UIAlertAction) -> Void = { _ in + completion() + } + + presentAlertWith(title: "진짜요?", + message: "정말로 삭제하시겠어요?", + preferredStyle: .alert, + actionConfigs: ("취소", .cancel, nil), + ("삭제", .destructive, deleteHandler)) + } + + func presentActivityView(shareItem: String) { + let activityViewController = UIActivityViewController( + activityItems: [shareItem], + applicationActivities: []) + + present(activityViewController, animated: true) + } } diff --git a/Diary/Model/ContainerManager.swift b/Diary/Model/ContainerManager.swift index 5cd7f3376..8db4f88e3 100644 --- a/Diary/Model/ContainerManager.swift +++ b/Diary/Model/ContainerManager.swift @@ -58,11 +58,11 @@ final class ContainerManager { do { let diaries = try context.fetch(request) - guard let diary = diaries.first else { return } - let diaryObject = diary as NSManagedObject - diaryObject.setValue(diaryContent.title, forKey: "title") - diaryObject.setValue(diaryContent.body, forKey: "body") + diaries.forEach { diary in + diary.title = diaryContent.title + diary.body = diaryContent.body + } save() } catch { From 1fb5d7d58b32eac9a0936626bcf629c8aab9772d Mon Sep 17 00:00:00 2001 From: MaryJo-github Date: Fri, 8 Sep 2023 16:18:24 +0900 Subject: [PATCH 11/26] =?UTF-8?q?:recycle:=20Refactor:=20presentActivityVi?= =?UTF-8?q?ew=20=EB=A9=94=EC=84=9C=EB=93=9C=EB=A5=BC=20PresentableActivity?= =?UTF-8?q?View=20=ED=94=84=EB=A1=9C=ED=86=A0=EC=BD=9C=EB=A1=9C=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Diary.xcodeproj/project.pbxproj | 4 ++++ .../EditingDiaryViewController.swift | 4 ++-- Diary/Extension/UIViewController+.swift | 14 +----------- Diary/Utility/PresentableActivityView.swift | 22 +++++++++++++++++++ 4 files changed, 29 insertions(+), 15 deletions(-) create mode 100644 Diary/Utility/PresentableActivityView.swift diff --git a/Diary.xcodeproj/project.pbxproj b/Diary.xcodeproj/project.pbxproj index 04fdb1828..740d25f75 100644 --- a/Diary.xcodeproj/project.pbxproj +++ b/Diary.xcodeproj/project.pbxproj @@ -13,6 +13,7 @@ 3C3C08F02AA6241D00C8D4CF /* DateFormatter+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C3C08EF2AA6241D00C8D4CF /* DateFormatter+.swift */; }; 3CCA1E772A9DBF56008683C3 /* .swiftlint.yml in Resources */ = {isa = PBXBuildFile; fileRef = 3CCA1E762A9DBF56008683C3 /* .swiftlint.yml */; }; 3CCA1E792A9F08C8008683C3 /* DiaryContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CCA1E782A9F08C8008683C3 /* DiaryContent.swift */; }; + 444E73F82AAAFA940079BEE5 /* PresentableActivityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 444E73F72AAAFA940079BEE5 /* PresentableActivityView.swift */; }; 4495F2EF2A9CCF62007D5278 /* DiaryTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4495F2EE2A9CCF62007D5278 /* DiaryTableViewCell.swift */; }; 44A761BA2A9F100900C10AE2 /* DecodingManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44A761B92A9F100900C10AE2 /* DecodingManager.swift */; }; 44A761BD2A9F142B00C10AE2 /* DataError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44A761BC2A9F142B00C10AE2 /* DataError.swift */; }; @@ -39,6 +40,7 @@ 3C3C08EF2AA6241D00C8D4CF /* DateFormatter+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateFormatter+.swift"; sourceTree = ""; }; 3CCA1E762A9DBF56008683C3 /* .swiftlint.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = .swiftlint.yml; sourceTree = ""; }; 3CCA1E782A9F08C8008683C3 /* DiaryContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryContent.swift; sourceTree = ""; }; + 444E73F72AAAFA940079BEE5 /* PresentableActivityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresentableActivityView.swift; sourceTree = ""; }; 4495F2EE2A9CCF62007D5278 /* DiaryTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryTableViewCell.swift; sourceTree = ""; }; 44A761B92A9F100900C10AE2 /* DecodingManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecodingManager.swift; sourceTree = ""; }; 44A761BC2A9F142B00C10AE2 /* DataError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataError.swift; sourceTree = ""; }; @@ -96,6 +98,7 @@ children = ( 44A761B92A9F100900C10AE2 /* DecodingManager.swift */, 44A762022AA6031800C10AE2 /* ReuseIdentifiable.swift */, + 444E73F72AAAFA940079BEE5 /* PresentableActivityView.swift */, ); path = Utility; sourceTree = ""; @@ -287,6 +290,7 @@ C739AE27284DF28600741E8F /* SceneDelegate.swift in Sources */, 44A761BD2A9F142B00C10AE2 /* DataError.swift in Sources */, 44A762012AA5F96700C10AE2 /* UITableViewCell+.swift in Sources */, + 444E73F82AAAFA940079BEE5 /* PresentableActivityView.swift in Sources */, 44A762302AA712A200C10AE2 /* ContainerManager.swift in Sources */, 3C3C08902AA3AB6C00C8D4CF /* UIViewController+.swift in Sources */, 44A7622E2AA710F000C10AE2 /* Diary+CoreDataProperties.swift in Sources */, diff --git a/Diary/Controller/EditingDiaryViewController.swift b/Diary/Controller/EditingDiaryViewController.swift index a0511021b..7b6193c0d 100644 --- a/Diary/Controller/EditingDiaryViewController.swift +++ b/Diary/Controller/EditingDiaryViewController.swift @@ -7,7 +7,7 @@ import UIKit -final class EditingDiaryViewController: UIViewController { +final class EditingDiaryViewController: UIViewController, PresentableActivityView { private var diaryContent: DiaryContent private let diaryTextView: UITextView = { @@ -68,7 +68,7 @@ final class EditingDiaryViewController: UIViewController { } let deleteHandler: (UIAlertAction) -> Void = { _ in - self.presentCheckDeleteAlert { + self.presentCheckDeleteAlert { _ in ContainerManager.shared.delete(id: self.diaryContent.id) self.navigationController?.popViewController(animated: true) } diff --git a/Diary/Extension/UIViewController+.swift b/Diary/Extension/UIViewController+.swift index 644b91d35..d5b1b68f6 100644 --- a/Diary/Extension/UIViewController+.swift +++ b/Diary/Extension/UIViewController+.swift @@ -27,24 +27,12 @@ extension UIViewController { present(alertController, animated: true) } - - func presentCheckDeleteAlert(completion: @escaping () -> Void) { - let deleteHandler: (UIAlertAction) -> Void = { _ in - completion() - } + func presentCheckDeleteAlert(deleteHandler: @escaping (UIAlertAction) -> Void) { presentAlertWith(title: "진짜요?", message: "정말로 삭제하시겠어요?", preferredStyle: .alert, actionConfigs: ("취소", .cancel, nil), ("삭제", .destructive, deleteHandler)) } - - func presentActivityView(shareItem: String) { - let activityViewController = UIActivityViewController( - activityItems: [shareItem], - applicationActivities: []) - - present(activityViewController, animated: true) - } } diff --git a/Diary/Utility/PresentableActivityView.swift b/Diary/Utility/PresentableActivityView.swift new file mode 100644 index 000000000..9f397280a --- /dev/null +++ b/Diary/Utility/PresentableActivityView.swift @@ -0,0 +1,22 @@ +// +// PresentableActivityView.swift +// Diary +// +// Created by Mary & Whales on 2023/09/08. +// + +import UIKit + +protocol PresentableActivityView where Self: UIViewController { + func presentActivityView(shareItem: String) +} + +extension PresentableActivityView { + func presentActivityView(shareItem: String) { + let activityViewController = UIActivityViewController( + activityItems: [shareItem], + applicationActivities: []) + + present(activityViewController, animated: true) + } +} From 0dcaad6e3ca5b49a3dfdd11bc03c56ea9082ecee Mon Sep 17 00:00:00 2001 From: WhalesJin <124643545+WhalesJin@users.noreply.github.com> Date: Fri, 8 Sep 2023 17:17:30 +0900 Subject: [PATCH 12/26] =?UTF-8?q?=F0=9F=93=9D=20Docs:=20Update=20README.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index fb38a0861..e5ba3608a 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,8 @@ ## 👀 Diagram ### 📐 UML - + +
@@ -100,7 +101,7 @@
-### 2️⃣ DiaryManager +### 2️⃣ DiaryManager (STEP2 진행 후 수정 예정) `Asset`에 넣은 `"sample"` 데이터를 받아오는 메서드를 구현하는데, 이 작업은 `View`나 `Controller`의 역할이 아니라고 생각해서 `DiaryManager`라는 객체를 `Model`로 만들었습니다. `DiaryViewController`가 `DiaryManager`를 가지고 있고, `DiaryManager`가 데이터 `fetch` 작업을 할 수 있도록 메서드를 구현하였습니다.
수정 전 @@ -178,6 +179,7 @@ extension DiaryViewController: UITableViewDataSource {
+ ### 3️⃣ SwiftLint 활용 더 깔끔하고 통일성있는 컨벤션을 위해 SwiftLint를 적용했습니다. @@ -193,19 +195,23 @@ extension DiaryViewController: UITableViewDataSource { - trailing_whitespace - trailing_comma ``` -
+ ## 📚 참고 링크 - [🍎Apple Docs: os_log](https://developer.apple.com/documentation/os/os_log) -- [🍎Apple Docs: UIScrollView.KeyboardDismissMode](https://developer.apple.com/documentation/uikit/uiscrollview/keyboarddismissmodee) +- [🍎Apple Docs: UIScrollView.KeyboardDismissMode](https://developer.apple.com/documentation/uikit/uiscrollview/1619437-keyboarddismissmode) - [🍎Apple Docs: Adaptivity and Layout](https://developer.apple.com/design/human-interface-guidelines/layout) - [🍎Apple Docs: DateFormatter](https://developer.apple.com/documentation/foundation/dateformatter) - [🍎Apple Docs: UITextView](https://developer.apple.com/documentation/uikit/uitextview) +- [🍎Apple Docs: Swipe-trailing](https://developer.apple.com/documentation/uikit/uitableviewdelegate/2902367-tableview) +- [🍎Apple Docs: UIActivityViewController](https://developer.apple.com/documentation/uikit/uiactivityviewcontroller) - [🍏WWDC: Making Apps Adaptive, Part 1](https://asciiwwdc.com/2016/sessions/222) - [🍏WWDC: Making Apps Adaptive, Part 2](https://www.youtube.com/watch?v=s3utpBiRbB0) - [🍏WWDC note: UIKit: Apps for Every Size and Shape](https://www.wwdcnotes.com/notes/wwdc18/235/) - [SwiftLint Rule Directory](https://realm.github.io/SwiftLint/rule-directory.html) +- [야곰닷넷: Test Double](https://yagom.net/courses/unit-test-작성하기/lessons/테스트를-위한-객체-만들기/topic/테스트를-위한-객체를-이용해서-테스트-작성하기/) +- [blog: URL 처리 방법](https://ios-development.tistory.com/1014)
@@ -217,5 +223,4 @@ extension DiaryViewController: UITableViewDataSource { | :--------: | :--------: | :--------: | | | **🐿️Mary🐿️** | **https://github.com/MaryJo-github** | - - [타임라인 링크](https://github.com/WhalesJin/ios-diary/wiki/타임라인) From 1c891499b1df51bb22de69d0bb5bff3dbd65087a Mon Sep 17 00:00:00 2001 From: MaryJo-github Date: Tue, 12 Sep 2023 10:48:09 +0900 Subject: [PATCH 13/26] =?UTF-8?q?:recycle:=20Refactor:=20=EC=9D=BC?= =?UTF-8?q?=EA=B8=B0=EC=9E=A5=20=EB=82=B4=EC=9A=A9=EC=9D=B4=20=EB=B9=84?= =?UTF-8?q?=EC=96=B4=EC=9E=88=EC=9C=BC=EB=A9=B4=20=EC=A0=80=EC=9E=A5?= =?UTF-8?q?=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8F=84=EB=A1=9D=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/Controller/EditingDiaryViewController.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Diary/Controller/EditingDiaryViewController.swift b/Diary/Controller/EditingDiaryViewController.swift index 7b6193c0d..a06f8f075 100644 --- a/Diary/Controller/EditingDiaryViewController.swift +++ b/Diary/Controller/EditingDiaryViewController.swift @@ -92,7 +92,7 @@ final class EditingDiaryViewController: UIViewController, PresentableActivityVie } private func setupDiaryTextView() { - if diaryContent.title.isEmpty == false { + if diaryContent.title.isEmpty == false || diaryContent.body.isEmpty == false { diaryTextView.text = diaryContent.title + diaryContent.body } else { diaryTextView.becomeFirstResponder() @@ -130,6 +130,11 @@ final class EditingDiaryViewController: UIViewController, PresentableActivityVie private func save() { guard let text = diaryTextView.text else { return } + if text.isEmpty { + ContainerManager.shared.delete(id: diaryContent.id) + return + } + if let index = text.firstIndex(of: "\n") { diaryContent.title = String(text[text.startIndex ..< index]) diaryContent.body = String(text[index ..< text.endIndex]) From 45db4029d5003f539ddcac75a970532c9a9a46d7 Mon Sep 17 00:00:00 2001 From: MaryJo-github Date: Tue, 12 Sep 2023 11:11:14 +0900 Subject: [PATCH 14/26] =?UTF-8?q?:sparkles:=20Feat:=20cell=20swipe?= =?UTF-8?q?=EC=8B=9C=20=EA=B3=B5=EC=9C=A0=20=EB=B0=8F=20=EC=82=AD=EC=A0=9C?= =?UTF-8?q?=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Diary/Controller/DiaryViewController.swift | 32 +++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/Diary/Controller/DiaryViewController.swift b/Diary/Controller/DiaryViewController.swift index bcf8cceb2..40ee278f5 100644 --- a/Diary/Controller/DiaryViewController.swift +++ b/Diary/Controller/DiaryViewController.swift @@ -125,7 +125,7 @@ extension DiaryViewController: UITableViewDataSource { } } -extension DiaryViewController: UITableViewDelegate { +extension DiaryViewController: UITableViewDelegate, PresentableActivityView { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { guard let diaryContents = diaryManager.diaryContents, let diaryContent = diaryContents[safe: indexPath.row] @@ -136,4 +136,34 @@ extension DiaryViewController: UITableViewDelegate { showEditingDiaryViewController(with: diaryContent) tableView.deselectRow(at: indexPath, animated: true) } + + func tableView(_ tableView: UITableView, + trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { + guard let diaryContents = diaryManager.diaryContents, + let diaryContent = diaryContents[safe: indexPath.row] + else { + return nil + } + + let share = UIContextualAction(style: .normal, + title: "Share...") { (_, _, success: @escaping (Bool) -> Void) in + let diaryContentItem = diaryContent.title + diaryContent.body + + self.presentActivityView(shareItem: diaryContentItem) + success(true) + } + + let delete = UIContextualAction(style: .destructive, + title: "Delete") { (_, _, success: @escaping (Bool) -> Void) in + self.presentCheckDeleteAlert { _ in + ContainerManager.shared.delete(id: diaryContent.id) + self.diaryManager.diaryContents?.remove(at: indexPath.row) + tableView.deleteRows(at: [indexPath], with: .fade) + } + + success(true) + } + + return UISwipeActionsConfiguration(actions: [delete, share]) + } } From 936d211e8f6a3a3a172f82f87612df09b84c9272 Mon Sep 17 00:00:00 2001 From: Whales Date: Tue, 12 Sep 2023 11:29:06 +0900 Subject: [PATCH 15/26] =?UTF-8?q?=E2=9C=A8=20Feat:=20=EB=AC=B8=EC=9E=90?= =?UTF-8?q?=EC=97=B4=20=EB=A1=9C=EC=BB=AC=EB=9D=BC=EC=9D=B4=EC=A0=80=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Diary/Controller/DiaryViewController.swift | 20 +++++++---- .../EditingDiaryViewController.swift | 34 +++++++++---------- Diary/Extension/UIViewController+.swift | 8 ++--- Diary/View/DiaryTableViewCell.swift | 13 ++++--- en.lproj/Localizable.strings | 14 ++++++++ ko.lproj/Localizable.strings | 14 ++++++++ 6 files changed, 71 insertions(+), 32 deletions(-) diff --git a/Diary/Controller/DiaryViewController.swift b/Diary/Controller/DiaryViewController.swift index 40ee278f5..40a7d0565 100644 --- a/Diary/Controller/DiaryViewController.swift +++ b/Diary/Controller/DiaryViewController.swift @@ -91,10 +91,10 @@ final class DiaryViewController: UIViewController { try diaryManager.fetchDiaryContents() } catch { os_log("%@", error.localizedDescription) - presentAlertWith(title: "데이터 불러오기 실패", - message: "앱을 다시 실행해주십시오.", + presentAlertWith(title: String(localized: "failedFatchDataAlertTitle"), + message: String(localized: "failedFatchDataAlertMessage."), preferredStyle: .alert, - actionConfigs: ("확인", .default, nil)) + actionConfigs: (String(localized: "failedFatchDataAlertAction"), .default, nil)) } } } @@ -145,16 +145,22 @@ extension DiaryViewController: UITableViewDelegate, PresentableActivityView { return nil } - let share = UIContextualAction(style: .normal, - title: "Share...") { (_, _, success: @escaping (Bool) -> Void) in + let share = UIContextualAction( + style: .normal, + title: String(localized: "share") + ) { (_, _, success: @escaping (Bool) -> Void) in + let diaryContentItem = diaryContent.title + diaryContent.body self.presentActivityView(shareItem: diaryContentItem) success(true) } - let delete = UIContextualAction(style: .destructive, - title: "Delete") { (_, _, success: @escaping (Bool) -> Void) in + let delete = UIContextualAction( + style: .destructive, + title: String(localized: "delete") + ) { (_, _, success: @escaping (Bool) -> Void) in + self.presentCheckDeleteAlert { _ in ContainerManager.shared.delete(id: diaryContent.id) self.diaryManager.diaryContents?.remove(at: indexPath.row) diff --git a/Diary/Controller/EditingDiaryViewController.swift b/Diary/Controller/EditingDiaryViewController.swift index a06f8f075..375ea7f19 100644 --- a/Diary/Controller/EditingDiaryViewController.swift +++ b/Diary/Controller/EditingDiaryViewController.swift @@ -62,24 +62,24 @@ final class EditingDiaryViewController: UIViewController, PresentableActivityVie } @objc private func tappedOthersButton() { - let shareHandler: (UIAlertAction) -> Void = { _ in - let diaryContentItem = self.diaryContent.title + self.diaryContent.body - self.presentActivityView(shareItem: diaryContentItem) - } + let shareHandler: (UIAlertAction) -> Void = { _ in + let diaryContentItem = self.diaryContent.title + self.diaryContent.body + self.presentActivityView(shareItem: diaryContentItem) + } - let deleteHandler: (UIAlertAction) -> Void = { _ in - self.presentCheckDeleteAlert { _ in - ContainerManager.shared.delete(id: self.diaryContent.id) - self.navigationController?.popViewController(animated: true) - } - } - - presentAlertWith(title: nil, - message: nil, - preferredStyle: .actionSheet, - actionConfigs: ("Share...", .default, shareHandler), - ("Delete", .destructive, deleteHandler), - ("Cancel", .cancel, nil)) + let deleteHandler: (UIAlertAction) -> Void = { _ in + self.presentCheckDeleteAlert { _ in + ContainerManager.shared.delete(id: self.diaryContent.id) + self.navigationController?.popViewController(animated: true) + } + } + + presentAlertWith(title: nil, + message: nil, + preferredStyle: .actionSheet, + actionConfigs: (String(localized: "share"), .default, shareHandler), + (String(localized: "delete"), .destructive, deleteHandler), + (String(localized: "cancel"), .cancel, nil)) } private func setupConstraints() { diff --git a/Diary/Extension/UIViewController+.swift b/Diary/Extension/UIViewController+.swift index d5b1b68f6..371bd8208 100644 --- a/Diary/Extension/UIViewController+.swift +++ b/Diary/Extension/UIViewController+.swift @@ -29,10 +29,10 @@ extension UIViewController { } func presentCheckDeleteAlert(deleteHandler: @escaping (UIAlertAction) -> Void) { - presentAlertWith(title: "진짜요?", - message: "정말로 삭제하시겠어요?", + presentAlertWith(title: String(localized: "checkDeleteAlertTitle"), + message: String(localized: "checkDeleteAlertMessage"), preferredStyle: .alert, - actionConfigs: ("취소", .cancel, nil), - ("삭제", .destructive, deleteHandler)) + actionConfigs: (String(localized: "checkDeleteAlertCancelAction"), .cancel, nil), + (String(localized: "checkDeleteAlertAction"), .destructive, deleteHandler)) } } diff --git a/Diary/View/DiaryTableViewCell.swift b/Diary/View/DiaryTableViewCell.swift index 757dfa0aa..0fc263bdd 100644 --- a/Diary/View/DiaryTableViewCell.swift +++ b/Diary/View/DiaryTableViewCell.swift @@ -63,10 +63,15 @@ final class DiaryTableViewCell: UITableViewCell { } func configureCell(data: DiaryContent) { - titleLabel.text = data.title - dateLabel.text = data.date - previewLabel.text = data.body.trimmingCharacters(in: .whitespacesAndNewlines) - } + if data.title.isEmpty { + titleLabel.text = String(localized: "noTitle") + } else { + titleLabel.text = data.title + } + + dateLabel.text = data.date + previewLabel.text = data.body.trimmingCharacters(in: .whitespacesAndNewlines) + } private func configureUI() { descriptionStackView.addArrangedSubview(dateLabel) diff --git a/en.lproj/Localizable.strings b/en.lproj/Localizable.strings index 66ad0685b..ecc9a92d3 100644 --- a/en.lproj/Localizable.strings +++ b/en.lproj/Localizable.strings @@ -7,3 +7,17 @@ */ "title" = "Diary"; +"NoTitle" = "No Title"; + +"failedFatchDataAlertTitle" = "Failed Fatch Data"; +"failedFatchDataAlertMessage" = "Try again"; +"failedFatchDataAlertAction" = "Yes"; + +"checkDeleteAlertTitle" = "Really?"; +"checkDeleteAlertMessage" = "Are you sure?"; +"checkDeleteAlertCancelAction" = "No"; +"checkDeleteAlertAction" = "Yes"; + +"share" = "Share..."; +"delete" = "Delete"; +"cancel" = "Cancel"; diff --git a/ko.lproj/Localizable.strings b/ko.lproj/Localizable.strings index 6361b9c70..e8e40a01a 100644 --- a/ko.lproj/Localizable.strings +++ b/ko.lproj/Localizable.strings @@ -7,3 +7,17 @@ */ "title" = "일기장"; +"noTitle" = "제목없음"; + +"failedFatchDataAlertTitle" = "데이터 불러오기 실패"; +"failedFatchDataAlertMessage" = "앱을 다시 실행해주십시오."; +"failedFatchDataAlertAction" = "확인"; + +"checkDeleteAlertTitle" = "진짜요?"; +"checkDeleteAlertMessage" = "정말로 삭제하시겠어요?"; +"checkDeleteAlertCancelAction" = "취소"; +"checkDeleteAlertAction" = "삭제"; + +"share" = "공유..."; +"delete" = "삭제"; +"cancel" = "취소"; From a45087f9a5831e07f1cc4403ddd4baa96d26dd4a Mon Sep 17 00:00:00 2001 From: Whales Date: Tue, 12 Sep 2023 11:46:52 +0900 Subject: [PATCH 16/26] =?UTF-8?q?=F0=9F=90=9B=20Fix:=20=EB=B9=88=20?= =?UTF-8?q?=ED=85=8D=EC=8A=A4=ED=8A=B8=EB=B7=B0=20=ED=81=B4=EB=A6=AD=20?= =?UTF-8?q?=ED=9B=84=20=EC=9E=85=EB=A0=A5=20=EC=8B=9C=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=EC=A0=80=EC=9E=A5=20=EC=95=88=EB=90=98=EB=8A=94=20?= =?UTF-8?q?=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 빈 일기 delete 처리 위치 변경 --- Diary/Controller/EditingDiaryViewController.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Diary/Controller/EditingDiaryViewController.swift b/Diary/Controller/EditingDiaryViewController.swift index 375ea7f19..c9447443a 100644 --- a/Diary/Controller/EditingDiaryViewController.swift +++ b/Diary/Controller/EditingDiaryViewController.swift @@ -41,6 +41,11 @@ final class EditingDiaryViewController: UIViewController, PresentableActivityVie override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) + if diaryTextView.text.isEmpty { + ContainerManager.shared.delete(id: diaryContent.id) + return + } + save() } @@ -130,11 +135,6 @@ final class EditingDiaryViewController: UIViewController, PresentableActivityVie private func save() { guard let text = diaryTextView.text else { return } - if text.isEmpty { - ContainerManager.shared.delete(id: diaryContent.id) - return - } - if let index = text.firstIndex(of: "\n") { diaryContent.title = String(text[text.startIndex ..< index]) diaryContent.body = String(text[index ..< text.endIndex]) From 1ec8d4a5e86c8a1fed22862ef96899e92e6d1931 Mon Sep 17 00:00:00 2001 From: MaryJo-github Date: Fri, 15 Sep 2023 01:40:56 +0900 Subject: [PATCH 17/26] =?UTF-8?q?:adhesive=5Fbandage:=20Chore:=20tableView?= =?UTF-8?q?=20=EC=83=81=EC=88=98=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F?= =?UTF-8?q?=20configure=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=B3=91=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Diary/Controller/DiaryViewController.swift | 9 ++++----- Diary/Controller/EditingDiaryViewController.swift | 6 +++--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Diary/Controller/DiaryViewController.swift b/Diary/Controller/DiaryViewController.swift index 40a7d0565..3c2693fa6 100644 --- a/Diary/Controller/DiaryViewController.swift +++ b/Diary/Controller/DiaryViewController.swift @@ -10,7 +10,7 @@ import OSLog final class DiaryViewController: UIViewController { private let diaryManager: DiaryManager - private var tableView: UITableView = { + private let tableView: UITableView = { let tableView = UITableView() tableView.translatesAutoresizingMaskIntoConstraints = false @@ -30,10 +30,8 @@ final class DiaryViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - configureView() + configureUI() configureTableView() - setupConstraints() - } override func viewWillAppear(_ animated: Bool) { @@ -43,11 +41,12 @@ final class DiaryViewController: UIViewController { tableView.reloadData() } - private func configureView() { + private func configureUI() { view.backgroundColor = .systemBackground view.addSubview(tableView) configureNavigationItem() + setupConstraints() } private func configureNavigationItem() { diff --git a/Diary/Controller/EditingDiaryViewController.swift b/Diary/Controller/EditingDiaryViewController.swift index c9447443a..548ae28da 100644 --- a/Diary/Controller/EditingDiaryViewController.swift +++ b/Diary/Controller/EditingDiaryViewController.swift @@ -32,8 +32,7 @@ final class EditingDiaryViewController: UIViewController, PresentableActivityVie override func viewDidLoad() { super.viewDidLoad() - configureView() - setupConstraints() + configureUI() setupDiaryTextView() setObserver() } @@ -49,11 +48,12 @@ final class EditingDiaryViewController: UIViewController, PresentableActivityVie save() } - private func configureView() { + private func configureUI() { view.backgroundColor = .systemBackground view.addSubview(diaryTextView) configureNavigationItem() + setupConstraints() } private func configureNavigationItem() { From 7eb44ef48f89646da298bdf5f24a189e08ebe141 Mon Sep 17 00:00:00 2001 From: MaryJo-github Date: Fri, 15 Sep 2023 02:05:14 +0900 Subject: [PATCH 18/26] =?UTF-8?q?:recycle:=20Refactor:=20String=20extensio?= =?UTF-8?q?n=20localized=20=ED=94=84=EB=A1=9C=ED=8D=BC=ED=8B=B0=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Diary.xcodeproj/project.pbxproj | 4 ++ Diary/Controller/DiaryViewController.swift | 12 +++--- .../EditingDiaryViewController.swift | 40 +++++++++---------- Diary/Extension/String+.swift | 12 ++++++ Diary/Extension/UIViewController+.swift | 12 +++--- Diary/View/DiaryTableViewCell.swift | 2 +- en.lproj/Localizable.strings | 6 +-- ko.lproj/Localizable.strings | 6 +-- 8 files changed, 55 insertions(+), 39 deletions(-) create mode 100644 Diary/Extension/String+.swift diff --git a/Diary.xcodeproj/project.pbxproj b/Diary.xcodeproj/project.pbxproj index 740d25f75..bd49f902b 100644 --- a/Diary.xcodeproj/project.pbxproj +++ b/Diary.xcodeproj/project.pbxproj @@ -24,6 +24,7 @@ 44A7622D2AA710F000C10AE2 /* Diary+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44A7622B2AA710F000C10AE2 /* Diary+CoreDataClass.swift */; }; 44A7622E2AA710F000C10AE2 /* Diary+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44A7622C2AA710F000C10AE2 /* Diary+CoreDataProperties.swift */; }; 44A762302AA712A200C10AE2 /* ContainerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44A7622F2AA712A200C10AE2 /* ContainerManager.swift */; }; + 44DC00332AB36F6000AF3C0E /* String+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44DC00322AB36F6000AF3C0E /* String+.swift */; }; C739AE25284DF28600741E8F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C739AE24284DF28600741E8F /* AppDelegate.swift */; }; C739AE27284DF28600741E8F /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C739AE26284DF28600741E8F /* SceneDelegate.swift */; }; C739AE29284DF28600741E8F /* DiaryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C739AE28284DF28600741E8F /* DiaryViewController.swift */; }; @@ -51,6 +52,7 @@ 44A7622B2AA710F000C10AE2 /* Diary+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Diary+CoreDataClass.swift"; sourceTree = ""; }; 44A7622C2AA710F000C10AE2 /* Diary+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Diary+CoreDataProperties.swift"; sourceTree = ""; }; 44A7622F2AA712A200C10AE2 /* ContainerManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerManager.swift; sourceTree = ""; }; + 44DC00322AB36F6000AF3C0E /* String+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+.swift"; sourceTree = ""; }; C739AE21284DF28600741E8F /* Diary.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Diary.app; sourceTree = BUILT_PRODUCTS_DIR; }; C739AE24284DF28600741E8F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; C739AE26284DF28600741E8F /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -79,6 +81,7 @@ 3C3C08EF2AA6241D00C8D4CF /* DateFormatter+.swift */, 3C3C088F2AA3AB6C00C8D4CF /* UIViewController+.swift */, 44A762002AA5F96700C10AE2 /* UITableViewCell+.swift */, + 44DC00322AB36F6000AF3C0E /* String+.swift */, ); path = Extension; sourceTree = ""; @@ -281,6 +284,7 @@ files = ( 44A761BA2A9F100900C10AE2 /* DecodingManager.swift in Sources */, 44A7622D2AA710F000C10AE2 /* Diary+CoreDataClass.swift in Sources */, + 44DC00332AB36F6000AF3C0E /* String+.swift in Sources */, 44A762032AA6031800C10AE2 /* ReuseIdentifiable.swift in Sources */, C739AE29284DF28600741E8F /* DiaryViewController.swift in Sources */, 4495F2EF2A9CCF62007D5278 /* DiaryTableViewCell.swift in Sources */, diff --git a/Diary/Controller/DiaryViewController.swift b/Diary/Controller/DiaryViewController.swift index 3c2693fa6..98b010396 100644 --- a/Diary/Controller/DiaryViewController.swift +++ b/Diary/Controller/DiaryViewController.swift @@ -55,7 +55,7 @@ final class DiaryViewController: UIViewController { action: #selector(tappedAddDiaryButton)) navigationItem.rightBarButtonItem = addDiaryBarButtonItem - navigationItem.title = String(localized: "title") + navigationItem.title = "title".localized } @objc private func tappedAddDiaryButton() { @@ -90,10 +90,10 @@ final class DiaryViewController: UIViewController { try diaryManager.fetchDiaryContents() } catch { os_log("%@", error.localizedDescription) - presentAlertWith(title: String(localized: "failedFatchDataAlertTitle"), - message: String(localized: "failedFatchDataAlertMessage."), + presentAlertWith(title: "failedFetchDataAlertTitle".localized, + message: "failedFetchDataAlertMessage".localized, preferredStyle: .alert, - actionConfigs: (String(localized: "failedFatchDataAlertAction"), .default, nil)) + actionConfigs: ("failedFetchDataAlertAction".localized, .default, nil)) } } } @@ -146,7 +146,7 @@ extension DiaryViewController: UITableViewDelegate, PresentableActivityView { let share = UIContextualAction( style: .normal, - title: String(localized: "share") + title: "share".localized ) { (_, _, success: @escaping (Bool) -> Void) in let diaryContentItem = diaryContent.title + diaryContent.body @@ -157,7 +157,7 @@ extension DiaryViewController: UITableViewDelegate, PresentableActivityView { let delete = UIContextualAction( style: .destructive, - title: String(localized: "delete") + title: "delete".localized ) { (_, _, success: @escaping (Bool) -> Void) in self.presentCheckDeleteAlert { _ in diff --git a/Diary/Controller/EditingDiaryViewController.swift b/Diary/Controller/EditingDiaryViewController.swift index 548ae28da..b49c7735f 100644 --- a/Diary/Controller/EditingDiaryViewController.swift +++ b/Diary/Controller/EditingDiaryViewController.swift @@ -36,7 +36,7 @@ final class EditingDiaryViewController: UIViewController, PresentableActivityVie setupDiaryTextView() setObserver() } - + override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) @@ -67,24 +67,24 @@ final class EditingDiaryViewController: UIViewController, PresentableActivityVie } @objc private func tappedOthersButton() { - let shareHandler: (UIAlertAction) -> Void = { _ in - let diaryContentItem = self.diaryContent.title + self.diaryContent.body - self.presentActivityView(shareItem: diaryContentItem) - } - - let deleteHandler: (UIAlertAction) -> Void = { _ in - self.presentCheckDeleteAlert { _ in - ContainerManager.shared.delete(id: self.diaryContent.id) - self.navigationController?.popViewController(animated: true) - } - } - - presentAlertWith(title: nil, - message: nil, - preferredStyle: .actionSheet, - actionConfigs: (String(localized: "share"), .default, shareHandler), - (String(localized: "delete"), .destructive, deleteHandler), - (String(localized: "cancel"), .cancel, nil)) + let shareHandler: (UIAlertAction) -> Void = { _ in + let diaryContentItem = self.diaryContent.title + self.diaryContent.body + self.presentActivityView(shareItem: diaryContentItem) + } + + let deleteHandler: (UIAlertAction) -> Void = { _ in + self.presentCheckDeleteAlert { _ in + ContainerManager.shared.delete(id: self.diaryContent.id) + self.navigationController?.popViewController(animated: true) + } + } + + presentAlertWith(title: nil, + message: nil, + preferredStyle: .actionSheet, + actionConfigs: ("share".localized, .default, shareHandler), + ("delete".localized, .destructive, deleteHandler), + ("cancel".localized, .cancel, nil)) } private func setupConstraints() { @@ -141,7 +141,7 @@ final class EditingDiaryViewController: UIViewController, PresentableActivityVie } else { diaryContent.title = text } - + ContainerManager.shared.update(diaryContent: diaryContent) } } diff --git a/Diary/Extension/String+.swift b/Diary/Extension/String+.swift new file mode 100644 index 000000000..f668c0ac4 --- /dev/null +++ b/Diary/Extension/String+.swift @@ -0,0 +1,12 @@ +// +// String+.swift +// Diary +// +// Created by Mary & Whales on 2023/09/15. +// + +extension String { + var localized: String { + return String(localized: LocalizationValue(self)) + } +} diff --git a/Diary/Extension/UIViewController+.swift b/Diary/Extension/UIViewController+.swift index 371bd8208..a7f6b1a71 100644 --- a/Diary/Extension/UIViewController+.swift +++ b/Diary/Extension/UIViewController+.swift @@ -24,15 +24,15 @@ extension UIViewController { handler: config.handler) alertController.addAction(action) } - + present(alertController, animated: true) } - + func presentCheckDeleteAlert(deleteHandler: @escaping (UIAlertAction) -> Void) { - presentAlertWith(title: String(localized: "checkDeleteAlertTitle"), - message: String(localized: "checkDeleteAlertMessage"), + presentAlertWith(title: "checkDeleteAlertTitle".localized, + message: "checkDeleteAlertMessage".localized, preferredStyle: .alert, - actionConfigs: (String(localized: "checkDeleteAlertCancelAction"), .cancel, nil), - (String(localized: "checkDeleteAlertAction"), .destructive, deleteHandler)) + actionConfigs: ("checkDeleteAlertCancelAction".localized, .cancel, nil), + ("checkDeleteAlertAction".localized, .destructive, deleteHandler)) } } diff --git a/Diary/View/DiaryTableViewCell.swift b/Diary/View/DiaryTableViewCell.swift index 0fc263bdd..e52a33924 100644 --- a/Diary/View/DiaryTableViewCell.swift +++ b/Diary/View/DiaryTableViewCell.swift @@ -64,7 +64,7 @@ final class DiaryTableViewCell: UITableViewCell { func configureCell(data: DiaryContent) { if data.title.isEmpty { - titleLabel.text = String(localized: "noTitle") + titleLabel.text = "noTitle".localized } else { titleLabel.text = data.title } diff --git a/en.lproj/Localizable.strings b/en.lproj/Localizable.strings index ecc9a92d3..31afb80e5 100644 --- a/en.lproj/Localizable.strings +++ b/en.lproj/Localizable.strings @@ -9,9 +9,9 @@ "title" = "Diary"; "NoTitle" = "No Title"; -"failedFatchDataAlertTitle" = "Failed Fatch Data"; -"failedFatchDataAlertMessage" = "Try again"; -"failedFatchDataAlertAction" = "Yes"; +"failedFetchDataAlertTitle" = "Failed Fetch Data"; +"failedFetchDataAlertMessage" = "Try again"; +"failedFetchDataAlertAction" = "Yes"; "checkDeleteAlertTitle" = "Really?"; "checkDeleteAlertMessage" = "Are you sure?"; diff --git a/ko.lproj/Localizable.strings b/ko.lproj/Localizable.strings index e8e40a01a..b491b90c5 100644 --- a/ko.lproj/Localizable.strings +++ b/ko.lproj/Localizable.strings @@ -9,9 +9,9 @@ "title" = "일기장"; "noTitle" = "제목없음"; -"failedFatchDataAlertTitle" = "데이터 불러오기 실패"; -"failedFatchDataAlertMessage" = "앱을 다시 실행해주십시오."; -"failedFatchDataAlertAction" = "확인"; +"failedFetchDataAlertTitle" = "데이터 불러오기 실패"; +"failedFetchDataAlertMessage" = "앱을 다시 실행해주십시오."; +"failedFetchDataAlertAction" = "확인"; "checkDeleteAlertTitle" = "진짜요?"; "checkDeleteAlertMessage" = "정말로 삭제하시겠어요?"; From 95dd5d95d996c4efcb19c9e4a062056e7c88fb48 Mon Sep 17 00:00:00 2001 From: MaryJo-github Date: Fri, 15 Sep 2023 14:30:43 +0900 Subject: [PATCH 19/26] =?UTF-8?q?:adhesive=5Fbandage:=20Chore:=20=EB=84=A4?= =?UTF-8?q?=EC=9D=B4=EB=B0=8D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Diary.xcodeproj/project.pbxproj | 8 +++---- Diary/Controller/DiaryViewController.swift | 10 ++++----- .../EditingDiaryViewController.swift | 14 ++++++------ Diary/Extension/UIViewController+.swift | 22 +++++++++---------- ...ew.swift => ActivityViewPresentable.swift} | 6 ++--- 5 files changed, 30 insertions(+), 30 deletions(-) rename Diary/Utility/{PresentableActivityView.swift => ActivityViewPresentable.swift} (74%) diff --git a/Diary.xcodeproj/project.pbxproj b/Diary.xcodeproj/project.pbxproj index bd49f902b..1dadf466f 100644 --- a/Diary.xcodeproj/project.pbxproj +++ b/Diary.xcodeproj/project.pbxproj @@ -13,7 +13,7 @@ 3C3C08F02AA6241D00C8D4CF /* DateFormatter+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C3C08EF2AA6241D00C8D4CF /* DateFormatter+.swift */; }; 3CCA1E772A9DBF56008683C3 /* .swiftlint.yml in Resources */ = {isa = PBXBuildFile; fileRef = 3CCA1E762A9DBF56008683C3 /* .swiftlint.yml */; }; 3CCA1E792A9F08C8008683C3 /* DiaryContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CCA1E782A9F08C8008683C3 /* DiaryContent.swift */; }; - 444E73F82AAAFA940079BEE5 /* PresentableActivityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 444E73F72AAAFA940079BEE5 /* PresentableActivityView.swift */; }; + 444E73F82AAAFA940079BEE5 /* ActivityViewPresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 444E73F72AAAFA940079BEE5 /* ActivityViewPresentable.swift */; }; 4495F2EF2A9CCF62007D5278 /* DiaryTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4495F2EE2A9CCF62007D5278 /* DiaryTableViewCell.swift */; }; 44A761BA2A9F100900C10AE2 /* DecodingManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44A761B92A9F100900C10AE2 /* DecodingManager.swift */; }; 44A761BD2A9F142B00C10AE2 /* DataError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44A761BC2A9F142B00C10AE2 /* DataError.swift */; }; @@ -41,7 +41,7 @@ 3C3C08EF2AA6241D00C8D4CF /* DateFormatter+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateFormatter+.swift"; sourceTree = ""; }; 3CCA1E762A9DBF56008683C3 /* .swiftlint.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = .swiftlint.yml; sourceTree = ""; }; 3CCA1E782A9F08C8008683C3 /* DiaryContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryContent.swift; sourceTree = ""; }; - 444E73F72AAAFA940079BEE5 /* PresentableActivityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresentableActivityView.swift; sourceTree = ""; }; + 444E73F72AAAFA940079BEE5 /* ActivityViewPresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityViewPresentable.swift; sourceTree = ""; }; 4495F2EE2A9CCF62007D5278 /* DiaryTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryTableViewCell.swift; sourceTree = ""; }; 44A761B92A9F100900C10AE2 /* DecodingManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecodingManager.swift; sourceTree = ""; }; 44A761BC2A9F142B00C10AE2 /* DataError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataError.swift; sourceTree = ""; }; @@ -101,7 +101,7 @@ children = ( 44A761B92A9F100900C10AE2 /* DecodingManager.swift */, 44A762022AA6031800C10AE2 /* ReuseIdentifiable.swift */, - 444E73F72AAAFA940079BEE5 /* PresentableActivityView.swift */, + 444E73F72AAAFA940079BEE5 /* ActivityViewPresentable.swift */, ); path = Utility; sourceTree = ""; @@ -294,7 +294,7 @@ C739AE27284DF28600741E8F /* SceneDelegate.swift in Sources */, 44A761BD2A9F142B00C10AE2 /* DataError.swift in Sources */, 44A762012AA5F96700C10AE2 /* UITableViewCell+.swift in Sources */, - 444E73F82AAAFA940079BEE5 /* PresentableActivityView.swift in Sources */, + 444E73F82AAAFA940079BEE5 /* ActivityViewPresentable.swift in Sources */, 44A762302AA712A200C10AE2 /* ContainerManager.swift in Sources */, 3C3C08902AA3AB6C00C8D4CF /* UIViewController+.swift in Sources */, 44A7622E2AA710F000C10AE2 /* Diary+CoreDataProperties.swift in Sources */, diff --git a/Diary/Controller/DiaryViewController.swift b/Diary/Controller/DiaryViewController.swift index 98b010396..a50dc1587 100644 --- a/Diary/Controller/DiaryViewController.swift +++ b/Diary/Controller/DiaryViewController.swift @@ -90,10 +90,10 @@ final class DiaryViewController: UIViewController { try diaryManager.fetchDiaryContents() } catch { os_log("%@", error.localizedDescription) - presentAlertWith(title: "failedFetchDataAlertTitle".localized, - message: "failedFetchDataAlertMessage".localized, - preferredStyle: .alert, - actionConfigs: ("failedFetchDataAlertAction".localized, .default, nil)) + presentAlert(title: "failedFetchDataAlertTitle".localized, + message: "failedFetchDataAlertMessage".localized, + preferredStyle: .alert, + actionConfigs: ("failedFetchDataAlertAction".localized, .default, nil)) } } } @@ -124,7 +124,7 @@ extension DiaryViewController: UITableViewDataSource { } } -extension DiaryViewController: UITableViewDelegate, PresentableActivityView { +extension DiaryViewController: UITableViewDelegate, ActivityViewPresentable { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { guard let diaryContents = diaryManager.diaryContents, let diaryContent = diaryContents[safe: indexPath.row] diff --git a/Diary/Controller/EditingDiaryViewController.swift b/Diary/Controller/EditingDiaryViewController.swift index b49c7735f..f311cc856 100644 --- a/Diary/Controller/EditingDiaryViewController.swift +++ b/Diary/Controller/EditingDiaryViewController.swift @@ -7,7 +7,7 @@ import UIKit -final class EditingDiaryViewController: UIViewController, PresentableActivityView { +final class EditingDiaryViewController: UIViewController, ActivityViewPresentable { private var diaryContent: DiaryContent private let diaryTextView: UITextView = { @@ -79,12 +79,12 @@ final class EditingDiaryViewController: UIViewController, PresentableActivityVie } } - presentAlertWith(title: nil, - message: nil, - preferredStyle: .actionSheet, - actionConfigs: ("share".localized, .default, shareHandler), - ("delete".localized, .destructive, deleteHandler), - ("cancel".localized, .cancel, nil)) + presentAlert(title: nil, + message: nil, + preferredStyle: .actionSheet, + actionConfigs: ("share".localized, .default, shareHandler), + ("delete".localized, .destructive, deleteHandler), + ("cancel".localized, .cancel, nil)) } private func setupConstraints() { diff --git a/Diary/Extension/UIViewController+.swift b/Diary/Extension/UIViewController+.swift index a7f6b1a71..0a1b72dd1 100644 --- a/Diary/Extension/UIViewController+.swift +++ b/Diary/Extension/UIViewController+.swift @@ -8,12 +8,12 @@ import UIKit extension UIViewController { - func presentAlertWith(title: String?, - message: String?, - preferredStyle: UIAlertController.Style, - actionConfigs: (title: String?, - style: UIAlertAction.Style, - handler: ((UIAlertAction) -> Void)?)...) { + func presentAlert(title: String?, + message: String?, + preferredStyle: UIAlertController.Style, + actionConfigs: (title: String?, + style: UIAlertAction.Style, + handler: ((UIAlertAction) -> Void)?)...) { let alertController = UIAlertController(title: title, message: message, preferredStyle: preferredStyle) @@ -29,10 +29,10 @@ extension UIViewController { } func presentCheckDeleteAlert(deleteHandler: @escaping (UIAlertAction) -> Void) { - presentAlertWith(title: "checkDeleteAlertTitle".localized, - message: "checkDeleteAlertMessage".localized, - preferredStyle: .alert, - actionConfigs: ("checkDeleteAlertCancelAction".localized, .cancel, nil), - ("checkDeleteAlertAction".localized, .destructive, deleteHandler)) + presentAlert(title: "checkDeleteAlertTitle".localized, + message: "checkDeleteAlertMessage".localized, + preferredStyle: .alert, + actionConfigs: ("checkDeleteAlertCancelAction".localized, .cancel, nil), + ("checkDeleteAlertAction".localized, .destructive, deleteHandler)) } } diff --git a/Diary/Utility/PresentableActivityView.swift b/Diary/Utility/ActivityViewPresentable.swift similarity index 74% rename from Diary/Utility/PresentableActivityView.swift rename to Diary/Utility/ActivityViewPresentable.swift index 9f397280a..d70d47c99 100644 --- a/Diary/Utility/PresentableActivityView.swift +++ b/Diary/Utility/ActivityViewPresentable.swift @@ -1,5 +1,5 @@ // -// PresentableActivityView.swift +// ActivityViewPresentable.swift // Diary // // Created by Mary & Whales on 2023/09/08. @@ -7,11 +7,11 @@ import UIKit -protocol PresentableActivityView where Self: UIViewController { +protocol ActivityViewPresentable where Self: UIViewController { func presentActivityView(shareItem: String) } -extension PresentableActivityView { +extension ActivityViewPresentable { func presentActivityView(shareItem: String) { let activityViewController = UIActivityViewController( activityItems: [shareItem], From 8928097c156d680823e1e71a84ee50357a7e559a Mon Sep 17 00:00:00 2001 From: Whales Date: Fri, 15 Sep 2023 15:14:31 +0900 Subject: [PATCH 20/26] =?UTF-8?q?=E2=9C=A8=20Feat:=20Logger=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EC=83=9D=EC=84=B1=ED=95=B4=EC=84=9C=20DiaryViewCon?= =?UTF-8?q?troller=EC=97=90=20=EC=A3=BC=EC=9E=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Diary.xcodeproj/project.pbxproj | 4 ++++ Diary/Controller/DiaryViewController.swift | 9 +++++---- Diary/Model/ContainerManager.swift | 13 ++++++------- Diary/Utility/Logger.swift | 14 ++++++++++++++ Diary/View/DiaryTableViewCell.swift | 13 ++++--------- 5 files changed, 33 insertions(+), 20 deletions(-) create mode 100644 Diary/Utility/Logger.swift diff --git a/Diary.xcodeproj/project.pbxproj b/Diary.xcodeproj/project.pbxproj index 1dadf466f..8526b4d33 100644 --- a/Diary.xcodeproj/project.pbxproj +++ b/Diary.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 3C3C08902AA3AB6C00C8D4CF /* UIViewController+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C3C088F2AA3AB6C00C8D4CF /* UIViewController+.swift */; }; 3C3C08EE2AA6055500C8D4CF /* Collection+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C3C08ED2AA6055500C8D4CF /* Collection+.swift */; }; 3C3C08F02AA6241D00C8D4CF /* DateFormatter+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C3C08EF2AA6241D00C8D4CF /* DateFormatter+.swift */; }; + 3C6B0B772AB427C30011E59D /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C6B0B762AB427C30011E59D /* Logger.swift */; }; 3CCA1E772A9DBF56008683C3 /* .swiftlint.yml in Resources */ = {isa = PBXBuildFile; fileRef = 3CCA1E762A9DBF56008683C3 /* .swiftlint.yml */; }; 3CCA1E792A9F08C8008683C3 /* DiaryContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CCA1E782A9F08C8008683C3 /* DiaryContent.swift */; }; 444E73F82AAAFA940079BEE5 /* ActivityViewPresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 444E73F72AAAFA940079BEE5 /* ActivityViewPresentable.swift */; }; @@ -39,6 +40,7 @@ 3C3C088F2AA3AB6C00C8D4CF /* UIViewController+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+.swift"; sourceTree = ""; }; 3C3C08ED2AA6055500C8D4CF /* Collection+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Collection+.swift"; sourceTree = ""; }; 3C3C08EF2AA6241D00C8D4CF /* DateFormatter+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateFormatter+.swift"; sourceTree = ""; }; + 3C6B0B762AB427C30011E59D /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; }; 3CCA1E762A9DBF56008683C3 /* .swiftlint.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = .swiftlint.yml; sourceTree = ""; }; 3CCA1E782A9F08C8008683C3 /* DiaryContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryContent.swift; sourceTree = ""; }; 444E73F72AAAFA940079BEE5 /* ActivityViewPresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityViewPresentable.swift; sourceTree = ""; }; @@ -102,6 +104,7 @@ 44A761B92A9F100900C10AE2 /* DecodingManager.swift */, 44A762022AA6031800C10AE2 /* ReuseIdentifiable.swift */, 444E73F72AAAFA940079BEE5 /* ActivityViewPresentable.swift */, + 3C6B0B762AB427C30011E59D /* Logger.swift */, ); path = Utility; sourceTree = ""; @@ -286,6 +289,7 @@ 44A7622D2AA710F000C10AE2 /* Diary+CoreDataClass.swift in Sources */, 44DC00332AB36F6000AF3C0E /* String+.swift in Sources */, 44A762032AA6031800C10AE2 /* ReuseIdentifiable.swift in Sources */, + 3C6B0B772AB427C30011E59D /* Logger.swift in Sources */, C739AE29284DF28600741E8F /* DiaryViewController.swift in Sources */, 4495F2EF2A9CCF62007D5278 /* DiaryTableViewCell.swift in Sources */, C739AE25284DF28600741E8F /* AppDelegate.swift in Sources */, diff --git a/Diary/Controller/DiaryViewController.swift b/Diary/Controller/DiaryViewController.swift index a50dc1587..bfa231bac 100644 --- a/Diary/Controller/DiaryViewController.swift +++ b/Diary/Controller/DiaryViewController.swift @@ -5,10 +5,10 @@ // last modified by Mary & Whales import UIKit -import OSLog final class DiaryViewController: UIViewController { private let diaryManager: DiaryManager + private let logger: Logger private let tableView: UITableView = { let tableView = UITableView() @@ -17,8 +17,9 @@ final class DiaryViewController: UIViewController { return tableView }() - init(diaryManager: DiaryManager = DiaryManager()) { + init(diaryManager: DiaryManager = DiaryManager(), logger: Logger = Logger()) { self.diaryManager = diaryManager + self.logger = logger super.init(nibName: nil, bundle: nil) } @@ -29,7 +30,7 @@ final class DiaryViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - + configureUI() configureTableView() } @@ -89,7 +90,7 @@ final class DiaryViewController: UIViewController { do { try diaryManager.fetchDiaryContents() } catch { - os_log("%@", error.localizedDescription) + Logger.osLog(error.localizedDescription) presentAlert(title: "failedFetchDataAlertTitle".localized, message: "failedFetchDataAlertMessage".localized, preferredStyle: .alert, diff --git a/Diary/Model/ContainerManager.swift b/Diary/Model/ContainerManager.swift index 8db4f88e3..a718b7cb7 100644 --- a/Diary/Model/ContainerManager.swift +++ b/Diary/Model/ContainerManager.swift @@ -6,13 +6,12 @@ // import CoreData -import OSLog final class ContainerManager { static let shared = ContainerManager() private init() { } - + private let persistentContainer: NSPersistentContainer = { let container = NSPersistentContainer(name: "Diary") container.loadPersistentStores { _, error in @@ -38,7 +37,7 @@ final class ContainerManager { managedObject.setValue(diaryContent.body, forKey: "body") managedObject.setValue(diaryContent.timeInterval, forKey: "timeInterval") managedObject.setValue(diaryContent.id, forKey: "id") - + save() } } @@ -47,7 +46,7 @@ final class ContainerManager { do { try context.save() } catch { - os_log("%@", error.localizedDescription) + Logger.osLog(error.localizedDescription) } } @@ -66,7 +65,7 @@ final class ContainerManager { save() } catch { - os_log("%@", error.localizedDescription) + Logger.osLog(error.localizedDescription) } } @@ -75,7 +74,7 @@ final class ContainerManager { let diaries = try context.fetch(Diary.fetchRequest()) return diaries } catch { - os_log("%@", error.localizedDescription) + Logger.osLog(error.localizedDescription) } return nil @@ -92,7 +91,7 @@ final class ContainerManager { context.delete(diary) try context.save() } catch { - os_log("%@", error.localizedDescription) + Logger.osLog(error.localizedDescription) } } } diff --git a/Diary/Utility/Logger.swift b/Diary/Utility/Logger.swift new file mode 100644 index 000000000..4a9e033b8 --- /dev/null +++ b/Diary/Utility/Logger.swift @@ -0,0 +1,14 @@ +// +// Logger.swift +// Diary +// +// Created by Mary & Whales on 9/15/23. +// + +import OSLog + +struct Logger { + static func osLog(_ message: String) { + os_log("%@", message) + } +} diff --git a/Diary/View/DiaryTableViewCell.swift b/Diary/View/DiaryTableViewCell.swift index e52a33924..5a53f43d4 100644 --- a/Diary/View/DiaryTableViewCell.swift +++ b/Diary/View/DiaryTableViewCell.swift @@ -63,15 +63,10 @@ final class DiaryTableViewCell: UITableViewCell { } func configureCell(data: DiaryContent) { - if data.title.isEmpty { - titleLabel.text = "noTitle".localized - } else { - titleLabel.text = data.title - } - - dateLabel.text = data.date - previewLabel.text = data.body.trimmingCharacters(in: .whitespacesAndNewlines) - } + titleLabel.text = data.title.isEmpty ? "noTitle".localized : data.title + dateLabel.text = data.date + previewLabel.text = data.body.trimmingCharacters(in: .whitespacesAndNewlines) + } private func configureUI() { descriptionStackView.addArrangedSubview(dateLabel) From d838492b6a5e4d2ae65973c540e692bb2b77835e Mon Sep 17 00:00:00 2001 From: MaryJo-github Date: Fri, 15 Sep 2023 18:11:05 +0900 Subject: [PATCH 21/26] =?UTF-8?q?:recycle:=20Refactor:=20TableViewCell?= =?UTF-8?q?=EC=9D=98=20AutoLayout=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Diary/View/DiaryTableViewCell.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Diary/View/DiaryTableViewCell.swift b/Diary/View/DiaryTableViewCell.swift index 5a53f43d4..d3f241cc6 100644 --- a/Diary/View/DiaryTableViewCell.swift +++ b/Diary/View/DiaryTableViewCell.swift @@ -21,6 +21,7 @@ final class DiaryTableViewCell: UITableViewCell { label.font = .preferredFont(forTextStyle: .body) label.adjustsFontForContentSizeCategory = true label.setContentCompressionResistancePriority(.required, for: .horizontal) + label.setContentHuggingPriority(.defaultHigh, for: .horizontal) return label }() @@ -29,7 +30,6 @@ final class DiaryTableViewCell: UITableViewCell { let label = UILabel() label.font = .preferredFont(forTextStyle: .caption1) label.adjustsFontForContentSizeCategory = true - label.setContentHuggingPriority( .init(100), for: .horizontal) return label }() From cfecbc7f2d83eb246b8e08dfa3b0aef8e04c85b0 Mon Sep 17 00:00:00 2001 From: Whales Date: Sat, 16 Sep 2023 01:46:03 +0900 Subject: [PATCH 22/26] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20if=20?= =?UTF-8?q?=EB=AC=B8=EC=9D=84=20guard=20=EB=AC=B8=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Diary/Controller/EditingDiaryViewController.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Diary/Controller/EditingDiaryViewController.swift b/Diary/Controller/EditingDiaryViewController.swift index f311cc856..5cef39504 100644 --- a/Diary/Controller/EditingDiaryViewController.swift +++ b/Diary/Controller/EditingDiaryViewController.swift @@ -40,12 +40,12 @@ final class EditingDiaryViewController: UIViewController, ActivityViewPresentabl override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) - if diaryTextView.text.isEmpty { - ContainerManager.shared.delete(id: diaryContent.id) + guard diaryTextView.text.isEmpty else { + save() return } - save() + ContainerManager.shared.delete(id: diaryContent.id) } private func configureUI() { From c9e449011c481dfb89b6b85164cf7c05050ba738 Mon Sep 17 00:00:00 2001 From: MaryJo-github Date: Sun, 17 Sep 2023 02:30:09 +0900 Subject: [PATCH 23/26] =?UTF-8?q?:recycle:=20Refactor:=20ContainerManager?= =?UTF-8?q?=EB=A5=BC=20DiaryManager=EC=97=90=20=EB=B3=91=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Diary.xcodeproj/project.pbxproj | 4 - Diary/Application/SceneDelegate.swift | 3 +- Diary/Controller/DiaryViewController.swift | 39 +++++--- .../EditingDiaryViewController.swift | 45 +++++++-- Diary/Model/ContainerManager.swift | 97 ------------------- Diary/Model/DiaryManager.swift | 73 +++++++++++++- Diary/Utility/Logger.swift | 2 +- 7 files changed, 133 insertions(+), 130 deletions(-) delete mode 100644 Diary/Model/ContainerManager.swift diff --git a/Diary.xcodeproj/project.pbxproj b/Diary.xcodeproj/project.pbxproj index 8526b4d33..104cb8b44 100644 --- a/Diary.xcodeproj/project.pbxproj +++ b/Diary.xcodeproj/project.pbxproj @@ -24,7 +24,6 @@ 44A762032AA6031800C10AE2 /* ReuseIdentifiable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44A762022AA6031800C10AE2 /* ReuseIdentifiable.swift */; }; 44A7622D2AA710F000C10AE2 /* Diary+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44A7622B2AA710F000C10AE2 /* Diary+CoreDataClass.swift */; }; 44A7622E2AA710F000C10AE2 /* Diary+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44A7622C2AA710F000C10AE2 /* Diary+CoreDataProperties.swift */; }; - 44A762302AA712A200C10AE2 /* ContainerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44A7622F2AA712A200C10AE2 /* ContainerManager.swift */; }; 44DC00332AB36F6000AF3C0E /* String+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44DC00322AB36F6000AF3C0E /* String+.swift */; }; C739AE25284DF28600741E8F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C739AE24284DF28600741E8F /* AppDelegate.swift */; }; C739AE27284DF28600741E8F /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C739AE26284DF28600741E8F /* SceneDelegate.swift */; }; @@ -53,7 +52,6 @@ 44A762022AA6031800C10AE2 /* ReuseIdentifiable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReuseIdentifiable.swift; sourceTree = ""; }; 44A7622B2AA710F000C10AE2 /* Diary+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Diary+CoreDataClass.swift"; sourceTree = ""; }; 44A7622C2AA710F000C10AE2 /* Diary+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Diary+CoreDataProperties.swift"; sourceTree = ""; }; - 44A7622F2AA712A200C10AE2 /* ContainerManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerManager.swift; sourceTree = ""; }; 44DC00322AB36F6000AF3C0E /* String+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+.swift"; sourceTree = ""; }; C739AE21284DF28600741E8F /* Diary.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Diary.app; sourceTree = BUILT_PRODUCTS_DIR; }; C739AE24284DF28600741E8F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -93,7 +91,6 @@ children = ( 3CCA1E782A9F08C8008683C3 /* DiaryContent.swift */, 44A761BE2A9F163600C10AE2 /* DiaryManager.swift */, - 44A7622F2AA712A200C10AE2 /* ContainerManager.swift */, ); path = Model; sourceTree = ""; @@ -299,7 +296,6 @@ 44A761BD2A9F142B00C10AE2 /* DataError.swift in Sources */, 44A762012AA5F96700C10AE2 /* UITableViewCell+.swift in Sources */, 444E73F82AAAFA940079BEE5 /* ActivityViewPresentable.swift in Sources */, - 44A762302AA712A200C10AE2 /* ContainerManager.swift in Sources */, 3C3C08902AA3AB6C00C8D4CF /* UIViewController+.swift in Sources */, 44A7622E2AA710F000C10AE2 /* Diary+CoreDataProperties.swift in Sources */, 3C3C08F02AA6241D00C8D4CF /* DateFormatter+.swift in Sources */, diff --git a/Diary/Application/SceneDelegate.swift b/Diary/Application/SceneDelegate.swift index 549950be2..48d188ab7 100644 --- a/Diary/Application/SceneDelegate.swift +++ b/Diary/Application/SceneDelegate.swift @@ -13,8 +13,9 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = (scene as? UIWindowScene) else { return } + guard let diaryManager = try? DiaryManager() else { return } - let rootViewController = DiaryViewController() + let rootViewController = DiaryViewController(diaryManager: diaryManager) let navigationController = UINavigationController(rootViewController: rootViewController) window = UIWindow(windowScene: windowScene) diff --git a/Diary/Controller/DiaryViewController.swift b/Diary/Controller/DiaryViewController.swift index bfa231bac..2221f1d61 100644 --- a/Diary/Controller/DiaryViewController.swift +++ b/Diary/Controller/DiaryViewController.swift @@ -17,7 +17,7 @@ final class DiaryViewController: UIViewController { return tableView }() - init(diaryManager: DiaryManager = DiaryManager(), logger: Logger = Logger()) { + init(diaryManager: DiaryManager, logger: Logger = Logger()) { self.diaryManager = diaryManager self.logger = logger @@ -38,7 +38,7 @@ final class DiaryViewController: UIViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - fetchDiaryContents() + refreshDiaries() tableView.reloadData() } @@ -66,7 +66,9 @@ final class DiaryViewController: UIViewController { } private func showEditingDiaryViewController(with diaryContent: DiaryContent) { - let editingDiaryViewController = EditingDiaryViewController(with: diaryContent) + let editingDiaryViewController = EditingDiaryViewController(diaryManager: diaryManager, + logger: logger, + with: diaryContent) show(editingDiaryViewController, sender: self) } @@ -86,22 +88,31 @@ final class DiaryViewController: UIViewController { ]) } - private func fetchDiaryContents() { + private func refreshDiaries() { do { - try diaryManager.fetchDiaryContents() + try diaryManager.refresh() } catch { - Logger.osLog(error.localizedDescription) + logger.osLog(error.localizedDescription) presentAlert(title: "failedFetchDataAlertTitle".localized, message: "failedFetchDataAlertMessage".localized, preferredStyle: .alert, actionConfigs: ("failedFetchDataAlertAction".localized, .default, nil)) } } + + private func deleteDiary(id: UUID) { + do { + try diaryManager.delete(id: id) + } catch { + logger.osLog(error.localizedDescription) + // Alert 추가 + } + } } extension DiaryViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return diaryManager.diaryContents?.count ?? .zero + return diaryManager.diaryContents.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { @@ -113,8 +124,7 @@ extension DiaryViewController: UITableViewDataSource { return UITableViewCell() } - guard let diaryContents = diaryManager.diaryContents, - let diaryContent = diaryContents[safe: indexPath.row] + guard let diaryContent = diaryManager.diaryContents[safe: indexPath.row] else { return UITableViewCell() } @@ -127,8 +137,7 @@ extension DiaryViewController: UITableViewDataSource { extension DiaryViewController: UITableViewDelegate, ActivityViewPresentable { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - guard let diaryContents = diaryManager.diaryContents, - let diaryContent = diaryContents[safe: indexPath.row] + guard let diaryContent = diaryManager.diaryContents[safe: indexPath.row] else { return } @@ -139,8 +148,7 @@ extension DiaryViewController: UITableViewDelegate, ActivityViewPresentable { func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { - guard let diaryContents = diaryManager.diaryContents, - let diaryContent = diaryContents[safe: indexPath.row] + guard let diaryContent = diaryManager.diaryContents[safe: indexPath.row] else { return nil } @@ -161,9 +169,8 @@ extension DiaryViewController: UITableViewDelegate, ActivityViewPresentable { title: "delete".localized ) { (_, _, success: @escaping (Bool) -> Void) in - self.presentCheckDeleteAlert { _ in - ContainerManager.shared.delete(id: diaryContent.id) - self.diaryManager.diaryContents?.remove(at: indexPath.row) + self.presentCheckDeleteAlert { [self] _ in + deleteDiary(id: diaryContent.id) tableView.deleteRows(at: [indexPath], with: .fade) } diff --git a/Diary/Controller/EditingDiaryViewController.swift b/Diary/Controller/EditingDiaryViewController.swift index 5cef39504..13abfb42a 100644 --- a/Diary/Controller/EditingDiaryViewController.swift +++ b/Diary/Controller/EditingDiaryViewController.swift @@ -8,6 +8,8 @@ import UIKit final class EditingDiaryViewController: UIViewController, ActivityViewPresentable { + private let diaryManager: DiaryManager + private let logger: Logger private var diaryContent: DiaryContent private let diaryTextView: UITextView = { @@ -19,7 +21,9 @@ final class EditingDiaryViewController: UIViewController, ActivityViewPresentabl return textView }() - init(with diaryContent: DiaryContent) { + init(diaryManager: DiaryManager, logger: Logger, with diaryContent: DiaryContent) { + self.diaryManager = diaryManager + self.logger = logger self.diaryContent = diaryContent super.init(nibName: nil, bundle: nil) @@ -45,7 +49,7 @@ final class EditingDiaryViewController: UIViewController, ActivityViewPresentabl return } - ContainerManager.shared.delete(id: diaryContent.id) + deleteDiary(id: diaryContent.id) } private func configureUI() { @@ -73,8 +77,8 @@ final class EditingDiaryViewController: UIViewController, ActivityViewPresentabl } let deleteHandler: (UIAlertAction) -> Void = { _ in - self.presentCheckDeleteAlert { _ in - ContainerManager.shared.delete(id: self.diaryContent.id) + self.presentCheckDeleteAlert { [self] _ in + deleteDiary(id: diaryContent.id) self.navigationController?.popViewController(animated: true) } } @@ -101,7 +105,7 @@ final class EditingDiaryViewController: UIViewController, ActivityViewPresentabl diaryTextView.text = diaryContent.title + diaryContent.body } else { diaryTextView.becomeFirstResponder() - ContainerManager.shared.insert(diaryContent: diaryContent) + insertDiary(diaryContent: diaryContent) } addGesture() @@ -142,7 +146,7 @@ final class EditingDiaryViewController: UIViewController, ActivityViewPresentabl diaryContent.title = text } - ContainerManager.shared.update(diaryContent: diaryContent) + updateDiary(diaryContent: diaryContent) } } @@ -151,3 +155,32 @@ extension EditingDiaryViewController: UITextViewDelegate { save() } } + +extension EditingDiaryViewController { + private func deleteDiary(id: UUID) { + do { + try diaryManager.delete(id: id) + } catch { + logger.osLog(error.localizedDescription) + // Alert 추가 + } + } + + private func insertDiary(diaryContent: DiaryContent) { + do { + try diaryManager.insert(diaryContent: diaryContent) + } catch { + logger.osLog(error.localizedDescription) + // Alert 추가 + } + } + + private func updateDiary(diaryContent: DiaryContent) { + do { + try diaryManager.update(diaryContent: diaryContent) + } catch { + logger.osLog(error.localizedDescription) + // Alert 추가 + } + } +} diff --git a/Diary/Model/ContainerManager.swift b/Diary/Model/ContainerManager.swift deleted file mode 100644 index a718b7cb7..000000000 --- a/Diary/Model/ContainerManager.swift +++ /dev/null @@ -1,97 +0,0 @@ -// -// ContainerManager.swift -// Diary -// -// Created by Mary & Whales on 2023/09/05. -// - -import CoreData - -final class ContainerManager { - static let shared = ContainerManager() - - private init() { } - - private let persistentContainer: NSPersistentContainer = { - let container = NSPersistentContainer(name: "Diary") - container.loadPersistentStores { _, error in - if let error = error { - fatalError("Unable to load persistent stores: \(error)") - } - } - - return container - }() - - private var context: NSManagedObjectContext { - return persistentContainer.viewContext - } - - func insert(diaryContent: DiaryContent) { - let entity = NSEntityDescription.entity(forEntityName: "Diary", in: context) - - if let entity = entity { - let managedObject = NSManagedObject(entity: entity, insertInto: context) - - managedObject.setValue(diaryContent.title, forKey: "title") - managedObject.setValue(diaryContent.body, forKey: "body") - managedObject.setValue(diaryContent.timeInterval, forKey: "timeInterval") - managedObject.setValue(diaryContent.id, forKey: "id") - - save() - } - } - - func save() { - do { - try context.save() - } catch { - Logger.osLog(error.localizedDescription) - } - } - - func update(diaryContent: DiaryContent) { - let request = Diary.fetchRequest() - - request.predicate = NSPredicate(format: "id == %@", diaryContent.id as CVarArg) - - do { - let diaries = try context.fetch(request) - - diaries.forEach { diary in - diary.title = diaryContent.title - diary.body = diaryContent.body - } - - save() - } catch { - Logger.osLog(error.localizedDescription) - } - } - - func fetchAll() -> [Diary]? { - do { - let diaries = try context.fetch(Diary.fetchRequest()) - return diaries - } catch { - Logger.osLog(error.localizedDescription) - } - - return nil - } - - func delete(id: UUID) { - let request = Diary.fetchRequest() - - request.predicate = NSPredicate(format: "id == %@", id as CVarArg) - - do { - let diaries = try context.fetch(request) - guard let diary = diaries.first else { return } - context.delete(diary) - try context.save() - } catch { - Logger.osLog(error.localizedDescription) - } - } -} diff --git a/Diary/Model/DiaryManager.swift b/Diary/Model/DiaryManager.swift index fc2db7639..3bb143c23 100644 --- a/Diary/Model/DiaryManager.swift +++ b/Diary/Model/DiaryManager.swift @@ -5,17 +5,35 @@ // Created by Mary & Whales on 2023/08/30. // -import OSLog +import CoreData final class DiaryManager { - var diaryContents: [DiaryContent]? + private(set) var diaryContents: [DiaryContent] = [] - func fetchDiaryContents() throws { - guard let data: [Diary] = ContainerManager.shared.fetchAll() else { return } + private let persistentContainer: NSPersistentContainer = { + let container = NSPersistentContainer(name: "Diary") + container.loadPersistentStores { _, error in + if let error = error { + fatalError("Unable to load persistent stores: \(error)") + } + } + return container + }() + + private var context: NSManagedObjectContext { + return persistentContainer.viewContext + } + + init() throws { + try refresh() + } + + func refresh() throws { + let diaries = try context.fetch(Diary.fetchRequest()) var contents: [DiaryContent] = [] - data.forEach { element in + diaries.forEach { element in contents.append(DiaryContent(id: element.id, title: element.title, body: element.body, @@ -24,4 +42,49 @@ final class DiaryManager { diaryContents = contents } + + func insert(diaryContent: DiaryContent) throws { + let entity = NSEntityDescription.entity(forEntityName: "Diary", in: context) + + if let entity = entity { + let managedObject = NSManagedObject(entity: entity, insertInto: context) + + managedObject.setValue(diaryContent.title, forKey: "title") + managedObject.setValue(diaryContent.body, forKey: "body") + managedObject.setValue(diaryContent.timeInterval, forKey: "timeInterval") + managedObject.setValue(diaryContent.id, forKey: "id") + + try context.save() + } + } + + func update(diaryContent: DiaryContent) throws { + let request = Diary.fetchRequest() + + request.predicate = NSPredicate(format: "id == %@", diaryContent.id as CVarArg) + + let diaries = try context.fetch(request) + + diaries.forEach { diary in + diary.title = diaryContent.title + diary.body = diaryContent.body + } + + try context.save() + } + + func delete(id: UUID) throws { + let request = Diary.fetchRequest() + + request.predicate = NSPredicate(format: "id == %@", id as CVarArg) + + let diaries = try context.fetch(request) + + guard let diary = diaries.first else { return } + + context.delete(diary) + try context.save() + + diaryContents = diaryContents.filter { $0.id != id } + } } diff --git a/Diary/Utility/Logger.swift b/Diary/Utility/Logger.swift index 4a9e033b8..fa33ea64c 100644 --- a/Diary/Utility/Logger.swift +++ b/Diary/Utility/Logger.swift @@ -8,7 +8,7 @@ import OSLog struct Logger { - static func osLog(_ message: String) { + func osLog(_ message: String) { os_log("%@", message) } } From dfc8a855d56f5f3ae5cb75fbb4c5827a277eb784 Mon Sep 17 00:00:00 2001 From: Whales Date: Sun, 17 Sep 2023 02:44:22 +0900 Subject: [PATCH 24/26] =?UTF-8?q?=E2=9C=A8=20Feat:=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=EC=A0=80=EC=9E=A5=20=EB=B0=8F=20=EC=82=AD=EC=A0=9C?= =?UTF-8?q?=20=EC=8B=A4=ED=8C=A8=EC=8B=9C=20Alert=20=EB=9D=84=EC=9A=B0?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Alert 메세지에 localization 적용 --- Diary/Controller/DiaryViewController.swift | 47 ++++++++++--------- .../EditingDiaryViewController.swift | 15 ++++-- en.lproj/Localizable.strings | 8 ++++ ko.lproj/Localizable.strings | 8 ++++ 4 files changed, 54 insertions(+), 24 deletions(-) diff --git a/Diary/Controller/DiaryViewController.swift b/Diary/Controller/DiaryViewController.swift index 2221f1d61..77239f6d6 100644 --- a/Diary/Controller/DiaryViewController.swift +++ b/Diary/Controller/DiaryViewController.swift @@ -87,27 +87,6 @@ final class DiaryViewController: UIViewController { tableView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor), ]) } - - private func refreshDiaries() { - do { - try diaryManager.refresh() - } catch { - logger.osLog(error.localizedDescription) - presentAlert(title: "failedFetchDataAlertTitle".localized, - message: "failedFetchDataAlertMessage".localized, - preferredStyle: .alert, - actionConfigs: ("failedFetchDataAlertAction".localized, .default, nil)) - } - } - - private func deleteDiary(id: UUID) { - do { - try diaryManager.delete(id: id) - } catch { - logger.osLog(error.localizedDescription) - // Alert 추가 - } - } } extension DiaryViewController: UITableViewDataSource { @@ -180,3 +159,29 @@ extension DiaryViewController: UITableViewDelegate, ActivityViewPresentable { return UISwipeActionsConfiguration(actions: [delete, share]) } } + +extension DiaryViewController { + private func refreshDiaries() { + do { + try diaryManager.refresh() + } catch { + logger.osLog(error.localizedDescription) + presentAlert(title: "failedFetchDataAlertTitle".localized, + message: "failedFetchDataAlertMessage".localized, + preferredStyle: .alert, + actionConfigs: ("failedFetchDataAlertAction".localized, .default, nil)) + } + } + + private func deleteDiary(id: UUID) { + do { + try diaryManager.delete(id: id) + } catch { + logger.osLog(error.localizedDescription) + presentAlert(title: "failedDeleteDataAlertTitle".localized, + message: "failedDeleteDataAlertMessage".localized, + preferredStyle: .alert, + actionConfigs: ("failedDeleteDataAlertAction".localized, .default, nil)) + } + } +} diff --git a/Diary/Controller/EditingDiaryViewController.swift b/Diary/Controller/EditingDiaryViewController.swift index 13abfb42a..81b717dda 100644 --- a/Diary/Controller/EditingDiaryViewController.swift +++ b/Diary/Controller/EditingDiaryViewController.swift @@ -162,7 +162,10 @@ extension EditingDiaryViewController { try diaryManager.delete(id: id) } catch { logger.osLog(error.localizedDescription) - // Alert 추가 + presentAlert(title: "failedDeleteDataAlertTitle".localized, + message: "failedDeleteDataAlertMessage".localized, + preferredStyle: .alert, + actionConfigs: ("failedDeleteDataAlertAction".localized, .default, nil)) } } @@ -171,7 +174,10 @@ extension EditingDiaryViewController { try diaryManager.insert(diaryContent: diaryContent) } catch { logger.osLog(error.localizedDescription) - // Alert 추가 + presentAlert(title: "failedSaveDataAlertTitle".localized, + message: "failedSaveDataAlertMessage".localized, + preferredStyle: .alert, + actionConfigs: ("failedSaveDataAlertAction".localized, .default, nil)) } } @@ -180,7 +186,10 @@ extension EditingDiaryViewController { try diaryManager.update(diaryContent: diaryContent) } catch { logger.osLog(error.localizedDescription) - // Alert 추가 + presentAlert(title: "failedSaveDataAlertTitle".localized, + message: "failedSaveDataAlertMessage".localized, + preferredStyle: .alert, + actionConfigs: ("failedSaveDataAlertAction".localized, .default, nil)) } } } diff --git a/en.lproj/Localizable.strings b/en.lproj/Localizable.strings index 31afb80e5..acc239125 100644 --- a/en.lproj/Localizable.strings +++ b/en.lproj/Localizable.strings @@ -13,6 +13,14 @@ "failedFetchDataAlertMessage" = "Try again"; "failedFetchDataAlertAction" = "Yes"; +"failedDeleteDataAlertTitle" = "Failed Delete Data"; +"failedDeleteDataAlertMessage" = "Try again"; +"failedDeleteDataAlertAction" = "Yes"; + +"failedSaveDataAlertTitle" = "Failed Save Data"; +"failedSaveDataAlertMessage" = "Try again"; +"failedSaveDataAlertAction" = "Yes"; + "checkDeleteAlertTitle" = "Really?"; "checkDeleteAlertMessage" = "Are you sure?"; "checkDeleteAlertCancelAction" = "No"; diff --git a/ko.lproj/Localizable.strings b/ko.lproj/Localizable.strings index b491b90c5..fd27edd64 100644 --- a/ko.lproj/Localizable.strings +++ b/ko.lproj/Localizable.strings @@ -13,6 +13,14 @@ "failedFetchDataAlertMessage" = "앱을 다시 실행해주십시오."; "failedFetchDataAlertAction" = "확인"; +"failedDeleteDataAlertTitle" = "데이터 삭제 실패"; +"failedDeleteDataAlertMessage" = "다시 시도해주십시오."; +"failedDeleteDataAlertAction" = "확인"; + +"failedSaveDataAlertTitle" = "데이터 저장 실패"; +"failedSaveDataAlertMessage" = "다시 시도해주십시오."; +"failedSaveDataAlertAction" = "확인"; + "checkDeleteAlertTitle" = "진짜요?"; "checkDeleteAlertMessage" = "정말로 삭제하시겠어요?"; "checkDeleteAlertCancelAction" = "취소"; From a3fae63d9606ab4823e42150cf8b75b80f9f59a6 Mon Sep 17 00:00:00 2001 From: MaryJo-github Date: Sun, 17 Sep 2023 03:57:30 +0900 Subject: [PATCH 25/26] =?UTF-8?q?:sparkles:=20Feat:=20DiaryEditable=20?= =?UTF-8?q?=ED=94=84=EB=A1=9C=ED=86=A0=EC=BD=9C=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=EB=B0=8F=20DiaryManager=EC=97=90=EC=84=9C=20=EC=B1=84=ED=83=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 뷰컨에서 diaryManager 프로퍼티의 타입을 DiaryEditable로 변경 --- Diary.xcodeproj/project.pbxproj | 4 ++++ Diary/Controller/DiaryViewController.swift | 4 ++-- .../Controller/EditingDiaryViewController.swift | 4 ++-- Diary/Model/DiaryManager.swift | 2 +- Diary/Utility/DiaryEditable.swift | 16 ++++++++++++++++ 5 files changed, 25 insertions(+), 5 deletions(-) create mode 100644 Diary/Utility/DiaryEditable.swift diff --git a/Diary.xcodeproj/project.pbxproj b/Diary.xcodeproj/project.pbxproj index 104cb8b44..d16749696 100644 --- a/Diary.xcodeproj/project.pbxproj +++ b/Diary.xcodeproj/project.pbxproj @@ -25,6 +25,7 @@ 44A7622D2AA710F000C10AE2 /* Diary+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44A7622B2AA710F000C10AE2 /* Diary+CoreDataClass.swift */; }; 44A7622E2AA710F000C10AE2 /* Diary+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44A7622C2AA710F000C10AE2 /* Diary+CoreDataProperties.swift */; }; 44DC00332AB36F6000AF3C0E /* String+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44DC00322AB36F6000AF3C0E /* String+.swift */; }; + 44DC00572AB630E900AF3C0E /* DiaryEditable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44DC00562AB630E900AF3C0E /* DiaryEditable.swift */; }; C739AE25284DF28600741E8F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C739AE24284DF28600741E8F /* AppDelegate.swift */; }; C739AE27284DF28600741E8F /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C739AE26284DF28600741E8F /* SceneDelegate.swift */; }; C739AE29284DF28600741E8F /* DiaryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C739AE28284DF28600741E8F /* DiaryViewController.swift */; }; @@ -53,6 +54,7 @@ 44A7622B2AA710F000C10AE2 /* Diary+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Diary+CoreDataClass.swift"; sourceTree = ""; }; 44A7622C2AA710F000C10AE2 /* Diary+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Diary+CoreDataProperties.swift"; sourceTree = ""; }; 44DC00322AB36F6000AF3C0E /* String+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+.swift"; sourceTree = ""; }; + 44DC00562AB630E900AF3C0E /* DiaryEditable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryEditable.swift; sourceTree = ""; }; C739AE21284DF28600741E8F /* Diary.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Diary.app; sourceTree = BUILT_PRODUCTS_DIR; }; C739AE24284DF28600741E8F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; C739AE26284DF28600741E8F /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -102,6 +104,7 @@ 44A762022AA6031800C10AE2 /* ReuseIdentifiable.swift */, 444E73F72AAAFA940079BEE5 /* ActivityViewPresentable.swift */, 3C6B0B762AB427C30011E59D /* Logger.swift */, + 44DC00562AB630E900AF3C0E /* DiaryEditable.swift */, ); path = Utility; sourceTree = ""; @@ -299,6 +302,7 @@ 3C3C08902AA3AB6C00C8D4CF /* UIViewController+.swift in Sources */, 44A7622E2AA710F000C10AE2 /* Diary+CoreDataProperties.swift in Sources */, 3C3C08F02AA6241D00C8D4CF /* DateFormatter+.swift in Sources */, + 44DC00572AB630E900AF3C0E /* DiaryEditable.swift in Sources */, C739AE2F284DF28600741E8F /* Diary.xcdatamodeld in Sources */, 44A761C12A9F7A6100C10AE2 /* EditingDiaryViewController.swift in Sources */, 3CCA1E792A9F08C8008683C3 /* DiaryContent.swift in Sources */, diff --git a/Diary/Controller/DiaryViewController.swift b/Diary/Controller/DiaryViewController.swift index 77239f6d6..d66dafac4 100644 --- a/Diary/Controller/DiaryViewController.swift +++ b/Diary/Controller/DiaryViewController.swift @@ -7,7 +7,7 @@ import UIKit final class DiaryViewController: UIViewController { - private let diaryManager: DiaryManager + private let diaryManager: DiaryEditable private let logger: Logger private let tableView: UITableView = { @@ -17,7 +17,7 @@ final class DiaryViewController: UIViewController { return tableView }() - init(diaryManager: DiaryManager, logger: Logger = Logger()) { + init(diaryManager: DiaryEditable, logger: Logger = Logger()) { self.diaryManager = diaryManager self.logger = logger diff --git a/Diary/Controller/EditingDiaryViewController.swift b/Diary/Controller/EditingDiaryViewController.swift index 81b717dda..d2085317d 100644 --- a/Diary/Controller/EditingDiaryViewController.swift +++ b/Diary/Controller/EditingDiaryViewController.swift @@ -8,7 +8,7 @@ import UIKit final class EditingDiaryViewController: UIViewController, ActivityViewPresentable { - private let diaryManager: DiaryManager + private let diaryManager: DiaryEditable private let logger: Logger private var diaryContent: DiaryContent @@ -21,7 +21,7 @@ final class EditingDiaryViewController: UIViewController, ActivityViewPresentabl return textView }() - init(diaryManager: DiaryManager, logger: Logger, with diaryContent: DiaryContent) { + init(diaryManager: DiaryEditable, logger: Logger, with diaryContent: DiaryContent) { self.diaryManager = diaryManager self.logger = logger self.diaryContent = diaryContent diff --git a/Diary/Model/DiaryManager.swift b/Diary/Model/DiaryManager.swift index 3bb143c23..ab5afa110 100644 --- a/Diary/Model/DiaryManager.swift +++ b/Diary/Model/DiaryManager.swift @@ -7,7 +7,7 @@ import CoreData -final class DiaryManager { +final class DiaryManager: DiaryEditable { private(set) var diaryContents: [DiaryContent] = [] private let persistentContainer: NSPersistentContainer = { diff --git a/Diary/Utility/DiaryEditable.swift b/Diary/Utility/DiaryEditable.swift new file mode 100644 index 000000000..2a190f692 --- /dev/null +++ b/Diary/Utility/DiaryEditable.swift @@ -0,0 +1,16 @@ +// +// DiaryEditable.swift +// Diary +// +// Created by Mary & Whales on 2023/09/17. +// + +import Foundation + +protocol DiaryEditable { + var diaryContents: [DiaryContent] { get } + func refresh() throws + func insert(diaryContent: DiaryContent) throws + func update(diaryContent: DiaryContent) throws + func delete(id: UUID) throws +} From 3883a950cd7ff63284c765ee87db0797728dd201 Mon Sep 17 00:00:00 2001 From: MaryJo-github Date: Fri, 2 Feb 2024 14:44:24 +0900 Subject: [PATCH 26/26] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20weak=20s?= =?UTF-8?q?elf=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Diary/Controller/DiaryViewController.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Diary/Controller/DiaryViewController.swift b/Diary/Controller/DiaryViewController.swift index d66dafac4..74de8fb95 100644 --- a/Diary/Controller/DiaryViewController.swift +++ b/Diary/Controller/DiaryViewController.swift @@ -135,21 +135,21 @@ extension DiaryViewController: UITableViewDelegate, ActivityViewPresentable { let share = UIContextualAction( style: .normal, title: "share".localized - ) { (_, _, success: @escaping (Bool) -> Void) in + ) { [weak self] (_, _, success: @escaping (Bool) -> Void) in let diaryContentItem = diaryContent.title + diaryContent.body - self.presentActivityView(shareItem: diaryContentItem) + self?.presentActivityView(shareItem: diaryContentItem) success(true) } let delete = UIContextualAction( style: .destructive, title: "delete".localized - ) { (_, _, success: @escaping (Bool) -> Void) in + ) { [weak self] (_, _, success: @escaping (Bool) -> Void) in - self.presentCheckDeleteAlert { [self] _ in - deleteDiary(id: diaryContent.id) + self?.presentCheckDeleteAlert { [weak self] _ in + self?.deleteDiary(id: diaryContent.id) tableView.deleteRows(at: [indexPath], with: .fade) }