Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use new Result type introduced in swift 5 URLSession?

Swift 5 introduces new Result type to handle the result of an asynchronous function. I want to know the way to use this new result type for URLSession.

I have this following code.

func getCategorByAPI()
    {
        //Base Url is from an static variable
        let url = URL(string: URLManager.aPIBaseURL+"category")!
        var request  = URLRequest(url: url)
        request.httpMethod = "GET"
        let boundary = "Boundary-\(UUID().uuidString)"
        request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")

        let task = URLSession.shared.dataTask(with: request as URLRequest) {
            data, response, error in

            if error != nil {
                //print("error=\(error)")
                return
            }

            do {
                let json = try JSONSerialization.jsonObject(with: data!, options: []) as? NSDictionary
                print(json)
            }catch
            {
                print(error)
            }

        }

        task.resume()
    }

How can i rewrite this function using swift 5 Result type?

like image 816
Asif Newaz Avatar asked Dec 22 '22 23:12

Asif Newaz


1 Answers

You want to create an enum that specifies the possible cases in your result (e.g. success or failure). Then you add a completion to your getCategorByAPI() method that is of type Result<Data, Error>. From there, inside url session, you will call your completion handler passing in either the data on .success or the error on .failure.

You can also do cool things like override Result's get() method and extend Result to decode your data :D

Check it out:

enum Result<Success, Error: Swift.Error> {
    case success(Success)
    case failure(Error)
}

// override the Result.get() method
extension Result {
    func get() throws -> Success {
        switch self {
        case .success(let value):
            return value
        case .failure(let error):
            throw error
        }
    }
}

// use generics - this is where you can decode your data
extension Result where Success == Data {
    func decoded<T: Decodable>(using decoder: JSONDecoder = .init()) throws -> T {
        let data = try get()
        return try decoder.decode(T.self, from: data)
    }
}


func getCategorByAPI(completion: (Result<Data, Error>) -> Void)
{
    // You might want to stick this into another method
    let url = URL(string: URLManager.aPIBaseURL+"category")!
    var request  = URLRequest(url: url)
    request.httpMethod = "GET"
    let boundary = "Boundary-\(UUID().uuidString)"
    request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")

    URLSession.shared.dataTask(with: request as URLRequest) {
        data, response, error in

        if error != nil {
            completion(.failure(error))
            return
        }

        if !(200...299).contains(httpResponse.statusCode) && !(httpResponse.statusCode == 304) {
            let httpError = // ... convert httpResponse.statusCode into a more readable error
                completion(.failure(httpError))
        }

        if let data = data {
            completion(.success(data))
        }
        }.resume()
}

I haven't tested the above but have something like this implemented in a current project. Here are some articles I read to learn about how to implement:

https://www.hackingwithswift.com/articles/161/how-to-use-result-in-swift
https://medium.com/@pavlepesic/how-to-use-swift-5-result-with-codable-protocol-824c9a951af9

like image 120
christinam Avatar answered Mar 19 '23 09:03

christinam