Search code examples
swiftprotocolsdecodable

How to make a struct conforms to a protocol which has a property conforms to another protocol in swift 4?


I was going to reflect some JSON data from web service into swift struct. So I created a protocol which conforms to decodable protocol and planed to create some structs to conform it. This is the protocol I had created:

protocol XFNovelApiResponse: Decodable {

   var data: Decodable {get}
   var error: NovelApiError {get}
}

struct NovelApiError: Decodable {

   let msg: String
   let errorCode: String
}

It was compiled. But when I started to write my struct I got an error. The struct's code is here:

struct XFNovelGetNovelsApiResponse: XFNovelApiResponse {

    let data: NovelsData

    let error: NovelApiError

    struct NovelsData: Decodable {

    }
}

The error says type 'XFNovelGetNovelsApiResponse' does not conform to protocol 'XFNovelApiResponse'. I know the 'data' property should be implemented in wrong way. How can I fix it? Thanks.


Solution

  • You are asking to describe the kind of type that data can hold, rather than the actual type. That means it needs to be an associatedtype:

    protocol XFNovelApiResponse: Decodable {
        associatedtype DataType: Decodable
        var data: DataType {get}
        var error: NovelApiError {get}
    }
    

    Note that protocols with associated types can generate a lot of complexity, so you should carefully consider if this protocol is really necessary, or if XFNovelApiResponse could, for example, be generic instead. It depends on what other types implement this protocol.

    For example, another implementation of a similar set of data structures without protocols would be:

    struct XFNovelApiResponse<DataType: Decodable>: Decodable {
        var data: DataType
        var error: NovelApiError
    }
    
    struct NovelsData: Decodable {
    }
    
    struct NovelApiError: Decodable {
    
        let msg: String
        let errorCode: String
    }
    
    let novels = XFNovelApiResponse(data: NovelsData(),
                                    error: NovelApiError(msg: "", errorCode: ""))
    

    Alternately, you can implement this with classes and subclasses, which allow inheritance. Structs do not inherit from protocols, they conform to protocols. If you really mean inheritance, classes are the right tool. (But I expect generics are the better solution here.)