How to create a segue on condition [duplicate] - ios

This question already has an answer here:
segue transition with condition storyboard
(1 answer)
Closed 7 years ago.
I have a login page backed up by Parse. I want to know how to create a segue only if the login has been confirmed through the Parse database, and then direct the user to a new View Controller.
This is the code for the login button:
#IBAction func logginginAction(sender: AnyObject) {
var username = self.usernameField.text
var password = self.passwordField.text
if (count(username.utf16) < 4 || count(password.utf16) < 5 ) {
var alert = UIAlertView(title: "Invalid", message: "Username/Password is too short!!", delegate: self, cancelButtonTitle: "OK")
alert.show()
}
else {
self.actInd.startAnimating()
PFUser.logInWithUsernameInBackground(username, password: password, block: { (user, error) ->
Void in
self.actInd.stopAnimating()
if ((user) != nil) {
}else {
var alert = UIAlertView(title: "Invalid", message: "Please recheck the information you just entered", delegate: self, cancelButtonTitle: "OK")
alert.show()
}
})
}
}

This is simple.
Connect your button to an IBAction, not directly to a segue.
Connect your segue from the view controller, not from the button. Give it a unique identifier.
In your IBAction method check the conditions you want to check, and if they are met, invoke your segue using performSegueWithIdentifier:sender:

Related

how to dismiss alert view after validation?

I had used SCLAlertView used for forgot password in this i had placed a textfield to enter email so that after making successful validation only it need to hide without this it should not hide can anyone help me how to implement this ?
My code is shown below
#IBAction func forgetPasswordButton(_ sender: Any) {
let appearance = SCLAlertView.SCLAppearance(showCloseButton: true)
let alert = SCLAlertView(appearance: appearance)
let txt = alert.addTextField("Enter your emailid")
_ = alert.addButton("Submit", action: {
let action = txt.text
print(action as Any)
})
if(txt.text?.isEmpty)! == true{
}else{
}
_ = alert.showEdit("Forgot Password", subTitle:"Please enter your email address below.You will receive a link to reset your password",closeButtonTitle: "Cancel")
}
use the property of hideView() and call like
let action = txt.text
print(action as Any)
alert.hideView()
})
for more reference you can get the sample here

Warning: Attempt to present <UIAlertController: x> on <x.x:x> whose view is not in the window hierarchy

