Prashant Tukadiya
Prashant Tukadiya

Reputation: 16456

Declare property of protocol shows Protocol can only be used as a generic constraint because it has Self or associated type requirements

I am stuck in situation where I could not able to find solution. Seen many similar question but could't able to figure it out.

So I have one View Controller which shows table view list for two types

1) Sales
2) Purchase

And I have decided to use same View Controller for both

enum SalesRowType {
    case salesOrderBooking
    case salesInvoicing
    case salesGrossContribution
}

enum PurchaseRowType {
    case purchaseOrders
    case amended_non_amended
    case pending_grn_po
}

and

// BASE PROTOCOL
 // ListRowType -> has title,image property 
protocol HomeSubItemType:ListRowType {
    associatedtype RowType
    var type:RowType {get}
}
// PURCHASE ITEMS

struct PurchaseSubItem: HomeSubItemType {

    typealias RowType = PurchaseRowType

    var image: UIImage
    var title: String
    var type: PurchaseRowType
}
// SALES ITEMS
struct SalesSubItem : HomeSubItemType {
    var image:UIImage
    var title:String
    var type:SalesRowType
}

In My View Controller I want to create array as per sales and purchase

if let type = type {
            switch type {
            case .purchase:
                self.title = "Purchase"
                self.itemList = [
                    PurchaseSubItem(image: UIImage(named: "purchase-orders")!, title: "Purchase Orders", type: .purchaseOrders),
                    PurchaseSubItem(image: UIImage(named: "amended-non")!, title: "Amended/NON-Amended-UNAutho-PO", type: .purchaseOrders),
                    PurchaseSubItem(image: UIImage(named: "purchase-pending")!, title: "Pending GRN PO", type: .purchaseOrders)]

            case .sales:
                self.title = "Sales"

                self.itemList =
                [
                    SalesSubItem(image: UIImage(named: "sales-order-booking")!, title: "Sales Order Booking", type: .salesOrderBooking),
                    SalesSubItem(image: UIImage(named: "sales-invoice")!, title: "Sales Invoicing", type: .salesInvoicing),
                    SalesSubItem(image: UIImage(named: "sale-gross")!, title: "Sales Gross Contribution", type: .salesGrossContribution)]

            default:
                assertionFailure("Only Purchase and sales are handle here")
            }
        }

But I am not able to declare array

var itemList  = [HomeSubItemType]() 
//ERROR HERE

Protocol 'HomeSubItemType' can only be used as a generic constraint because it has Self or associated type requirements

Please help me to fix this. Thanks in advance

Upvotes: 3

Views: 5242

Answers (1)

Joakim Danielson
Joakim Danielson

Reputation: 52053

Update

This is a completely different solution, focused on solving the problem rather than using generics

First I created a protocol for the enums, it's empty since it is just meant group them together

protocol RowType {}

both enums implements this, not shown here, and I modified ListRowType to

protocol ListRowType {
    var title: String {get}
    var image: UIImage {get}
    var type: RowType  {get}
}

and skipped HomeSubItemType completely.

SubItem is now

struct SubItem: ListRowType {
    var type: RowType
    var title: String
    var image: UIImage   
}

and my test code

var itemList  = [ListRowType]()
let sale = SubItem(type: SalesRowType.salesInvoicing, title: "XYZ", image: UIImage())
itemList.append(sale)

let purchase = SubItem(type: PurchaseRowType.purchaseOrders, title: "ABC", image: UIImage())
itemList.append(purchase)

This can of course be used with the original PurchaseSubItem and SalesSubItem instead of SubItem if that is preferred


I replaced the two structs with a generic struct

struct SubItem<T>: HomeSubItemType {    
    typealias RowType = T

    var type: T
    var title: String
    var image: UIImage
}

and used it like this

var itemList  = [SubItem<SalesRowType>]()
var item = SubItem(type: SalesRowType.salesInvoicing, title: "XYZ", image: UIImage())

itemList.append(item)

So you need to arrays which is probably best anyway, if you for some reason must have only one then declare it

var itemList  = [Any]()

Upvotes: 2

Related Questions