개발/Swift

[Swift] CoreData 사용해보기: CRUD 구현 가이드

덤벨로퍼 2024. 5. 18. 13:50

문제

- API 네트워킹시 네트워크 문제가 있을경우 내부저장 데이터를 리턴해주는 로직 필요.

- CoreData는 내부데이터 이므로 Repository 에서 접근 하는게 맞다고 판단 했다.

근데 CoreData는 Appdelegate의 persistentContainer.viewContext 에 접근 해야함

Repository -> CoreData -> Appdelegate

이는 클린 아키텍쳐 구성상 의존성의 방향이 잘못되었다 생각

 

해결

- CoreData 를 사용해서 데이터를 저장 & 사용

- Repository 생성시 viewContext를 주입받아 사용

Coordinator 패턴이나 의존성 주입 하는 부분에서 해주면 좋을것 같다.

        let appDelegate = UIApplication.shared.delegate as? AppDelegate
        let viewContext = appDelegate?.persistentContainer.viewContext

        let repository = NewsRepository(viewContext: viewContext, network: NewsNetwork())
        let viewModel = ViewModel(repository: repository)
        let viewController = ViewController(viewModel: viewModel)
        let rootVC = UINavigationController(rootViewController: viewController)

 

구현

  • CoreData Entity 설정은 이미 해둠

삭제

    private func deleteAllNewsCoreData() {
        let fetchRequestResult: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "NewsItem")

        let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequestResult)
        do {
            try viewContext?.execute(deleteRequest)
            try viewContext?.save()
        } catch let error {
            print("deleteAllNewsCoreData Error - \\(error)")
        }
       
    }

NewsItem 엔티티 저장된 리스트 모두 지워버림

 

저장

    private func saveNewsCoreData(news: [News]) {
        guard let viewContext = viewContext,
            let entity = NSEntityDescription.entity(forEntityName: "NewsItem", in: viewContext) else { return }
      
        news.forEach { newsItem in
            let newsObject = NSManagedObject(entity: entity, insertInto: viewContext)
            newsObject.setValue(newsItem.publishedAt, forKey: "publishedAt")
            newsObject.setValue(newsItem.title, forKey: "title")
            newsObject.setValue(newsItem.urlToImage, forKey: "urlToImage")
            newsObject.setValue(newsItem.url, forKey: "url")
        }
        do {
            try viewContext.save()
        } catch let error {
            print("saveNewsCoreData Error - \\(error)")
        }
    }

객체의 데이터를 모두 꺼내서 Coredata 엔티티 생성 후 (반복) 저장

 

읽기

    private func readNewsCoreData() -> [News] {
        let fetchRequest: NSFetchRequest<NewsItem> = NewsItem.fetchRequest()

        do {
            guard let result = try viewContext?.fetch(fetchRequest) else { return [] }
            let news: [News] = result.compactMap { news in
                guard let title = news.value(forKey: "title") as? String,
                  let url = news.value(forKey: "url") as? String else { return nil }

            let publishedAt = news.value(forKey: "publishedAt") as? Date
            let urlToImage = news.value(forKey: "urlToImage") as? String
                return News(title: title, url: url, urlToImage: urlToImage, publishedAt: publishedAt)
            }
            return news
        } catch let error {
            print("readNewsCoreData Error - \\(error)")
            return []
        }

    }

Coredata 엔티티 (리스트) 꺼내와서 객체 생성후 리턴