Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to confirm an enumeration to Identifiable protocol in Swift?

I'm trying to make a list with the raw values of the cases from an enumeration with the new SwiftUI framework. However, I'm having a trouble with conforming the 'Data' to Identifiable protocol and I really cannot find information how to do it. It tells me "Initializer 'init(_:rowContent:)' requires that 'Data' conform to 'Identifiable'" The stub provides me with an ObjectIdentifier variable in the last extension, but don't know what should I return. Could you tell me how do it? How do I conform Data to Identifiable, so I can make a list with the raw values?

enum Data: String {     case firstCase = "First string"     case secondCase = "Second string"     case thirdCase = "Third string" }  extension Data: CaseIterable {     static let randomSet = [Data.firstCase, Data.secondCase] }  extension Data: Identifiable {     var id: ObjectIdentifier {         return //what?     }  }  //-------------------------ContentView------------------------ import SwiftUI  struct Lala: View {     var name: String      var body: some View {         Text(name)     } }  struct ContentView: View {     var body: some View {         return List(Data.allCases) { i in             Lala(name: i.rawValue)         }     } } 
like image 520
Атанас Начков Avatar asked Aug 08 '19 09:08

Атанас Начков


People also ask

Can enum conform to Swift protocol?

Yes, enums can conform protocols. You can use Swift's own protocols or custom protocols. By using protocols with Enums you can add more capabilities.

What is identifiable protocol Swift?

This is one of the protocols built into Swift, and means “this type can be identified uniquely.” It has only one requirement, which is that there must be a property called id that contains a unique identifier. We just added that, so we don't need to do any extra work – our type conforms to Identifiable just fine.

What is case iterable in Swift?

You can iterate over all of the possible cases of an enumeration in Swift by making it conform to the CaseIterable protocol. When using CaseIterable, you can access a collection of all enum cases by using a allCases property and then iterate over it as an array.


2 Answers

⚠️ Try not to use already used names like Data for your internal module. I will use MyEnum instead in this answer


When something conforms to Identifiable, it must return something that can be identified by that. So you should return something unique to that case. For String base enum, rawValue is the best option you have:

extension MyEnum: Identifiable {     var id: RawValue { rawValue } } 

Also, enums can usually be identified by their selves:

extension MyEnum: Identifiable {     var id: Self { self } } 

⚠️ Note 1: If you return something that is unstable, like UUID() or an index, this means you get a new object each time you get the object and this will kill reusability and can cause epic memory and layout process usage beside view management issues like transition management and etc.

Take a look at this weird animation for adding a new pet: UUID Example

Note 2: From Swift 5.1, single-line closures don't need the return keyword.

Note 3: Try not to use globally known names like Data for your own types. At least use namespace for that like MyCustomNameSpace.Data


Inline mode

You can make any collection iterable inline by one of it's element's keypath:

For example to self:

List(MyEnum.allCases, id:\.self) 

or to any other compatible keypath:

List(MyEnum.allCases, id:\.rawValue) 

✅ The checklist of the identifier: (from WWDC21)

  • Exercise caution with random identifiers.
  • Use stable identifiers.
  • Ensure the uniqueness, one identifier per item.
like image 134
Mojtaba Hosseini Avatar answered Oct 11 '22 04:10

Mojtaba Hosseini


Another approach with associated values would be to do something like this, where all the associated values are identifiable.

enum DataEntryType: Identifiable {     var id: String {         switch self {         case .thing1ThatIsIdentifiable(let thing1):             return thing1.id         case .thing2ThatIsIdentifiable(let thing2):             return thing2.id         }     }          case thing1ThatIsIdentifiable(AnIdentifiableObject)     case thing2ThatIsIdentifiable(AnotherIdentifiableObject) 
like image 29
Dr. Mr. Uncle Avatar answered Oct 11 '22 04:10

Dr. Mr. Uncle