2

I want to decode json responses of a websocket "notification" where the type of notification is within the json response.

JSON example:

{
    "jsonrpc": "2.0",
    "method": "Application.OnVolumeChanged",
    "params": {
        "data": {
            "muted": false,
            "volume": 88.6131134033203125
        },
        "sender": "xbmc"
    }
}

This is what I currently have:

func notificationMessage(text: String) {
    do {
        if let jsonData = text.data(using: .utf8),
            let json = try JSONSerialization.jsonObject(with: jsonData) as? [String: Any],
            let method = json["method"] as? String,
            let methodName = method.split(separator: ".").last?.description {

            let decoder = JSONDecoder()
            let object = try decoder.decode(OnVolumeChanged.self, from: jsonData)

            print(object)
        }
    } catch {
        print("Error deserializing JSON: \(error)")
    }
}

Now I somehow want to use methodName instead of OnVolumeChanged.self. But I don't feel like making a huge switch case on the methodName since I can get like hundreds of diferent methods I have tried NSClassFromString(methodName) but this is giving me AnyClass? which is not a concrete type.

Is there a way to get a class type from string?

5
  • Have you tried NSClassFromString(method) instead of stripping the namespace part of the method string? Commented Aug 13, 2017 at 14:32
  • decode() is expecting a concrete type T.Type and AnyClass? is basically anything. Commented Aug 13, 2017 at 15:26
  • This goes against Swift's static typing system so you will have to swim against a pretty strong current. But before you do that, ask yourself: is there any difference that you want to capture for OnVolumeChanged and AnotherMethodName? If so, please edit your question to add an example. Commented Aug 14, 2017 at 15:16
  • I had the same issue for matching JSON to class types, and I ended up using enumerations with raw values matching the JSON string values. Commented Aug 15, 2017 at 5:26
  • Yes @CodeDifferent, the data object within the json is diferent for every method. But i'll stick to an enum containing all possible methods and then determine with a switch case which object should be used for decoding the json. Thanks for information! Commented Aug 16, 2017 at 17:23

1 Answer 1

1

I have faced the same problem and here is my solution. You can add methods to Mapper dictionary any time you want.

//1

let Mapper: [String: Any] = ["OnVolumeChanged"  : OnVolumeChanged.self]

//2

func notificationMessage(text: String) {
                    do {
                        if let jsonData = text.data(using: .utf8),
                            let json = try JSONSerialization.jsonObject(with: jsonData) as? [String: Any],
                            let method = json["method"] as? String,
                            let methodName = method.split(separator: ".").last?.description {

                            let className = Mapper[methodName] as! Codable.Type
                            let object = try className.init(jsonData: jsonData)


                            print(object)
                        }
                    } catch {
                        print("Error deserializing JSON: \(error)")
                    }
                }

//3

extension Decodable {
                    init(jsonData: Data) throws {
                        self = try JSONDecoder().decode(Self.self, from: jsonData)
                    }
                }
Sign up to request clarification or add additional context in comments.

1 Comment

there is nothing "dynamic" in this solution, there should be a solution where we don't need to manually enter all the classes we auto generated for json responses, even supplying the "decoder" utility with the expected json response and do Bundle.main.classNamed("MyClassName") is better, (like here: stackoverflow.com/a/55170964/530884)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.