iOS 8 and segues throwing unexpected exception - ios

Well I'm coding an app with xcode 6.4 for iOS 8+, I have made a story board and connected several controllers the problem lies when I log out of the app and try to create a new user and go to the menu the app crashes I have an exception break point but I don't really understand why is crashing, any help?
The offending code:
#IBAction func listoTapped(sender: UIButton) {
if usernameRegisterField.text != "" && passwordRegisterField.text != "" {
let user = PFUser()
//2
user.username = usernameRegisterField.text
user.password = passwordRegisterField.text
//3
user.signUpInBackgroundWithBlock { succeeded, error in
if (succeeded) {
//The registration was successful, go to the wall
//self.performSegueWithIdentifier(self.tableViewWallSegue, sender: nil)
self.performSegueWithIdentifier(self.showMenuSegue, sender: self)
//self.dismissViewControllerAnimated(true, completion: nil)
} else if let error = error {
//Something bad has occurred
self.showErrorView(error)
}
}
} else {
var alert: UIAlertController = UIAlertController(title: "Error", message: "Los campos no pueden estar vacios", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil))
presentViewController(alert, animated: true, completion: nil)
}
}
And the segue crashing:
It only crashes when I log out and then try to register a new user, I'm using a push segue.
EDIT: The solution to the no segues showing, is to enable size classes if you have them off, that will let you choose the show segue.

How do you create the "Login" controller when logging out? Don't you forget to create it with navigation controller to be able to push..
Otherwise please attach some crash information / log

From your diagram, you have a login screen which lets you sign in or add a new user. This screen is the root of a navigation controller.
I assume from the diagram that if you sign in with a know account then it pushes to the main app.
Your new user dialog however is being presented as modal from what I can see. When sign up occurs you then have a show/push segue to the app. However you cannot show/push as there is no navigation controller from this point which is probably your issue.
You could either:
1) Dismiss the signup screen and force a login.
2) Push/Show the signup screen rather than presenting it modally.
If you go for 1), then rather than having to dismiss and hard wire you own data pass back, you could use an unwind segue to pass back the username and password to populate the sign-in screen with the new user details.

In iOS 8, push segue is deprecated. You should use show segue instead.

Related

UITest cases to handle with location services alert

I am writing UI test cases for my project.
My project flow is as below:
Login Screen. User enters credentials and press login.
Home Screen. There is location requirement so system as for user's permission. I allow it.
Logout.
So when I do fresh install of application this flow is recorded in test case and works if I perform on new fresh build.
But problem is when I test on old build there is no alert for location permission and the test's gets fail. How can I handle this cases or ask user for permission every time when I run tests?
For resetting credentials of user I am passing launchArguments to XCUIApplication() and handle in AppDelegate.
I have implemented code let me know if its correct way:
addUIInterruptionMonitor(withDescription: "Allow “APP” to access your location?") { (alert) -> Bool in
alert.buttons["Only While Using the App"].tap()
return true
}
The above code works for both if alert comes or not.
Using an interruption monitor is the correct way. However, it's safer to check if the alert being displayed is the alert you're expecting before you interact with the alert:
addUIInterruptionMonitor(withDescription: "Allow “APP” to access your location?") { (alert) -> Bool in
let button = alert.buttons["Only While Using the App"]
if button.exists {
button.tap()
return true // The alert was handled
}
return false // The alert was not handled
}
Try this
let app2 = XCUIApplication(bundleIdentifier: "com.apple.springboard")
let button = app2.alerts.firstMatch.buttons["Allow While Using App"]
button.waitForExistence(timeout: 10)
button.tap()
I use the following code to allow user's location:
// MARK: - Setup
override func setUp() {
super.setUp()
continueAfterFailure = false
app = XCUIApplication()
app.launch()
addUIInterruptionMonitor(withDescription: "System Dialog") { (alert) -> Bool in
alert.buttons["Allow Once"].tap()
return true
}
}
In this setup, I "register" the interruption monitor for tapping the allow button, so in this case I can dismiss that modal. Now, there's my test:
// MARK: - Test change mall
func testChangeMall() {
let selectorChangeButton = app.buttons["change_mall_button"]
XCTAssert(selectorChangeButton.exists, "Selector change button does not exist")
selectorChangeButton.tap()
app.navigationBars.firstMatch.tap()
let cell = app.staticTexts["Shopping Centre"]
XCTAssert(cell.exists, "There's no cell with this title")
cell.tap()
sleep(1)
let label = app.staticTexts["Shopping Centre"]
XCTAssert(label.exists, "Nothing changes")
}
In this test, simply I go to a view controller with a list sorted by location. First, I need to dismiss the location's system alert. So, first I dismiss that modal and then I tap a cell from my TableView. Then, I need to show it in my main view controller so I dismiss my view controller and I expect the same title.
Happy Coding!

