2
\$\begingroup\$

I've been working on this login screen logic.

It checks for the parameters in the JSON response to decide which action to follow. It works but it's kinda ugly and verbose. Any suggestions to refactor to make it look more elegant and simple?

 Alamofire.request(apiLink, method: .post, parameters: parameters, encoding: URLEncoding.default, headers: nil)
 .responseJSON { response in
 guard response.result.error == nil else {
 print("error calling POST in /###/###/###")
 print("Error: \(response.result.error)")
 return
 }
 if let JSON = response.result.value {
 print(response.result)
 print("JSON: \(JSON)")
 if let dictionary = response.result.value as? [String: Any] {
 if let status = dictionary["status"] as? String {
 if status == "ok" {
 self.performSegue(withIdentifier: "Home", sender: nil)
 } else {
 if let message = dictionary["message"] as? String {
 if message == "invalid_username" {
 let alertMessage = UIAlertController(title: "Invalid User", message: "Please check out the user name", preferredStyle: .alert)
 alertMessage.addAction(UIAlertAction(title: "Ok", style: .default, handler: nil))
 self.present(alertMessage, animated: true, completion: nil)
 } else {
 if let message = dictionary["message"] as? String {
 if message == "incorrect_password" {
 let alertMessage = UIAlertController(title: "Invalid Password", message: "Please check out the password", preferredStyle: .alert)
 alertMessage.addAction(UIAlertAction(title: "Ok", style: .default, handler: nil))
 self.present(alertMessage, animated: true, completion: nil)
 }
 }
 }
 }
 }
 }
 }
 }
 }
 }
200_success
145k22 gold badges190 silver badges478 bronze badges
asked Nov 17, 2016 at 1:29
\$\endgroup\$
1
  • \$\begingroup\$ You should be careful with giving hints about reasons, why a login could not be processed. Knowing that the password is wrong but the the username is right is a valueable information for an attacker. \$\endgroup\$ Commented Nov 17, 2016 at 6:08

2 Answers 2

3
\$\begingroup\$

Guard Statements

My proposed changes takes advantage of Swift's guard statements. In this case I find them useful not only for breaking up the nested if statements, but also for clarifying the "requirements" or key conditions and increasing the readability for others and yourself.

For example:

guard let dictionary = json as? [String: Any] else {
 return
}

Can be interpreted as:

"This dictionary must be valid or else we cannot continue"

The alternative to this is using a traditional if statement. However, this can lead to additional nesting and potentially also requires you to handle the alternative conditions, or in other words the else and else if conditions.

if let dictionary as? [String: Any] else {
 // I need another if statement to get something from the dictionary, but what if that key is not in the dictionary?
}
// Do I need an else condition? What if the dictionary is not valid?

Repetitive Code

The use of the guard statements also had the effect of eliminating some repetitive code used for setting up the UIAlertController. Repetitive code can lead to additional maintenance overhead and also bugs where you make a change in one place but forgot to replicate it in the duplicate code.

Unused Constant

I also made use of the json constant you declared. It seems that after it was declared, you did not use it in the block that follows.

Additional note

My proposed changes also incorporates oopexpert's advice. From a security standpoint, it would be better to not be specific as to why a login attempt has failed.

Proposed Refactoring

Alamofire.request(apiLink, method: .post, parameters: parameters, encoding: URLEncoding.default, headers: nil).responseJSON {
 response in
 guard response.result.error == nil else {
 print("error calling POST in /###/###/###")
 print("Error: \(response.result.error)")
 return
 }
 if let json = response.result.value {
 print(response.result)
 print("JSON: \(json)")
 guard let dictionary = json as? [String: Any] else {
 return
 }
 guard let status = dictionary["status"] as? String, let message = dictionary["message"] as? String else {
 return
 }
 guard status == "ok" else {
 if message == "invalid_login" {
 let alertMessage = UIAlertController(title: "Unable to login", message: "Please try again", preferredStyle: .alert)
 alertMessage.addAction(UIAlertAction(title: "Ok", style: .default, handler: nil))
 self.present(alertMessage, animated: true, completion: nil)
 }
 return
 }
 self.performSegue(withIdentifier: "Home", sender: nil)
 }
}
answered Nov 21, 2016 at 3:33
\$\endgroup\$
0
0
\$\begingroup\$

You could do something like this to eliminate one if statement :

if let status = dictionary["status"] as? String where status == "ok" {
 self.performSegue(withIdentifier: "Home", sender: nil)
}

Also SwiftyJSON might be your friend!

answered Nov 17, 2016 at 16:04
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.