Thread 1: break point error to send post method - ios

I would like to send post method (to login user) but when I click on login button in run time I got this message :
my class:
typealias ServiceResponse = (JSON, NSError?) -> Void
class RestApiManager: NSObject {
static let sharedInstance = RestApiManager()
let baseURL = "***********"
func login(body: [String: AnyObject],onCompletion: #escaping (JSON) -> Void) {
let route = baseURL+"o/token/"
makeHTTPPostRequest(path: route,body: body, onCompletion: { json, err in
onCompletion(json as JSON)
})
}
func getCategories(onCompletion: #escaping (JSON) -> Void) {
let route = baseURL+"o/token/"
makeHTTPGetRequest(path: route, onCompletion: { json, err in
onCompletion(json as JSON)
})
}
func getRandomUser(onCompletion: #escaping (JSON) -> Void) {
let route = baseURL
makeHTTPGetRequest(path: route, onCompletion: { json, err in
onCompletion(json as JSON)
})
}
// MARK: Perform a GET Request
private func makeHTTPGetRequest(path: String, onCompletion: #escaping ServiceResponse) {
let request = NSMutableURLRequest(url: NSURL(string: path)! as URL)
let session = URLSession.shared
let task = session.dataTask(with: request as URLRequest, completionHandler: {data, response, error -> Void in
if let jsonData = data {
let json:JSON = JSON(data: jsonData)
onCompletion(json, error as NSError?)
} else {
onCompletion(nil, error as NSError?)
}
})
task.resume()
}
// MARK: Perform a POST Request
private func makeHTTPPostRequest(path: String, body: [String: AnyObject], onCompletion: #escaping ServiceResponse) {
let request = NSMutableURLRequest(url: NSURL(string: path)! as URL)
// Set the method to POST
request.httpMethod = "POST"
do {
// Set the POST body for the request
let jsonBody = try JSONSerialization.data(withJSONObject: body, options: .prettyPrinted)
request.httpBody = jsonBody
let session = URLSession.shared
let task = session.dataTask(with: request as URLRequest, completionHandler: {data, response, error -> Void in
if let jsonData = data {
let json:JSON = JSON(data: jsonData)
onCompletion(json, nil)
} else {
onCompletion(nil, error as NSError?)
}
})
task.resume()
} catch {
// Create your personal error
onCompletion(nil, nil)
}
}
}
in my login controller :
//after click on login button
let parameters = ["grant_type": "password",
"username": "test29#gmail.com",
"password": "1",
"client_id": "toS899lbMGolv8j24piz0JI38VUCi6Mvzru27iBA",
"client_secret":"lG14Tk7m2mGYLMvBndW2yFZ1NGRLNrriIPH6gw30gAnMAcFMa5xJE3wP8H 4SDHAK0ND5nKIoSLZskFQQ1knEYiaPC3i5LNutPJlusiMNiuvhUHWnbvTCjmNkuCBkGgqO"]
RestApiManager.sharedInstance.login(body: parameters as [String : AnyObject]) { (json: JSON) in
print(json)
}

Try to put it back to the main thread:
func login(body: [String: AnyObject],onCompletion:#escaping(JSON) -> Void) {
let route = baseURL+"o/token/"
makeHTTPPostRequest(path: route,body: body, onCompletion: { json, err in
DispatchQueue.main.async {
onCompletion(json as JSON)
}
})
}

Related

No value associated with key CodingKeys - JSONDecoder() Error

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?

Swift 5, RxSwift: Network request with RxSwift

