Reputation: 425
I have some basics in Swift, and I'm now trying to learn iOS development. I'm currently working in a small app that will ask resource on an API I've made that returns json made from :
struct A : Codable {
let name: String
let age: Int
}
struct B : Codable {
let something: String
}
Both API and app have these structs defined. As I'm always querying the same API, I thought of wrapping the part that ask the API some resources and decode this so I have an instance of the struct to use in my callback. Here's this method :
static func getContent(urlRequest: URLRequest, decodable: Decodable, completion: @escaping (Codable?, ErrorEnum?)->Void) {
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
let task = session.dataTask(with: urlRequest) {
data, response, error in
guard let data = data else {
completion(nil, .noData) // Handling errors in an enum
return
}
let decoder = JSONDecoder()
if let full = try? decoder.decode(decodable, from: data) {
completion(full, nil)
}
}
task.resume()
}
My problem concerns the decodable param. This shows an error and prevent me from compiling the app. After finding some resources on StackOverflow, I tried to change the parameters as
static func getContent(urlRequest: URLRequest, decodable: Decodable.Type, completion: @escaping (Codable?, ErrorEnum?)->Void)
I also tried to keep the parameter like this, and instead change inside the decode params
if let full = try? decoder.decode(decodable, from: data) {
completion(full, nil)
}
but nothing seems to satisfy the compiler... And looking at decode
method inside Swift source code didn't help me that much as it requires T.Type where T is Decodable
My wish is to be able to use this as follow :
static func getA() {
guard let url = URL(string: "http://localhost/a") else { return }
let urlRequest = URLRequest(url: url)
getContent(urlRequest: urlRequest, decodable: A.self) {
a, error in
guard a = a else { return }
print(a.name!)
}
}
Do you have any idea how I could achieve this ? I also don't really know how to call this type of parameters or what to search on google that can lead me to the answer (lack of vocabulary).
Thank you !
Upvotes: 0
Views: 1615
Reputation: 41
You can use this:
func genericRequest<T: Decodable>(_ request: URLRequest, completion: @escaping APIGenericRequestCompletion<T>) {
Alamofire.request(request).responseData { (response) in
guard let data = response.data else {
completion(nil)
return
}
do {
let decodedObject = try JSONDecoder().decode(T.self, from: data)
completion(decodedObject)
} catch {
completion(nil)
}
}
}
where APIGenericRequestCompletion is:
typealias APIGenericRequestCompletion<T: Decodable> = (_ result: T?) -> Void
Then you use it as:
genericRequest(request) { (decodableObjectResponse) in
// your code here
}
Upvotes: 1
Reputation: 5088
try this just add a generic
.Type
of Codable
and use its type as a parameter to pass foo.self
static func getContent<T: Codable>(urlRequest: URLRequest, decodable: T.Type, completion: @escaping (T?, ErrorEnum?)->Void) {
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
let task = session.dataTask(with: urlRequest) {
data, response, error in
guard let data = data else {
completion(nil, .noData) // Handling errors in an enum
return
}
let decoder = JSONDecoder()
if let full = try? decoder.decode(decodable, from: data) {
completion(full, nil)
}
}
task.resume()
}
Upvotes: 1