Home > Enterprise >  How to express a relationship between generic parameters in a SwiftUI view
How to express a relationship between generic parameters in a SwiftUI view

Time:12-20

I have defined the following protocols:

protocol ListableArrayElement: Identifiable, Equatable {
    associatedtype T = Hashable
    
    var id:T { get }
}

protocol Listable: ObservableObject, RandomAccessCollection where Element == ArrayElement {
    associatedtype ArrayElement: ListableArrayElement
    
    var _elements: [ArrayElement] { get set }
    var count: Int { get }
    var isEmpty: Bool { get }
    var endIndex: Int { get }
    var startIndex: Int { get }
    
    subscript(position: Int) -> ArrayElement { get set }

    func index(after i: Int) -> Int
    func append(_ element: ArrayElement)
    func insert(_ newElement: ArrayElement, at i: Int)
    func remove(at index: Int)
    func removeAll()
    func index(of element: ArrayElement) -> Int?
}

protocol FavouritesArray: Listable, Codable where Element: Codable {
        
    // MARK: - PROPERTIES
    var _elements: [Element] { get set }
    var key: String { get set }
    
    init()
    
    init(key: String)
}

There is an associated extension to FavouritesArray that provides conforming types with the ability to add/remove elements, and persist/load the array to/from UserDefaults via the key. All well and good. (Also note the listable protocol helps me avoid writing some boiler plate code for ViewModels that have an array of 'something' at their heart.)

Now I also want to write a generic SwiftUI view that can build a menu, using the FavouriteArray functions. I am struggling to understand how to express the type signature:

I'm passing instances of types conforming to FavouritesArray via EnvironmentObject, and thus want to write something like:

struct FavouritesArrayView<Favourites: FavouritesArray, Favourite>: View
   where Favourite == FavouritesArray.Element {
    
    @EnvironmentObject var favourites: Favourites
    @ObservedObject var favourite: Favourite
    // Other properties here

    var body: some View {
        // Layout code here
    }
}

This gives the compiler error: Associated type 'Element' can only be used with a concrete type or generic parameter base

Any tips on how to achieve this?

CodePudding user response:

First of all you need to declare that Favourite conforms to ObservableObject and then the where condition should use your associated type and not the protocol it conforms to, where Favourites.Element == Favourite

struct FavouritesArrayView<Favourites: FavouritesArray, Favourite: ObservableObject>: View where Favourites.Element == Favourite
  • Related