1

I am trying to write a generic parser method which will parse data with respect to return type.

Edit

We get complex json response from multiple servers. Sometimes same response model could mean different things or vice versa. My target is to inject parsers to my repositories/services. And get the same result at the end.

Edit End

Edit 2

For "FeedTypeSerializable" custom init method should be called

For "FeedTypeDecodable" should be called.

Edit 2 End

protocol FeedResponseProtocol {
    var feed : String { get }
    var success : Bool { get }
}


protocol Serializable {
    init(_ json : [String:Any])
}

Structure which could be constructed using JSON object

struct FeedTypeSerializable : FeedResponseProtocol, Serializable {
    
    var feed: String
    var success: Bool
    
    init(_ json: [String : Any]) {
        feed = ""
        self.success = false
    }
    
}

Decodable Struct

struct FeedTypeDecodable : FeedResponseProtocol {
    var feed: String
    var success: Bool
}

extension FeedTypeDecodable : Decodable {
    private enum CodingKeys : String, CodingKey {
        case feed, success
    }
}

Finally my parser protocol and implementation. I am getting two different error for trying two different things

1: Error : Cannot convert value of type 'Decodable?' to expected argument type 'T.Type' I want to know how I could cast one protocol to another, if possible

2: Error : Static member 'init' cannot be used on instance of type 'Serializable'

protocol ParserProtcol {
    func getFeed<T : FeedResponseProtocol>(forData data: Data) -> T
}

struct FeedParser : ParserProtcol {
    func getFeed<T>(forData data: Data) -> T where T : FeedResponseProtocol {

        if T.self is Decodable {
            let response : T = JSONDecoder().decode((T.Type as? Decodable), from: data)
// 1: Error    
        }
        else if T.self is Serializable {
            do {
                let jsonObject = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! [String:Any]
                let response = (T as! Serializable).init(jsonObject)
//2: Error shown
                }
                catch {
                    //error handling
                }
            }
        }
    }
ibnetariq
  • 718
  • 1
  • 4
  • 20
  • 1
    Your `Serializable` protocol is seriously flawed. Not all JSONs are a `Dictionary`. Also, why do you need it in the first place? What value does it add that `Decodable` does not have already? Using `[String:Any]` is also a big red flag. You should avoid using `Any` as much as possible. – Dávid Pásztor Jul 21 '20 at 08:37
  • 2
    This is a common mistake: The type argument of `decode(_:from:)` **must** be a concrete type. You can use a generic constrained to `Decodable` but never `Decodable` itself. – vadian Jul 21 '20 at 08:37
  • 1
    ... and this kind of runtime type check is pretty ugly and *unswifty*. Write two methods with the same signature once constrained to `Decodable` the other to `Serializable`. The compiler will choose the proper one (at compile time). – vadian Jul 21 '20 at 08:54
  • @DávidPásztor, your point is valid. Actually I provided this code just to give a general idea. And why I need it, we have pretty complex json structure coming from multiple servers. My target to achieve is, I could inject parsers to repositories/services. These parsers will parse response and return a struct conforming to a protocol. I'll add this info to question also. – ibnetariq Jul 21 '20 at 09:24
  • @vadian, you comment is making sense to me. Can you guide me to some code example/documentation/tutorial – ibnetariq Jul 21 '20 at 09:27
  • @ibnetariq [Trying to write a generic function for parsing JSON into Codable structs](https://stackoverflow.com/questions/50909240/trying-to-write-a-generic-function-for-parsing-json-into-codable-structs) explains how to correct your decodable. – Dávid Pásztor Jul 21 '20 at 09:41
  • @DávidPásztor, I am already using this. My problem is, in most cases I would be using JSONDecoder. But in select few cases, I would like to call init with Dictionary, where I can check key/value pairs and take decisions accordingly. And example would be I get "Users" and "UsersList" from two different servers. So "FeedTypeDecodable" uses JSONDecoder and "FeedTypeSerializable" uses custom init method – ibnetariq Jul 21 '20 at 09:49
  • @ibnetariq you can easily handle that using `JSONDecoder` and `Codable`, no need to manually parse a `Dictionary`. On how to do that, check [swift 4 Codable - how to decode if there is string or dictionary?](https://stackoverflow.com/questions/54077932/swift-4-codable-how-to-decode-if-there-is-string-or-dictionary/54078158#54078158) – Dávid Pásztor Jul 21 '20 at 09:52
  • This question and accepted answer explains one of the issues I'll face -> https://stackoverflow.com/questions/58236188/use-multiple-codingkeys-for-a-single-property I want to take two different approaches based on need. – ibnetariq Jul 21 '20 at 09:58
  • @DávidPásztor, vadian suggestion was different than the link you provided. A solution is possible. Can you please revert the [duplicate] tag, so I can post answer and get it reviewed from community. – ibnetariq Jul 21 '20 at 12:03

0 Answers0