5

I am learning JSONParsing. I followed tutorials and what I got is this:

    guard let url = URL(string: "http://localhost/test-api/public/api/register") else { return }

    var request  = URLRequest(url: url)

    request.httpMethod = "POST"

    let newUser = User.init(name: self.collectionTF[0].text, email: self.collectionTF[1].text, password: self.collectionTF[2].text)

    do {

        let jsonBody = try JSONEncoder().encode(newUser)

        request.httpBody = jsonBody

    } catch { }

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

        guard let data = data else { return }

        do {

            let json = try JSONSerialization.jsonObject(with: data) as? [String:Any]

            print(json!)

            DispatchQueue.main.async {

            if json!["status"] as! Int == 200
            {
                GeneralHelper.shared.keepLoggedIn()

                NavigationHelper.shared.moveToHome(fromVC: self)
            }

            }

        } catch { print(error.localizedDescription)}

        }.resume()

Ok, this is what I have done for register. Now, I want to create a Helper, which will do the same thing with @escaping as I we all need the parsed JSON in return.

So, I am passing the endPoint as String and then trying to pass this newUser which is a Encodable, it can be a Decodable as well in future, but it throws an error Cannot invoke 'encode' with an argument list of type '(Codable)'. Can anyone please help? And, is it better this way, by calling this function multiple times when it comes to JSONParsing?

Edit: - So, I am now using the networkRequestfunction and here is what I have done.

 let newData = User.init(name: "Rob", email: "abc@gmail.com", password: "12345678")

ApiHelper.sharedInstance.networkRequest_Post(urlString: "register", header: nil, encodingData: newData) { (response: User, urlRes, error) in
        <#code#> }

Now, it gives me this error: Cannot convert value of type '(User, _, _) -> ()' to expected argument type '(_?, HTTPURLResponse?, Error?) -> ()'. Any help?

Rob13
  • 381
  • 8
  • 20

2 Answers2

3

I have used the same functionality in my project

Hope the below code will help

    func networkRequest_Post<T: Decodable, Q: Encodable>(urlString: String,header:[String:String]?,encodingData: Q,completion: @escaping (T?, HTTPURLResponse?, Error?) -> ()) {

    guard let url = URL(string: urlString) else { return }
    let config = URLSessionConfiguration.default
    config.timeoutIntervalForRequest = 300.0
    config.timeoutIntervalForResource = 300.0
    if header != nil{
        config.httpAdditionalHeaders = header
    }
    let session = URLSession(configuration: config)
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    do {
        let jsonBody = try JSONEncoder().encode(encodingData)
        request.httpBody = jsonBody
    } catch {}
    let task = session.dataTask(with: request) { (data,response, err) in

        if let response = response {
            print(response)
        }
        if let err = err {
            print("Failed to fetch data:", err.localizedDescription, "Error Description\(err)")
            return
        }
        guard let data = data else { return }
        do {
            print(String(data: data, encoding: String.Encoding.utf8) as Any)
            let dataReceived = try JSONDecoder().decode(T.self, from: data)
                completion(dataReceived,response as? HTTPURLResponse,err)
        } catch let jsonErr {
            print("Failed to serialize json:", jsonErr, jsonErr.localizedDescription)
            completion( nil,response as? HTTPURLResponse,jsonErr)
        }
    }
    task.resume()
}

Use -

         let newdata = User(name: "Abhi", email: "jhjhj@jhj.co", password: "123hguhj")
    networkRequest_Post(urlString: "YOUR_URL", header: nil, encodingData: newdata) { (RESPONSE_DATA:User?, URL_RESPONSE, ERROR) in
        // Do your network work here
    }

    struct User : Codable {
      var name: String?
      var email: String?
      var password: String?
    }
Abhinav Jha
  • 295
  • 1
  • 17
  • Ok, can you show me a function like calling the function with a decodable and an encodable, sorry but things are not so much clear to me, when it comes to generics, I am very new to it. A little guide will help me. – Rob13 Oct 26 '18 at 07:54
  • Do I need the Header? – Rob13 Oct 26 '18 at 09:52
  • I had a requirement for header so added it. I have made it option and updated my code – Abhinav Jha Oct 26 '18 at 09:57
  • I have updated my question, please check the Edit part this is giving me a headache now. Do you know what is wrong here? – Rob13 Oct 26 '18 at 15:08
  • The expected type is optional and you are passing non optional value, Please pass the value as below ApiHelper.sharedInstance.networkRequest_Post(urlString: "register", header: nil, encodingData: newData) { (response: User?, urlRes?, error?) in – Abhinav Jha Oct 26 '18 at 15:21
  • I have updated my answer and tested in xcode, Kindly check my ans – Abhinav Jha Oct 26 '18 at 15:30
  • My `struct User` was of type `Encodable`. should I change it to `Codable`. That's why it was not working. – Rob13 Oct 26 '18 at 15:39
  • Yes, please make it codable and let me know if that worked – Abhinav Jha Oct 26 '18 at 15:40
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/182600/discussion-between-rob13-and-abhinav-jha). – Rob13 Oct 26 '18 at 15:42
1

You can user Generics in following way...

func requestWith<T>(method: HTTPMethod, action: String,  params: Parameters?, for type: T.Type, success: @escaping (AnyObject) -> Void, failure: @escaping (AnyObject) -> Void) where T: Codable {


//do your stuff..your logic goes here..see below example

     guard let serverData = response.data else {
           return
        }

   do {
     let decoder = JSONDecoder()
     let responseData = try decoder.decode(type, from: serverData)
       success(responseData as AnyObject)
    } catch {
      print("Error = \(error.localizedDescription)")
       failure(error.localizedDescription as AnyObject)
   }

}
Mahendra
  • 8,448
  • 3
  • 33
  • 56
  • Codable is super of Decodable & Encodable protocol...just try it. – Mahendra Oct 26 '18 at 06:30
  • 2
    Unspecified `AnyObject` is inappropriate for a generic function. Better `func requestWith(method: HTTPMethod, action: String, params: Parameters?, success: @escaping (T) -> Void, failure: @escaping (Error) -> Void) where T: Codable { ` – vadian Oct 26 '18 at 06:31
  • I know, here you have used `JSONDecoder`, so I am asking what should I do if I want to `encode`? – Rob13 Oct 26 '18 at 06:31
  • @vadian, it is just an example:) – Mahendra Oct 26 '18 at 06:53
  • 1
    Yes, but please suggest better examples for the people who copy and paste code together. – vadian Oct 26 '18 at 06:58
  • @vadian Can you show me an example or anything where both JSONEncoder and Decoder are used? Or any tutorial that I can follow? – Rob13 Oct 26 '18 at 07:12
  • Great example! I think this just got me hooked to generics... – LinusGeffarth Apr 08 '19 at 20:13