I have created APINetworkManagerAll here i have created serviceCall and calling that in ViewController. here postGenericCall2() is called and response also coming but my given param values not coming instead it says nil why? i am passing same values in postmn then response coming properly? where am i wrong? guide me please.
struct RequestObjectAll {
var params: [String: Any]? = nil
var method: HTTPMethod
var urlPath: String
var isTokenNeeded: Bool
var isLoaderNeed: Bool = false
var isErrorNeed: Bool = false
var vc: UIViewController?
}
class APINetworkManagerAll: NSObject {
static let sharedInstance = APINetworkManagerAll()
fileprivate override init() {
super.init()
}
func serviceCall<T: Decodable>(requestObject: RequestObjectAll, completion: @escaping (Result<T, Error>) -> Void) {
if requestObject.isLoaderNeed {
requestObject.vc?.showLoader()
}
guard let url = URL(string: requestObject.urlPath) else {
if requestObject.isLoaderNeed {
requestObject.vc?.hideLoader()
}
completion(.failure(NetworkError.invalidURL))
return
}
var urlRequest = URLRequest(url: url)
urlRequest.httpMethod = requestObject.method.rawValue
guard let httpBody = try? JSONSerialization.data(withJSONObject: requestObject.params ?? ["" : ""], options: []) else {
return
}
urlRequest.httpBody = httpBody
let task = URLSession.shared.dataTask(with: urlRequest) { data, _, error in
if requestObject.isLoaderNeed {
requestObject.vc?.hideLoader()
}
if let error = error {
completion(.failure(error))
return
}
if let data = data {
do {
let response = try JSONDecoder().decode(T.self, from: data)
completion(.success(response))
} catch {
completion(.failure(error))
}
} else {
let error = NSError(domain: "YourDomain", code: 0, userInfo: [NSLocalizedDescriptionKey: "No data received"])
completion(.failure(error))
}
}
task.resume()
}
}
And
func postGenericCall2(){
let param = ["name": "john",
"job": "AAA"
]
let requestObject = RequestObjectAll(
params: param,
method: .post,
urlPath: "https://reqres.in/api/users",
isTokenNeeded: false,
isLoaderNeed: true,
vc: self
)
APINetworkManagerAll.sharedInstance.serviceCall(requestObject: requestObject) { (result: Result<PostGenModelSmall, Error>) in
switch result {
case .success(let response):
// Handle a successful response
print("result of post service call.....: \(response)")
case .failure(let error):
// Handle the error
print("Error decoding JSON: \(error)")
}
}
}
struct PostGenModelSmall: Codable {
let name: String
let job: String
let id: String
let createdAt: String
}
error:
Error decoding JSON: keyNotFound(CodingKeys(stringValue: "name", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: "name", intValue: nil) ("name").", underlyingError: nil))
EDIT: if i write service call like this then i am getting response as postman. so is there any issue in above my APINetworkManagerAll > serviceCall method while adding urlRequest.httpBody = httpBody body to urlRequest?
func postServiceCall(){
let params: [String : Any] = ["name": "john", "job": "AAA"]
var request = URLRequest(url: URL(string: "https://reqres.in/api/users")!)
request.httpMethod = "POST"
request.httpBody = try? JSONSerialization.data(withJSONObject: params, options: [])
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
DispatchQueue.global().async {
let session = URLSession.shared
let task = session.dataTask(with: request, completionHandler: { data, response, error -> Void in
print(response!)
do {
guard let data = data, error == nil else{
print(error?.localizedDescription ?? "No data")
return
}
let json = try JSONSerialization.jsonObject(with: data) as? [String : Any]
print("post data with response....\(json)")
} catch {
print("error")
}
})
task.resume()
}
}
o/p:
post data with response....Optional(["id": 582, "createdAt": 2024-03-12T06:28:51.904Z, "job": AAA, "name": john])
As a general rule, we should examine the response and make sure it is what we expected.
In this case, your JSON response to the
POSTrequest with JSON body is very strange. It contains:It looks like it was not expecting JSON in the body of the request. It is a question of what your backend is expecting. In your case, as your particular backend appears to accept both
application/x-www-form-urlencodedandapplication/jsonrequests, you have two options:You can set the
Content-Typeheader of your request to let it know that the payload is JSON:You can change the request to be an
application/x-www-form-urlencodedrequest:Note, your particular server defaults
But, as your code stands, the request is not well-formed (in the absence of a
Content-Typeheader ofapplication/json, it assumes it isapplication/x-www-form-urlencoded, but the body is JSON), and not only did the backend not detect this, but it also returned a nonsensical response.You really should fix the backend so that a malformed request returns a non-200 response, e.g., a 400 status code. And, often (in dev environments, at least) we would include a body (with some consistent “error” JSON payload) in that response that indicates the nature of the error, namely that
nameandjobwere not successfully parsed from the request. The specifics of how the backend should validate requests and report errors is beyond the scope of this question, but FYI.If we replace the
httpBodywith anapplication/x-www-form-urlencodedsort of payload, like below, it worked correctly:It generated the response you were looking for:
Note, if your request must be in
application/x-www-form-urlencoded, make sure to property escape the values. See HTTP Request in Swift with POST method if doing this manually. Or use a framework, like Alamofire, which will greatly simply the creation of well-formedx-www-form-urlencodedrequests.As an unrelated observation, I might suggest defining
createdAtto be aDate:And, then when decoding, set a date formatter for the
JSONDecoder:Then, it will parse the date for you.
By the way, you mentioned that you have a working Postman request. In Postman, you can tap on the
</>button, choose “Swift – URLSession” and it will show you a simple example of what the Swift code might look like:Like most auto-generated code, the code is not great, but it will give you a starting place from which you can start to compare and contrast against your code. In this case, note the
httpBodyof the request (and the setting of theContent-Typeheader of the request, which is not required, but is best practice).Obviously, when you do this, your request might be a little different, but hopefully it illustrates how you can use Postman features to see what the Swift code might look like.