0

I want to create a Swift protocol that my different enums can conform to, so I can use the same 'type' while utilizing the enum's rawValue. Basically, the protocol should look like this:

protocol SidebarCustomFilter {
    
    var image: UIImage { get }

    var filterPredicate: NSPredicate { get }
}

An example enum that conforms to this:

enum SidebarFilterLogs : String, CaseIterable, SidebarCustomFilter{
    case filterAll = "All"
    case filterThisWeek = "This Week"

    var image: UIImage {
        return UIImage(systemName: "tray.full.fill")!
    }
    
    var filterPredicate: NSPredicate {
        return NSPredicate.init(format: "TRUEPREDICATE")
    }
}

Now I want to use this enum inside a struct:

struct CJSidebarFilterItem: Hashable {
    private var identifier: String = ""
    var sectionType: SectionType
    var sidebarCustomFilter: SidebarCustomFilter?

    init(logsFilter: SidebarFilterLogs) {
        self.sectionType = .filters
        self.sidebarCustomFilter = logsFilter
        
        self.identifier = UUID().uuidString
    }
    
    func hash(into hasher: inout Hasher) {
        hasher.combine(identifier)
    }

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

So far so good. However, if I try to use the 'rawValue' for the enum (of the protocol type I've described above), it gives me an error

sidebarItem.sidebarCustomFilter?.rawValue // Value of type 'SidebarCustomFilter' has no member 'rawValue'

That makes sense, since the SidebarCustomFilter is only a protocol. However, if I try to make it inherit from RawRepresentable (which should allow it to work with rawValue),

protocol SidebarCustomFilter: RawRepresentable {
    
    var image: UIImage { get }

    var filterPredicate: NSPredicate { get }
}

I get a different error:

var sidebarCustomFilter: SidebarCustomFilter? // Protocol 'SidebarCustomFilter' can only be used as a generic constraint because it has Self or associated type requirements

I believer this has something to do with RawRepresentable using an associatedtype RawValue, but I'm not sure how to resolve it.

So how do I get my protocol to work such that it enforces that only other enums are conforming to it (and hence it's valid to use 'rawValue' against it)?

Z S
  • 7,039
  • 12
  • 53
  • 105
  • Because I want to have different enums for 'filters'. There could be a `SidebarFilterFiles`, `SidebarFilterLocations` etc. If they can all conform to the same protocol, then it makes it easier and simpler to store them with the generic type. – Z S Aug 14 '20 at 01:13

1 Answers1

1

Found a solution:

protocol SidebarCustomFilter {
    var name: String { get }

    var image: UIImage { get }
    
    var filterPredicate: NSPredicate { get }
}

extension SidebarCustomFilter where Self: RawRepresentable {
    var name: Self.RawValue {
        return self.rawValue
    }
}

Now I can use sidebarItem.sidebarCustomFilter?.name instead of directly asking for the rawValue, and it works!

EDIT: adding code to show how it's stored:

struct CJSidebarFilterItem: Hashable {
    private var identifier: String = ""
    var sectionType: SectionType
    var sidebarCustomFilter: SidebarCustomFilter? // storing as generic type

    init(logsFilter: SidebarFilterLogs) {
        self.sectionType = .filters
        self.sidebarCustomFilter = logsFilter
        self.identifier = UUID().uuidString
    }
    
    func hash(into hasher: inout Hasher) {
        hasher.combine(identifier)
    }

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

and how it's used:

let titleString = sidebarItem.sidebarCustomFilter?.name ?? ""
Z S
  • 7,039
  • 12
  • 53
  • 105
  • Added code to show how it's stored. I also found this answer to a similar question, which helped me: https://stackoverflow.com/a/47272196/145552. Can you clarify why it doesn't make sense? I'm just getting started with using Swift, so I'm curious. – Z S Aug 14 '20 at 04:16
  • a) by creating the generic type constraint in the extension. This is given in the code example: "extension SidebarCustomFilter where Self: RawRepresentable"; b) the initializer could be based on the different raw values from the enum. for e.g. CJSidebarFilterItem(logsFilter: .filterAllLogs) ... I wouldn't be able to do that if it was taking in the generic type. – Z S Aug 14 '20 at 06:15