---
# System prepended metadata

title: 광기의 토요스터디
tags: [토요스터디]

---

# CoreData

## 실험 CoreData로 데이터 생성하고 불러오기
### Use CoreData를 사용하여 Project를 생성하지 않는 방법으로 해보기
### CoreData 생성 순서
1. Use CoreData를 체크하지 않은 일반 Project 생성
2. 왼쪽 하단 + > file > Data Model 파일 생성
3. Data Model 파일에서 Entity를 추가하고 Attribute 설정
4. Manual/None으로 설정
<Img src="https://hackmd.io/_uploads/rkXQ0F62h.png" width=350/>
5. Editor >  Create NSManagedObject Subclass로 모델 swift 파일 생성
6. Model Struct 만들기
> [🍎 Apple Developer - Creating a CoreData Model](https://developer.apple.com/documentation/coredata/creating_a_core_data_model)

---

* NavigationBarTitle에 Preferes Large Title
    <Img src="https://hackmd.io/_uploads/H1tdxqT22.png" width=350/>
* Tab Bar 구조 참고용
    <Img src="https://hackmd.io/_uploads/B1NuMqpn3.png" width=500>

---

### Use CoreData를 사용하여 Project를 생성하는 방법으로 해보기
-> AppDelegate에 NSPersistentContainer를 생성하여 해보기
```swift
class AppDelegate: UIResponder, UIApplicationDelegate {
    // MARK: - Core Data stack
    lazy var persistentContainer: NSPersistentContainer = {
        let container = NSPersistentContainer(name: "test")
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        return container
    }()

    // MARK: - Core Data Saving support
    func saveContext () {
        let context = persistentContainer.viewContext
        if context.hasChanges {
            do {
                try context.save()
            } catch {
                let nserror = error as NSError
                fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
            }
        }
    }
}

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?

    func sceneDidEnterBackground(_ scene: UIScene) {
        (UIApplication.shared.delegate as? AppDelegate)?.saveContext()
    }
}
```
> [🍎 Apple Developer - Setting up a Core Data](https://developer.apple.com/documentation/coredata/setting_up_a_core_data_stack)

---

## CoreData에 저장한 데이터 확인하기
[📒 참고 Blog](https://zziruru.medium.com/core-data-로-저장한-데이터-확인하기-508efaeeb439)

---

## App LifeCycle에 따라 save point 고민해보기
- 메모리에 올리와 있는 `data`를 디스크에 저장하는 것을 CRUD를 진행하는 모든 메소드에서 하는 것이 과연 효율적일까 하는 의문이 들었다
- `data`를 메소드에서 저장하는 것이 아닌 특정 포인트에서 몰아서 저장함으로 디스크에 저장하는 액션의 횟수를 최소화하기위해 몇가지 `save point`를 고민해봤다
    - `viewWillDisappear`
    - `deinit`
    - `sceneDidEnterBackground`
- 앱이 background로 가는 순간인 `sceneDidEnterBackground`가 최종 저장 포인트로 적합하다고 생각했다
    - 앱을 바로 종료할 경우 `View LifeCycle`(`viewWillDisappear`, `viewDidDisappear`, `deinit`) 메서드들이 동작 완수를 보장하지 않기 때문
    - 홈으로 나갈 때 `sceneDidEnterBackground`는 확실하게 수행됨
- 하지만 앱 실행 중간에 오류가 날 수 있다는 점을 고려했을 때, save point를 하나만 주는 것은 위험하다고 생각한다
    - 따라서 CRUD를 진행하며 저장까지 수행하는 것이 안전한 방법이라고 생각한다.
```swift
// TableView에서 Cell delete 수행 후 저장 예시
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
    let deleteAction = UIContextualAction(style: .destructive, title: "delete") { [weak self] contextualAction, view, completion in
        guard let managedObject = self?.jokeManagedObjects[indexPath.row] else {
            completion(false)
            return
        }

        self?.container.viewContext.delete(managedObject)
        do {
            try self?.container.viewContext.save()
        } catch {

        }
        self?.getAllItems()

        completion(true)
    }

    return UISwipeActionsConfiguration(actions: [deleteAction])
}
```

## NSPredicate를 활용해 Core Data에서 가져오는 데이터 필터링
1. `NSPredicate` 생성자의 `format` 파라미터를 통해 필터링할 조건을 지정할 수 있다.
2. `managed object`의 `request`를 만든 후 `request`의 `predicate` 프로퍼티에 1번에서 생성한 `predicate`를 할당한다.
3. 해당 `request`를 사용해 `viewContext`에서 데이터를 불러온다.

> [🍎 Apple Document - NSPredicate](https://developer.apple.com/documentation/foundation/nspredicate)
> [📒 참고 Blog](https://onelife2live.tistory.com/35)
```swift
func getAllItems() {
    do {
        guard let category = tabBarController?.tabBar.selectedItem?.title else {
            return
        }
        let request = Joke.fetchRequest()
        let predicate = NSPredicate(format: "category == %@", category)
        request.predicate = predicate

        jokeManagedObjects = try container.viewContext.fetch(request)
        tableView.reloadData()
    } catch {

    }
}
```
