Matt Manion
Matt Manion

Reputation: 69

Updating Realm through a custom button on the UITableViewCell

I have a tableview of "Books" that are stored in Realm. I want to set the "CurrentBook" property to "True" when hitting a button on a custom UITableViewCell.

I believe my error has something to do with getting the correct book value in "func selectCurrentBook", when I use an optional like below nothing happens.

@objc func selectCurrentBook(sender: UIButton) {
        try! realm.write {
            book?.currentlyReading = true
        }
    }

When I don't use an optional for book and use book.currentlyReading = true I get the error "Unexpectedly found nil while implicitly unwrapping an Optional value:"

Am I incorrectly passing the book value somewhere? I can't seem to find out how. Maybe I'm delegating wrong?

My TableViewCell is:

import UIKit
import RealmSwift

protocol MyBooksDelegate {
    func currentlyReadingButton()
}

class MyBooksTableViewCell: UITableViewCell {
    
    let realm = try! Realm()
    
    @IBOutlet weak var titleLabel: UILabel!
    
    @IBOutlet weak var authorLabel: UILabel!
    
    @IBOutlet weak var smallThumbnailImageView: UIImageView!
    
    @IBOutlet weak var currentlyReadingButton: UIButton!
    
    @IBAction func currentlyReadingButton(_ sender: Any) {

    }
    
    private var book: Book!
    
    func loadImage(smallThumbnailURL: String) {
        let imageURL = URL(string: smallThumbnailURL ?? "")
        smallThumbnailImageView.sd_setImage(with: imageURL)
        }

    func configureCell(book: Book, delegate: MyBooksDelegate?) {
       titleLabel.text = book.bookTitle
       authorLabel.text = book.bookAuthor
        loadImage(smallThumbnailURL: book.bookSmallThumbnailImageURL)
        
        currentlyReadingButton.addTarget(self, action: #selector(selectCurrentBook(sender:)), for: .touchUpInside)
                        
    }
    
    @objc func selectCurrentBook(sender: UIButton) {
        try! realm.write {
            book?.currentlyReading = true
        }
    }
}

My View Controller with TableView is :

import SwiftyJSON
import RealmSwift

class BooksViewController: UIViewController, UITextFieldDelegate, UITableViewDataSource, UITableViewDelegate {
    
    
    @IBOutlet weak var myBooksTableView: UITableView!
    
    let realm = try! Realm()
    
    var books: Results<Book>?
    
    // Search Bar Properties
    var searchParameter = "intitle"
    
    var booksArray: [Book] = []

    override func viewDidLoad() {
        super.viewDidLoad()
        
        loadBooks()
        // Setting up the TableView
               self.myBooksTableView.delegate = self
               self.myBooksTableView.dataSource = self
               self.myBooksTableView.rowHeight = 120.0
        
        // Setup Title
        title = "My Books"
       // navigationController?.navigationBar.prefersLargeTitles = true

    }
    
    override func viewWillAppear(_ animated: Bool) {
         navigationController?.navigationBar.barStyle = .black
        loadBooks()
     }
    
    func loadBooks() {
        books = realm.objects(Book.self).sorted(byKeyPath: "DateCreated", ascending: false)
        myBooksTableView.reloadData()
    }

    // TABLEVIEW
    
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return books?.count ?? 1
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        if let cell = tableView.dequeueReusableCell(withIdentifier: "MyBooksTableViewCell", for: indexPath) as? MyBooksTableViewCell {
            cell.configureCell(book: (books?[indexPath.row])!, delegate: self as? MyBooksDelegate)
     //       cell.selectionStyle = UITableViewCell.SelectionStyle.none

            return cell
        } else {
            return UITableViewCell()
        }
    }
    
    func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
        return true
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        self.performSegue(withIdentifier: "myBooksTOMyBooksDetail", sender: self)
        myBooksTableView.deselectRow(at: indexPath, animated: true)
    }

And my Book Model is:

class Book: Object {
    @objc dynamic var bookTitle: String!
    @objc dynamic var bookAuthor: String!
    @objc dynamic var bookSmallThumbnailImageURL: String!
    @objc dynamic var bookThumbnailImageURL: String!
    @objc dynamic var bookDescription: String!
    @objc dynamic var bookISBN_13: String!
    @objc dynamic var currentlyReading = false
    @objc dynamic var DateCreated = Date()
    @objc dynamic var WordID = UUID().uuidString
    
    // words
    let words = List<Word>()
    
    override static func primaryKey() -> String? {
        return "WordID"
    }
}

enter image description here

enter image description here

enter image description here

Upvotes: 0

Views: 76

Answers (1)

vadian
vadian

Reputation: 285082

The most compatible syntax is

currentlyReadingButton.addTarget(self, action: #selector(selectCurrentBook), for: .touchUpInside)

and

@objc func selectCurrentBook(_ sender: UIButton) {

However as the cell is custom anyway I'd prefer an IBAction over target/action

And the protocol MyBooksDelegate seems to be unused.


Side note:

Force unwrap the cell

let cell = tableView.dequeueReusableCell(withIdentifier: "MyBooksTableViewCell", for: indexPath) as! MyBooksTableViewCell

A crash – with report – reveals a design mistake which can be fixed instantly. With the if let you'll see nothing and have no clue why.


Update:

The crash occurs because you don't set book in the cell, add the first line after the {

func configureCell(book: Book, delegate: MyBooksDelegate?) {
   self.book = book
   titleLabel.text = book.bookTitle
   ...

Upvotes: 1

Related Questions