Jasica Peter
Jasica Peter

Reputation: 91

Generic function with Alamofire

I work with iOS app that use Alamofire, I want to write a generic function(s) which used to send and retrieve data from server to a decodable objects, my function was as below :

func pop <T : Codable>  (_ Url: inout String, _ popedList: inout [T]) {
    let url = URL(string:Url)
    Alamofire.request(url!, method: .post).responseJSON { response in
        let result = response.data
        do {
            let data = try JSONDecoder().decode(popedList, from: result!)// get error here
            print(data[0])

            let jsonEncoder = JSONEncoder()
            let jsonData = try! jsonEncoder.encode(data[0])
            let jsonString = String(data: jsonData, encoding: .utf8)
            print("jsonString: \(String(describing: jsonString))")

        } catch let e as NSError {
            print("error : \(e)")
        }
    }
} 

and a function to send an object to server as below:

func push <T : Codable>  (_ Url: inout String, _ pushObject: inout T) {
    let jsonData = try! JSONEncoder().encode(pushObject)
    let jsonString = String(data: jsonData, encoding: .utf8)
    print("jsonString: \(String(describing: jsonString))")

    let url = URL(string:Url)

    Alamofire.request(url!,
                      method: .post,
                      parameters:jsonString)//it's need to creat a Dictionary instate of String
        .validate(statusCode: 200..<300)
        .validate(contentType: ["application/json"])
        .response { response in
            // response handling code
             let result = response.data
            print(response.data)
    }
}

I get an error in first function,

"Cannot invoke 'decode' with an argument list of type '([T], from: Data)'"

and

"Escaping closures can only capture inout parameters explicitly by value"

What is the best way to write these to function as generic?

Upvotes: 1

Views: 2447

Answers (3)

Jasica Peter
Jasica Peter

Reputation: 91

After a few searches and trying to edit my functions I capable to rewrite my two functions in such away that I get what I need:

 func pop<T: Decodable>(from: URL, decodable: T.Type, completion:@escaping (_ details: [T]) -> Void)
            {
       Alamofire.request(from, method: .post).responseJSON { response in
                let result_ = response.data
                do {
                    let data = try JSONDecoder().decode([T].self, from: result_!)
                    //let data = try JSONDecoder().decode(decodable, from: result_!)// get error here
                    //print(data[0])
                    print("data[0] : \(data[0])")
                    completion(data)
                } catch let e as NSError {
                    print("error : \(e)")
                }
            }
        }

    func push <T : Codable>  (_ Url:  String, _ pushObject:  T)
        {
            let jsonData = try! JSONEncoder().encode(pushObject)
            let jsonString = String(data: jsonData, encoding: .utf8)
            print("jsonString: \(String(describing: jsonString))")

            let url = URL(string:Url)

            Alamofire.request(url!,
                              method: .post,
                              parameters:convertToDictionary(text: jsonString!))//it's need to creat a Dictionary instate of String
                .validate(statusCode: 200..<300)
                .validate(contentType: ["application/json"])
                .response { response in
                    // response handling code
                    print(response.data!)
                    if let jsonData = response.data {
                        let jsonString = String(data: jsonData, encoding: .utf8)
                        print("response.data: \(String(describing: jsonString))")
                    }
            }
        }

        func convertToDictionary(text: String) -> [String: Any]? {
            if let data = text.data(using: .utf8) {
                do {
                    return try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
                } catch {
                    print(error.localizedDescription)
                }
            }
            return nil
        }

Upvotes: 3

Mattia C.
Mattia C.

Reputation: 731

For the first function, JSONDecoder.decode() wants 2 parameters:

  • Type to decode to: the class/struct you want it to decode to. This is not an instantiated object, just the type.
  • Data to decode from: the generic Data object that will be converted to the type you specified.

So, in order to be able to write your function so that it has a generic URL and result object, you would need to pass it the object type and a callback to pass the result to, since network operations are asynchronous.

func dec<T: Decodable>(from: URL, decodable: T.Type, result: (T) -> Void) { 
    // your Alamofire logic
    let data = try JSONDecoder().decode(popedList, from: result!)
    result(data)
}

You can apply the same logic to the second function.

Note that this is not the best way to take care of eventual errors, just an example of how you can handle encoding with a generic function.

Upvotes: 1

Bilal
Bilal

Reputation: 19156

JSONDecoder().decode method takes type and data parameter. Pass type not popedList.

let data = try JSONDecoder().decode([T].self, from: result!)

Inout Paramaters Function parameters are constants by default. Trying to change the value of a function parameter from within the body of that function results in a compile-time error. This means that you can’t change the value of a parameter by mistake. If you want a function to modify a parameter’s value, and you want those changes to persist after the function call has ended, define that parameter as an in-out parameter instead.

You are not changing value of popedList in both functions, so using inout is meaningless.

Upvotes: 0

Related Questions