I am fairly new to Swift but I am creating a app that requires after a certain time to enter either Touch-ID or a PIN. I am checking a timer from AppDelegate.swift to see if it has expired and if it has expired I am making a call to my "BaseTableViewController" which holds my function authenticateUser. Again I am calling this from my AppDelegate.swift file by creating an instance of BaseTableViewController var baseTableVC = BaseTableViewController() and making a call if timer expired to self.baseTableVC.authenticateUser().
Anyways I am getting: Warning: Attempt to present <UIAlertController: 0x7fed5ae1dcf0> on <admin.BaseViewController: 0x7fed5ad279d0> whose view is not in the window hierarchy!
Thank you in advance for you help!
func showPasswordAlert(){
let alertController = UIAlertController(title: "Touch ID Password", message: "Please enter your password", preferredStyle: .Alert)
let defaultAction = UIAlertAction(title: "OK", style: .Cancel) {(action) -> Void in
if let textField = alertController.textFields?.first as UITextField?{
if textField.text == "hello" {
print("Authentication successfull!")
}
else{
self.showPasswordAlert()
}
}
}
alertController.addAction(defaultAction)
alertController.addTextFieldWithConfigurationHandler{(textField) -> Void in
textField.placeholder = "Password"
textField.secureTextEntry = true
}
presentViewController(alertController, animated: true, completion: nil)
}
func authenticateUser(){
let context = LAContext()
var error: NSError?
let reasonString = "Authentication is required for Admin!"
context.localizedFallbackTitle = "Enter your PIN Code"
if context.canEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, error: &error){
context.evaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, localizedReason: reasonString, reply: {(success, policyError) ->Void in
if success{
print("Authentication successful!")
}
else{
switch policyError!.code{
case LAError.SystemCancel.rawValue:
print("Authentication was cancelled by the system!")
case LAError.UserCancel.rawValue:
NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
self.showPasswordAlert()
})
print("Authentication was cancelled by the user!")
case LAError.UserFallback.rawValue:
print("User selected to enter password.")
NSOperationQueue.mainQueue().addOperationWithBlock({() -> Void in
self.showPasswordAlert()
})
default:
print("Authentication failed!")
NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
self.showPasswordAlert()
})
}
}
})
}
else{
print(error?.localizedDescription)
NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
self.showPasswordAlert()
})
}
var baseTableVC = BaseTableViewController()
func applicationWillEnterForeground(application: UIApplication) {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
let logInStatus = NSUserDefaults.standardUserDefaults()
let currentTime = NSDate().timeIntervalSince1970
let roundCurrentTime = (round(currentTime))
// Pin expire limit
let pinExpLimit: Double = 30
// Set the exact time of expire for pin
let pinExpDate = (currentTime + pinExpLimit)
let newPinExpDate = (round(pinExpDate))
if (logInStatus.doubleForKey("expPinTime") <= roundCurrentTime) {
self.baseTableVC.authenticateUser()
print("AppDelegate Pin Exp Time")
print(logInStatus.doubleForKey("expPinTime"))
//print(newPinExpDate)
print("AppDelegate Current Time")
print(roundCurrentTime)
logInStatus.setDouble(newPinExpDate, forKey: "expPinTime")
NSUserDefaults.standardUserDefaults().synchronize()
}
}
I suspect you simply create an instance of BaseTableViewController but you don't add its view to the view hierarchy before presenting the UIAlertController's instance.
If self.baseTableVC is the root view controller of your app, then a call like this
baseTableVC.presentViewController(instanceOfUIAlertController, animated: true, completion: yourCompletionBlock)
should work from within the AppDelegate.
If self.baseTableVC is not the root view controller, then or you make sure to invoke the previous command on the root VC of your app
window.rootViewController.presentViewController(instanceOfUIAlertController, animated: true, completion: yourCompletionBlock)
or make sure you embed the view of self.baseTableVC in the view hierarchy and then call
baseTableVC.presentViewController(instanceOfUIAlertController, animated: true, completion: yourCompletionBlock)
As a side note, if your alert must be displayed from anywhere in the app, then your approach is ok. If instead your alert must be displayed only from a specific screen, I would remove the timer logic from the app delegate and move it inside the presenting view controller. This would keep your app delegate clean from unnecessary code and would confine the control logic in the right place: the presenting view controller
You can't create instance of view controller just by calling default constructor, use storyboard. Correct me if I'm wrong

How to condition segues using CloudKit query?

