I have a collection view with Album
objects. An Album
could be favorited or unfavorited by tapping a button inside of the cell. The buttons image changes depending on if the Album
is favorited or not. This part is easy and works.
The problem I have is:
Album
object in the cell. Within this view controller, you can favorite or unfavorite the Album
object. Now when I dismiss this view controller, the button in the cell is "not" updated based on Album
's isFavorite
property. I think the solution is to use Realm's Object-Level Notifications inside the UICollectionViewCell's. So that when you favorite/unfavorite an Album
in different view controllers, when you come back to the collection view, the button is up to date. But I have no idea how to add and remove the notifications - like where to add/remove and where to update based on the notification?
Note: Please do not say to use collectionView.reloadData()
.
This is what I have so far (notice the comment: var notificationToken: NotificationToken? // Is this where I add the notification for Realm?
):
class Album: Object {
dynamic var title = ""
dynamic var isFavorite = false
convenience init(json: [String: Any]) throws {
self.init()
guard let title = json["title"] as? String else {
throw SerializationError.invalidJSON("Album")
}
self.title = title
}
}
protocol AlbumCollectionViewCellDelegate: class {
func didTapFavoriteButton(_ favoriteButton: UIButton, album: Album)
}
class AlbumCollectionViewCell: UICollectionViewCell {
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var favoriteButton: UIButton!
weak var delegate: AlbumCollectionViewCellDelegate?
var album: Album!
// HELP
var notificationToken: NotificationToken? // Is this where I add the notification for Realm?
@IBAction func didTapFavoriteButton(_ sender: UIButton) {
delegate?.didTapFavoriteButton(sender, album: album)
}
func configure(with album: Album) {
titleLabel.text = album.title
favoriteButton.isSelected = album.isFavorite
self.album = album
}
}
class FavoritesListViewController: UIViewController, AlbumCollectionViewCellDelegate {
// MARK: - AlbumCollectionViewCellDelegate
func didTapFavoriteButton(_ favoriteButton: UIButton, album: Album) {
favoriteButton.isSelected = album.isFavorite
}
}
Any thoughts?
I know you said not to mention reloadData()
, but if the view controller was guaranteed to be off screen and would be called back onto the screen, I would instead just call reloadData()
(or even just reloadItems(at: [IndexPath])
passing in the index paths of the visible items) into the viewWillAppear(animated:)
method of the view controller.
It would be much easier than managing sets of notification tokens, especially if you're not even using notification tokens when the view controller is on screen to begin with. :)
That being said, if you want to avoid manually updating the view, when it's onscreen and offscreen and rely on notifications all the time, then yes, Realm's object-level notifications would be the most appropriate feature to use, and yes, adding the logic to the collection view cell subclass would be the most appropriate. :)
class AlbumCollectionViewCell: UICollectionViewCell {
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var favoriteButton: UIButton!
weak var delegate: AlbumCollectionViewCellDelegate?
var album: Album!
var notificationToken: NotificationToken?
@IBAction func didTapFavoriteButton(_ sender: UIButton) {
delegate?.didTapFavoriteButton(sender, album: album)
}
override func prepareForReuse() {
notificationToken.stop()
notificationToken = nil
}
func configure(with album: Album) {
titleLabel.text = album.title
favoriteButton.isSelected = album.isFavorite
self.album = album
notificationToken = self.album.addNotificationBlock { change in
switch change {
case .change(let properties):
for property in properties {
if property.name == "isFavorite" {
self.favoriteButton.isSelected = self.album.isFavorite
}
}
}
}
}
}