Subsequent presentations of UIAlertController stutter

In a very loaded ViewController in an in-house app I've developed (iPad app written in Swift) for my company, I present alert style UIAlertControllers in a few circumstances. The first time an alert is presented, it always displays very smoothly. Any subsequent displays of an alert however end up being very stuttery, even for alerts that don't have much code surrounding their presentation and dismissal.
For example, if the user tries to dismiss the vc without saving after making changes (simple conditional of "if bool_Saved == false { create and display alert }") an alert is presented asking if they're sure they want to exit without saving. If they choose Yes, the vc is dismissed, otherwise, the alert is dismissed. The first creation and presentation of the alert animates smoothly, but anytime this conditional or any other code needs to present an alert, the presentation stutters its way through the animation.
The UI on this vc is pretty loaded. The whole screen is a scrollView with a contentView that contains 10 sub UIViews which in turn each have 3 UITextFields, ~9 UILabels, and ~8 UIButtons (the company wanted an exact digital replica of a paper form they've been using). Dismissing the vc and reloading it causes the next alert to again display smoothly, but again, subsequent alerts stutter through their present animation.
I've begun profiling in Instruments, but am a bit inexperienced in it and intend to use most of today getting more familiar with the various tools to hopefully find a source for this issue. What I'd like to know is if anyone has any suggestions on what might be causing this stuttering problem.
Thanks, and please let me know if there's any additional information I can provide.
Editing with code snippet described above:
func cancelTapped() {
if savedOnce == false {
let alert_Exit = UIAlertController(title: "Exit Inspection?", message: "Unsaved changes to the sheet will be lost upon exit. Are you sure you want to exit without saving?", preferredStyle: .alert)
let action_No = UIAlertAction(title: "No", style: .cancel, handler: nil )
let action_Yes = UIAlertAction(title: "Yes", style: .default, handler: { [unowned self]
(action) in
self.exitSheet()
})
alert_Exit.addAction(action_No)
alert_Exit.addAction(action_Yes)
present(alert_Exit, animated: true, completion: nil)
} else {
exitSheet()
}
}
You are not dismissing the alert. Insert that line in your Yes button.
let action_Yes = UIAlertAction(title: "Yes", style: .default, handler: { [unowned self]
(action) in
alert_Exit.dismiss(animated: true)
self.exitSheet()
})
When you create the alert a second time, it probably results in a conflict with previous alert hidden somewhere in your view that wasn't dismissed. How loaded the VC is, it probably doesn't make any difference.
Style .cancel will remove itself anyway, whilst .default will require you to remove the alert.

How to display a dialog with (multiselect listview) before displaying "app would like to send you push notifications" dialog box

I would like to display a pop-up where I'll ask the user about their preferences related to the push notifications and for that one, I want to display a list of options to the user. User can select more than one options.
I think that I'll have to display a tableview inside the UIAlertView, but it is deprecated now. So, how can I display a pop (with some small message + multiple select list ) before the APN system permissions dialog in Swift.
Any help will be appreciated.
You can use this code:
let alert = UIAlertController(title: title, message:message, preferredStyle: .Alert)
let action = UIAlertAction(title: "OK", style: .Default) { _ in
acceptNotification = true //code to execute when the user taps that OK
}
alert.addAction(action)
//you can add more actions
self.presentViewController(alert, animated: true){ // this part if provided, will be invoked after the dismissed controller's viewDidDisappear: callback is invoked.
}

App Crashing after changing Photo Settings

I've got an app that needs access to the Photos on your device. I check to see the device status, and if they deny access I trigger a modal which will warn them that they did not provide the necessary access, and then gives them the option to go to their settings and correct the choice.
When this happens, my app crashes with the following error:
This application is modifying the autolayout engine from a background thread, which can lead to engine corruption and weird crashes. This will cause an exception in a future release.
Here is my code for triggering the redirect. Any ideas what could be causing this or suggestions on how I should do this better?
let title = "You didn't allow us to view your photos!"
let message = "Without access, we cannot help you add photos from your device."
let alertController = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)
alertController.addAction(UIAlertAction(title: "I know!", style: UIAlertActionStyle.Cancel, handler: nil))
alertController.addAction(UIAlertAction(title: "Settings (Required)", style: UIAlertActionStyle.Default, handler: { (action: UIAlertAction) -> Void in
let settingsURL = NSURL(string: UIApplicationOpenSettingsURLString)
UIApplication.sharedApplication().openURL(settingsURL!)
}))
self.presentViewController(alertController, animated: true, completion: nil)
This code is called within a method that I call from the following spot:
PHPhotoLibrary.requestAuthorization({ (status: PHAuthorizationStatus) -> Void in
if(status == .Authorized){
self.getPhonePhotos()
}else{
self.showDeniedPhotosPopup()
}
})
Thanks in advance.
UPDATE
I just realized something I didn't earlier. The code only crashes if I activate the "Photos" switch in the settings. The navigation itself doesn't cause the app to crash, its changing the photo settings configuration while the app is still running. To test my theory, I never triggered the popup, and simply went to the settings, and activated the photos switch and the app crashed. So the crash is definitely sourcing from the change in photo settings.
From the error it appears you are presenting your view controller on a thread which is not main thread leading to implementation of UI stuff (auto layout etc.) on your new view controller on a background thread.
Try encapsulating your view controller presentation code on main queue. Something like this:
weak var aBlockSelf = self
dispatch_async(dispatch_get_main_queue(), {
aBlockSelf.presentViewController(alertController, animated: true, completion: nil)
})
If it still crashes, profile your application to find out the exact culprit.

