Authentication with POST request is failing to validate data - swift

So I have a created a POST request which validates the Username and Password when someone tries to login, The problem is when i press the button sender it doesn't validate the data at all even if the Username and Password field are empty it still segues you to the main Dashboard which I find it very weird.
The Button Sender from signInViewController class :
#IBAction func signInSegueToDashboard(_ sender: Any) {
APICallerPOST.shared.signInToAccount(username: emailFieldSignIn.text!, password: passwordFieldSignIn.text!) { [self] (result, error) in
switch result?.StatusCode {
case 0:
DispatchQueue.main.async {
activityLoaderSignIn.startAnimating()
}
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
guard let mainTabBarController = self.storyboard?.instantiateViewController(withIdentifier: "mainTabBarController")
else {
return
}
self.activityLoaderSignIn.stopAnimating()
mainTabBarController.modalPresentationStyle = .custom
self.present(mainTabBarController, animated: true, completion: nil)
}
case 1:
print("error")
case 2:
print("error2")
case 3:
print("error3")
case 4:
print("error4")
case 5:
print("error5")
default:
break
}
}
}
The problem is that even if the emailFieldSignIn.text! and passwordFieldSignIn.text! are empty it still segues you to the mainTabBarController without any validation of the data.
The POST request from APICallerPOST class :
func signInToAccount(username: String, password: String, completion: #escaping (SignInResponse?, Error?) -> Void) {
//declare parameter as a dictionary which contains string as key and value combination.
let parameters = ["User": username, "Password": password]
//create the url with NSURL
let url = URL(string: "https://censoredurl/Signin")!
//create the session object
let session = URLSession.shared
//now create the Request object using the url object
var request = URLRequest(url: url)
request.httpMethod = "POST" //set http method as POST
do {
request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted) // pass dictionary to data object and set it as request body
} catch let error {
print(error.localizedDescription)
completion(nil, error)
}
//HTTP Headers
request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Accept")
//create dataTask using the session object to send data to the server
let task = session.dataTask(with: request, completionHandler: { data, response, error in
guard error == nil else {
completion(nil, error)
return
}
guard let data = data else {
completion(nil, NSError(domain: "dataNilError", code: -100001, userInfo: nil))
return
}
do {
//create json object from data
let decoder = JSONDecoder()
guard let json = try? decoder.decode(SignInResponse.self, from: data) else {
completion(nil, NSError(domain: "invalidJSONTypeError", code: -100009, userInfo: nil))
return
}
print(json)
completion(json, nil)
} catch let error {
print(error.localizedDescription)
completion(nil, error)
}
})
task.resume()
}

Please check my answer it will be helpfull for you I have added compulsory checks to validate if UITextfields are empty or not you can also add valid email check.
#IBAction func signInSegueToDashboard(_ sender: Any) {
if emailFieldSignIn.text!.isEmpty || passwordFieldSignIn.text!.isEmpty{
// show some error
return
}
APICallerPOST.shared.signInToAccount(username: emailFieldSignIn.text!, password: passwordFieldSignIn.text!) { [self] (result, error) in
switch result?.StatusCode {
case 0:
DispatchQueue.main.async {
activityLoaderSignIn.startAnimating()
}
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
guard let mainTabBarController = self.storyboard?.instantiateViewController(withIdentifier: "mainTabBarController")
else {
return
}
self.activityLoaderSignIn.stopAnimating()
mainTabBarController.modalPresentationStyle = .custom
self.present(mainTabBarController, animated: true, completion: nil)
}
case 1:
print("error")
case 2:
print("error2")
case 3:
print("error3")
case 4:
print("error4")
case 5:
print("error5")
default:
break
}
}
}

Related

How to send Apple Pay Token to PayU

