본문 바로가기
iOS Swift/Study

[Swift] 책검색 App (4) - 상세페이지로 이동, CoreData 로 책 담기

by 야고이 2024. 5. 7.
728x90

검색한 책을 클릭하면 상세페이지로 이동하는 것과 코어데이터로 책을 저장해보겠어요,,

 

책 선택하면 상세페이지로 띄우기

셀 선택시 상세페이지로 이동

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let selectBook = searchBookDocuments[indexPath.row]
        let detailVC = DetailViewController()
        detailVC.selectBook = selectBook
        let navigationController = UINavigationController(rootViewController: detailVC)

        // 현재 뷰 컨트롤러에서 모달 방식으로 네비게이션 컨트롤러 표시
        present(navigationController, animated: true)
        
    }

선택한 셀의 행의 정보를 해당 배열에서 가져옵니다

상수에 클릭시 넘어갈 상세페이지 컨트롤러에 할당합니다

상세페이지의 selectBook 에 선택한 셀을 할당할게요

그리고 새로운 창은 모달형식으로 표시할거예요!

 

선택한 책의 정보를 나타내는 속성값

var selectBook: Document? {
    didSet {
        self.loadData()
    }
}

새로운 값이 할당 될 때마다 didSet 블록이 실행됩니다

loadData 메서드로 값들이 새로 할당 돼요

func loadData() {
    guard let book = selectBook else { return }
    backgroundThumbnail.loadFromURL(book.thumbnail)
    thumnailImage.loadFromURL(book.thumbnail)
    booktitleLabel.text = book.title
    authorLabel.text = book.authors.joined(separator: ", ")
    translatorLabel.text = book.translators.joined(separator: ", ")
    publisherLabel.text = book.publisher
    datetimeLabel.text = book.datetime
    salePriceLabel.text = String((book.salePrice).formatted(.currency(code: "KRW")))
    priceLabel.text = String(book.price.formatted(.currency(code: "KRW")))
    contentLabel.text = book.contents
}

근데 이미지는 들어오는데 나머지 데이터들이 안들어오는거예요,,,,

UI 는 비동기적으로 메인쓰레드에서 그려줘야하는데 그걸 안한거죠

var selectBook: Document? {
    didSet {
        DispatchQueue.main.async {
            self.loadData()
        }
    }
}

비동기적으로 메인스레드에서 loadData 를 해줍시다!

 

이미지만 잘 들어왔던 이유는 이전포스팅에 보면 이미지 데이터는 메인쓰레드에서 받아왔기 때문이었어요

이제 잘 들어오네요 ui도 수정해주고 책정보 label 도 넣어줘야겠어요


Core Data 로 책 담기

여전히 수정해야할 UI 가 있지만 일단 저장하는걸 해보고 싶어서 무시하고 해볼게요

담기를 누르면 다른 뷰컨에서 보여지는걸 해볼거예요

 

버튼에 이벤트 만들기

addButton.addTarget(self, action: #selector(saveAddBook), for: .touchUpInside)

담기 버튼을 눌렀을 때 이벤트가 발생하는거니까 addTarget 으로  touchUpInside 가 실행되었을 때 saveAddBook을 실행해볼게요

근데 메서드명 이제보니 구려요

메서드를 만들기 전에

먼저,, 영구저장소에 접근?할 수 있는 코드 작성

 var persistentContainer: NSPersistentContainer? {
    (UIApplication.shared.delegate as? AppDelegate)?.persistentContainer
}

프로젝트를 만들 때 코어데이터 파일을 같이 만들었는데요 

그러면 AppDelegate 에 아래 코드가 만들어져 있을거예요

백퍼 이해하진 못해서 여기서 설명은 못하지만 

하여간 이 영구 저장소에 접근하기 위해 위 코드를 작성합니다,,

그래야 저장 되는 데이터들을 알 수 있어요

하하 코어데이터 하하

   // MARK: - Core Data stack

    lazy var persistentContainer: NSPersistentContainer = {
        /*
         The persistent container for the application. This implementation
         creates and returns a container, having loaded the store for the
         application to it. This property is optional since there are legitimate
         error conditions that could cause the creation of the store to fail.
        */
        let container = NSPersistentContainer(name: "Model")
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                // Replace this implementation with code to handle the error appropriately.
                // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
                 
                /*
                 Typical reasons for an error here include:
                 * The parent directory does not exist, cannot be created, or disallows writing.
                 * The persistent store is not accessible, due to permissions or data protection when the device is locked.
                 * The device is out of space.
                 * The store could not be migrated to the current model version.
                 Check the error message to determine what the actual problem was.
                 */
                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 {
                // Replace this implementation with code to handle the error appropriately.
                // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
                let nserror = error as NSError
                fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
            }
        }
    }

 