I created a login page backed up by CloudKit.
I want to know how to create a conditioned segue if the login has a certain value, and then direct the user to a View Controller based on that value
In details, I have three Segues I want to connect:
sign in id segue:LoginSuccessSegue
staff tab bar id segue:idStaffTabBar
student tab bar id segue:idStudentTabBar
First Segue
LoginSuccessSegue:
the sign in view controller has a show segue id LoginSuccessSegue the connects to staff tab bar controller and student tab bar controller.
Second Segue
idStaffTabBar:
After queryProfileType() is executed, it will look for the profile type in a list to check if the user profile is a teacher value or something else. Then if that is true "LoginSuccessSegue" will automatically take the user to staff tab bar controller, "StaffBookedVC.self" by usingits segueidStaffTabBar `
Third Segue
idStudentTabBar:
if the user is not a teacher then after pressing the sign in button redirect to student tab bar controller, "stdBookingVC.self" by using idStudentTabBar or
How can I achieve an automatic conditional sign in in multiple views suing segues and cloud kit query?
This is the code for the sign-in button:
#IBAction func btnSignInTapped(sender: UIButton)
{
let userEmailAddress = userEmailAddressTextField.text
let userPassword = userPasswordTextField.text
if(userEmailAddress!.isEmpty || userPassword!.isEmpty)
{
notifyUser("Empty fields", message: "all fields are required")
}
print("fetching is in progress")
queryProfileType()
print("\nfetching had stopped")
}//end of signInbtn
func queryProfileType()
{
queryCredentials()
print("\nProfile Query starting")
//execute query
let organizers = ["Teacher || Youtuber || Instagrammer || "]
let predicate = NSPredicate(format: "ProfileType = '\(organizers)' ")
print("\nThis is the predicate\n\(predicate)")
let query = CKQuery(recordType: "RegisteredAccounts", predicate: predicate)
publicDatabase!.performQuery(query, inZoneWithID: nil) { results, error in
if (error != nil)
{
print(error)
}else
{
if (results! == organizers)
{
self.performSegueWithIdentifier("idStaffTabBar", sender: StaffBookedVC.self)
}else{
self.performSegueWithIdentifier("idStudentTabBar", sender: stdBookingVC.self)
}
print("\(results)\nthese are the printed results")
}
}
let firstFetch = CKFetchRecordsOperation()
let secondFetch = CKFetchRecordsOperation()
secondFetch.addDependency(firstFetch)
let queue = NSOperationQueue()
queue.addOperations([firstFetch, secondFetch], waitUntilFinished: false)
}
here is a picture of the storyboard segues Storyboard
if your answer will contain these methods, please show me some examples:
shouldPerformSegueWithIdentifier and prepareForSegue
this did not work from me either
self.presentViewController(SignInNavigationVCTabBars, animated: true,
{ results, error in
if (results! == organizers)
{
self.performSegueWithIdentifier("idStaffTabBar", sender: StaffUITABbarVC.self)
}else{
self.performSegueWithIdentifier("idStudentTabBar", sender: StdUITABbarVC.self)
}
}
`
You need to do something like presentModalViewController to show the navigationController first, within your queryProfileType method. And then do some smart logic to determine which route to go, after the navigationController is loaded. So a custom UINavigationController is needed.
Or an easier way:
Move your loginViewController into the navigation controller stack, and then link the two existing segues, i.e. idStaffTabBar and idStudentTabBar to it. That will solve the problem.
Here is the answer
I did not expect valueforkey would have it all
what can I see? never stop trying
//log in function
func queryCredentials()
{
print("*********\nQueryCredentials starting")
// System indicator
let spinningActivity = MBProgressHUD.showHUDAddedTo(self.view, animated: true)
spinningActivity.labelText = "Signing in"
spinningActivity.detailsLabelText = "Please wait"
// querying predicate in cloud kit to check via email and password property
let predicate = NSPredicate(format: "Email = %#", userEmailAddressTextField.text!)
let query = CKQuery(recordType: "RegisteredAccounts", predicate: predicate)
publicDatabase?.performQuery(query, inZoneWithID: nil,
completionHandler: ({results, error in
if (error != nil)
{
dispatch_async(dispatch_get_main_queue())
{
// if the user is not signed, display this error
self.notifyUser("Cloud Access Error",
message: "to fix this error Sign in you icloud \n go to settings\nthen sign in icloud account\n error code:\(error!.localizedDescription)")
}
}else
{
if (results!.count > 0)
{
// the results after success case of the query
let record = results![0] as! CKRecord
// read from the result to navigate via profiletype attribute
let proftype = record.valueForKey("ProfileType") as! String
switch proftype
{
case("Teacher"):
self.staffView()
break;
case("Manager"):
self.staffView()
break;
case("Student"):
// stdView() is a student coded segue as a function to navigate to student view
self.stdView()
break;
case("Worker"):
self.stdView()
break;
default:
break;
}
self.currentRecord = record
dispatch_async(dispatch_get_main_queue())
{
// if credentials are correct, display you are logged in
self.userPasswordTextField!.text =
record.objectForKey("Password") as! String
self.notifyUser("Welcome", message: "You are loogedin")
}
}else
{
dispatch_async(dispatch_get_main_queue())
{
self.notifyUser("No Match Found",
message: "No record matching")
}
}
}
}))
// hiding indicator
spinningActivity.hide(true)
}