I haver an application which should use Apple Pay.
As soon as user accept payment and Apple Pay works well, like Face ID or PIN,
I would like to send Apple Pay Token to PayU.
Unfortunatelly I did not find and support in PayU (https://czech.payu.com/en/)
In my ApplePayViewController I have method like:
class ApplePayViewController: {
func submitToken(payment: PKPayment, completion: #escaping (PKPaymentAuthorizationResult) -> Void) {
let result = PKPaymentAuthorizationResult(status: .failure, errors: nil)
let jsonPayment = try? JSONSerialization.jsonObject(with: payment.token.paymentData, options: []) as? Dictionary<String, AnyObject>
if let jsonData = jsonPayment {
let jsonTokenString = String(decoding: payment.token.paymentData, as: UTF8.self)
print("Json Data String: \(jsonTokenString)")
}
// What should be sent to PayU?
// paymentData? https://developer.apple.com/documentation/passkit/pkpaymenttoken/1617000-paymentdata
// Or data from Payment Token Structure https://developer.apple.com/documentation/passkit/apple_pay/payment_token_format_reference#//apple_ref/doc/uid/TP40014929
// Data are sent to my server over API like
// https://<URL>/api/send_payment&user=<username>&token=<token>&payment_data=<ApplePayToken>
do {
send_payment(data: paymentData, completion: { apiResponse in
switch apiResponse {
case .success(let zapisStatus):
result.status = PKPaymentAuthorizationStatus.success
completion(result)
case .failure(let error):
result.status = PKPaymentAuthorizationStatus.failure
completion(result)
}
})
}
}
extension ApplePayViewController: PKPaymentAuthorizationViewControllerDelegate {
func paymentAuthorizationViewController(_ controller: PKPaymentAuthorizationViewController!, didAuthorizePayment payment: PKPayment!, handler completion: #escaping (PKPaymentAuthorizationResult) -> Void) {
let status = PKPaymentAuthorizationStatus(rawValue: 0)!
switch status.rawValue {
case 0:
self.paymentStatus = PKPaymentAuthorizationStatus.success
completion(PKPaymentAuthorizationResult(status: PKPaymentAuthorizationStatus.success, errors: nil))
case 1:
self.paymentStatus = PKPaymentAuthorizationStatus.failure
completion(PKPaymentAuthorizationResult(status: PKPaymentAuthorizationStatus.failure, errors: nil))
default:
self.paymentStatus = PKPaymentAuthorizationStatus.failure
completion(PKPaymentAuthorizationResult(status: PKPaymentAuthorizationStatus.failure, errors: nil))
}
if payment.token.paymentData.count > 0 {
self.submitToken(payment: payment)
}
}
The data are send over by this code:
public func getURLRequest(endpoint: String, data: Data) -> URLRequest{
guard let url = URL(string: RestAPIHelper.RestAPIKeys.url_pub + endpoint) else {
fatalError()
}
var urlRequest = URLRequest(url: url)
urlRequest.httpMethod = "POST"
urlRequest.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
urlRequest.httpBody = data
return urlRequest
}
func send_payment(data: Data, completion: #escaping(Result<[UserPlatbyInfo], APIError>) -> Void) {
let urlRequest = getURLRequest(endpoint: APIInterface.user_zapisplatbu.rawValue, data: data)
let dataTask = URLSession.shared.dataTask(with: urlRequest) { respData, resp, _ in
// Handle return status
dataTask.resume()
}
Basically I don't know what PayU expects in order to accept ApplePayToken.

log-in after checking the specific response from the API-POST request

I am having problems with POST request function I made, The problem is that I don't know how to use that specific response of StatusCode that comes after validating a user, I made the function that sets-up the POST request but now I need to make the statement to check if the StatusCode is for example 0 the user gets logged in if its 1 it shows error.
My POST request:
func signInToAccount(username: String, password: String, completion: #escaping ([String: Any]?, Error?) -> Void) {
let parameters = ["User": username, "Password": password]
let url = URL(string: "https://randomurl/Signin")!
let session = URLSession.shared
var request = URLRequest(url: url)
request.httpMethod = "POST"
do {
request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)
} catch let error {
print(error.localizedDescription)
completion(nil, error)
}
request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Accept")
let task = session.dataTask(with: request, completionHandler: { data, response, error in
guard error == nil else {
completion(nil, error)
return
}
guard let data = data else {
completion(nil, NSError(domain: "dataNilError", code: -100001, userInfo: nil))
return
}
do {
guard let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] else {
completion(nil, NSError(domain: "invalidJSONTypeError", code: -100009, userInfo: nil))
return
}
print(json)
completion(json, nil)
} catch let error {
print(error.localizedDescription)
completion(nil, error)
}
})
task.resume()
}
My sign-in button sender of sign-in viewController:
#IBAction func signInSegueToDashboard(_ sender: Any) {
APICallerPOST.shared.signInToAccount(username: "admin", password: "admin123") { (result, error) in
if let result = result {
print("success: \(result)")
} else if let error = error {
print("error: \(error.localizedDescription)")
}
self.activityLoaderSignIn.startAnimating()
guard let mainTabBarController = self.storyboard?.instantiateViewController(withIdentifier: "mainTabBarController") else {
return
}
Timer.scheduledTimer(withTimeInterval: 2, repeats: false) {_ in
self.activityLoaderSignIn.stopAnimating()
mainTabBarController.modalPresentationStyle = .custom
self.present(mainTabBarController, animated: true, completion: nil)
}
}
}
Now the problem is here:
I don't know how to implement a proper way of logging-in, I need for example the if statement to check for the StatusCode if its 0 then the rest of code executed and to the dashboard if its error then show the error.
APICallerPOST.shared.signInToAccount(username: "admin", password: "admin123") { (result, error) in
if let result = result {
print("success: \(result)")
} else if let error = error {
print("error: \(error.localizedDescription)")
}
The response that comes from the POST request:
{
"StatusCode": 0,
"Result": {
"First": "admin",
"Last": "admin123",
"CompleteName": "admin admin123",
"PhoneNumber": "+000 (00) 000-000",
"Email": "admin.admin123#gmail.com",
"IsConnectedToCustomer": true,
"Token": "a3311cc231994f34bfjksadf82f7a4djska3",
"TokenExpireDate": "2023-05-19T13:49:15.383"
}
}
Thanks in advance for those who contribute to help on this specific topic, And to all the newcomers this post will help a lot if the correct answer is found.
Create a decodable struct which holds your server's response
struct SignInResponse: Decodable {
struct SignInResult: Decodable {
let First: String
let Last: String
let CompleteName: String
let PhoneNumber: String
let Email: String
let IsConnectedToCustomer: Bool
let Token: String
let TokenExpireDate: String
}
let Result: SignInResult?
let StatusCode: Int
}
IMPORTANT: This requires that your server either responds with an Result object as you have posted OR (when login failed) with "Result": null or with no Result object at all. But not an empty string or something like that.
Then modify the completion closure definition of your signInToAccount function to
func signInToAccount(username: String, password: String, completion: #escaping (SignInResponse?, Error?) -> Void) { ...
Inside this function, use a JSONDecoder to decode the response from your server.
let decoder = JSONDecoder()
guard let json = try? decoder.decode(SignInResponse.self, from: data) else {
completion(nil, NSError(domain: "invalidJSONTypeError", code: -100009, userInfo: nil))
return
}
print(json)
completion(json, nil)
Now you can access the StatusCode property in your function call's closure.
signInToAccount (username: "admin", password: "admin123") { (result, error) in
if let result = result {
print ("StatusCode is", result.StatusCode)
if let signInData = result.Result {
print ("Complete name:", signInData.CompleteName)
}
print("success: \(result)")
} else if let error = error {
print("error: \(error.localizedDescription)")
}
}

Authentication error/ purple warning when trying to segue after validating data from API

I have created a POST request which validates if the username and password are correct through a StatusCode: 0 that comes from the response of the POST request if the data are correct, At the signInViewController class I have created the button signInSegueToDashboard which when pressed must validate the data and if the data are valid then the user will be logged in without any problem.
The button sender at signInViewController:
#IBAction func signInSegueToDashboard(_ sender: Any) {
APICallerPOST.shared.signInToAccount(username: emailTextField.text!, password: passwordTextField.text!) { (result, error) in
if let result = result {
if result.StatusCode == 0 {
guard let mainTabBarController = self.storyboard?.instantiateViewController(withIdentifier: "mainTabBarController") else {
return
}
Timer.scheduledTimer(withTimeInterval: 1, repeats: false) {_ in
mainTabBarController.modalPresentationStyle = .custom
self.present(mainTabBarController, animated: true, completion: nil)
}
}else if result.StatusCode == 5 {
print("error: \(error!.localizedDescription)")
}
}
}
}
When i press the button after typing the correct data it just does nothing and just shows a purple warning that is saying to put it on Main thread, When i did put on main thread the segue part then it doesn't validate the data at all instead it just logs you in without any validation.
the POST request from APICallerPOST class:
func signInToAccount(username: String, password: String, completion: #escaping (SignInResponse?, Error?) -> Void) {
//declare parameter as a dictionary which contains string as key and value combination.
let parameters = ["User": username, "Password": password]
//create the url with NSURL
let url = URL(string: "https://censoredurl/Signin")!
//create the session object
let session = URLSession.shared
//now create the Request object using the url object
var request = URLRequest(url: url)
request.httpMethod = "POST" //set http method as POST
do {
request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted) // pass dictionary to data object and set it as request body
} catch let error {
print(error.localizedDescription)
completion(nil, error)
}
//HTTP Headers
request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Accept")
//create dataTask using the session object to send data to the server
let task = session.dataTask(with: request, completionHandler: { data, response, error in
guard error == nil else {
completion(nil, error)
return
}
guard let data = data else {
completion(nil, NSError(domain: "dataNilError", code: -100001, userInfo: nil))
return
}
do {
//create json object from data
let decoder = JSONDecoder()
guard let json = try? decoder.decode(SignInResponse.self, from: data) else {
completion(nil, NSError(domain: "invalidJSONTypeError", code: -100009, userInfo: nil))
return
}
print(json)
completion(json, nil)
} catch let error {
print(error.localizedDescription)
completion(nil, error)
}
})
task.resume()
}
Confused a lot.
The dataTask is asynchronous, and so is the code it runs in the completion handler. However all updates to UI need to be performed on the main thread, and so the parts of the completion handler that update the UI need to be pushed back onto the main thread.
In your case you could do it like this:
if result.StatusCode == 0 {
DispatchQueue.main.async {
guard let mainTabBarController = self.storyboard?.instantiateViewController(withIdentifier: "mainTabBarController")
else {
return
}
Timer.scheduledTimer(withTimeInterval: 1, repeats: false) {_ in
mainTabBarController.modalPresentationStyle = .custom
self.present(mainTabBarController, animated: true, completion: nil)
}
}
// ...
However it seems you are trying to use the Timer to delay presentation of the viewController, and there is a better way of doing this than using the Timer. You can use a delayed execution with DispatchQueue's asyncAfter(deadline:qos:flags:execute:) method:
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
// do your UI work
}

How to execute a synchronous api call after an asynchronous api call

I have two services that are working perfectly independently one is a synchronous call to get shopping-lists and another is an asynchronous call to add shopping-lists. The problem comes when i try to get a shopping-lists just after the add-Shopping-lists call has successfully completed.
The function to get shopping-lists never returns it just hangs after i call it in the closure of the add-Shopping-lists function. What is the best way to make these two calls without promises.
Create ShoppingList
func createURLRequest(with endpoint: String, data: ShoppingList? = nil, httpMethod method: String) -> URLRequest {
guard let accessToken = UserSessionInfo.accessToken else {
fatalError("Nil access token")
}
let urlString = endpoint.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
guard let requestUrl = URLComponents(string: urlString!)?.url else {
fatalError("Nil url")
}
var request = URLRequest(url:requestUrl)
request.httpMethod = method
request.httpBody = try! data?.jsonString()?.data(using: .utf8)
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization")
return request
}
func createShoppingList(with shoppingList: ShoppingList, completion: #escaping (Bool, Error?) -> Void) {
let serviceURL = environment + Endpoint.createList.rawValue
let request = createURLRequest(with: serviceURL, data: shoppingList, httpMethod: HttpBody.post.rawValue)
let session = URLSession.shared
let task = session.dataTask(with: request, completionHandler: { data, response, error -> Void in
guard let _ = data,
let response = response as? HTTPURLResponse,
(200 ..< 300) ~= response.statusCode,
error == nil else {
completion(false, error)
return
}
completion(true, nil)
})
task.resume()
}
Get shoppingLists
func fetchShoppingLists(with customerId: String) throws -> [ShoppingList]? {
var serviceResponse: [ShoppingList]?
var serviceError: Error?
let serviceURL = environment + Endpoint.getLists.rawValue + customerId
let request = createURLRequest(with: serviceURL, httpMethod: HttpBody.get.rawValue)
let semaphore = DispatchSemaphore(value: 0)
let session = URLSession.shared
let task = session.dataTask(with: request, completionHandler: { data, response, error -> Void in
defer { semaphore.signal() }
guard let data = data, // is there data
let response = response as? HTTPURLResponse, // is there HTTP response
(200 ..< 300) ~= response.statusCode, // is statusCode 2XX
error == nil else { // was there no error, otherwise ...
serviceError = error
return
}
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let shoppingList = try decoder.decode([ShoppingList].self, from: data)
serviceResponse = shoppingList
} catch let error {
serviceError = error
}
})
task.resume()
semaphore.wait()
if let error = serviceError {
throw error
}
return serviceResponse
}
Usage of function
func addShoppingList(customerId: String, shoppingList: ShoppingList, completion: #escaping (Bool, Error?) -> Void) {
shoppingListService.createShoppingList(with: shoppingList, completion: { (success, error) in
if success {
self.shoppingListCache.clearCache()
let serviceResponse = try? self.fetchShoppingLists(with: customerId)
if let _ = serviceResponse {
completion(true, nil)
} else {
let fetchListError = NSError().error(description: "Unable to fetch shoppingLists")
completion(false, fetchListError)
}
} else {
completion(false, error)
}
})
}
I would like to call the fetchShoppingLists which is a synchronous call and get new data then call the completion block with success.
This question is predicated on a flawed assumption, that you need this synchronous request.
You suggested that you needed this for testing. This is not true: One uses “expectations” to test asynchronous processes; we don’t suboptimize code for testing purposes.
You also suggested that you want to “stop all processes” until the request is done. Again, this is not true and offers horrible UX and subjects your app to possibly be killed by watchdog process if you do this at the wrong time while on slow network. If, in fact, the UI needs to be blocked while the request is in progress, we usually just throw up a UIActivityIndicatorView (a.k.a. a “spinner”), perhaps on top of a dimming/blurring view over the whole UI to prevent users from interacting with the visible controls, if any.
But, bottom line, I know that synchronous requests feel so intuitive and logical, but it’s invariably the wrong approach.
Anyway, I’d make fetchShoppingLists asynchronous:
func fetchShoppingLists(with customerId: String, completion: #escaping (Result<[ShoppingList], Error>) -> Void) {
var serviceResponse: [ShoppingList]?
let serviceURL = environment + Endpoint.getLists.rawValue + customerId
let request = createURLRequest(with: serviceURL, httpMethod: .get)
let session = URLSession.shared
let task = session.dataTask(with: request) { data, response, error in
guard let data = data, // is there data
let response = response as? HTTPURLResponse, // is there HTTP response
200 ..< 300 ~= response.statusCode, // is statusCode 2XX
error == nil else { // was there no error, otherwise ...
completion(.failure(error ?? ShoppingError.unknownError))
return
}
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let shoppingList = try decoder.decode([ShoppingList].self, from: data)
completion(.success(shoppingList))
} catch let jsonError {
completion(.failure(jsonError))
}
}
task.resume()
}
And then you just adopt this asynchronous pattern. Note, while I’d use the Result pattern for my completion handler, I left yours as it was to minimize integration issues:
func addShoppingList(customerId: String, shoppingList: ShoppingList, completion: #escaping (Bool, Error?) -> Void) {
shoppingListService.createShoppingList(with: shoppingList) { success, error in
if success {
self.shoppingListCache.clearCache()
self.fetchShoppingLists(with: customerId) { result in
switch result {
case .failure(let error):
completion(false, error)
case .success:
completion(true, nil)
}
}
} else {
completion(false, error)
}
}
}
Now, for example, you suggested you wanted to make fetchShoppingLists synchronous to facilitate testing. You can easily test asynchronous methods with “expectations”:
class MyAppTests: XCTestCase {
func testFetch() {
let exp = expectation(description: "Fetching ShoppingLists")
let customerId = ...
fetchShoppingLists(with: customerId) { result in
if case .failure(_) = result {
XCTFail("Fetch failed")
}
exp.fulfill()
}
waitForExpectations(timeout: 10)
}
}
FWIW, it’s debatable that you should be unit testing the server request/response at all. Often instead mock the network service, or use URLProtocol to mock it behind the scenes.
For more information about asynchronous tests, see Asynchronous Tests and Expectations.
FYI, the above uses a refactored createURLRequest, that uses the enumeration for that last parameter, not a String. The whole idea of enumerations is to make it impossible to pass invalid parameters, so let’s do the rawValue conversion here, rather than in the calling point:
enum HttpMethod: String {
case post = "POST"
case get = "GET"
}
func createURLRequest(with endpoint: String, data: ShoppingList? = nil, httpMethod method: HttpMethod) -> URLRequest {
guard let accessToken = UserSessionInfo.accessToken else {
fatalError("Nil access token")
}
guard
let urlString = endpoint.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
let requestUrl = URLComponents(string: urlString)?.url
else {
fatalError("Nil url")
}
var request = URLRequest(url: requestUrl)
request.httpMethod = method.rawValue
request.httpBody = try! data?.jsonString()?.data(using: .utf8)
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization")
return request
}
I am sure it could be alot better, but this is my 5 minute version.
import Foundation
import UIKit
struct Todo: Codable {
let userId: Int
let id: Int
let title: String
let completed: Bool
}
enum TodoError: String, Error {
case networkError
case invalidUrl
case noData
case other
case serializationError
}
class TodoRequest {
let todoUrl = URL(string: "https://jsonplaceholder.typicode.com/todos")
var todos: [Todo] = []
var responseError: TodoError?
func loadTodos() {
var responseData: Data?
guard let url = todoUrl else { return }
let group = DispatchGroup()
let task = URLSession.shared.dataTask(with: url) { [weak self](data, response, error) in
responseData = data
self?.responseError = error != nil ? .noData : nil
group.leave()
}
group.enter()
task.resume()
group.wait()
guard responseError == nil else { return }
guard let data = responseData else { return }
do {
todos = try JSONDecoder().decode([Todo].self, from: data)
} catch {
responseError = .serializationError
}
}
func retrieveTodo(with id: Int, completion: #escaping (_ todo: Todo? , _ error: TodoError?) -> Void) {
guard var url = todoUrl else { return }
url.appendPathComponent("\(id)")
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let todoData = data else { return completion(nil, .noData) }
do {
let todo = try JSONDecoder().decode(Todo.self, from: todoData)
completion(todo, nil)
} catch {
completion(nil, .serializationError)
}
}
task.resume()
}
}
class TodoViewController: UIViewController {
let request = TodoRequest()
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.global(qos: .background).async { [weak self] in
self?.request.loadTodos()
self?.request.retrieveTodo(with: 1, completion: { [weak self](todoData, error) in
guard let strongSelf = self else { return }
if let todoError = error {
return debugPrint(todoError.localizedDescription)
}
guard let todo = todoData else {
return debugPrint("No todo")
}
debugPrint(strongSelf.request.todos)
debugPrint(todo)
})
}
}
}