I am starting to use RxSwift to make the service call.
This was my old code:
class Service: GraphQLService {
func graphQL(body: [String: Any?], onSuccess: #escaping (Foundation.Data) throws -> (), onFailure: #escaping (Error) -> ()) {
guard let urlValue = Bundle.main.urlValue else { return }
guard let url = URL(string: urlValue) else { return
print("Error with info.plist")
}
var request = URLRequest(url: url)
let userKey = Bundle.main.userKeyValue
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue(userKey, forHTTPHeaderField: "userid")
request.httpBody = try? JSONSerialization.data(withJSONObject: body, options: .fragmentsAllowed)
URLSession.shared.dataTask(with: request) { (data, response, error) in
if let error = error {
onFailure(error)
}
if let data = data {
do{
try onSuccess(data)
}
catch{
onFailure(error)
}
}
}.resume()
}
And here I do the function to get time deposits:
final class TimeDepositManager: Service, TimeDepositManagerProtocol {
let timeDepositQuery = Bundle.main.queryValue
func getTimeDeposits(onSuccess: #escaping ([TimeDeposits]) -> (), onFailure: #escaping (Error) -> ()) {
let body = ["query": timeDepositQuery]
Service().graphQL(body: body, onSuccess: { data in
let json = try? JSONDecoder().decode(GraphQLResponse.self, from: data)
onSuccess(json?.data?.account?.timeDeposits ?? [])
}, onFailure: onFailure)
}
And so far this is my code with RxSwift:
class Service: GraphQLService {
func graphQL(body: [String : Any?]) -> Observable<Foundation.Data> {
return Observable.create { observer in
let urlValue = Bundle.main.urlValue
let url = URL(string: urlValue ?? "")
let session = URLSession.shared
var request = URLRequest(url: url!)
let userKey = Bundle.main.userKeyValue
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue(userKey, forHTTPHeaderField: "userid")
request.httpBody = try? JSONSerialization.data(withJSONObject: body, options: .fragmentsAllowed)
session.dataTask(with: request) { (data, response, error) in
if let error = error {
observer.onError(error)
}
if let data = data {
do{
try onSuccess(data)
observer.onNext(data)
}
catch{
//onFailure(error)
observer.onError(error)
print("Error: \(error.localizedDescription)")
}
}
}.resume()
return Disposables.create {
session.finishTasksAndInvalidate()
}
}
}
This is where I don't understand how in my getTimeDeposits () I can do the deserialization with try? JSONDecoder () ... with RxSwift without using onSuccess?
final class TimeDepositManager: Service, TimeDepositManagerProtocol {
let timeDepositQuery = Bundle.main.queryValue
func getTimeDeposits() -> Observable<[TimeDeposits]> {
let body = ["query": timeDepositQuery]
Service().graphQL(body: body)
}
You can have getTimeDeposits() return an Observable as well and handle the deserialization in a map closure. A couple of other things.
RxCocoa already has a method on URLSession so you don't need to write your own.
I suggest reducing the amount of code you have in a function that makes the network request. You want to be able to test your logic for making the request without actually making it.
Something like this:
final class TimeDepositManager: Service, TimeDepositManagerProtocol {
let timeDepositQuery = Bundle.main.queryValue
func getTimeDeposits() -> Observable<[TimeDeposits]> {
let body = ["query": timeDepositQuery]
return Service().graphQL(body: body)
.map { try JSONDecoder().decode(GraphQLResponse.self, from: $0).data?.account?.timeDeposits ?? [] }
}
}
class Service: GraphQLService {
func graphQL(body: [String: Any?]) -> Observable<Data> {
guard let urlValue = Bundle.main.urlValue else { fatalError("Error with info.plist") }
let request = urlRequest(urlValue: urlValue, body: body)
return URLSession.shared.rx.data(request: request) // this is in RxCocoa
}
func urlRequest(urlValue: String, body: [String: Any?]) -> URLRequest {
guard let url = URL(string: urlValue) else { fatalError("Error with urlValue") }
var request = URLRequest(url: url)
let userKey = Bundle.main.userKeyValue
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue(userKey, forHTTPHeaderField: "userid")
request.httpBody = try? JSONSerialization.data(withJSONObject: body, options: .fragmentsAllowed)
return request
}
}
If you don't want to use RxCocoa for some reason, here is the correct way to wrap the URLSession.dataTask method:
extension URLSession {
func data(request: URLRequest) -> Observable<Data> {
Observable.create { observer in
let task = self.dataTask(with: request, completionHandler: { data, response, error in
guard let response = response as? HTTPURLResponse else {
observer.onError(URLError.notHTTPResponse(data: data, response: response))
return
}
guard 200 <= response.statusCode && response.statusCode < 300 else {
observer.onError(URLError.failedResponse(data: data, response: response))
return
}
guard let data = data else {
observer.onError(error ?? RxError.unknown)
return
}
observer.onNext(data)
observer.onCompleted() // be sure to call `onCompleted()` when you are done emitting values.
// make sure every possible path through the code calls some method on `observer`.
})
return Disposables.create { task.cancel() } // don't forget to handle cancelation properly. You don't want to kill *all* tasks, just this one.
}
}
}
enum URLError: Error {
case notHTTPResponse(data: Data?, response: URLResponse?)
case failedResponse(data: Data?, response: HTTPURLResponse)
}

After URLSession.shared.dataTask its either not returning error or success

After URLSession.shared.dataTask it's not either returning error or success.
The completion handler is not getting called. How can I check or how can I proceed further. There is no error the app is working as such, but without data on the screen which is displayed.
func getPromotionsData() {
ConnectionManager.sharedInstance()?.getPromotions(PROMOTIONS, withCompletion: {
result, error in
if let result = result {
print("result: \(result)")
}
var arrPromotions: [Any] = []
if let object = result?["promotions"] as? [Any] {
arrPromotions = object
}
self.dataSource = []
if let arrPromotions = arrPromotions as? [AnyHashable] {
self.dataSource = arrPromotions
}
DispatchQueue.main.async(execute: {
self.collectionView.reloadData()
})
})
}
func getPromotions(_ path: String?, withCompletion completion: #escaping (_ result: [AnyHashable : Any]?, _ error: Error?) -> Void) {
let strPath = "/\(API)/\(path ?? "").json"
let url = strPath.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)
makeRequest(BASE_URL, path: url, httpMethod: GET_METHOD, httpBody: nil, completion: completion)
}
func makeRequest(_ url: String?, path: String?, httpMethod: String?, httpBody httpBoday: Data?, completion: #escaping (_ result: [AnyHashable : Any]?, _ error: Error?) -> Void) {
let headers = [
"cache-control": "no-cache",
"Authorization": "Token f491fbe3ec54034d51e141e28aaee87d47bb7e74"
]
var request: URLRequest? = nil
if let url = URL(string: "\(url ?? "")\(path ?? "")") {
request = URLRequest(url: url, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0)
}
request?.httpMethod = httpMethod ?? ""
request?.allHTTPHeaderFields = headers
let configuration = URLSessionConfiguration.default
configuration.httpCookieStorage = nil
configuration.requestCachePolicy = NSURLRequest.CachePolicy.reloadIgnoringLocalAndRemoteCacheData
if #available(iOS 11.0, *) {
configuration.waitsForConnectivity = false
}
let session = URLSession(configuration: configuration)
// let session = URLSession.shared
var task: URLSessionDataTask? = nil
print ("Request =======>",request)
if let req = request {
task = session.dataTask(with: request! , completionHandler: {
data, response, error in
var result: Any? = nil
if error != nil {
if let error = error {
print("\(error)")
}
if completion != nil {
completion(nil, error)
}
} else
{
var string: String? = nil
if let data = data {
string = String(data: data, encoding: .utf8)
}
string = self.string(byRemovingControlCharacters: string)
do {
if let data = string?.data(using: .utf8) {
result = try JSONSerialization.jsonObject(with: data, options: []) as! [AnyHashable : Any]
print ("Result ===============>",result)
}
} catch {
}
if completion != nil {
completion(result as! [AnyHashable : Any], error)
}
}
})
}
task?.resume()
}
Actually the completion block is an asynchronous process and i was waiting for the control to go back immediately after the process ends in debugging mode. It works now as expected

how do i get ios swift json return function value in another view controller

func data_request(){
let url:NSURL = NSURL(string: url_to_request)!
let session = URLSession.shared
let request = NSMutableURLRequest(url: url as URL)
//request.addValue("application/json", forHTTPHeaderField: "Content-Type")
//request.addValue("application/json", forHTTPHeaderField: "Accept")
request.httpMethod = "POST"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
//let paramString = "data=Hello"
let paramStrings = paramString
request.httpBody = paramStrings.data(using: String.Encoding.utf8)
let task = session.dataTask(with: request as URLRequest) { ( data, response, error) in
guard let :NSData = data as NSData?, let :URLResponse = response , error == nil else {
// print("error") //
return //
}
let dataString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
print(dataString)
};
task.resume()
}
how do i get the return value datastring in another view ?? how do i pass this value..iam a beginner in ios..plshelp ....thanks in advance
Using block you can do this
For Swift. Use AnyObject for id objc type.
func callWebservice (serviceName: String, withMethod method: String, andParams params: NSDictionary, showLoader loader: Bool, completionBlockSuccess aBlock: ((AnyObject) -> Void), andFailureBlock failBlock: ((AnyObject) -> Void)) {
if loader {
// Show loader
}
performRequestWithServiceName(serviceName, method: method, andParams: params, success: aBlock, failure: failBlock)
}
func performRequestWithServiceName(serviceName: String, method methodName: String, andParams params: NSDictionary, success successBlock: ((AnyObject) -> Void), failure failureBlock: ((AnyObject) -> Void)) {
if callSuceess {
successBlock("Success")
}else {
successBlock(nil)
}
}
An example when you want call web service. See code below
callWebservice("your-service-name", withMethod: "your-method", andParams: ["your-dic-key": "your dict value"], showLoader: true/*or false*/, completionBlockSuccess: { (success) -> Void in
// your successful handle
}) { (failure) -> Void in
// your failure handle
}
from :https://stackoverflow.com/a/31491077/3901620

How to make a synchronous GET request

I have a method for GET request in my code:
func makeHTTPGetRequest(path: String, parameters: [String: AnyObject], completionHandler: (NSData?, NSURLResponse?, NSError?) -> Void) -> NSURLSessionTask {
let parameterString = parameters.stringFromHttpParameters()
let requestURL = NSURL(string:"\(path)?\(parameterString)")!
let request = NSMutableURLRequest(URL: requestURL)
request.HTTPMethod = "GET"
request.setValue("Bearer " + userInfoDefaults.stringForKey("accessToken")!, forHTTPHeaderField: "Authorization")
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request, completionHandler:completionHandler)
task.resume()
return task
}
That is called by an another method that populates a picker view on a specific scene:
func getAffiliateds() -> [String]? {
var affiliateds:[String] = []
makeHTTPGetRequest(baseURL + "affiliateds", parameters: [:], completionHandler: { (data, response, error) in
do {
affiliateds = try NSJSONSerialization.JSONObjectWithData(data!, options:[]) as! [String]
print (affiliateds)
}
catch { print("Error: \(error)") }
})
return affiliateds
}
I need to get all affiliateds from my webservice and then list it on the picker view. But when I debugged the code I noticed that affiliateds are first returned as a null array and then it is returned with the correct information. I need to return the array from getAffiliateds only when it has already received the data from the webservice. How can I make this?
You can't. Your getAffiliateds() cannot return a value dependent on the asynchronous code that it will run. That is the nature of asynchronous code. Instead, perform a callback of some sort in the completion handler when it is called:
makeHTTPGetRequest(baseURL + "affiliateds", parameters: [:], completionHandler: { (data, response, error) in
do {
affiliateds = try NSJSONSerialization.JSONObjectWithData(data!, options:[]) as! [String]
print (affiliateds)
// DO SOMETHING HERE
}
}
A frequent strategy is for the caller to provide another completion handler which this completion handler will call.
You have a routine:
func getAffiliateds() -> [String]? {
var affiliateds:[String] = []
makeHTTPGetRequest(baseURL + "affiliateds", parameters: [:], completionHandler: { (data, response, error) in
do {
affiliateds = try NSJSONSerialization.JSONObjectWithData(data!, options:[]) as! [String]
print (affiliateds)
}
catch { print("Error: \(error)") }
})
return affiliateds
}
And you presumably have some code that does something like:
func populatePicklist() {
let affiliateds = getAffiliateds()
// populate picklist here
}
You should change this to:
func getAffiliatedsWithCompletionHandler(completionHandler: ([String]?) -> ()) {
makeHTTPGetRequest(baseURL + "affiliateds", parameters: [:]) { data, response, error in
do {
let affiliateds = try NSJSONSerialization.JSONObjectWithData(data!, options:[]) as? [String] // two notes here: first, define local var here, not up above; second, use `as?` to gracefully handle problems where result was not `[String]`
print (affiliateds)
completionHandler(affiliateds)
}
catch {
print("Error: \(error)")
completionHandler(nil)
}
}
}
func populatePicklist() {
getAffiliatedsWithCompletionHandler { affiliateds in
// populate picklist here
}
// but not here
}

Resources