diff --git a/Happiggy-bank/Happiggy-bank/CoreData/PersistenceStore.swift b/Happiggy-bank/Happiggy-bank/CoreData/PersistenceStore.swift index de5b9da8..43695d9a 100644 --- a/Happiggy-bank/Happiggy-bank/CoreData/PersistenceStore.swift +++ b/Happiggy-bank/Happiggy-bank/CoreData/PersistenceStore.swift @@ -20,7 +20,7 @@ class PersistenceStore { PersistenceStore(name: StringLiteral.sharedPersistenceStoreName) }() - static private(set) var fatalErrorNeeded = false + static private(set) var fatalErrorDescription: String? /// persistence container 의 viewContext 에 접근하기 위한 syntactic sugar private(set) lazy var context: NSManagedObjectContext = { @@ -43,7 +43,10 @@ class PersistenceStore { self.persistentContainer = NSPersistentContainer(name: name).then { $0.loadPersistentStores { _, error in if let error = error as NSError? { - PersistenceStore.fatalErrorNeeded = true + PersistenceStore.fatalErrorDescription = """ + \(error.localizedDescription) + \(error.userInfo) + """ print(error.localizedDescription) print(error.userInfo) } diff --git a/Happiggy-bank/Happiggy-bank/SceneDelegate.swift b/Happiggy-bank/Happiggy-bank/SceneDelegate.swift index 617d1b43..5e976eff 100644 --- a/Happiggy-bank/Happiggy-bank/SceneDelegate.swift +++ b/Happiggy-bank/Happiggy-bank/SceneDelegate.swift @@ -19,12 +19,14 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { guard let scene = (scene as? UIWindowScene) else { return } - guard PersistenceStore.fatalErrorNeeded == false + guard let errorMessage = PersistenceStore.fatalErrorDescription else { - scene.windows.first?.rootViewController = ErrorViewController() + /// 정상적인 경우 + PersistenceStore.shared.windowScene = scene return } - PersistenceStore.shared.windowScene = scene + /// 코어데이터 에러 발생 + scene.windows.first?.rootViewController = ErrorViewController(errorMessage: errorMessage) } func sceneDidDisconnect(_ scene: UIScene) { diff --git a/Happiggy-bank/Happiggy-bank/Utils/Constants.swift b/Happiggy-bank/Happiggy-bank/Utils/Constants.swift index 160a4033..eb384edb 100644 --- a/Happiggy-bank/Happiggy-bank/Utils/Constants.swift +++ b/Happiggy-bank/Happiggy-bank/Utils/Constants.swift @@ -1174,6 +1174,24 @@ let mainStoryboardName = "Main" extension ErrorViewController { + /// 상수 + enum Metric { + + /// 상하좌우 패딩 (24, -24, 24, -24) + static let paddings = ( + top: edgeInset, + bottom: -edgeInset, + leading: edgeInset, + trailing: -edgeInset + ) + + /// 뷰 간 간격: 16 + static let spacing: CGFloat = 24 + + /// 패딩: 24 + private static let edgeInset: CGFloat = 24 + } + /// 문자열 enum StringLiteral { diff --git a/Happiggy-bank/Happiggy-bank/ViewController/ErrorViewController.swift b/Happiggy-bank/Happiggy-bank/ViewController/ErrorViewController.swift index 03a4dc3b..af504d03 100644 --- a/Happiggy-bank/Happiggy-bank/ViewController/ErrorViewController.swift +++ b/Happiggy-bank/Happiggy-bank/ViewController/ErrorViewController.swift @@ -10,13 +10,37 @@ import UIKit /// 코어데이터 로딩 오류 발생 시 나타나는 뷰 컨트롤러 final class ErrorViewController: UIViewController { + // MARK: - Properties + + /// 에러메시지 + private var errorMessage: String! + + /// 유저에게 에러와 메일 주소를 안내하는 라벨 + private var informationLabel: UILabel! + + /// 디버깅용 문구를 나타내는 텍스트뷰 + private var errorMessageTextView: UITextView! + + + // MARK: - Inits + + init(errorMessage: String) { + super.init(nibName: nil, bundle: nil) + self.errorMessage = errorMessage + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + } + + // MARK: - Life Cycle override func viewDidLoad() { super.viewDidLoad() - self.view.backgroundColor = .systemBackground - self.configureInformationLabel() + self.configureView() + self.configureViewHierarchy() } @@ -30,26 +54,79 @@ final class ErrorViewController: UIViewController { // MARK: - Functions + /// 뷰 초기설정 + private func configureView() { + self.view.backgroundColor = .systemBackground + self.view.addGestureRecognizer(UITapGestureRecognizer( + target: self, + action: #selector(userDidTap(sender:)) + )) + self.configureInformationLabel() + self.configureErrorMessageTextView() + } + /// 에러 안내 라벨 생성 private func configureInformationLabel() { - let informationLabel = UILabel().then { + self.informationLabel = UILabel().then { $0.textColor = .customWarningLabel $0.numberOfLines = .zero $0.textAlignment = .center $0.text = StringLiteral.informationLabelText $0.translatesAutoresizingMaskIntoConstraints = false } - - self.view.addSubview(informationLabel) + } + + /// 텍스트뷰 초기 설정 + private func configureErrorMessageTextView() { + self.errorMessageTextView = UITextView().then { + $0.textColor = .label + $0.font = UIFont.systemFont(ofSize: UIFont.labelFontSize) + $0.isEditable = false + $0.showsHorizontalScrollIndicator = false + $0.text = self.errorMessage + $0.translatesAutoresizingMaskIntoConstraints = false + } + } + + /// 뷰 체계 구성 + private func configureViewHierarchy() { + self.view.addSubview(self.informationLabel) + self.view.addSubview(self.errorMessageTextView) + self.configureInformationLabelConstraints() + self.configureErrorMessageTextViewConstraints() + } + + /// 정보 라벨 오토레이아웃 설정 + private func configureInformationLabelConstraints() { NSLayoutConstraint.activate([ - informationLabel.centerXAnchor.constraint(equalTo: self.view.centerXAnchor), - informationLabel.centerYAnchor.constraint(equalTo: self.view.centerYAnchor) + self.informationLabel.centerXAnchor.constraint(equalTo: self.view.centerXAnchor), + self.informationLabel.topAnchor.constraint( + equalTo: self.view.safeAreaLayoutGuide.topAnchor, + constant: Metric.paddings.top + ) + ]) + } + + /// 에러 메시지 텍스트뷰 오토레이아웃 설정 + private func configureErrorMessageTextViewConstraints() { + NSLayoutConstraint.activate([ + self.errorMessageTextView.topAnchor.constraint( + equalTo: self.informationLabel.bottomAnchor, + constant: Metric.spacing + ), + self.errorMessageTextView.bottomAnchor.constraint( + equalTo: self.view.safeAreaLayoutGuide.bottomAnchor, + constant: Metric.paddings.bottom + ), + self.errorMessageTextView.leadingAnchor.constraint( + equalTo: self.view.leadingAnchor, + constant: Metric.paddings.leading + ), + self.errorMessageTextView.trailingAnchor.constraint( + equalTo: self.view.trailingAnchor, + constant: Metric.paddings.trailing + ) ]) - - self.view.addGestureRecognizer(UITapGestureRecognizer( - target: self, - action: #selector(userDidTap(sender:)) - )) } }