Parse signup going to next view controller if fields are missing

So I am trying to create a signup page, and I am running into some trouble. I want the user to HAVE to fill in all the information before they hit signup. After they hit the signup button, if they do not have all of the fields filled in they get an UIAlertView error for many different things. When I hit signup, it takes the user to the next page and then displays the UIAlertView, I do not want this to happen. I can't figure out what is happening. If the UIAlertView comes up if they do not have a username or email filled in I need the UIAlertView to display after they click the button and not go to the next page. I have a performSegue method in my code but it is performing first, and not last. Can anybody help me? I will include the screenshot of my storyboard as well.
user.signUpInBackgroundWithBlock {
(succeeded: Bool, error: NSError?) -> Void in
if let error = error {
let errorString = error.userInfo?["error"] as? NSString
if fullname.isEmpty || email.isEmpty || username.isEmpty || password.isEmpty {
var emptyFields:UIAlertView = UIAlertView(title: "Plese try again", message: "It looks like you forgot to fill out all the fields. Please make sure all the fields are filled out so we can create an account for you.", delegate: self, cancelButtonTitle: "Try again")
emptyFields.show()
} else if error.code == 203 {
var takenEmail:UIAlertView = UIAlertView(title: "Please try again", message: "It looks like that email has already been taken, please try again.", delegate: self, cancelButtonTitle: "Try again")
} else if error.code == 202 {
var usernameTaken:UIAlertView = UIAlertView(title: "Please try again", message: "It looks like that username is already in use, please try again.", delegate: self, cancelButtonTitle: "Try again")
}
}
}
There are a few ways to do this. The way I would do it is as follows.
Step 1.
Ok you want to select your Sign Up View Controller.
Step 2.
You are going to control click and drag from the Yellow View Controller Icon to the Second View Controller. In the Menu that appears choose the Show Or Present Modally Option.
Step 3.
Choose the Segue you just created and change its Identifier, In the right side panel to "signupSuccessful".
Step 4.
You can set the code up like this.
if usernameTF.text.isEmpty || passwordTF.text.isEmpty {
var emptyFields = UIAlertView()
emptyFields.title = "Plese try again"
emptyFields.message = "It looks like you forgot to fill out all the fields. Please make sure all the fields are filled out so we can create an account for you."
emptyFields.addButtonWithTitle("Try Again!")
emptyFields.show()
}else if usernameTF.text == "ERROR CODE 203" {
//do same thing with the alert view!
}else if usernameTF.text == "ERROR CODE 202" {
//Do another alert view here
//You can keep adding the "else ifs" for any other error codes
}
//Here is where it will send to the second view when everything above is false!
else {
//Here we present the second view.
self.performSegueWithIdentifier("signupSuccessful", sender: self)
}
There you go.
Here is a download of the project if you need to see the segue again, or any of the above code.
https://github.com/bobm77/ParseSegueExample
Implement the method func shouldPerformSegueWithIdentifier(identifier: String?, sender: AnyObject?) -> Bool in your view controller and return false if you don't want the segue to happen.

Resources