Creating a general function to parse any json data (SwiftUI)

I am trying to write a function that can have all the parameters like fileName and model passed in and use them to return that model filled with the desired json data from a local file.

In the courses we have done specific file parsing numerous times, so my question is not so much about how to parse json data but how to make this generalized function to parse any file wanted (into an appropriately formatted model of course)

Here is a general idea of the json parsing I already am doing:

class DataServices{ 
    static func parseExampleJson() -> [ExampleModel]{
        let jsonUrl = Bundle.main.url(forResource: "example", withExtension: "json")
        do{
        let jsonData = try Data(contentsOf: jsonUrl!)
            let jsonDecoder = JSONDecoder()
            let examples = try jsonDecoder.decode([ExampleModel].self, from: jsonData)
            return examples
        }
        catch{
            print("error 1")
        }
        return [ExampleModel]()
    }
}

Obviously this function can only parse a single json file into a single model, I can easily pass in a string to hold the file name but I can’t figure out how to pass in a model and return that model afterwords, especially because this model with be of a different data type every time. Any help would be greatly appreciated!

This is exactly the kind of thing that Generics are meant for. I would suggest reading up on them.

But the basic idea is something like this:

class DataServices{ 
    static func parseExampleJson<JSONType: Decodable>(from filename: String) -> [JSONType] {
        let jsonUrl = Bundle.main.url(forResource: filename, withExtension: "json")
        do{
            let jsonData = try Data(contentsOf: jsonUrl!)
            let jsonDecoder = JSONDecoder()
            let examples = try jsonDecoder.decode([JSONType].self, from: jsonData)
            return examples
        }
        catch{
            print("error 1")
        }
        return [JSONType]()
    }
}

<JSONType: Decodable> indicates that JSONType is a generic placeholder for whatever type you are actually decoding and that type has to conform to the Decodable protocol so that it can be decoded. The placeholder type gets filled in with a specific type when you call it, like so:

//let's say you have a Decodable type called Stuff 
//and your JSON is an array of Stuff items
//stored in a file called "myStuff.json"
let stuff: [Stuff] = DataServices.parseExampleJson(from: "myStuff")

Since you’ve said that your return type is [Stuff], the compiler can determine what the placeholder type JSONType actually is (because the function definition says it returns [JSONType]) and substitute Stuff wherever JSONType occurs in the function.

This essentially is the same as if you had written this function:

class DataServices{ 
    static func parseExampleJson(from filename: String) -> [Stuff]{
        let jsonUrl = Bundle.main.url(forResource: filename, withExtension: "json")
        do{
            let jsonData = try Data(contentsOf: jsonUrl!)
            let jsonDecoder = JSONDecoder()
            let examples = try jsonDecoder.decode([Stuff].self, from: jsonData)
            return examples
        }
        catch{
            print("error 1")
        }
        return [Stuff]()
    }
}

but by using generics you won’t have to actually write a function like that for each type that you want to decode.

Hope that gives you an idea what you need to do and makes you want to investigate generics more. They can be quite powerful and even a little fun!

1 Like

Thank you!

No problem!