MBerndt
MBerndt

Reputation: 23

Swift Extension to Observable with Generic Type Constraint

I'm trying to add an extension to Observable. The code looks like this:

extension Observable where Element == ApiResponse<ItemsContainer<T>>, T:Codable

I'm receiving the following exception: Use of undeclared type T.

So apparently this doesn't work. The only thing missing is to constrain the generic inside ItemsContainer to conform to Codable. Could be as simple as a syntactical issue or maybe I'm just not good enough with generics. Any help is appreciated!

Edit: To give the idea - ApiResponse and ItemsContainer look like this

public struct ApiResponse<ApiModel> {  
  public let data: ApiModel?  
}  

struct ItemsContainer<Items>: Codable  where Items: Codable {  
   let items: [Items]
}

Upvotes: 1

Views: 1999

Answers (1)

E-Riddie
E-Riddie

Reputation: 14780

Issue

You cannot constraint extensions to a Model Type which holds generic values, without specifying the Model Type of the generic value.

You only constrain protocols based on their associatedtypes or generics based on their generic type, on the extension signature. Therefore T is not recognized, because none of the protocols or generic declare it.

Ray Wenderlich Scheme

Solution

So by keeping in mind what I said above, a Model Type needs to be fully defined on the extension context. But wait that does not satisfy our requirement, we want it to be generic!

Then we do not need a Model Type, we need a protocol!

We have two Model Types (ApiResponse and ItemsContainer) which we need to know the generic type, therefore we need two protocols for each of them.

ApiResponse

Let's create one named ApiResponseProtocol

public protocol ApiResponseProtocol {
    associatedtype Model
    var data: Model? { get }
}

Cool, the associatedtype Model will play our role as the generic value for the ApiModel on the object. Let's make ApiResponse conform to ApiResponseProtocol

public struct ApiResponse<ApiModel>: ApiResponseProtocol {
    public let data: ApiModel?
}

Generic ApiModel here can be defined as Model from the protocol.

ItemsContainer

Next steps would be the same for the ItemsContainer

public protocol ItemsContainerProtocol {
    associatedtype Item
    var items: [Item] { get }
}

public struct ItemsContainer<Items>: Codable, ItemsContainerProtocol where Items: Codable {
    public let items: [Items]
}

Extension

Now since we can access each of the generic types from the protocol (associatedtypes), the output would become something like this:

// This would be for example ApiResponse<ItemsContainer<Model>> where Model is a Model Type conforming to Codable
extension Observable where Element: ApiResponseProtocol, Element.Model: ItemsContainerProtocol, Element.Model.Item: Codable {}

Upvotes: 2

Related Questions