Reputation: 137
I am trying to implement login functionality in an app. I have a url that takes a username and password, verifies if they are correct and returns true or false. If true a new view controller should load up.
1) How to get the result from the asynchronous url call in my viewController? The response from the server is correct and the json passing is correct. However I am not sure how to get the result in the viewController. I have read other questions on stack overflow, a few mentioned the use of a delegate and completionHandler. I looked up how to use a delegate but I am not too sure how I can use it in this situation.
So here is my code. In one class I have this function. I access this function through a singleton. I call it in the shouldPerformSegueWithIdentifier method of the viewController (Shown below).
func loginRequest() -> Bool {
let urlPath: String = "http:**************.mybluemix.net/login/login.php?username=Tony&password=pass"
var url: NSURL = NSURL(string: urlPath)!
var request1: NSURLRequest = NSURLRequest(URL: url)
let queue:NSOperationQueue = NSOperationQueue()
var loginRequest:Bool = false
NSURLConnection.sendAsynchronousRequest(request1, queue: queue, completionHandler:{ (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in
var err: NSError
var jsonResult: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments, error: nil)
println("AsSynchronous\(jsonResult)")
if let jsonDictionary = jsonResult as? NSDictionary {
if let success = jsonDictionary["Success"] as? NSString {
if success == "true" {
// User has permission to login.
loginRequest = true
println("login should be true: \(loginRequest)")
}else{
// User does not have permission to login.
loginRequest = false
println("login should be false: \(loginRequest)")
}
}
}
})
}
This function is in the view controller.
override func shouldPerformSegueWithIdentifier(identifier: String?, sender: AnyObject?) -> Bool {
if identifier == "SubmitLogin" {
// Username empty.
if(username.text.isEmpty){
let alert = UIAlertView()
alert.title = "no Text"
alert.message = "No username or password"
alert.addButtonWithTitle("OK")
alert.show()
return false
}else{
// Send login credentials to server.
var login = (Model.sharedInstance.loginRequest())
// loginRequest returns ture if credentials are correct.
if (Model.sharedInstance.loginRequest()){
// Start the next view controller.
return true
}else{
// Login failed
let alert = UIAlertView()
alert.title = "Failed Login"
alert.message = "The username or password was wrong. Please try again"
alert.addButtonWithTitle("OK")
alert.show()
return false
}
}
}
}
So how can I use a delegate with the NSURLConnection.sendAsynchronousRequest to retrieve the result in the viewcontroller and use the result to determine if I should log the user in?
****** Edit for clarification ******
I have a class called BluemixDAO which looks like:
class BluemixDAO {
init(){
}
// The login function that I posted above
func loginRequest() -> Bool {
}
}
The next class is the Model which is used to call the BluemixDAO loginRequest function.
class Model {
private let bluemix:BluemixDAO
private struct Static
{
static private var instance:Model?
}
class var sharedInstance: Model
{
if !(Static.instance != nil)
{
return Static.instance!
}
}
private init(){
bluemix = BluemixDAO()
}
// I call this function in my view controller using Model.sharedInstance.loginRequest()
func loginRequest() -> Bool
{
// This calls the function in the BluemixDAO class
return bluemix.loginRequest()
}
}
The last class is the viewController. It only has the above function "shouldPerformSegueWithIdentifier"
class viewController: UIViewController {
//This function is detailed at the top of the post.
override func shouldPerformSegueWithIdentifier(identifier: String?, sender:AnyObject?) -> Bool {
// I call the loginRequest() function in here.
}
}
I probably wrote too much. This should be all the relevant code. Cheers
Upvotes: 3
Views: 1291
Reputation: 14845
It seems that you have your async block and your function in the same class so the only thing you need to do is to call your function back in the main thread adding this code in the async block to do so:
dispatch_async(dispatch_get_main_queue())
{
//call your function
}
You can also send notification or send the result via protocol if you want to send the result to a different class.
I personally stop using UIAlertView for login as you can't update it to show details such login fail or use to show a spinning wheel while processing the data among other, I prefer to create a new UIViewController and pop it into the screen do all the login process and dismiss.
Using Protocol
To pass values from one class to the other you will need to implement protocols. In the class that execute the login declare a protocol like this:
protocol LoginDelegate
{
func loginResult(result: Bool)
}
Declare a optional delegate as a global in the same control:
var loginDelegate:LoginDelegate? = nil
Now test if this delegate exist before unwrap it and if it does call the function declare in the protocol:
if let loginDel = self.loginDelegate{
loginDel.loginResult(userResult)
}
this will help us to pass the value to the class that request it, in this class you need to extend this new protocol:
extension MainViewController: LoginDelegate{ }
In viewDidLoad declare this view controller as a delegate from the login class
login.loginDelegate = self
Now to conform with the protocol we just need to implement the function that will get called by the detail view controller with the boolean value:
extension LoginViewController: LoginDelegate
{
func loginResult(result: Bool)
{
//Save the result value to be used by the MainViewController
}
}
I know it is not a very simple process both after use it few times it will be easier to use and understand, it always a good idea read more in the documentation here
Using Notification
To use notification add this code to send new notifications when the process to check the user finish, the dictionary contains the data you want to pass in the notification in this case the result of the login process:
dispatch_async(dispatch_get_main_queue())
{
let dic = ["result":result]
NSNotificationCenter.defaultCenter().postNotificationName("loginResult", object: nil, userInfo: dic)
}
Register your class responsible for processing the message with this code:
NSNotificationCenter.defaultCenter().addObserver(self, selector: "login:", name:"loginResult", object: nil)
Once the notification is received the selector will call the function bellow, cast the notification the retrive the key add in the dictionary:
func login(notification: NSNotification){
if let result: AnyObject = notification.userInfo?["result"]
{
//do your logic
}
}
Once you finish with the class don't forget to deregister the notification or you will have a memory leak
NSNotificationCenter.defaultCenter().removeObserver(self)
Upvotes: 3