Parse.com PFUser signUpInBackgroundWithBlock: block not being called on first tap - ios

i am developing an iOS-App with Swift and the parse.com framework and have a big problem with registering new users.
The block of "signUpInBackgroundWithBlock" is not being called on the first tap, although the new user is getting registered. When i am tapping the button a second time, the block gets finally called and i get an error, that the username is already registered.
var newUser = PFUser()
newUser.username = registerView.nicknameTextField.text.trim()
newUser.email = registerView.emailTextField.text
newUser.password = registerView.passwordTextField.text
newUser.signUpInBackgroundWithBlock {
(succeeded: Bool, error: NSError!) -> Void in
self.registerCompletionBlock(succeeded, error: error)
}
Is someone having the same problem and knows a solution for this strange behaviour?
Thanks!
Edit:
The completion block should call the "registerCompletionBlock()" function:
func registerCompletionBlock(succeeded: Bool, error: NSError!) {
if error == nil {
let subscriptionStoryboard = UIStoryboard(name: "Subscription", bundle: nil)
let viewcontroller: UIViewController = subscriptionStoryboard.instantiateInitialViewController() as UIViewController
self.presentViewController(viewcontroller, animated: true, completion: nil)
} else {
if let errorString = error.userInfo?["error"] as? NSString {
println(errorString)
if error.userInfo?["code"] as Float == 202{
let alert = UIAlertView(title: "vergeben", message: "name vergeben", delegate: nil, cancelButtonTitle: "abbrechen")
alert.show()
}
}
}
}

I tried the solution posted previously (eliminating PFUser.enableAutomaticUser()), and the issue persisted.
In case anyone else is still looking for a solution to this issue, try changing the if error == nil to if succeeded == true in the block of signUpInBackground. This worked for me and all is functional in backend.

Its calling at first time, but its taking a little time for calling this .. because its sending data on server in asynchronous. Asynchronous never block the main thread..
Because : -
Asynchronous never block the main thread waiting for a network response.
Asynchronous can be either synchronous on a separate thread, or scheduled in the run loop of any thread.
Asynchronous can be either synchronous on a separate thread, or scheduled in the run loop of any thread.
Synchronous blocks main thread until they complete request.

Because you call the method asynchronous, it takes some time to do it and your main thread doesn't wait for the method to finish. So you should, if you want to show a message or perform a segue, after the registration, put it inside the completion-block:
newUser.signUpInBackgroundWithBlock {
(succeeded: Bool!, error: NSError!) -> Void in
if error == nil {
// Perform a segue, show a message or whatever you want
} else {
let errorString = error.userInfo["error"] as NSString
// Show the errorString somewhere and let the user try again.
}
}
Also if you don't want to do it asynchronous, you can call the signUp() method(without the inBackgroundWithBlock. That way the application waits for the signup to finish until it continues.

i figured it out. Problem was, that i used PFUser.enableAutomaticUser() in the AppDelegate. I removed that line and the signup-block works flawlessly now!

Related

Layer synchronizeWithRemoteNotification not getting called in iOS

I'm building an iOS app and making use of layer.
According to the documentation, when a push notification for a message comes in, you should not immediately switch to the conversation because the message might not have finished synching. So you call a synchronise method and pass it a completion function as follows:
self.layerClient!.synchronizeWithRemoteNotification(userInfo, completion: { (conversation, message, error) in
if (conversation != nil || message != nil) {
NSNotificationCenter.defaultCenter().postNotificationName(CXOConstants.Notifications.BadgeChatTabNotification.rawValue, object: nil, userInfo: userInfo)
let msgPart = message?.parts.first
if msgPart?.MIMEType == "text/plain"{
let msgText = String(data: msgPart!.data!,encoding: NSUTF8StringEncoding)
debugPrint("msgText:: \(msgText)")
} else {
debugPrint("The user sent an image")
}
completionHandler(.NewData)
} else {
completionHandler(.Failed)
}
})
Problem is, this method never gets called. I have tried implementing the same logic outside of the synchronizeWithRemoteNotification method, but when I switch to the conversation, it takes a couple seconds for the message to appear in the conversation.
Could use some help here figuring out why the method isn't getting called.
Thanks :)