How to create proper completion handler for server login in swift?

I have an api manager class in my swift application and it has a server login with username and password.
I want to know how to create a completion handler for it that when the server responses with 200 status code, the function handles that response and for example performs a segue in the viewcontroller.
I did not find any tutorials for this. Thanks for your help!
EDIT 1:
What i need is: The completion handler is immediately run when the function is called. I want the completion handler run after server responds.
And this is my login function:
public class func Login(username: String, password: String, complitionHandler: #escaping (Int) -> Void) {
let urlS = "http://server.com/" + "login.php"
let url = URL(string: urlS)
var request = URLRequest(url: url!)
request.httpMethod = "POST"
let body = "username=\(username.lowercased())&password=\(password)"
request.httpBody = body.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data, error == nil else {
print(error!)
print("error")
logedIn = 2
return
}
do{
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? NSDictionary
if let parseJson = json {
let code = parseJson["status"] as! String
if code == "200" {
print("loged inn")
logedIn = 1
}else if code == "400" {
print("uuuser/pass error")
logedIn = 0
}
}
}catch{
print("json error")
logedIn = 2
}
}
task.resume()
DispatchQueue.main.async {
complitionHandler(logedIn)
}
}
And how i call the function in my ViewController:
Manager.Login(username: "1", password: "1") { (i) in
switch i {
case 0:
print("user/pass error")
case 1:
print("loged in")
self.performSegue(withIdentifier: "toMain", sender: self)
case 2:
print("json error")
default:
()
}
}
You have all of the pieces in place. You just need to move your call to the completion handler to the correct place:
}catch{
print("json error")
logedIn = 2
}
DispatchQueue.main.async {
complitionHandler(logedIn)
}
}
task.resume()
Also note that method names should start with lowercase letters so your Login function should be named login.
Now you can use this login method like:
login(username: someUsername, password: somePassword) { (result) in
if result == 1 {
// success - do your segue
} else if result == 0 {
// bad username/password
} else {
// some error
}
}