Present Alert on ViewController only once

I am having various ViewControllers in my app. On one of them I want a alert to be displayed on load of the VC once to the user.
I have followed the instructions to set a glob var under the import section:
var disalert:Bool = true
and in the function I got:
if disalert {
let actionSheetController: UIAlertController = UIAlertController(title: "How-to use Holiday List", message: "message here", preferredStyle: .Alert)
//Create and add the Cancel action
//Create and an option action
let nextAction: UIAlertAction = UIAlertAction(title: "OK", style: .Default) { action -> Void in
}
actionSheetController.addAction(nextAction)
//Add a text field
//Present the AlertController
self.presentViewController(actionSheetController, animated: true, completion: nil)
disalert = false
}
The alert is not presented whilst the app is open. When I restart the phone or quit the app completely its again there.
Thank you!
If I am reading your question properly, my suggestion would be to user NSUserDefaults to save a key when the user first opens the view. Then just use an IF statement to decide whether an alertView should be displayed.
Before showing the alert, wherever you want to show it, check the value against the "disalert" key in your userDefaults with this statement:
var disalert: Bool = NSUserDefaults.standardUserDefaults.boolForKey("disalert");
if disalert {
// The alert has already been shown so no need to show it again
}
else
{
// The alert hasn't been shown yet. Show it now and save in the userDefaults
// After showing the alert write this line of code
NSUserDefaults.standardUserDefaults.setBool(true, forKey: "disalert")
}
Adeel's code worked for me, with a slight improvement:
var disalert: Bool =
NSUserDefaults.standardUserDefaults().boolForKey("disalert");
if disalert {
// The alert has already been shown so no need to show it again
}
else
{
// The alert hasn't been shown yet. Show it now and save in the userDefaults
// After showing the alert write this line of code
NSUserDefaults.standardUserDefaults.setBool(true, forKey: "disalert")
}
NSUserDefaults cried for the following: NSUserDefaults.standardUserDefaults()

Facebook iOS SDK session state closing randomly after uploaded to iTunes connect

