Reputation: 91
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
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
Reputation: 731
For the first function, JSONDecoder.decode() wants 2 parameters:
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
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