Let’s consider you are trying to download a json. You have a completion handler as shown below
func downloadUrl(url: URL, completion: @escaping (_ result: ([StudentInfo]?, error) -> Void)) {
If you look at the method declaration, you can see that it has a completion handler to it.
Downloader.self.init().downloadUrl(url: URL(string: url)!, completion: {
students, downloaderError in
self.students = students
You can use the completion handler as shown below when using the Download method.
This type of code design is good. But we can make the code more cleaner, by making use of the Result enum is Swift. The Result enum is available since Swift 5 and allows us to define a success and failure case. The type is useful for defining the result of a failable operation in which we want to define both the value and error output type.
This is how the result enum is defined in Swift
public enum Result<Success, Failure> where Failure : Error {
/// A success, storing a `Success` value.
case success(Success)
/// A failure, storing a `Failure` value.
case failure(Failure)
Now to make the changes. You can alter the method as follows
func downloadUrl(url: URL, completion: @escaping (_ result: Result<[StudentInfo]?, error>) -> Void) {
let task = URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in
guard error == nil else {
completion(.failure(Downloader.error.fail))
return
}
guard let response = response as? HTTPURLResponse, (200...299).contains(response.statusCode) else {
completion(.failure(Downloader.error.fail))
return
}
if let mimetype = response.mimeType, mimetype == "application/json", let data = data {
do {
let decoder = JSONDecoder()
let students = try decoder.decode([StudentInfo].self, from: data)
completion(.success(students))
} catch {
completion(.failure(Downloader.error.fail))
}
}
})
task.resume()
}
If you notice, I changed the signature of the method to use the Result enum. It has a success and failure value. When we have a success condition, we pass the actual data. When its a failure condition, we pass the error.
This is how you can use it.
Downloader.self.init().downloadUrl(url: URL(string: url)!, completion: {
result in
switch result {
case .success(let students):
self.students = students
print("success")
case .failure(let downloadError):
print("failure \(downloadError)")
}
print("downloaded")
hideActivityView = true
})
As simple as that. Now the code looks much cleaner than before. Have fun coding.
Leave a Reply