I am new to swift and struggling a bit. I have several GET queries which all end up doing the same just using another class (Target iOS 10.3, Swift 5).
This method receives a bulk of datasets and returns them through the completion handler. Currently I have this code multiple times for each type but I can imagine that there is not a more efficient way.
CLASS marks the only differences:
func getMultiple (completion: @escaping(Result<[CLASS], APIError>) -> Void) {
let data = try JSONDecoder().decode([CLASS].self, from: data!)
Here my method:
func getMultiple (completion: @escaping(Result<[CLASS], APIError>) -> Void) {
do {
urlRequest.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: urlRequest) {(data, response, error) in
if let error = error {
print("error: \(error)")
}
else {
if let httpResponse = response as? HTTPURLResponse {
switch httpResponse.statusCode {
case 200:
do {
let data = try JSONDecoder().decode([CLASS].self, from: data!)
completion(.success(data))
return
} catch {
print("caught: \(error)")
}
default:
do {
let data = try JSONDecoder().decode(ServerMessage.self, from: data!)
completion(.failure(
APIError(
code: httpResponse.statusCode,
msg: data)
))
} catch {
print("caught: \(error)")
}
return
}
}
}
}
task.resume()
}
}
This is exactly the problem generics are designed to fix. Replace "CLASS" with a generic parameter "Item" everywhere in your function, and the rest should work exactly as you'd like.
func getMultiple<Item: Decodable>(completion: @escaping(Result<[Item], APIError>) -> Void) {
^^^^^^^^^^^^^^^^^ ^^^^
For more, see Generic Parameters and Arguments in the Swift Programming Language.
I often suggest adding an extra parameter to your function signature to make it easier to pass the type (this is similar to how JSONDecoder works):
func getMultiple<Item: Decodable>(of: Item.Type = Item.self, completion: @escaping(Result<[Item], APIError>) -> Void) {
^^^^^^^^^^^^^^^^^^^^^^^^^^
This of
parameter isn't used anywhere; it's just means you can include the type directly in the call a little more easily:
.getMultiple(of: User.self) { user in ... }
The addition of = Item.self
just means that if the type is known from context, you don't have to pass it, which can be convenient.