TouchID canceling should dismiss the view controller

For my app I need to save one page of it with TouchID. So the user is kinda forced to use TouchID or if the device does not support it, a password. If the user cancels the TouchID authentication, I want to View to disappear and get back to the root view. I already had that working, but somehow it does not work anymore and I really don't know why?! I just copied until the canceled option, the rest is does not matter I guess.
func authenticateUser() {
let context = LAContext()
var error: NSError?
let reasonString = "Authentication is needed to access your App"
if context.canEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, error: &error){
context.evaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, localizedReason: reasonString, reply: { (success, policyError) -> Void in
if success {
print("authentification successful")
}
}else{
switch policyError!.code{
case LAError.SystemCancel.rawValue:
print("Authentification was canceled by the system")
case LAError.UserCancel.rawValue:
print("Authentication was canceled by user")
self.navigationController?.dismissViewControllerAnimated(true, completion: nil)
//Yes I also tried popToRootViewController, still doesn't work
}
The documentation for the evaluatePolicy call says:
"Reply block that is executed when policy evaluation finishes. This block is evaluated on a private queue internal to the framework in an unspecified threading context."
So the problem is that you are trying to call navigation from the wrong thread. You need to make that call on the UI thread. For example:
dispatch_async(dispatch_get_main_queue()) {
// Navigation goes here
}

Best way to use findObjectsInBackgroundWithBlock when moving data around

I have a Tableview that gets data with findObjectsInBackgroundWithBlock in viewDidLoad and passes that data to a Detail View Controller no problem.
Im having trouble managing the flow of findObjectsInBackgroundWithBlock. Here is a example: I have a like button on the detail view and when pressed it increments the UILabel and displays it. It also then gets the object in Parse then increments and saves it... Everything good.
#IBAction func likeButtonPressed(sender: AnyObject) {
print("likeButtonPressed()")
// Adding the like to label
mixLike!++
var stringForCount: String = String(mixLike!)
mixLikeLabel.text = stringForCount
// Saving the like back to Parse
var query = PFQuery(className: "musicMixes")
query.whereKey("info", equalTo: mixNameLabel.text)
query.findObjectsInBackgroundWithBlock { (objects:[AnyObject]!, error:NSError!) -> Void in
if error == nil {
for object in objects {
//var votes = object["votes"] as! Int
let mixObject:PFObject = object as! PFObject
mixObject.incrementKey("votes", byAmount: 1)
mixObject.saveInBackgroundWithTarget(nil, selector: nil)
print("mixObjectSaved")
}
} else {
print("Error getLikeCount()")
}
print("sending Notification...")
NSNotificationCenter.defaultCenter().postNotificationName("reload", object: nil)
print("sent Notification...")
}
} // likeButtonPressed End
I also then call a NSNotification back to the Table View so the Table View can update the likes to match the users like click on the detail view (See bellow)
The NSNotification calls this function in the Table View, which removes the like array, grabs the new likes again and then reloads the Table View.
# objc func reloadTableData(notification: NSNotification){
print("Notification Recived, Removing Likes and Reloading. reloadTableData()...")
self.mixLikeArray.removeAll()
//self.stringForCountArray.removeAll()
print("Like array Data removed, getting data again...")
var query = PFQuery(className: "musicMixes")
query.orderByAscending("date")
query.findObjectsInBackgroundWithBlock { (objects:[AnyObject]!,error: NSError!) -> Void in
if error == nil {
for object in objects {
let mixLike = object["votes"] as! Int
self.mixLikeArray.append(mixLike)
print("New mixLikeArray data is \(self.mixLikeArray)")
}
} else {
print("error getting like object")
}
}
dispatch_async(dispatch_get_main_queue(),{
self.allTableView.reloadData()
});
}
I see three issues wrong with how this works at the moment. likeButtonPressed() Is sometimes sending the NSNotification before mixObject.saveInBackgroundWithTarget is finished. Meaning that the incremented like won't be displayed on the table view.
Secondly if I was to click like then click back to tableview swiftly the app will crash. This is because I'm guessing both likeButtonPressed() and the NSNotification function still has not been completed.
Also in # objc func reloadTableData(notification: NSNotification) once again the
dispatch_async(dispatch_get_main_queue(),{
self.allTableView.reloadData()
});
Is being called before the findObjectsInBackgroundWithBlock is being completed? Anyway round this?
How would you suggest I can remodel this to work efficiently? Im pretty new to coding and a bit rusty with designing the best ways to do things... I know the concept behind completion handlers could I use these? I know that Parse likes to work in the background though hhhmmmm.....
to fix your reloadTableData problem, you should trigger the reload once the parse block is done executing, which means moving this line
dispatch_async(dispatch_get_main_queue(),{
self.allTableView.reloadData()
});
inside the block
query.findObjectsInBackgroundWithBlock { (objects:[AnyObject]!,error: NSError!) -> Void in
if error == nil {
for object in objects {
let mixLike = object["votes"] as! Int
self.mixLikeArray.append(mixLike)
print("New mixLikeArray data is \(self.mixLikeArray)")
}
dispatch_async(dispatch_get_main_queue(),{
self.allTableView.reloadData()
});
} else {
print("error getting like object")
}
}
That will ensure that it gets triggered once parse is done updating objects. Currently its triggering before that while the block is executing. It also means that it won't reload if you get an error as you probably need to handle that differently anyway.
As for your problem of the notification happening before the saving is complete, you are calling . saveInBackgroundWithTarget but don't seem to send anything into it. You could use saveInBackgroundWithBlock and then use dispatch_group dispatch_group_enter, dispatch_group_leave, and dispatch_group_notify inside the block to make your program wait till everything is done being saved before sending the notification.
So you would create a dispatch_group
dispatch_group_t group = dispatch_group_create();
And then call it dispatch_group_enter in the for loop through the objects
for object in objects {
dispatch_group_enter(group);
let mixObject:PFObject = object as! PFObject
.....
}
Then call dispatch_group_leave on the mixObject.saveInBackgroundWithBlock
and wrap the notification in dispatch_group_notify
dispatch_group_notify(group, dispatch_get_main_queue(), ^{ // 4
NSNotificationCenter.defaultCenter().postNotificationName("reload", object: nil)
});
Something like that
It sounds more daunting than it is, here's a Ray Wenderlich tutorial to bring you up to speed on how to use it, if your not familiar

