I am using the equatable protocol in order to compare two custom objects based on one property named mediaUID
.
Is there a way to switch between comparing on different properties?
In func fetchNotificationsRemoved
sometimes I need to compare by mediaUID
or by likeUID.
var notificationsArray = [NotificationInformation]()
class NotificationInformation {
let type: String
let mediaUID: String?
let commentUID: String?
let likeUID:String?
}
extension NotificationInformation {
func fetchNotificationsRemoved(query: DatabaseQuery) {
NotificationInformation.observeNewNotificationsChildRemoved(query: query) { [weak self] (newNotification: NotificationInformation?) in
guard let strongSelf = self else {return}
guard let notification = newNotification else {return}
if notification.type == "like" {
// How to compare based on likeUID using equatable?
//compare based on likeUID
//get the index of the item of type 'like' in notificationsArray and do something with it
guard let index = strongSelf.notificationsArray.index(of: notification) else {return}
}else if notification.type == "media" {
// How to compare based on mediaUID using equatable?
//compare based on mediaUID
//get the index of the item of type 'media' in notificationsArray
guard let index = strongSelf.notificationsArray.index(of: notification) else {return}
} else if if notification.type == "commentUID" {
....
}
guard let index = strongSelf.notificationsArray.index(of: notification) else {return}
strongSelf.notificationsArray.remove(at: index)
let visibleIndexes = strongSelf.tableView.indexPathsForVisibleRows
let indexPathOfRemovedNotification = IndexPath(row: index, section: 0)
if let indexes = visibleIndexes,
indexes.contains(indexPathOfRemovedNotification) {
strongSelf.tableView.deleteRows(at: [indexPathOfRemovedNotification], with: .fade)
}
}
}
}//end extension
//enables us to compare two objects of type NotificationInformation
extension NotificationInformation: Equatable { }
func ==(lhs: NotificationInformation ,rhs: NotificationInformation) -> Bool {
guard let mediaUIDLeft = lhs.mediaUID else {return false}
guard let mediaUIDRight = rhs.mediaUID else {return false}
return mediaUIDLeft == mediaUIDRight
}
You could use a static var
to establish the field you want to use for comparison:
class NotificationInformation: Equatable {
enum CompareField {
case type, mediaUID, commentUID, likeUID
}
static var compareField: CompareField = .mediaUID
let type: String
let mediaUID: String?
let commentUID: String?
let likeUID:String?
init(type: String, mediaUID: String? = nil, commentUID: String? = nil, likeUID: String? = nil) {
self.type = type
self.mediaUID = mediaUID
self.commentUID = commentUID
self.likeUID = likeUID
}
static func ==(lhs: NotificationInformation, rhs: NotificationInformation) -> Bool {
switch NotificationInformation.compareField {
case .type:
return lhs.type == rhs.type
case .mediaUID:
return lhs.mediaUID == rhs.mediaUID
case .commentUID:
return lhs.commentUID == rhs.commentUID
case .likeUID:
return lhs.likeUID == rhs.likeUID
}
}
}
Example:
let a = NotificationInformation(type: "foo", mediaUID: "123")
let b = NotificationInformation(type: "bar", mediaUID: "123")
NotificationInformation.compareField = .type
if a == b {
print("same type")
}
NotificationInformation.compareField = .mediaUID
if a == b {
print("same mediaUID")
}
Output:
same mediaUID
Comparing multiple fields using an OptionSet
If you replace the enum
with an OptionSet
, you could select multiple fields to compare:
struct CompareFields: OptionSet {
let rawValue: Int
static let type = CompareFields(rawValue: 1 << 0)
static let mediaUID = CompareFields(rawValue: 1 << 1)
static let commentUID = CompareFields(rawValue: 1 << 2)
static let likeUID = CompareFields(rawValue: 1 << 3)
}
static var compareFields: CompareFields = .mediaUID
static func ==(lhs: NotificationInformation, rhs: NotificationInformation) -> Bool {
var equal = true
if NotificationInformation.compareFields.contains(.type) {
equal = equal && (lhs.type == rhs.type)
}
if NotificationInformation.compareFields.contains(.mediaUID) {
equal = equal && (lhs.mediaUID == rhs.mediaUID)
}
if NotificationInformation.compareFields.contains(.commentUID) {
equal = equal && (lhs.commentUID == rhs.commentUID)
}
if NotificationInformation.compareFields.contains(.likeUID) {
equal = equal && (lhs.likeUID == rhs.likeUID)
}
return equal
}
Example
let a = NotificationInformation(type: "foo", mediaUID: "123", commentUID: "111")
let b = NotificationInformation(type: "bar", mediaUID: "123", commentUID: "111")
NotificationInformation.compareFields = .mediaUID
if a == b {
print("same mediaUID")
}
NotificationInformation.compareFields = [.mediaUID, .commentUID]
if a == b {
print("same mediaUID and commentUID")
}
Output
same mediaUID same mediaUID and commentUID
Multithreaded issue
There is an issue if your code is modifying the compareFields
value in another thread. The meaning of equals would change for all threads. One possible solution is to only change and use equality for NotificationInformation
in the main thread.
...
} else if notification.type == "media" {
DispatchQueue.main.async {
NotificationInformation.compareFields = .mediaUID
guard let index = strongSelf.notificationsArray.index(of: notification) else {return}
// use index
...
}
}
...