WWDC22 SALE: Save 50% on all my Swift books and bundles! >>

Day 47 Challenge - Equatable Help

Forums > 100 Days of SwiftUI

Hello,

Can someone try to explain the attached code? My code for the Day 47 challenge works, but I don't understand the static func portion of my struct for Equatable:

struct HabitItem: Codable, Identifiable, Equatable {

    // MARK: PROPERTIES
    var id = UUID()
    let title: String
    let description: String
    var timesCompleted: Int = 0

    // MARK: METHODS
    static func == (lhs: HabitItem, rhs: HabitItem) -> Bool {
        return lhs.title == rhs.title
    }
}

and I don't understand how the comparison works inside the DetailsView:

struct HabitDetailView: View {

    // MARK: PROPERTIES
    @ObservedObject var habits: Habits
    var habitItem: HabitItem

    @State private var timesCompleted: Int = 0

    // MARK: BODY
    var body: some View {
        VStack {
            Spacer()
            Text("Description")
                .font(.title.bold())
            Text(habitItem.description)
                .frame(width: 300, height: 200, alignment: .center)
                .background(.ultraThinMaterial)
                .clipShape(RoundedRectangle(cornerRadius: 20))
            Spacer()
            Stepper("Completed times: \(timesCompleted)", value: $timesCompleted, in: 0...1000)
                .padding()
            Spacer()
        }
        .onAppear {
            self.timesCompleted = self.habitItem.timesCompleted
        }
        .onDisappear {
            if let index = self.habits.items.firstIndex(where: { $0 == self.habitItem }) {
                self.habits.items.remove(at: index)
                var tempHabit = self.habitItem
                tempHabit.timesCompleted = self.timesCompleted
                self.habits.items.insert(tempHabit, at: index)
            }
        }
    }
}

Trying to work through this challenge, I had to resort to copyinmg someone's code but don't like that I don't understand it.

Please try to explain how this is working.

   

When you conform a data type to Equatable, you tell the compiler that that items of that type can be tested for equality, i.e., if itemA is equal to itemB.

To conform to Equatable, a data type must implement the == method, which tells the compiler how items are tested for equality.

So here:

    static func == (lhs: HabitItem, rhs: HabitItem) -> Bool {
        return lhs.title == rhs.title
    }

you are saing that 2 HabitItems are equal if they have the same title.

Here in the onDisappear handler:

if let index = self.habits.items.firstIndex(where: { $0 == self.habitItem })

(specifically the $0 == self.habitItem part)

you are looping through the list of habits and trying to determine if any of the existing HabitItems are equal to the new HabitItem that has been created. This uses the Equatable conformance to enable testing for equality.

   

Thank you @roosterboy,

So, theoretically, is the code not great?

For example, the .title could be the same for several HabitItems as long as the UUID is different.

  • Is the static func required or could it be removed and any HabitItem would be compared to another HabitItem using all properties?
  • Does the code, as written, force a comparison of only the .title? I believe this to be true!
  • Should I instead compare against the UUID instead (as it should always be unique)? OR
  • Should I chain other items into the comparison (i.e, return lhs.title == rhs.title && lhs.description == rhs.description)

I really appreciate the help. This one has me really confused and I want to ensure I understand before I move on.

   

Is the static func required or could it be removed and any HabitItem would be compared to another HabitItem using all properties?

You should keep == as a static func since it belongs to the HabitItem type itself, not to any particular instance of HabitItem.

Does the code, as written, force a comparison of only the .title? I believe this to be true!

And you are correct!

Should I instead compare against the UUID instead (as it should always be unique)?

Should I chain other items into the comparison (i.e, return lhs.title == rhs.title && lhs.description == rhs.description)

That depends. What does it mean for two HabitItems to be equal? That they have the same id? The same title? The same title and the same description?

Once you figure out what makes two HabitItems equal, that's what you should use in the == function.

You can actually have the compiler synthesize Equatable conformance for you if you are using all the properties as part of an equality test and all the properties are themselves Equatable, but in this case that's probably not what you want.

   

@IceMonster has questions about Equatable.

He received many correct answers from our regulars.

I hope I can add some clarification without further confusing Ice?

Take a step away from your solution, and think about how you compare two items. Start with Integers. How can you tell if one integer is the same as another? That's trivial!

How can you determine if one string is the same as another? Also pretty trivial!

But how do you determine if two songs are the same?

struct Song {
    var title: String
    var group: String
    var yearReleased: Int
}

let song1 = Song(title: "Walk Like an Egyptian", group: "The Bangles", yearReleased: 1986)
let song2 = Song(title: "Walk Like an Egyptian", group: "The Puppini Sisters", yearReleased: 2007)
// Hey! Check out the Puppini Sister's version. It's terrific!

song1 == song2     // <-- Are they the same song?

Examine the two songs! Are they, in fact, the same song? What would be appropriate rules to declare two songs are the same?

If you just compare song titles, you'll have problems with the song named "Try" (Nelly Furtado, John Mayer, Pink). If you compare song title and group, you'll have issues when groups release dance versions, extended versions, instrumental versions, and possible greatest hits versions.

Instead, to compare two of your Song structs, you have to code your own rule! First you make your struct conform to the Equatable protocol. To do this, as shown in other responses, you must add a function that takes two of your objects (two Song objects) and returns true or false. It's totally up to you to determine if two of the song objects are equal or not!

It may seem strange to you, but the "name" of that function is == .

And the reason it's a static function? The method belongs to the struct Song. It doesn't belong to song1, or song2. This is another topic that you should stop and go back and review.

Keep coding!

   

Save 50% in my Black Friday sale.

SAVE 50% To celebrate WWDC22, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.