Init(from decoder: Decoder) implemented in struct extension in separate module is not getting called

I have a pod which contains

public struct TransactionResponse: Codable, JSONEncodable, Hashable

In my app module, I created an extension with

public extension TransactionResponse {
    init(from decoder: Decoder) throws {
        self.init(...

When I run the app, the init method declared in the extension is not getting called. If I move the implementation of init(from decoder: Decoder) throws into the struct file inside the pod, then it get called.

What am I missing, i'd like to keep this as an extension and not have to patch the pod...

TransactionResponse already has an implementation of init(from:), and implementations added via extension cannot override the original implementation, only offer an overload of the method. Swift allows this, but it's extremely fragile — depending on where the method is called from, one of the two implementations may get called. In this case, the call to TransactionResponse.init(from:) is dispatching to the original implementation, and not the one in your extension.

This isn't something that you should rely on, and in general, this isn't safe to do. Instead, you will either need to:

  1. Wrap TransactionResponse in a type you control whose init(from:) you can implement directly, or
  2. Change the source of TransactionResponse to suit your needs (which may be a pain)

What does your attempted override doing that the original TransactionResponse does not?

Thanks for that response. I suspected something like this but hoped for something different :slight_smile:

Essentially, I use OpenApi generator to create a pod that contains the network code to interact with a REST API. Unfortunately, the REST client does not send the data correctly (it sends a string containing a JSON object instead of the JSON object directly), so the code generated does not work for this specific class.
So the override that I create simply decode the data as a string, re-encodes it as an object and decodes it as the object...

init(from decoder: Decoder) throws {
        
        let values = try decoder.container(keyedBy: CodingKeys.self)
        let statusesString = try? values.decode(String.self, forKey: .statuses)
        var decodedStatuses = [Status]()
        if let statusData = statusesString?.data(using: .utf8) {
            let decodeResult = CodableHelper.decode([Status].self, from: statusData)
            switch decodeResult {
                case let .success(parsedStatuses):
                    decodedStatuses = parsedStatuses
                case let .failure(error):
                    throw (error)
            }
        }
        
        self.init(..., statuses:decodedStatuses)
    }

I can patch the pod that I generate, but I'd love to keep the modification outside of the pod so it does not get deleted when the pod is regenerated... (I have already done that mistake once :-))

Ah, interesting! And a bit unfortunate. If the type were coming out of a pod/framework/package you didn't control, I'd highly recommend wrapping it in another type which performs this same unwrapping that you do, but in this case, it'd defeat the purpose.

I've never used the OpenAPI generator tooling, but I wonder: could you either templating or file post-processing help you inject this code directly into the pod in a tool-assisted manner? Or is this the kind of patching you're talking about that you'd like to avoid doing?

Yes, right now I'm using the patch command during pod install. But this is not ideal as if the patch was already done, it rejects the patch. i'll have to dig a little deeper.

Thanks

Rather than mess with the decoding of who knows how many types, I'd instead fix the server response before passing it to my parser. If your "stringified" responses are simply JSON in quotes, dropping the first and last byte should fix it. Data(data.dropFirst().dropLast()) should get what you want.

Ah, that's a good point — I took the original question to mean that there are certain types inside of the payload which are stringified, but if it's only the top-level type, this could work (so long as the contents of the string are not escaped; if they are, then simply dropping the leading and trailing quotes will not be sufficient).

Unfortunately, the client code generated by the OpenApi generator does not allow for this.