Generic method to fetch data from local files in Swift & SwiftUI.
Learnings from the article:
- What are Generics?
- How to fetch data from a local JSON file?
Generics are basically a dynamic type which allows us to create a single Method or Class that can accept/use different data types.
let’s look at a simple example of writing a generic method.
func getTypeFor<T>(value: T) {
print(type(of: value))
}//Method CallinggetTypeFor(value: "Sam") //Output: StringgetTypeFor(value: 24.5) //Output: DoublegetTypeFor(value: UIColor.red) //Output: UICachedDeviceRGBColor
In the above function we have used type parameter <T>
for the getTypeFor()
method.
When ever you are calling the method a different input like String
, Float
, UIColor
, where the return type is seen in Output.
I understand it would be tricky for the new developers to make it on a single go, best part of a developer is to try implementing things for better insights.
Now we will move on to how to use local Json file to decode and get the data from it.
Let start with the code by fetch data from a local JSON file!!!
We will start by writing an extension to Bundle.
Bundle is used to read a URL from the file from the main app bundle.
Import UIKitextension Bundle { ... }
Now we are declaring a simple method which accepts dataType and file name as inputs.
func decode<T: Decodable>(_ type: T.Type, from file: String) -> T { ... }
Confused!!! why decodable is been added. Let’s fall back to it in later part of code.
We will now take a look at how locate path and convert it to URL.
Don’t worry its so simple Apple has its own properties and methods for it.
Add this line in the extension body to take the code further.
guard let url = self.url(forResource: file, withExtension: nil) else {
fatalError("Failed to locate \(file) in bundle.")
}
If you have observed forResource
will accept a file of type String
.
Here you will pass the Json file you have created in your project folder e.g FoodList.json
As url(forResource: file, withExtension: nil)
will return an optional value we have used gurad statement to unwrap it.
guard let data = try? Data(contentsOf: url) else {fatalError("Failed to load \(file) from bundle.")}
Here Data(contentsOf: url)
will initialise a data object with the data from the location specified by a given URL.
We are now converting the URL path ->Data format to decode and convert it to Json Model.
let decoder = JSONDecoder()guard let loaded = try? decoder.decode(T.self, from: data) else {fatalError("Failed to decode \(file) from bundle.")}
JSONDecoder which is used to decode the Data that we are sending to the Model type that we pass in.
Technically it maps the JSON data to your model types by making them conform to the Decodable protocol.
The decode(T.self, from: data) will take in the Model & the Data object from the URL we converted before.
At last we will return the decoded JSON.
return loaded
Finally we have created a generic method to pull the data from local file converting to Data and then decoding it to the model we have created and using it around to pass the data.
Method usage:
Bundle.main.decode(([FoodList].self, from: "FoodList.json"))
Hope you have gained some overview.
#Happy Learning.
Please do support by Clap, commenting your views and share it with your collegues.