2017-02-04 4 views
0

Я пытаюсь написать функцию входа в swift, которая отправляет запрос HTTP POST на веб-страницу, а затем получает объект JSON в ответ. В этой части я отлично работал; хотя, я немного смущен с моим кодом. Части этого я получил из онлайн-уроков, и я не совсем понимаю это. То, что я хочу сделать, - это вызвать segue для другого представления, когда логин был успешным. Кроме того, при неудачном входе в систему я хочу отобразить UILabel, в котором говорится: «Имя пользователя и пароль не совпадают». Может кто-нибудь помочь объяснить код, который я написал, и сообщить мне, как я могу лучше его реализовать? Спасибо.Swift 3 Функция входа в систему

ViewController:

class ViewController: UIViewController { 
@IBOutlet var _username: UITextField! 
@IBOutlet var _password: UITextField! 
@IBOutlet var _button: UIButton! 
@IBOutlet var errorText: UILabel! 

@IBAction func loginButtonPress(_ sender: Any) { 
    let username = _username.text 
    let password = _password.text 

    if(username == "" || password == "") { 
     return 
    } 

    DoLogin(username: username!, password: password!) 
} 

DoLogin Функция:

func DoLogin(username: String, password: String) { 
    let myUrl = URL(string: "http://MYURL.com"); 
    var request = URLRequest(url:myUrl!) 
    request.httpMethod = "POST"// Compose a query string 
    let postString = "username=\(username)&password=\(password)"; 
    request.httpBody = postString.data(using: String.Encoding.utf8); 

    let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in 

     if error != nil 
     { 
      print("error=\(error)") 
      return 
     } 

     // Print out response object 
     print("response = \(response)") 

     //Convert response sent from a server side script to a NSDictionary object: 
     do { 
      let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary 
      if let parseJSON = json { 
       // Access value of username, name, and email by its key 
       let usernameValue = parseJSON["username"] as? String 
       let nameValue = parseJSON["name"] as? String 
       let emailValue = parseJSON["email"] as? String 
       print("usernameValue: \(usernameValue)") 
       print("nameValue: \(nameValue)") 
       print("emailValue: \(emailValue)") 
       if(usernameValue != nil && nameValue != nil && emailValue != nil) { 
        //The Login WAS SUCCESSFUL 
        //This is where I want to perform a segue to another view (like this) 
        //performSegue(withIdentifier: "loginToMain", sender: self) //This does not work 
        //Error above is (implicit use of 'self' in closuer; use 'self.' to make capture semantics explicit 
       } 
      } else { 
       //Username and Password do not match 
       print("Username and password do not match. Please Try again"); 
       //This is what I want to do 
       self.errorText.isHidden = false 
       //But nothing happens to the errorText UILabel 

      } 
     } catch { 
      print(error) 
     } 
    } 
    task.resume() 
} 

ответ

2

Вместо performSegue поставил self.performSegue

И добавить DispatchQueue обертку, чтобы оба этих

DispatchQueue.main.async 
{ 
    self.performSegue(withIdentifier: "loginToMain", sender: self) 
} 

..

DispatchQueue.main.async { 
    self.errorText.isHidden = false 
} 

Когда вы вызываете URLSession код в обработчик завершения выполняется в фоновом потоке, но GUI должен быть обновлен в основном потоке. Добавление DispatchQueue.main.async обновляет их в основном потоке.

+0

Это сработало ОТЛИЧНО! Огромное спасибо за помощь. Я никогда не слышал о DispatchQueue. –

0

«Grundwald ест coelacanth» предложил ответ на ваш вопрос и постарался дать инструкции о том, как избежать нежелательного сбоя, возникающего при попытке выполнить задачу пользовательского интерфейса в фоновом потоке (в обработчике завершения задание данных URLRequest). Однако вы задали другой вопрос, попросив немного объяснить, что делает ваш код. Насколько я предлагаю вам избегать написания кода, который вы не понимаете, конечно, нет никакого способа понять без объяснения (или много практики). Таким образом, я напишу короткое объяснение того, что происходит логически в вашем коде, и выполните несколько разных действий, которые, я думаю, улучшают читаемость вашего кода и/или повышают стандартизацию.