I am only experiencing this issue after uploaded test builds to iTunes Connect, but not on any of my development builds.
Are there any "gotchas" that could be causing things to behave differently when the code itself is identical between the development and iTunes Connect builds?
What happens is the user presumably silently logs into Facebook fine, but then upon certain calls to our API, which only pass our API Token and do nothing with Facebook, the app then hits this line (couldn't use debugger, so had to use the alert to show the problem for iTunesConnect builds):
//let alert = UIAlertView(title: "Setup Guest Acct", message: "#3", delegate: nil, cancelButtonTitle: "Okay")
//alert.show()
which means what the session state became closed. So I then sign them into one of our own guest accounts. Is this behavior normal, should I try reopening the Facebook session, or something else? Any help would be greatly appreciated!
Here is the relevant Facebook code:
func applicationDidBecomeActive(application: UIApplication!) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
FBAppCall.handleDidBecomeActive()
println("applicationDidBecomeActive")
}
...
func setupAppUserManagerBySigningInWithLoginFlow() {
// Whenever a person opens the app, check for a cached session
if FBSession.activeSession().state == FBSessionState.CreatedTokenLoaded {
// If there IS one, just open the session silently, w/o showing the user the login UI
println("Cached facebook token found")
FBSession.openActiveSessionWithReadPermissions(facebookSessionPermissions, allowLoginUI: false,
completionHandler: { session, state, error in
// Handler for session state changes
// This method will be called EACH time the session state changes,
// also for intermediate states and NOT just when the session open
self.sessionStateChanged(session, state: state, error: error)
}
)
} else {
// If no facebook token, check keychain info for user
println("Cached facebook token unavailable, check keychain for user")
var userID: Int? = SSKeychain.passwordForService(service, account: userIDKeychainKey) != nil ? SSKeychain.passwordForService(service, account: userIDKeychainKey).toInt() : nil
var userAPIToken: String? = SSKeychain.passwordForService(service, account: userAccessTokenKeychainKey)
var userGuestBool: String? = SSKeychain.passwordForService(service, account: userGuestBoolKeychainKey)
if userID == nil || userAPIToken == nil || userGuestBool == nil {
// If no user, then request a guest user to be created, set it up, save in keychain, save to UserManager
println("user NOT found in keychain")
//let alert = UIAlertView(title: "Setup Guest Acct", message: "#1", delegate: nil, cancelButtonTitle: "Okay")
//alert.show()
UserManager.sharedUser.setupGuestAccount()
} else {
// Got user from keychain
println("user WAS found in keychain")
if userGuestBool == userGuestBoolKeychainValueIsGuest {
// Sign them into the guest account
println("keychain user is guest")
UserManager.sharedUser.signIntoGuestAccount(userID!, userAccessToken: userAPIToken!)
} else if userGuestBool == userGuestBoolKeychainValueIsNotGuest {
// Show them the Facebook login UI and give them chance to sign in
println("keychain user is NOT guest")
FBSession.openActiveSessionWithReadPermissions(facebookSessionPermissions, allowLoginUI: true,
completionHandler: { session, state, error in
// Handler for session state changes
// This method will be called EACH time the session state changes,
// also for intermediate states and NOT just when the session open
self.sessionStateChanged(session, state: state, error: error)
}
)
} else {
println("ERROR: invalid guest bool keychain value")
}
}
}
}
...
// MARK: Facebook SDK related code
func sessionStateChanged(session: FBSession, state: FBSessionState, error: NSError!) {
// Handles ALL the session state changes in the app
// Handle the session state
// Usually the only interesting states are opened session, closed session, and failed login
if error == nil && state == FBSessionState.Open { //FBSessionStateOpen {
println("Facebook session state change: Open")
let accessTokenData = session.accessTokenData
let userFBAccessToken = accessTokenData.accessToken
println("Facebook session access token:\(userFBAccessToken)")
var userID: Int? = SSKeychain.passwordForService(service, account: userIDKeychainKey) != nil ? SSKeychain.passwordForService(service, account: userIDKeychainKey).toInt() : nil
var userAPIToken: String? = SSKeychain.passwordForService(service, account: userAccessTokenKeychainKey)
if userID != nil && userAPIToken != nil {
println("Found userID and userAPIToken from keychain, sign in with Facebook account")
println("userID from keychain:\(userID)")
println("userAPIToken from keychain:\(userAPIToken)")
println("userfbAuthToken:\(userFBAccessToken)")
if userFBAccessToken != nil {
UserManager.sharedUser.signIntoFullAccount(userID!, userAccessToken: userAPIToken!, fbAuthToken: userFBAccessToken)
} else {
// Reset all data and let user know to sign back into facebook
// The Facebook SDK session state will change to closed / login failed, and will be handled accordingly
/*
UserManager.sharedUser.deleteAllSavedUserInformation(
completion: {
let alert = UIAlertView(title: "Facebook Information Expired", message: "The Facebook login information has expired. Please restart the app and sign in again. The temporary new guest account that has been provided does not have any information from the Facebook verified account", delegate: nil, cancelButtonTitle: "Ok")
alert.show()
}
)*/
// Try to have them sign back into Facebook
FBSession.openActiveSessionWithReadPermissions(facebookSessionPermissions, allowLoginUI: true,
completionHandler: { session, state, error in
// Handler for session state changes
// This method will be called EACH time the session state changes,
// also for intermediate states and NOT just when the session open
self.sessionStateChanged(session, state: state, error: error)
}
)
}
} else {
println("UserID and userAPIToken NOT found from keychain, setup guest user")
//let alert = UIAlertView(title: "Setup Guest Acct", message: "#2", delegate: nil, cancelButtonTitle: "Okay")
//alert.show()
UserManager.sharedUser.setupGuestAccount()
}
} else if (state == FBSessionState.Closed) || (state == FBSessionState.ClosedLoginFailed) {
// was using FBSessionStateClosed and FBSessionStateClosedLoginFailed until using forked facebook iOS SDK
// If the session is closed, delete all old info and setup a guest account if the user had a full account
println("Facebook session state change: Closed/Login Failed")
if UserManager.sharedUser.guest != nil && UserManager.sharedUser.guest == false {
println("User was NOT a guest; delete all their saved info & clear facebook token, setup new guest account")
UserManager.sharedUser.deleteAllSavedUserInformation(
completion: {
//let alert = UIAlertView(title: "Setup Guest Acct", message: "#3", delegate: nil, cancelButtonTitle: "Okay")
//alert.show()
UserManager.sharedUser.setupGuestAccount()
}
)
} else {
// If user was a guest (could only occur during sign in...?) then just delete the facebook info
println("User WAS a guest; clear facebook token")
FBSession.activeSession().closeAndClearTokenInformation()
// Make sure splash screen would get closed at this point in the Login Flow
NSNotificationCenter.defaultCenter().postNotificationName(FinishedLoginFlowNotification, object: nil)
}
} else if (error != nil) {
println("Facebook session state change: Error")
// Make sure splash screen would get closed at this point in the Login Flow
NSNotificationCenter.defaultCenter().postNotificationName(FinishedLoginFlowNotification, object: nil)
var alertText: String?
var alertTitle: String?
// If the error requires people using an app to make an action outside of the app in order to recover
if FBErrorUtility.shouldNotifyUserForError(error) == true {
alertTitle = "Session Error"
alertText = FBErrorUtility.userMessageForError(error)
let alert = UIAlertView(title: alertTitle, message: alertText, delegate: nil, cancelButtonTitle: "Ok")
alert.show()
} else {
// If the user cancelled login, do nothing
if FBErrorUtility.errorCategoryForError(error) == FBErrorCategory.UserCancelled {
println("User cancelled login")
} else if FBErrorUtility.errorCategoryForError(error) == FBErrorCategory.AuthenticationReopenSession {
// If session closure outside of the app happened
alertTitle = "Session Error"
alertText = "Your Facebook current session is no longer valid. Please log in again."
let alert = UIAlertView(title: alertTitle, message: alertText, delegate: nil, cancelButtonTitle: "Ok")
alert.show()
} else {
// All other errors handled with generic message
// Get more info from the error
// TODO: figure out a way around this issue
/*
let errorMessageObject: AnyObject? = error.userInfo["com.facebook.sdk:ParsedJSONResponseKey"]?["body"]?["error"]?["message"]
if let errorMessage = errorMessageObject as? String {
alertTitle = "Something went wrong"
alertText = "Please retry. If the problem persists contact us and mention this error code: \(errorMessage)"
let alert = UIAlertView(title: alertTitle, message: alertText, delegate: nil, cancelButtonTitle: "Ok")
alert.show()
}
*/
}
}
// Clear the token for all errors
FBSession.activeSession().closeAndClearTokenInformation()
// Show the user the logged out UI
}
NSNotificationCenter.defaultCenter().postNotificationName(FacebookSessionChangeNotification, object: nil)
}
// Manages results of all the actions taken outside the app (successful login/auth or cancellation)
func application(application: UIApplication!, openURL url: NSURL!, sourceApplication: String!, annotation: AnyObject!) -> Bool {
return FBAppCall.handleOpenURL(url, sourceApplication: sourceApplication)
}
}

Resources