xcode present segue on restful callback (swift)

I am self taught Swift user and trying to do something simple but it's got me pretty stumped. I have a simple registration form. After submitting the items for registration, I want to move the page to a "how it works" page via a segue, but ONLY when my restful API returns success. Here's what I have so far; feel free to send me a better way to do this as well. All criticisms are welcome.
let myUrl = NSURL(string:"http://www.example.com/scripts/Register.php")
let request = NSMutableURLRequest(URL: myUrl!)
request.HTTPMethod = "POST"
let postString = "email=\(email)&password=\(pass)"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request){
data, response, error in
if (error != nil) {
println("Error: \(error)")
return
}
var err: NSError?
var json = NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers, error: &err) as? NSDictionary
var showTutorial : Bool = false
if let parseJSON = json {
var returnValue = parseJSON["status"] as? String
println("Status: \(returnValue)")
var isUserRegistered: Bool = false
if (returnValue == "Success") {
showTutorial = true
} else if (returnValue == "Error") {
// handle error
}
}
// if successful registration, show how it works page
if (showTutorial) {
self.performSegueWithIdentifier("howItWorksSegue", sender: self)
}
}
task.resume()
I have a segue named howItWorksSegue attached to this view controller going to the HowItWorksViewController. I'm receiving this error from Xcode:
2015-10-12 21:22:43.261 ZiftDine[11396:2307755] Assertion failure in -[UIKeyboardTaskQueue waitUntilAllTasksAreFinished], /SourceCache/UIKit_Sim/UIKit-3347.44.2/Keyboard/UIKeyboardTaskQueue.m:374
2015-10-12 21:22:43.391 ZiftDine[11396:2307755] Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[UIKeyboardTaskQueue waitUntilAllTasksAreFinished] may only be called from the main thread.'
Anything done with UI should be done on the main thread, try wrapping you performSegue call like this:
dispatch_async(dispatch_get_main_queue(),{
self.performSegueWithIdentifier("howItWorksSegue", sender: self)
})
#Swinny89 gave the solution to your problem but some explanation is in order.
If you read the description of dataTaskWithRequest:completionHandler:, which is the method you are using (although your Swift code uses trailing closure syntax to drop the completionHandler label and put the closure outside the parentheses) it says:
completionHandler: The completion handler to call when the load
request is complete. This handler is executed on the delegate queue.
Then if you read the description of the init method sessionWithConfiguration:delegate:delegateQueue: it says:
queue: A queue for scheduling the delegate calls and completion
handlers. If nil, the session creates a serial operation queue for
performing all delegate method calls and completion handler calls.
Serial operation queues run on a different thread.
So, taking all of those pieces of information together, it means that your completion closure is going to be executed on a thread other than the main thread.
A cardinal rule of iOS/Mac development is that you must do all UI calls from the main thread. If a call changes anything on the screen, it's a UI call.
Your code is invoking performSegueWithIdentifier: from a background thread. It changes what's displayed on the screen, so it must be a UI call. Therefore it needs to be run on the main thread.
The GCD function dispatch_async(), with a queue of dispatch_get_main_queue(), submits a closure to be run on the main dispatch queue, a queue that runs on the main thread.
So Swinny's solution fixes your problem.
The take-away here:
Any time you are running code in a closure, stop and think: "Am I positive that this closure will always be run on the main thread?" If the answer is no, enclose the code in a call to dispatch_async(dispatch_get_main_queue(), like Swinny's answer.
The answers by #Duncan C and #Swinny89 are good. For anyone coming in from Google, the syntax in Swift 3 has changed a little:
DispatchQueue.main.async(execute: {
self.performSegueWithIdentifier("howItWorksSegue", sender: self)
})

Setting a subview.hidden = false locks up UI for many seconds

I'm using a button to populate a UIPickerView on a hidden UIVisualEffectView. The user clicks the button, the VisualEffectView blurs everything else, and the PickerView displays all the names in their contact list (I'm using SwiftAddressBook to do this.)
This works fine except when the user clicks the button, the UI locks up for about 5-10 seconds. I can't find any evidence of heavy CPU or memory usage. If I just print the sorted array to the console, it happens almost immediately. So something about showing the window is causing this bug.
#IBAction func getBffContacts(sender: AnyObject) {
swiftAddressBook?.requestAccessWithCompletion({ (success, error) -> Void in
if success {
if let people = swiftAddressBook?.allPeople {
self.pickerDataSource = [String]()
for person in people {
if (person.firstName != nil && person.lastName != nil) {
//println("\(person.firstName!) \(person.lastName!)")
self.pickerDataSource.append(person.firstName!)
}
}
//println(self.pickerDataSource)
println("done")
self.sortedNames = self.pickerDataSource.sorted { $0.localizedCaseInsensitiveCompare($1) == NSComparisonResult.OrderedAscending }
self.pickerView.reloadAllComponents()
self.blurView.hidden = false
}
}
else {
//no success, access denied. Optionally evaluate error
}
})
}
You have a threading issue. Read. The. Docs!
requestAccessWithCompletion is merely a wrapper for ABAddressBookRequestAccessWithCompletion. And what do we find there?
The completion handler is called on an arbitrary queue
So your code is running in the background. And you must never, never, never attempt to interact with the user interface on a background thread. All of your code is wrong. You need to step out to the main thread immediately at the start of the completion handler. If you don't, disaster awaits.

Resources