0

I am new on swift and trying to do something like that :

I have struct named Api Response :

struct ApiResponse {
    var IsSuccess : Bool = false
    var Message : String?
    var ReturnedData : Data?
}

and have a func in another class named CommonHandler, that makes api call

public class CommonHandler {

    public func CallApi(_ apiUrl : String , _ parameters : [String : Any] ) -> ApiResponse
    {
        var apiResponse = ApiResponse()

        let url = URL(string: apiUrl)!
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.httpBody = try! JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)

        let task = URLSession.shared.dataTask(with: request , completionHandler : { data, response, error in
            if let error = error {
                // handle the transport error
                return
            }
            guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
                // handle the server error
                return
            }
            apiResponse.ReturnedData = data
            apiResponse.IsSuccess = true
            apiResponse.Message = "Succeed"

        })
        task.resume()

        return apiResponse
    }
}

I want to call this function in UIViewController like that :

var handler = CommonHandler()
let param :[String : String] = ["param":"param"]
let url = "url"

let response = handler.CallApi(url, param)
print(response.IsSuccess)
print(response.Message!)

I am aware that i can not use dataTask method like this. It's async. But how can i do api call in a non-void func and return its response data ? I parse ReturnedData json to struct then. What is the best approach in this case ? Thanks

rmaddy
  • 314,917
  • 42
  • 532
  • 579
cod3r
  • 21
  • 1
  • 6
  • please take a look https://stackoverflow.com/questions/26784315/can-i-somehow-do-a-synchronous-http-request-via-nsurlsession-in-swift – Yongjoon Nov 13 '17 at 07:33

1 Answers1

2

Use an asynchronous completion handler

public func callApi(with url : String , parameters : [String : Any], completion: @escaping (ApiResponse?) -> () )
{
    var apiResponse = ApiResponse()

    let url = URL(string: apiUrl)!
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")
    request.httpBody = try! JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)

    let task = URLSession.shared.dataTask(with: request , completionHandler : { data, response, error in
        if let error = error {
            // handle the transport error
            completion(nil)
            return
        }
        guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
            // handle the server error
            completion(nil)
            return
        }
        apiResponse.ReturnedData = data
        apiResponse.IsSuccess = true
        apiResponse.Message = "Succeed"
        completion(apiResponse)

    })
    task.resume()
}

and call it:

var handler = CommonHandler()
let param = ["param":"param"]
let url = "url"

handler.callApi(with: url, parameters: param) { response in
    if let response = response {
       print(response.IsSuccess)
       print(response.Message!)
    }
}

Note:

Please conform to the naming convention that variable and function names start with lowercase letter and use parameter labels for better readability.

vadian
  • 274,689
  • 30
  • 353
  • 361
  • Thanks for reply. This worked for me. Is this cause UI freeze ? And should i use this approach? I just want to prevent code repating on api calls – cod3r Nov 13 '17 at 08:18
  • It does not freeze the UI nor the current thread and this is the recommended way to handle asynchronous API calls. If UI is involved make sure that the UI is updated (also asynchronously) on the main thread. And be aware to keep a strong reference to the class containing the asynchronous task until it has finished. – vadian Nov 13 '17 at 08:26
  • Using completion handlers is a really good approach, however you should remember that it breaks the flow and may sometimes be not what you need. Specifically, when you need a set of operations executed consequently in the same context/thread/scope, this is not an option. But for the case of trivial networking operation, I believe it's better than GCD. – Hexfire Nov 13 '17 at 12:51
  • @Hexfire I disagree with the statement *it breaks the flow*. The flow in a piece of software has nothing to do with the *order of appearance* in the code. Many tasks are based on asynchronous execution. It's up to the developer to understand and use those patterns. Notifying is always better than polling. *Don't ask, tell!* – vadian Nov 13 '17 at 13:05
  • @vadian I didn't refer *flow* as any kind of special technical term. I explained it right away in the comment. When sequence of execution is important (to keep things within a scope, context (e.g. when working with DB context) or a thread), approach *will vary* **for sure**. So notifying is definitely not *always* better, rather it depends on a particular need. But I'd agree with you that for great deal of tasks (the one in question, for instance) notifying is clean and good. – Hexfire Nov 13 '17 at 13:16