func doLogin(username: String, password: String) { 
    let myUrl: URL? = URL(string: "http://MYURL.com") // instantiate a URL object (this is failable, so type is Optional) 
    var request = URLRequest(url:myUrl!) // instantiate a URLRequest by implicitly unwrapping the URL (asserting it isn't nil) 
    request.httpMethod = "POST" // perform the http request using the POST verb 
    let postString = "username=\(username)&password=\(password)" // create a list of query parameters (not actually JSON) 
    request.httpBody = postString.data(using: String.Encoding.utf8) // encode the string as UTF8 and assign httpBody with a Data type object 

    // now create a data task object, but don't do anything with it yet; just define it 
    let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in 
     guard error == nil else { // if there is an error, print it and return 
      print("error=\(error)") 
      return 
     } 

     // convert http response to a standard Swift dictionary of type [String:Any] (aka Dictionary<String, Any>) 
     // since you aren't handling the potential for an error from this `try` anyway, I've converted it to a `try?` 
     // this does away with the `do`/`catch` blocks and makes things a bit less verbose 
     let json = try? JSONSerialization.jsonObject(with: data!, options: .mutableContainers) 

     guard let parseJSON = json as? [String:Any] else { // confirm that our serialization and cast to [String:Any] didn't produce nil, otherwise: 
      print("Serializing JSON object failed") // note: this has nothing to do with the names matching (as you suggested before) 
      self.errorText.isHidden = false // this probably doesn't do anything considering we never set `isHidden` as true in the first place 
      return 
     } 

     // make sure we received all of the parameters we expected (though you don't use these, so not sure that you need it) 
     guard let usernameValue = parseJSON["username"] as? String, 
      let _ = parseJSON["name"] as? String, 
      let _ = parseJSON["email"] as? String else { 
       print("Did not receive all return parameters") 
       return 
     } 

     guard usernameValue == username else { // if our usernames don't match, there was some kind of problem? 
      print("Usernames did not match") 
      return 
     } 

     // if we haven't failed anything before this, then we're in the clear; go ahead and segue 
     DispatchQueue.main.async(execute: {() -> Void in 
      // we perform this in the `main` queue since it is a UI task and cannot be executed in a background queue 
      self.performSegue(withIdentifier: "loginToMain", sender: self) 
     }) 
    } 
    task.resume() // start the actual URLRequest data task; before this line, none of the above code has been executed yet 
} 

Без всех комментариев, и за исключением некоторых из (на мой взгляд) ненужные разделы, функция теперь выглядит следующим образом:

func doLogin(username: String, password: String) { 
    let myUrl: URL? = URL(string: "http://MYURL.com") 
    var request = URLRequest(url:myUrl!) 
    request.httpMethod = "POST" 
    let postString = "username=\(username)&password=\(password)" 
    request.httpBody = postString.data(using: String.Encoding.utf8) 

    let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in 
     guard error == nil else { return } 

     let json = try? JSONSerialization.jsonObject(with: data!, options: .mutableContainers) 
     guard let parseJSON = json as? [String:Any] else { 
      DispatchQueue.main.async(execute: {() -> Void in 
       self.errorText.isHidden = false 
      }) 
      return 
     } 

     guard let usernameValue = parseJSON["username"] as? String, 
      usernameValue == username else { 
       print("Usernames did not match") 
       return 
     } 

     DispatchQueue.main.async(execute: {() -> Void in 
      self.performSegue(withIdentifier: "loginToMain", sender: self) 
     }) 
    } 
    task.resume() 
} 

Конечно, это пропускает несколько важных шагов. Если бы я был вами, я бы немного изменил это, чтобы, возможно, взять обработчик завершения и/или обработчик ошибок. Поскольку он выполняется асинхронно, этот вид метода довольно распространен для получения желаемого результата.

enum APIError: Error { 
    case parseError(String) 
    case usernameMismatch(String) 
} 

func doLogin(username: String, password: String, onCompletion: (() -> Void)? = nil, onError: ((Error?) -> Void)? = nil) { 
    var request = URLRequest(url: URL(string: "http://MYURL.com")!) 
    request.httpMethod = "POST" 
    request.httpBody = "username=\(username)&password=\(password)".data(using: String.Encoding.utf8) 
    let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in 
     do { 
      guard error == nil else { 
       onError?(error) 
       return 
      } 

      let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) 
      guard let parseJSON = json as? [String:Any] else { 
       onError?(APIError.parseError("Cannot parse JSON result!")) 
       DispatchQueue.main.async(execute: {() -> Void in 
        self.errorText.isHidden = false 
       }) 
       return 
      } 

      guard let usernameValue = parseJSON["username"] as? String, 
       usernameValue == username else { 
        onError?(APIError.usernameMismatch("Usernames do not match!")) 
        return 
      } 

      DispatchQueue.main.async(execute: {() -> Void in 
       self.performSegue(withIdentifier: "loginToMain", sender: self) 
       onCompletion?() 
      }) 
     } catch let error { 
      onError?(error) 
     } 
    } 
    task.resume() 
} 

Надеюсь, это поможет в ответе на другую часть вашего вопроса. Дайте мне знать, если я могу что-то объяснить.

Смежные вопросы