I want to make a networking request in which I could control the response is JSON raw value [String: Any
or the Decodable
.
Here is the example:
func reqest<T>(endpoint: EndPoint, completion: @escaping (Result<T, Error>) -> Void) {
session.dataTask(with: request) { (data, response, error) in
if T.self is Decodable.Type {
try? JSONDecoder().decode(T.self, from: data)
} else {
try? JSONSerialization.jsonObject(with: data, options: .mutableLeaves)
}
}.resume()
}
then when I want JSON value I just call with T
as [String: Any]
or just use any model confirm Decodable
protocol.
The question is for this line:
try? JSONDecoder().decode(T.self, from: data)
How to cast T
to Decodable
?
I know to use:
func reqest<T: Decodable >(endpoint: EndPoint, completion: @escaping (Result<T, Error>) -> Void)
but [String: Any]
isn't Decodable
.
Or any better solution to achieve what I want? thanks.
I would recommend to use overload for the reqest(endpoint:completion:)
function to achieve what you want.
A structure that I would like for example is this:
enum ResponseError: Error {
case noData
case typeMismatch
}
func reqest<T>(endpoint: EndPoint, completion: @escaping (Result<T, Error>) -> Void) {
baseReqest(endpoint: endpoint) { result in
switch result {
case .success(let data):
do {
guard let json = try JSONSerialization.jsonObject(with: data) as? T else {
completion(.failure(ResponseError.typeMismatch))
return
}
completion(.success(json))
} catch {
completion(.failure(error))
}
case .failure(let error):
completion(.failure(error))
}
}
}
func reqest<T: Decodable>(endpoint: EndPoint, completion: @escaping (Result<T, Error>) -> Void) {
baseReqest(endpoint: endpoint) { result in
switch result {
case .success(let data):
do {
let response = try JSONDecoder().decode(T.self, from: data)
completion(.success(response))
} catch {
completion(.failure(error))
}
case .failure(let error):
completion(.failure(error))
}
}
}
private func baseReqest(endpoint: EndPoint, completion: @escaping (Result<Data, Error>) -> Void) {
session.dataTask(with: request) { (data, response, error) in
if let error = error {
completion(.failure(error))
return
}
guard let data = data else {
completion(.failure(ResponseError.noData))
return
}
completion(.success(data))
}.resume()
}
That way you can have generic response handling code in baseReqest(endpoint:completion:)
function and separate only the response parsing in the other two functions.
Then calling reqest(endpoint:completion:)
function could be
[String: Any]
as response type:reqest(endpoint: endpoint) { (result: Result<[String: Any], Error>) in
// Handle result
}
[[String: Any]]
as response type:reqest(endpoint: endpoint) { (result: Result<[[String: Any]], Error>) in
// Handle result
}
Decodable
object as response type:struct Response: Decodable {}
reqest(endpoint: endpoint) { (result: Result<Response, Error>) in
// Handle result
}
Do it in Swift way. Use the struct Codable (that gives you the Decodable together).
For example:
struct testStruct: Codable {
public var testString:String!
public var testAny:Any!
init(
testString:String!,
testAny:Any!
)
{
self.testString = testString
self.testAny = testAny
}
Then you initialize it with this:
var testStructToUse:[testStruct] = []
From here you can populate it with append method:
testStructToUse.append(testStruct(testString: "any String", testAny: "any value"))
And encode with JSONencoder
let jsonData = try JSONEncoder().encode(testStruct)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.