✅ 코어 데이터 적용
- 코어 데이터 엔티티 생성
- DataPersistenceManager 클래스 생성
- downloadTitleWith 함수 구현
- CollectionViewTableViewCell 이라는 파일 안에서 downloadTitleAt 이라는 함수구현
- 해당 함수는 indexPath를 통해 전달 받은 데이터를 DataPersistenceManager에 전달
- DownloadsViewController 안에 테이블뷰 생성
- DataPersistenceManager 내에 코어데이터로부터 데이터를 가져오는 fetchingTitlesFromDataBase() 라는 메서드 생성
- DowloadsViewController 클래스 내에 코어 데이터로부터 데이터를 가져오는 fetchLocalStorageForDownload() 라는 메서드 생성
- DataPersistenceManager 클래스 내에 코어데이터의 데이터를 삭제하는 deleteTitleWith () 메서드 생성
- ⭐ 중요한 점이 코어 데이터에서 데이터 삭제가 먼저 ➡ 임시 배열에서 삭제 ➡ 테이블뷰에서 삭제 ⭐
- DownloadsViewController 클래스에서 extension 부분에서 editingStyle 이라는 파라미터를 갖는 tableView 함수에서 삭제 구현
- Downloads 라는 알림을 노티피케이션이라는 기능 사용
- 먼저 데이터 알림을 보내주는 역할을 CollectionViewTableViewCell 에서 downloadTitleAt 메서드에서 구현
- 데이터 알림을 수신하기 위한 역할을 DownloadsViewController 클래스에서 NotificationCenter 라는 함수로 구현
✅ 코어 데이터 적용
- CollectionViewTableViewCell 파일 내에 extension 부분에 작성
func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemsAt indexPaths: [IndexPath], point: CGPoint) -> UIContextMenuConfiguration? {
let config = UIContextMenuConfiguration(
identifier: nil,
previewProvider: nil) { _ in
let downloadAction = UIAction(title: "Download", subtitle: nil, image: nil, identifier: nil, discoverabilityTitle: nil, state: .off) { _ in
print("Download Tapped")
}
return UIMenu(title: "", image: nil, identifier: nil, options: .displayInline, children: [downloadAction])
}
return config
}
- 다운로드 함수 설정
// 다운로드 함수
private func downloadTitleAt(indexPath: IndexPath) {
print("Downloading \(String(describing: titles[indexPath.row].original_title))")
}
- CollectionViewTableViewCell 안에 extension 부분에 작성한 부분 수정
func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemsAt indexPaths: [IndexPath], point: CGPoint) -> UIContextMenuConfiguration? {
let config = UIContextMenuConfiguration(
identifier: nil,
previewProvider: nil) { _ in
let downloadAction = UIAction(title: "Download", subtitle: nil, image: nil, identifier: nil, discoverabilityTitle: nil, state: .off) { _ in
var indexPath = indexPaths[0]
self.downloadTitleAt(indexPath: indexPath)
}
return UIMenu(title: "", image: nil, identifier: nil, options: .displayInline, children: [downloadAction])
}
return config
}
- 코어 데이터 모델 생성
- 코어 데이터를 관리하기 위해 DataPersistenceManager 파일을 생성
import Foundation
import UIKit
import CoreData
class DataPersistenceManager {
enum DatabaseError: Error {
case failedToSaveData
}
static let shared = DataPersistenceManager()
func downloadTitleWith(model: Title, completion: @escaping (Result<Void, Error>) -> Void) {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let context = appDelegate.persistentContainer.viewContext
let item = TitleItem(context: context)
item.original_title = model.original_title
item.id = Int64(model.id)
item.original_name = model.original_name
item.overview = model.overview
item.media_type = model.media_type
item.poster_path = model.poster_path
item.release_date = model.release_date
item.vote_count = Int64(model.vote_count)
item.vote_average = model.vote_average
do {
try context.save()
completion(.success(()))
} catch {
completion(.failure(DatabaseError.failedToSaveData))
}
}
}
- collectionViewTableViewCell 파일 내 downloadTitleAt 메서드 생성
// 다운로드 함수
private func downloadTitleAt(indexPath: IndexPath) {
DataPersistenceManager.shared.downloadTitleWith(model: titles[indexPath.row]) { result in
switch result {
case.success():
print("Download to Database")
case.failure(let error):
print(error.localizedDescription)
}
}
}
- 영화를 눌러서 다운로드 창이 뜬걸 누르면 콘솔창에 나오는 출력을 확인
- DownloadsViewController 파일 생성
import UIKit
class DownloadsViewController: UIViewController {
private var titles: [TitleItem] = [TitleItem]()
private let downloadedTable: UITableView = {
let table = UITableView()
table.register(TitleTableViewCell.self, forCellReuseIdentifier: TitleTableViewCell.identifier)
return table
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
title = "Downloads"
navigationController?.navigationBar.prefersLargeTitles = true
navigationController?.navigationItem.largeTitleDisplayMode = .always
downloadedTable.delegate = self
downloadedTable.dataSource = self
}
}
extension DownloadsViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return titles.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: TitleTableViewCell.identifier, for: indexPath) as? TitleTableViewCell else { return UITableViewCell() }
let title = titles[indexPath.row]
cell.configure(with: TitleViewModel(titleName: (title.original_name ?? title.original_title) ?? "Unknown title name", posterURL: title.poster_path ?? ""))
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 140
}
}
- DataPersistenceManager 클래스에서 fetching~ 메서드 생성
import Foundation
import UIKit
import CoreData
class DataPersistenceManager {
enum DatabaseError: Error {
case failedToSaveData
case failedToFetchData
}
static let shared = DataPersistenceManager()
func downloadTitleWith(model: Title, completion: @escaping (Result<Void, Error>) -> Void) {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let context = appDelegate.persistentContainer.viewContext
let item = TitleItem(context: context)
item.original_title = model.original_title
item.id = Int64(model.id)
item.original_name = model.original_name
item.overview = model.overview
item.media_type = model.media_type
item.poster_path = model.poster_path
item.release_date = model.release_date
item.vote_count = Int64(model.vote_count)
item.vote_average = model.vote_average
do {
try context.save()
completion(.success(()))
} catch {
completion(.failure(DatabaseError.failedToSaveData))
}
}
func fetchingTitlesFromDataBase(completion: @escaping (Result<[TitleItem], Error>) -> Void) {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let context = appDelegate.persistentContainer.viewContext
let request: NSFetchRequest<TitleItem>
request = TitleItem.fetchRequest()
do {
let titles = try context.fetch(request)
completion(.success(titles))
} catch {
completion(.failure(DatabaseError.failedToFetchData))
}
}
}
- DownloadsViewcontroller 에 돌아가서 fetchLocalStorageForDownload() 생성
import UIKit
class DownloadsViewController: UIViewController {
private var titles: [TitleItem] = [TitleItem]()
private let downloadedTable: UITableView = {
let table = UITableView()
table.register(TitleTableViewCell.self, forCellReuseIdentifier: TitleTableViewCell.identifier)
return table
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
title = "Downloads"
navigationController?.navigationBar.prefersLargeTitles = true
navigationController?.navigationItem.largeTitleDisplayMode = .always
view.addSubview(downloadedTable)
downloadedTable.delegate = self
downloadedTable.dataSource = self
fetchLocalStorageForDownload()
}
// 다운로드한 영화 보여주기
private func fetchLocalStorageForDownload() {
DataPersistenceManager.shared.fetchingTitlesFromDataBase { [weak self] result in
switch result {
case .success(let titles):
self?.titles = titles
DispatchQueue.main.async {
self?.downloadedTable.reloadData()
}
case.failure(let error):
print(error.localizedDescription)
}
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
downloadedTable.frame = view.bounds
}
}
extension DownloadsViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return titles.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: TitleTableViewCell.identifier, for: indexPath) as? TitleTableViewCell else { return UITableViewCell() }
let title = titles[indexPath.row]
cell.configure(with: TitleViewModel(titleName: (title.original_name ?? title.original_title) ?? "Unknown title name", posterURL: title.poster_path ?? ""))
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 140
}
}
- DataPersistenceManager 내에 delete 기능 추가
import Foundation
import UIKit
import CoreData
class DataPersistenceManager {
enum DatabaseError: Error {
case failedToSaveData
case failedToFetchData
case failedToDeleteData
}
static let shared = DataPersistenceManager()
func downloadTitleWith(model: Title, completion: @escaping (Result<Void, Error>) -> Void) {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let context = appDelegate.persistentContainer.viewContext
let item = TitleItem(context: context)
item.original_title = model.original_title
item.id = Int64(model.id)
item.original_name = model.original_name
item.overview = model.overview
item.media_type = model.media_type
item.poster_path = model.poster_path
item.release_date = model.release_date
item.vote_count = Int64(model.vote_count)
item.vote_average = model.vote_average
do {
try context.save()
completion(.success(()))
} catch {
completion(.failure(DatabaseError.failedToSaveData))
}
}
func fetchingTitlesFromDataBase(completion: @escaping (Result<[TitleItem], Error>) -> Void) {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let context = appDelegate.persistentContainer.viewContext
let request: NSFetchRequest<TitleItem>
request = TitleItem.fetchRequest()
do {
let titles = try context.fetch(request)
completion(.success(titles))
} catch {
completion(.failure(DatabaseError.failedToFetchData))
}
}
func deleteTitleWith(model: TitleItem, completion: @escaping (Result<Void, Error>) -> Void) {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let context = appDelegate.persistentContainer.viewContext
context.delete(model)
do {
try context.save()
completion(.success(()))
} catch {
completion(.failure(DatabaseError.failedToDeleteData))
}
}
}
- DownloadViewController 내에 아래 코드 추가
extension DownloadsViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return titles.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: TitleTableViewCell.identifier, for: indexPath) as? TitleTableViewCell else { return UITableViewCell() }
let title = titles[indexPath.row]
cell.configure(with: TitleViewModel(titleName: (title.original_name ?? title.original_title) ?? "Unknown title name", posterURL: title.poster_path ?? ""))
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 140
}
// 삭제 기능 구현
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
switch editingStyle {
case.delete:
DataPersistenceManager.shared.deleteTitleWith(model: titles[indexPath.row]) { [weak self] result in
switch result {
case .success():
print("Deleted from the database")
case .failure(let error):
print(error.localizedDescription)
}
self?.titles.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
}
default:
break;
}
}
}
- CollectionViewTableViewCell 내에 알림 설정
// 다운로드 함수
private func downloadTitleAt(indexPath: IndexPath) {
DataPersistenceManager.shared.downloadTitleWith(model: titles[indexPath.row]) { result in
switch result {
case.success():
NotificationCenter.default.post(name: NSNotification.Name("downloaded"), object: nil)
case.failure(let error):
print(error.localizedDescription)
}
}
}
- 위에서 다운로드하면 알림이 가게 하면 DownloadsViewCotroller 에서 받게 설정
class DownloadsViewController: UIViewController {
private var titles: [TitleItem] = [TitleItem]()
private let downloadedTable: UITableView = {
let table = UITableView()
table.register(TitleTableViewCell.self, forCellReuseIdentifier: TitleTableViewCell.identifier)
return table
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
title = "Downloads"
navigationController?.navigationBar.prefersLargeTitles = true
navigationController?.navigationItem.largeTitleDisplayMode = .always
view.addSubview(downloadedTable)
downloadedTable.delegate = self
downloadedTable.dataSource = self
fetchLocalStorageForDownload()
NotificationCenter.default.addObserver(forName: NSNotification.Name("downloaded"), object: nil, queue: nil) { _ in
self.fetchLocalStorageForDownload()
}
}
- DownloadViewController 내에 상세 페이지로 이동하는 함수 추가
// 데이터를 누르면 상세 페이지로 옮겨가기
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let title = titles[indexPath.row]
guard let titleName = title.original_title ?? title.original_name else { return }
APICaller.shared.getMovie(with: titleName) { [weak self] result in
switch result {
case .success(let videoElement):
DispatchQueue.main.async {
let vc = TitlePreviewViewController()
vc.configure(with: TitlePreviewViewModel(title: titleName, youtubeView: videoElement, titleOverview: title.overview ?? ""))
self?.navigationController?.pushViewController(vc, animated: true)
}
case.failure(let error):
print(error.localizedDescription)
}
}
}
https://youtu.be/-swDydWTjEc?si=xpynvOoVH0n7d5mF
'Project > NETFLIX' 카테고리의 다른 글
NETFLIX CLONE 12편 (헤더뷰 랜덤기능, 상세 페이지 적용) (0) | 2024.05.16 |
---|---|
넷플릭스 앱 클론 11편 (영화 상세페이지) (0) | 2024.05.16 |
NETFLIX CLONE 10편 (유튜브 API 갖고오기) (0) | 2024.05.14 |
NETFLIX CLONE 9편 (Search) (0) | 2024.05.11 |
NETFLIX CLONE 8편 (업커밍 관련 뷰 생성) (0) | 2024.05.09 |