I am beginner in programming. I actually have my own answer of this questions and the app worked as I am expected, but I am not sure if this is the correct way to to this.
This check out action will be triggered after the user click chechoutButton. but before before this chechoutButton.isEnabled , I have to make sure 3 parameters are available (not nil). before doing this check out action, I need 3 parameters :
get user's coordinate from GPS.
get user's location address from Google Place
API
Get current date time from server for verification.
method to get user location address from Google Place API will be triggered only if I get the coordinate from GPS, and as we know, fetching data from the internet (to take date and time) also takes time, it should be done asynchronously.
how do I manage this checkoutButton only enabled if those 3 parameters are not nil ? Is there a better way according to apple guideline to do this
the simplified code are below
class CheckoutTVC: UITableViewController {
#IBOutlet weak var checkOutButton: DesignableButton!
var checkinAndCheckoutData : [String:Any]? // from MainMenuVC
var dateTimeNowFromServer : String?
var userLocationAddress : String?
let locationManager = LocationManager()
var coordinateUser : Coordinate? {
didSet {
getLocationAddress()
}
}
override func viewDidLoad() {
super.viewDidLoad()
// initial state
checkOutButton.alpha = 0.4
checkOutButton.isEnabled = false
getDateTimeFromServer()
getCoordinate()
}
#IBAction func CheckoutButtonDidPressed(_ sender: Any) {
}
}
extension CheckoutTVC {
func getDateTimeFromServer() {
activityIndicator.startAnimating()
NetworkingService.getDateTimeFromServer { (result) in
switch result {
case .failure(let error) :
self.activityIndicator.stopAnimating()
// show alert
case .success(let timeFromServer) :
let stringDateTimeServer = timeFromServer as! String
self.dateTimeNowFromServer = stringDateTimeServer
self.activityIndicator.stopAnimating()
}
}
}
func getCoordinate() {
locationManager.getPermission()
locationManager.didGetLocation = { [weak self] userCoordinate in
self?.coordinateUser = userCoordinate
self?.activateCheckOutButton()
}
}
func getLocationAddress() {
guard let coordinateTheUser = coordinateUser else {return}
let latlng = "\(coordinateTheUser.latitude),\(coordinateTheUser.longitude)"
let request = URLRequest(url: url!)
Alamofire.request(request).responseJSON { (response) in
switch response.result {
case .failure(let error) :// show alert
case .success(let value) :
let json = JSON(value)
let locationOfUser = json["results"][0]["formatted_address"].string
self.userLocationAddress = locationOfUser
self.locationAddressLabel.text = locationOfUser
self.activateNextStepButton()
}
}
}
func activateCheckoutButton() {
if dateTimeNowFromServer != nil && userLocationAddress != nil {
checkOutButton.alpha = 1
checkOutButton.isEnabled = true
}
}
}
I manage this by using this method, but I don't know if this is the correct way or not
func activateCheckoutButton() {
if dateTimeNowFromServer != nil && userLocationAddress != nil {
checkOutButton.alpha = 1
checkOutButton.isEnabled = true
}
}
You can use DispatchGroup to know when all of your asynchronous calls are complete.
func notifyMeAfter3Calls() {
let dispatch = DispatchGroup()
dispatch.enter()
API.call1() { (data1)
API.call2(data1) { (data2)
//DO SOMETHING WITH RESPONSE
dispatch.leave()
}
}
dispatch.enter()
API.call3() { (data)
//DO SOMETHING WITH RESPONSE
dispatch.leave()
}
dispatch.notify(queue: DispatchQueue.main) {
finished?(dispatchSuccess)
}
}
You must have an equal amount of enter() and leave() calls. Once all of the leave() calls are made, the code in DispatchGroupd.notify will be called.
I am making two asynchronous network calls and would like to use a Dispatch Group to wait until the call complete and then resume. My program is freezing.
class CommentRatingViewController: UIViewController, UITextViewDelegate {
let myDispatchGroup = DispatchGroup()
#IBAction func saveRatingComment(_ sender: Any) {
rating = ratingView.rating
if rating != 0.0 {
myDispatchGroup.enter()
saveRating(articleID: post.articleID, userID: post.userID) //Network call
self.updatedRating = true
}
if commentsTextView.text != "" {
myDispatchGroup.enter()
saveComment(articleID: post.articleID, userID: post.userID, comment: commentsTextView.text!) //Network call self.addedComment = true
}
myDispatchGroup.wait()
DispatchQueue.main.async {
self.delegate?.didCommentOrRatePost(updatedRating: self.updatedRating, addedComment: self.addedComment)
}
}
And here is one of the network calls:
func saveRating (articleID: String, userID: String) {
let userPostURLRaw = "http://www.smarttapp.com/DesktopModules/DnnSharp/DnnApiEndpoint/Api.ashx?method=UpdatePostRating"
Alamofire.request(
userPostURLRaw,
method: .post,
parameters: ["articleID": articleID,
"newRating": self.rating,
"UserID": userID]
)
.responseString { response in
guard let myString = response.result.value else { return }
DispatchQueue.main.async {
self.myDispatchGroup.leave()
}
}
}
The network calls worked until I introduced Dispatch Group code.
I've resolved this.
The problem was that myDispatchGroup.enter() and self.myDispatchGroup.leave() where being called on different threads. I moved the call to the beginning and very end of the network requests and it works fine now.
I would like somehow to asynchronously validate the pin in ABPadLockScreen since pins are not saved on the device. I'm using Alamofire for http requests along with PromiseKit to have promises.
I have tried to use AwaitKit but the problem is that i get into a deadlock.
I have also tried to use semaphore as well, but the result is the same. Since i can't change the ABPadLock method to accommodate something like a completion handler i need some solution, it doesn't matter if it blocks the main thread, just that it works.
Alamofire request method:
public func loginAsync(pinCode: String?, apiPath: String?) -> Promise<LoginResult>{
return Promise { fullfil, reject in
let params = [
"Pin": pinCode!
]
Alamofire.request(.POST, "\(baseUrl!)/\(apiPath!)", parameters: params).responseObject{(response: Response<LoginResult, NSError>) in
let serverResponse = response.response
if serverResponse!.statusCode != 200 {
reject(NSError(domain: "http", code: serverResponse!.statusCode, userInfo: nil))
}
if let loginResult = response.result.value {
fullfil(loginResult)
}
}
}
}
ABPadLockScreen pin validation method:
public func padLockScreenViewController(padLockScreenViewController: ABPadLockScreenViewController!, validatePin pin: String!) -> Bool {
let pinCode = pin!
let defaults = NSUserDefaults.standardUserDefaults()
let serverUrl = defaults.stringForKey(Util.serverUrlKey)
let service = AirpharmService(baseUrl: serverUrl)
service.loginAsync(pinCode, apiPath: "sw/airpharm/login").then { loginResult -> Void in
if loginResult.code == HTTPStatusCode.OK {
AirpharmService.id = loginResult.result!.id
}
}
return false // how do i get the result of above async method here?
}
With semaphore:
public func padLockScreenViewController(padLockScreenViewController: ABPadLockScreenViewController!, validatePin pin: String!) -> Bool {
var loginResult: LoginResult?
let defaults = NSUserDefaults.standardUserDefaults()
let baseUrl = defaults.stringForKey(Util.serverUrlKey)
let service = AirpharmService(baseUrl: baseUrl)
let semaphore: dispatch_semaphore_t = dispatch_semaphore_create(0)
service.loginAsync(pin, apiPath: "sw/airpharm/login").then { loginResultRaw -> Void in
loginResult = loginResultRaw
dispatch_semaphore_signal(semaphore)//after a suggestion from Josip B.
}
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
return loginResult != nil // rudimentary check for now
}
EDIT:
After a suggestion from Josip B. i added semaphore signal in then, but it still doesn't work
AirpharmService is a class that contains a static property called id, and the Alamofire request method.
ABPadLockScreen pin validation is done on main thread in a ViewController
SOLVED EDIT:
Thanks to everyone for being so patient with me and my, not so good, knowledge of swift and iOS. There are a lot of good answers here and in the end i just went with, in my opinion, simplest solution. I listened to Losiowaty-s suggestion; implemented a spinner and manually dismissed the lock screen when i get the response from the server. I've used a SwiftSpinner. The final solution looked like this:
public func padLockScreenViewController(padLockScreenViewController: ABPadLockScreenViewController!, validatePin pin: String!) -> Bool {
let defaults = NSUserDefaults.standardUserDefaults()
let baseUrl = defaults.stringForKey(Util.serverUrlKey)
let service = AirpharmService(baseUrl: baseUrl)
SwiftSpinner.show("Logging in. Please wait...")
service.loginAsync(pin, apiPath: "sw/airpharm/login").then { loginResult -> Void in
if loginResult.code == HTTPStatusCode.OK {
SwiftSpinner.hide()
AirpharmService.id = loginResult.result!.id
self.unlockWasSuccessfulForPadLockScreenViewController(padLockScreenViewController)
} else if loginResult.code == HTTPStatusCode.Unauthorized {
let toast = JLToast.makeText("Invalid pin, please try again", duration: 5)
toast.show()
SwiftSpinner.hide()
} else {
let toast = JLToast.makeText("\(loginResult.code) sent from server. Please try again.", duration: 5)
toast.show()
SwiftSpinner.hide()
}
}.error { error in
let toast = JLToast.makeText("\((error as NSError).code) sent from server. Please try again.", duration: 5)
toast.show()
SwiftSpinner.hide()
}
return false
}
It's great that a lot of people tried to help you make your asynchronous call synchronous. Personally I agree with #OOPer and his comment that you should redesign your code, especially after looking through ABPadLockScreen code. It seems they don't support asynchronous pin verification, which is a shame. Also it seems from their github repo that the original author has abandoned the project, for the time being at least.
I'd attempt to solve your issue like this :
public func padLockScreenViewController(padLockScreenViewController: ABPadLockScreenViewController!, validatePin pin: String!) -> Bool {
let pinCode = pin!
let defaults = NSUserDefaults.standardUserDefaults()
let serverUrl = defaults.stringForKey(Util.serverUrlKey)
let service = AirpharmService(baseUrl: serverUrl)
service.loginAsync(pinCode, apiPath: "sw/airpharm/login").then { loginResult -> Void in
if loginResult.code == HTTPStatusCode.OK {
AirpharmService.id = loginResult.result!.id
self.handleLoginOk()
} else {
self.handleLoginFailed()
}
}
// disable interaction on padlock screen
// indicate to user that an async operation is going on, show a spinner
return false // always return false here
}
func handleLoginOk() {
// dismiss the ABPadlockScreenViewController manually
}
func handleLoginFailed() {
// dismiss the spinner indicating the async operation
// restore user interaction to padlock screen
}
With this approach your users will know that something is going on (the spinner, you can use for example SVProgressHUD as a drop-in solution) and that the app didn't hang. It is quite important, ux-wise, as users with poor connection could get frustrated thinking the app hanged and close it.
There is a potential problem though - if the padlock screen shows some kind of "wrong pin" message when you return false from the delegate method, it could be visible to the user creating some confusion. Now this can be tackled by making/positioning the spinner so that it obscures the message, though this is a very crude and unelegant solution. On the other hand, maybe it can be customised enough so that no message gets shown, and you'd display your own alert after server side verification.
Let me know what you think about this!
... it doesn't matter if it blocks the main thread... but the problem is that i get into a deadlock.
One problem could be it is blocking the main thread with dispatch_semaphore_wait, so the Alamofire response never get a chance to run on the main thread and you're deadlocking.
The solution to this could be create another queue on which the Alamofire completion handler is dispatched.
For example:
If you making a request like this:
Alamofire.request(.GET, "https://jsonplaceholder.typicode.com/posts").validate().responseData() { response in
print(response.result.value)
}
You can modify this call to dispatch completion handler in your defined queue like this:
let queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT)
let request = Alamofire.request(.GET, "https://jsonplaceholder.typicode.com/posts", parameters: .None).validate()
request.response(queue: queue, responseSerializer: Request.JSONResponseSerializer(options: .AllowFragments)) { response in
print(response.result.value)
}
A simplified version for test.
//MARK: Lock Screen Delegate
func padLockScreenViewController(padLockScreenViewController: ABPadLockScreenViewController!, validatePin pin: String!) -> Bool {
print("Validating Pin \(pin)")
let queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT)
let semaphore = dispatch_semaphore_create(0)
let request = Alamofire.request(.GET, "https://jsonplaceholder.typicode.com/posts", parameters: .None).validate()
request.response(queue: queue, responseSerializer: Request.JSONResponseSerializer(options: .AllowFragments)) { response in
print(response.result.value)
//isPinValid = ???
dispatch_semaphore_signal(semaphore);
}
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
return thePin == pin
//return isPinValid
}
Try this:
add dispatch_group:
static let serviceGroup = dispatch_group_create();
Then after calling the function, wait for this group:
public func padLockScreenViewController(padLockScreenViewController: ABPadLockScreenViewController!, validatePin pin: String!) -> Bool {
var loginResult: LoginResult?
let defaults = NSUserDefaults.standardUserDefaults()
let baseUrl = defaults.stringForKey(Util.serverUrlKey)
let service = AirpharmService(baseUrl: baseUrl)
let semaphore: dispatch_semaphore_t = dispatch_semaphore_create(0)
service.loginAsync(pin, apiPath: "sw/airpharm/login").then { loginResultRaw -> Void in
loginResult = loginResultRaw
}
dispatch_group_wait(yourClass.serviceGroup, DISPATCH_TIME_FOREVER);
return loginResult != nil // rudimentary check for now
}
And release the group after the function returns an answer:
public func loginAsync(pinCode: String?, apiPath: String?) -> Promise<LoginResult>{
return Promise { fullfil, reject in
let params = [
"Pin": pinCode!
]
Alamofire.request(.POST, "\(baseUrl!)/\(apiPath!)", parameters: params).responseObject{(response: Response<LoginResult, NSError>) in
let serverResponse = response.response
if serverResponse!.statusCode != 200 {
reject(NSError(domain: "http", code: serverResponse!.statusCode, userInfo: nil))
}
if let loginResult = response.result.value {
fullfil(loginResult)
}
dispatch_group_leave(yourClass.serviceGroup)
}
}
}
Based on the comments we exchanged, it sounds like the endless wait when you tried using a semaphore is because the semaphore signal is never being sent. Let's try to simplify this down to the minimum code needed to test:
public func padLockScreenViewController(padLockScreenViewController: ABPadLockScreenViewController!, validatePin pin: String!) -> Bool {
var success = false
let defaults = NSUserDefaults.standardUserDefaults()
let baseUrl = defaults.stringForKey(Util.serverUrlKey)
let semaphore: dispatch_semaphore_t = dispatch_semaphore_create(0)
let params = ["Pin": pin]
Alamofire.request(.POST, "\(baseUrl!)/sw/airpharm/login", parameters: params).responseObject {
(response: Response<LoginResult, NSError>) in
if let loginResult = response.result.value where loginResult.code == HTTPStatusCode.OK {
AirpharmService.id = loginResult.result!.id
success = true
}
dispatch_semaphore_signal(semaphore)
}
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
return success
}
This should either:
crash because you are force unwrapping several variables (e.g.baseUrl!, loginResult.result!.id, etc. and one of them is nil
return true if you got a valid LoginResult
return false if you didn't get a valid LoginResult
But theoretically, it shouldn't deadlock.
I've tried to make the ABPadLockScreen support asynchronous pin verification.
I've modified ABPadLockScreenViewController. Added a new ABPadLockScreenViewControllerDelegate protocol method shouldValidatePinManuallyForPadLockScreenViewController:.
/**
Call when pin validation is needed manually
Call processUnlock method to validate manually if return true from this method
*/
- (BOOL)shouldValidatePinManuallyForPadLockScreenViewController:(ABPadLockScreenViewController *)padLockScreenViewController;
Added a new instance method processUnlock
- (void)processUnlock {
if ([self isPinValid:self.currentPin]) {
[self unlockScreen];
} else {
[self processFailure];
}
}
Modified the processPin method
- (void)processPin {
if ([self.lockScreenDelegate respondsToSelector:#selector(shouldValidatePinManuallyForPadLockScreenViewController:)]) {
if ([self.lockScreenDelegate shouldValidatePinManuallyForPadLockScreenViewController:self]) {
return;
}
}
[self processUnlock];
}
Now in your viewController implement shouldValidatePinManuallyForPadLockScreenViewController
func shouldValidatePinManuallyForPadLockScreenViewController(padLockScreenViewController: ABPadLockScreenViewController!) -> Bool {
print("Requesting server...")
Alamofire.request(.GET, "https://jsonplaceholder.typicode.com/posts").validate().responseJSON() { response in
//isPinValid = ???
print("Request complete")
padLockScreenViewController.processUnlock()
}
return true
}
Made a demo project at https://github.com/rishi420/ABPadLockScreen
See the swift demo example.
I think semaphore can help. Here is a usage example:
- (NSArray *)tasksForKeyPath:(NSString *)keyPath {
__block NSArray *tasks = nil;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
if ([keyPath isEqualToString:NSStringFromSelector(#selector(dataTasks))]) {
tasks = dataTasks;
} else if ([keyPath isEqualToString:NSStringFromSelector(#selector(uploadTasks))]) {
tasks = uploadTasks;
} else if ([keyPath isEqualToString:NSStringFromSelector(#selector(downloadTasks))]) {
tasks = downloadTasks;
} else if ([keyPath isEqualToString:NSStringFromSelector(#selector(tasks))]) {
tasks = [#[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:#"#unionOfArrays.self"];
}
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
return tasks;
}
This is a function comes from AFNetworking. The method getTasksWithCompletionHandler is a method of NSURLSession which will
Asynchronously calls a completion callback with all data, upload, and download tasks in a session.
Semaphore_wait will ensure that tasks has be assigned with proper value. This way you can get the asynchronously request result.
What I want to do: I have a login screen, the user fill the username and password, and then it press the login button. An async call to the server is done to check if user is registered and password is okay, and if yes (async function set a bool to yes) then do a segue to the next view controller. Simple as that, I've tried many ways but with always the same problem, the main thread runs the shouldPerformSegueWithIdentifier method, do the async call and check the global bool var (false by default) before the background thread has updated it, so the segue is not performed because the global variable is set to true AFTER. Only if I use sleep(1) the UI is refreshed but I don't want to use this. Is there a way to do this without sleep?? Every method I run has a completion handler.
I don't know how to sync the main with the background thread. I've read it's posible to update UI from async call so this should be posible. I've been looking questions for a while and tried lot of snippets, and still haven't found a solution for my problem.
This is the code I have so far:
override func shouldPerformSegueWithIdentifier(identifier: String, sender: AnyObject?) -> Bool {
let apiCall = webApi()
dispatch_async(dispatch_get_main_queue()) {
apiCall.callCheckIsUserLogged(nil, password : self.passwordField.text, email: self.mailField.text){ (ok) in
}
}
//sleep(1) if I uncomment this, my method works because it will return true
return userIsLogged
}
apiCall.callCheckIsUserLogged() :
typealias successClosure = (success : Bool) -> Void
//Make a call to check if the user exist, server returns json with success or failure
func callCheckIsUserLogged(username: String?, password : String?, email: String?,completed : successClosure){
userApiCallUrl = "http://apiurl.com/users/login"
let call = asyncCallClass()
call.doAsyncCallWithParams(userApiCallUrl, calltype: "POST", username: username, pass: password, mail: email){ (success) in
completed(success: true)
}
}
call.doAsyncCallWithParams() code:
internal typealias completion = (success : Bool) -> Void
private var flagCompletion : Bool = false
//Handle async class with this method
//var callType is aditioned everytime an arg is not passed nil.
//callType == 3 it is a call to check if user is logged
//callType == 2 is a call to register a new user
func doAsyncCallWithParams(url : String, calltype : String, username : String?, pass : String?, mail : String?, completed : completion){
var callType : Int = 0
//Set Async Url
setUrl(url)
//Set Post Params
if let user : String = username{
self.username = "username=\(user)"
callType += 1
}
if let password : String = pass{
self.password = "password=\(password)"
callType += 1
}
if let mail : String = mail{
self.email = "email=\(mail)"
callType += 1
}
//register a new user
if(callType == 3){
paramString = "\(self.username)&\(self.password)&\(self.email)"
}
//check if user is logged, send email and password
if(callType == 2){
paramString = "\(self.email)&\(self.password)"
}
//Do call
callWithCompletionHandler { (success) in
self.flagCompletion = true
completed(success: self.flagCompletion)
}
}
callWithCompletionHandler() code:
private typealias completionAsyncCall = (success : Bool) -> Void
private func callWithCompletionHandler(completed : completionAsyncCall){
asyncJson.removeAllObjects()
//Set async call params
let request = NSMutableURLRequest(URL: NSURL(string: self.url!)!)
request.HTTPMethod = "POST"
let trimmedPostParam : String = self.paramString!.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet())
request.HTTPBody = trimmedPostParam.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in
guard error == nil && data != nil else {
// check for fundamental networking error
print("error=\(error)")
return
}
if let httpStatus = response as? NSHTTPURLResponse where httpStatus.statusCode != 200 {
// check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(response)")
}
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)
let result : AnyObject = responseString!.parseJSONString!
if let nsMutableResult = result as? NSMutableArray{
print("NSMutableArray")
}
if let nsDictResult = result as? NSMutableDictionary{
self.parseMutableDictionary(nsDictResult)
}
self.flag = true // true if download succeed,false otherwise
completed(success: flagAsyncCall!)
}
task.resume()
}
On login button press call :
apiCall.callCheckIsUserLogged(nil, password : self.passwordField.text, email: self.mailField.text){ (ok) in
if ok {
self.performSegueWithIdentifier("Identifier", sender: self)
} else {
print("User not logged in")
}
}
Because your callCheckIsUserLogged method already returns if user logged in or not.
internal typealias completion = (success : Bool) -> Void
I have a function Admin that runs asynchronously in the background.
Is there a way to make sure that the function is completed before calling the code after it?
(I am using a flag to check the success of the async operation. If the flag is 0, the user is not an admin and should go to the NormalLogin())
#IBAction func LoginAction(sender: UIButton) {
Admin()
if(bool.flag == 0) {
NormalLogin()
}
}
func Admin() {
let userName1 = UserName.text
let userPassword = Password.text
let findTimeLineData2:PFQuery = PFQuery(className: "Admins")
findTimeLineData2.findObjectsInBackgroundWithBlock { (objects: [AnyObject]?, error: NSError?) -> Void in
if !(error != nil){
for object in objects as! [PFObject] {
let userName2 = object.objectForKey("AdminUserName") as! String
let userPassword2 = object.objectForKey("AdminPassword") as! String
if(userName1 == userName2 && userPassword == userPassword2) {
//hes an admin
bool.flag = 1
self.performSegueWithIdentifier("AdminPage", sender: self)
self.UserName.text = ""
self.Password.text = ""
break;
}
}
}
}
}
You need to look into completion handlers and asynchronous programming. Here's an example of an async function that you can copy into a playground:
defining the function
notice the "completion" parameter is actually a function with a type of (Bool)->(). Meaning that the function takes a boolean and returns nothing.
func getBoolValue(number : Int, completion: (result: Bool)->()) {
if number > 5 {
// when your completion function is called you pass in your boolean
completion(result: true)
} else {
completion(result: false)
}
}
calling the function
here getBoolValue runs first, when the completion handler is called (above code) your closure is run with the result you passed in above.
getBoolValue(8) { (result) -> () in
// do stuff with the result
print(result)
}
applying the concept
You could apply this concept to your code by doing this:
#IBAction func LoginAction(sender: UIButton) {
// admin() calls your code, when it hits your completion handler the
// closure {} runs w/ "result" being populated with either true or false
Admin() { (result) in
print("completion result: \(result)") //<--- add this
if result == false {
NormalLogin()
} else {
// I would recommend managing this here.
self.performSegueWithIdentifier("AdminPage", sender: self)
}
}
}
// in your method, you pass a `(Bool)->()` function in as a parameter
func Admin(completion: (result: Bool)->()) {
let userName1 = UserName.text
let userPassword = Password.text
let findTimeLineData2:PFQuery = PFQuery(className: "Admins")
findTimeLineData2.findObjectsInBackgroundWithBlock { (objects: [AnyObject]?, error: NSError?) -> Void in
if !(error != nil){
for object in objects as! [PFObject] {
let userName2 = object.objectForKey("AdminUserName") as! String
let userPassword2 = object.objectForKey("AdminPassword") as! String
if(userName1 == userName2 && userPassword == userPassword2) {
// you want to move this to your calling function
//self.performSegueWithIdentifier("AdminPage", sender: self)
self.UserName.text = ""
self.Password.text = ""
// when your completion handler is hit, your operation is complete
// and you are returned to your calling closure
completion(result: true) // returns true
} else {
completion(result: false) // returns false
}
}
}
}
}
Of course, I'm not able to compile your code to test it, but I think this will work fine.