Core Data 에 저장 메서드

모든 정보를 다 저장하진 않을거고 저자,가격, 썸네일이미지, 제목 정도만 저장할래요

// MARK: Core Data 에 저장
@objc func saveAddBook() {
    print("coreData 저장")
    guard let context = self.persistentContainer?.viewContext else { return }
    guard let selectBook = self.selectBook else { return }
    let addBook = Book(context: context)

    addBook.title = selectBook.title
    addBook.salePrice = Int32(selectBook.salePrice)
    addBook.thumbnail = selectBook.thumbnail
    try? context.save()
}

코어데이터를 읽고 쓰는 viewContext 상수 context 에 할당해줍니다

선택한 책도 상수에 할당해줍니다

Entity 에 만든 Book 도 할당해주구여,,

뿌려줄 데이터 값을 넣어주고 save 해줍니다!

 

아아 여기서 애먹었던거

더보기
let addBook = Book(context: context)

이걸 생성하는데 자꾸 Book 이 없다고 그러는거예요!

내가 분명 엔티티 만들어줬는데!! 그럼 Book 클래스가 자동으로 설정 될텐데!!

설정이 Class Definition으로 되어 있으면 자동으로 클래스가 생성 되거덩여

근데 자꾸 없다고 하니까 머지 내가 뭘 잘못했지 했는데

그냥 코어데이터 파일 이름 바꿔주니까 인식함

Xcode 오류 아닐까 생각해 봅니다,,,

파일이름 바꿔주면 AppDelegate 에서도 이름 바꿔줘야합니다!

 이렇게 !! 이거 때문에 울분토한거 생각하면 열받음

담은 책들 배열에 저장

var bookList: [Book] = []

담은 책들을 보여줄 뷰컨에서 빈배열을 만들어줍니다

 

var persistentContainer: NSPersistentContainer? {
    (UIApplication.shared.delegate as? AppDelegate)?.persistentContainer
}

코어데이터 저장소를 읽기 위해서 이 변수를 또 만들어줘요

이렇게 여러군데에서 쓸거면 클래스에 메서드를 생성해서 사용하면 또 편리하겠네요

그건 나중에 해볼게요 일단은 패스

 

CoreData에서 상품 정보를 가져와서 bookList변수에 저장

// MARK: CoreData에서 상품 정보를 불러와, bookList 변수에 저장
private func setBookList() {
    guard let context = self.persistentContainer?.viewContext else { return }
    let request = Book.fetchRequest()
    if let bookList = try? context.fetch(request) {
    self.bookList = bookList
    }
}

Book 엔터티에 정보를 가져오기 위해 fetchRequest 를 호출합니다

context 로 가져온 데이터를 bookList 에 저장해줍니다

그렇게 가져온 데이터를 bookList 에 담아줍니다

 

셀에 그려주기

 func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        guard let cell = libraryCollectionView.dequeueReusableCell(withReuseIdentifier: LibraryCollectionViewCell.identifier, for: indexPath) as? LibraryCollectionViewCell else { return LibraryCollectionViewCell() }
        
        let bookList = bookList[indexPath.item]
        cell.updateData(bookList)
        
        return cell

그렇게 저장한 bookList 배열을 셀에 그려줘요

 

짜잔 그럼 이렇게 담겨있답니다!

근데 바로 여기 페이지에 반영이 안되는데 아마 리로드를 안해서 그런듯?

코드 추가하러 가겠슴다 총총,,

 

728x90

댓글