Here I have 3 files loginView(SwiftUI file) for UI purpose, LoginViewModel for handling the logic, ServiceManager for handling the Network call
Below code is in loginView(SwiftUI file)
Button("Login") {
loginVM.loginWebserviceCall()
}
Below code is in loginVM class
protocol LoginViewModelService: AnyObject {
func getLoginWebServiceCall(url: URL, params: [String: Any], completion: @escaping (Result<LoginRequest, APIError>) -> ())
}
class LoginViewModel: ObservableObject {
private weak var movieService: LoginViewModelService?
@Published var error: NSError?
init(movieService: LoginViewModelService = LoginStore.shared) {
self.movieService = movieService
}
fileprivate func loginWebserviceCall() {
let loginParams = ["username": "userEnteredUserName", "password": "userEnteredPassword", "request_token": "token"]
self.movieService!.getLoginWebServiceCall(url: "API_URL",
params: loginParams) { [weak self] (result) in
guard let self = self else { return }
switch result {
case .success(let response):
print(response)
case .failure(let error):
self.error = error as NSError
}
}
}
}
class LoginStore: LoginViewModelService {
static let shared = LoginStore()
private init() {}
func getLoginWebServiceCall(url: URL, params: [String: Any], completion: @escaping (Result<LoginRequest, APIError>) -> ()) {
ServiceManager.shared().requestWebServiceCall(requestType: .POST, url: url, params: params, completion: completion)
}
}
Below code is in ServiceManager class
class ServiceManager: NSObject {
private static var manager: ServiceManager? = nil
static func shared() -> ServiceManager {
if manager == nil {
manager = ServiceManager()
}
return manager!
}
func requestWebServiceCall<Response: Decodable>(requestType: HTTPMethod,
url: URL, params: [String: Any]? = nil,
completion: @escaping (Result<Response, APIError>) -> ()) {
var urlRequest = URLRequest.init(url: url)
if let params = params {
let postData = try? JSONSerialization.data(withJSONObject: params, options: .init(rawValue: 0))
urlRequest.httpBody = postData
}
urlRequest.httpMethod = requestType.rawValue
urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
URLSession.shared.dataTask(with: urlRequest) { [weak self] (data, response, error) in
guard let self = self else { return }
guard let data = data else {
self.executeCompletionHandlerInMainThread(with: .failure(.noData), completion: completion)
return
}
do {
if let str = String(data: data, encoding: .utf8) {
print(str)
// Output: {"success":true,"expires_at":"2022-06-23 08:56:52 UTC","request_token":"6587563498567begjhgf3r5387853"}
}
let decodedResponse = try JSONDecoder().decode(Response.self, from: data)
self.executeCompletionHandlerInMainThread(with: .success(decodedResponse), completion: completion)
} catch let DecodingError.keyNotFound(key, context) {
print("Key '\(key)' not found:", context.debugDescription)
print("codingPath:", context.codingPath)
} catch {
print(error)
}
}.resume()
}
private func executeCompletionHandlerInMainThread<Response: Decodable>(with result: Result<Response, APIError>,
completion: @escaping (Result<Response, APIError>) -> ()) {
DispatchQueue.main.async {
completion(result)
}
}
}
Below is the JSON we are expecting as response
{
"success": true,
"expires_at": "2018-07-24 04:10:26 UTC",
"request_token": "1531f1a558c8357ce8990cf887ff196e8f5402ec"
}
But once I get response, the decoding is getting failed and it is going inside catch block(in ServiceManager class) and it print's below error.
Key 'CodingKeys(stringValue: "username", intValue: nil)' not found: No value associated with key CodingKeys(stringValue: "username", intValue: nil) ("username").
codingPath: []
It is showing username
as not found. But in my API response, I don't have username
at all.
But I am passing username
as httpBody
to this API.
What could be the reason? Why it is throwing error?