How to filter array from selected tableviewcell in swift? - ios

I am quite puzzled on how will I construct my codes regarding on how I will filter the selected array from a tableviewcell. The JSON below is the content of the tableview which displays like
[
{
"hospitalNumber": "00000001",
"patientName": "Test Patient",
"totalAmount": 1111.3
},
{
"hospitalNumber": "00000002",
"patientName": "Test Patient 2",
"totalAmount": 1312
},
{
"hospitalNumber": "00000003",
"patientName": "Test Patient 3",
"totalAmount": 475
}
]
The problem is how can I display the selected hospitalNumber and patientName in the next View Controller, which will display like
This is what my `PaymentDetailsViewController' have:
var patientList: [Patient]! {
didSet {
latestCreditedAmountTableView.reloadData()
}
}
override func viewDidLoad() {
super.viewDidLoad()
getPatientList()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showPatientPaymentDetailsVC" {
if let patientPaymentDetailsVC = segue.destination as? PatientPaymentDetailsViewController {
patientPaymentDetailsVC.isBrowseAll = self.isBrowseAll
if !isBrowseAll {
patientPaymentDetailsVC.patientPayoutDetails = self.selectedPatientPayment
patientPaymentDetailsVC.currentRemittance = self.currentRemittance
patientPaymentDetailsVC.doctorNumber = self.doctorNumber
}
}
}
}
func getPatientList() {
SVProgressHUD.setDefaultMaskType(.black)
SVProgressHUD.show(withStatus: "Retrieving Patient List")
APIService.PatientList.getPatientList(doctorNumber: doctorNumber, periodId: currentRemittance.periodId) { (patientListArray, error) in
guard let patientListPerPayout = patientListArray, error == nil else {
if let networkError = error {
switch networkError {
case .noRecordFound:
let alertController = UIAlertController(title: "No Record Found", message: "You don't have current payment remittance", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default))
self.present(alertController, animated: true, completion: nil)
case .noNetwork:
let alertController = UIAlertController(title: "No Network", message: "\(networkError.rawValue)", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default))
self.present(alertController, animated: true, completion: nil)
default:
let alertController = UIAlertController(title: "Error", message: "There is something went wrong. Please try again", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default))
self.present(alertController, animated: true, completion: nil)
}
}
SVProgressHUD.dismiss()
return
}
self.patientList = patientListPerPayout
self.latestCreditedAmountTableView.reloadData()
SVProgressHUD.dismiss()
return
}
}
**getPerPatientPAyoutDetails(from: String) function**
func getPerPatientPayoutDetails(from: String) {
SVProgressHUD.setDefaultMaskType(.black)
SVProgressHUD.showInfo(withStatus: "Retrieving Patient Details")
APIService.PatientList.getPatientDetailsPerPayout(periodId: currentRemittance.periodId, doctorNumber: doctorNumber, parameterName: .selectedByHospitalNumber, hospitalNumber: from) { (patientPayout, error) in
guard let patientPerPayoutDetails = patientPayout, error == nil else {
if let networkError = error {
switch networkError {
case .noRecordFound:
let alertController = UIAlertController(title: "No Record Found", message: "You don't have current payment remittance", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default))
case .noNetwork:
let alertController = UIAlertController(title: "No Network", message: "\(networkError.rawValue)", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default))
self.present(alertController, animated: true, completion: nil)
default:
let alertController = UIAlertController(title: "Error", message: "There is something went wrong. Please try again", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default))
self.present(alertController, animated: true, completion: nil)
}
}
SVProgressHUD.dismiss()
return
}
self.selectedPatientPayment = patientPerPayoutDetails
print(self.selectedPatientPayment)
SVProgressHUD.dismiss()
return
}
}
Base on the gePatientList() function, it will just pull the full list of the patients. I don't know how I will pass the data of the selected patient to another VC. Hope you can help me. Thank you so much.
Codes that might help to understand the flow of my codes
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
switch indexPath.section {
case 0: break
case 1: let selectedpatient = patientList[indexPath.row].hospitalNumber
print(selectedpatient!)
self.isBrowseAll = false
getPerPatientPayoutDetails(from: selectedpatient!)
default: break
}
}
Below is the another View Controller that will display the patientName and hospitalNumber
PatientPaymentDetailsVC
class PatientPaymentDetailsViewController: UIViewController {
#IBOutlet weak var patientProcedureTableView: UITableView!
#IBOutlet weak var hospitalNumberLabel: UILabel!
#IBOutlet weak var patientNameLabel: UILabel!
var currentRemittance: CurrentRemittance!
var doctorNumber: String!
var isBrowseAll: Bool!
var patientList: [Patient]!
var patientPayoutDetails: [PatientPayoutDetails]!
override func viewDidLoad() {
super.viewDidLoad()
setupPatientInfo()
}
//MARK: FUNCTION
func setupPatientInfo() {
self.patientNameLabel.text = patient.patientName
self.hospitalNumberLabel.text = patient.hospitalNumber
}
The pulled data under the getPerPatientPayoutDetails function from the didselect will be displayed in PatientPaymentDetailsVC. Below is the output, as you can I see I can pull the data under getPerPatientPayoutDetails but the patientName and hospitalNumber does not display the data.

First of all don't get the data from the table view cell, get it from the data source
Connect the segue to the cell.
Delete the entire method didSelectRowAt
When prepare(for segue is called the sender parameter is the cell.
Get the index path from the cell and get the patient at that index path.
Rather than passing multiple parameters declare a var patient : Patient! property in the destination controller and hand over the patient instance.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard segue.identifier == "showPatientPaymentDetailsVC",
let cell = sender as? UITableViewCell,
let indexPath = tableView.indexPath(for: cell) else { return }
let patient = patientList[indexPath.row]
getPerPatientPayoutDetails(from: patient.hospitalNumber)
let patientPaymentDetailsVC = segue.destination as! PatientPaymentDetailsViewController
patientPaymentDetailsVC.patient = patient
patientPaymentDetailsVC.patientPayoutDetails = self.selectedPatientPayment
patientPaymentDetailsVC.currentRemittance = self.currentRemittance
patientPaymentDetailsVC.doctorNumber = self.doctorNumber
}
class PatientPaymentDetailsViewController: UIViewController {
#IBOutlet weak var patientProcedureTableView: UITableView!
#IBOutlet weak var hospitalNumberLabel: UILabel!
#IBOutlet weak var patientNameLabel: UILabel!
var currentRemittance: CurrentRemittance!
var doctorNumber = ""
var isBrowseAll = false
var patient : Patient!
var patientPayoutDetails: [PatientPayoutDetails]!
override func viewDidLoad() {
super.viewDidLoad()
setupPatientInfo()
}
//MARK: FUNCTION
func setupPatientInfo() {
self.patientNameLabel.text = patient.patientName
self.hospitalNumberLabel.text = patient.hospitalNumber
}
Side note:
Don't declare patientList as implicit unwrapped optional, declare it as non-optional empty array
var patientList : [Patient]()

Use tableView(_:didSelectRowAt:) method by conforming to UITableViewDelegate.
Get the selected patient as displayed below:
selectedPatient = tableView[indexpath.row] as! [String:Any]
As per your edited question, try this:
let patient = patientList[indexPath.row] as! Patient

Related

Validating user swift 4, login page

I need to my button proceedToAppFeatures to run the code and display an alert of success or failure login(just a single user, for now)
I have tried self.showAlert and self.present
Code:
import UIKit
import FLAnimatedImage
import ImageIO
import UserNotifications//import this to use user notifications
import Firebase
class ViewController: UIViewController, UITextFieldDelegate{
#IBOutlet weak var background: FLAnimatedImageView!
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
#IBOutlet weak var lblValidationTxt: UILabel!
#IBOutlet weak var lblValidationMessage: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
background.loadGif(name: "neon lights")
passwordTextField.isSecureTextEntry = true// the password will no be visible
emailTextField.delegate = self
passwordTextField.delegate = self
let Tap:UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(DismissKeyboard))//here i have used the function to didmiss the keyboard
view.addGestureRecognizer(Tap)
let center = UNUserNotificationCenter.current()
//this initialize the permission that will appear on user screen
center.requestAuthorization(options: [.alert, .sound, .badge]) {
(granted,error) in
//enable or disable features based on the authorization
}
// emailTextField.text = ""
// passwordTextField.text = ""//on view did load to store text field property
}
#objc func DismissKeyboard() {//function that implements the type outside the screen
view.endEditing(true)
}
#IBAction func sigInButton(_ sender: UIButton) {
performSegue(withIdentifier: "userData", sender: true)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if textField == emailTextField {
textField.resignFirstResponder()
}else{
passwordTextField.becomeFirstResponder()//just to decide which of the buttons will respond after a tab is pressed
passwordTextField.resignFirstResponder()
}
return true
}
func showAlert(title: String, message: String){
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: {(action) in alert.dismiss(animated: true, completion: nil)}))
self.present(alert, animated: true, completion: nil)
}
func isValidEmail(testStr:String) -> Bool {
let emailRegEx = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
let emailTest = NSPredicate(format:"SELF MATCHES %#", emailRegEx)
return emailTest.evaluate(with: testStr)
}
#IBAction func proceedToAppFeatures(_ sender: Any) {
let email = isValidEmail(testStr: emailTextField.text!)
if email == false{
self.showAlert(title: "ERROR", message: "This is not a valid e-mail. Please try again")
emailTextField.text = ""
}else{
self.showAlert(title: "Success", message: "Yousucessifully loged in")
//performSegue(withIdentifier: "formPage", sender: true)
}
let myEmail: String!
myEmail = emailTextField.text!
let myPassword: String!
myPassword = passwordTextField.text!
if(myEmail == "leandroaaramos" && myPassword == "123456"){
//performSegue(withIdentifier: "homeSegue", sender: nil)
//code for the truth
print("You are success")
}else if (myEmail.isEmpty && myPassword.isEmpty){
//here i put the alert message
let alert = UIAlertController(title: "Wow", message: "Hold on fill the gaps", preferredStyle: UIAlertController.Style.alert)
//add an action to the alert
alert.addAction(UIAlertAction(title: "Close", style: UIAlertAction.Style.default, handler: nil))
self.present(alert, animated: true, completion: nil)
self.showAlert(title: "Empty field", message: "Try again")
}else {
//here i put the alert message
let alert = UIAlertController(title: "Ops", message: "Your account is not here", preferredStyle: UIAlertController.Style.alert)
//add an action to the alert
alert.addAction(UIAlertAction(title: "Try Again", style: UIAlertAction.Style.default, handler: nil))
self.present(alert, animated: true, completion: nil)
self.showAlert(title: "Sorry", message: "Can't find you.")
}
}
}
I want go to my loginViewController, if the user leandroaaramos and password 123456 was typed.

Parsing JSON Tableview Information as Separate Strings

I am currently trying to use information in Tableview cells that I have populated with JSON to execute various operations but I am unable to call the specific information due to the fact that it isn't in individual strings. Is there any way to take the group of data I have pulled into each tableview cell and turn it into a series of individual strings? Here is what I currently have:
import UIKit
import GoogleMobileAds
class OngoingViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var userUsernameLabel: UILabel!
#IBOutlet weak var bannerView: GADBannerView!
#IBOutlet weak var ongoingTable: UITableView!
var list:[MyStruct] = [MyStruct]()
struct MyStruct
{
var user1 = ""
var user2 = ""
var wager = ""
var amount = ""
init(_ user1:String, _ user2:String, _ wager:String, _ amount:String)
{
self.user1 = user1
self.user2 = user2
self.wager = wager
self.amount = amount
}
}
override func viewDidLoad() {
super.viewDidLoad()
let username = UserDefaults.standard.string(forKey: "userUsername")!
userUsernameLabel.text = username
/// The banner view.
print("Google Mobile Ads SDK version: " + GADRequest.sdkVersion())
bannerView.adUnitID = "ca-app-pub-3940256099942544/2934735716"
bannerView.rootViewController = self
bannerView.load(GADRequest())
ongoingTable.dataSource = self
ongoingTable.delegate = self
get_data("http://cgi.soic.indiana.edu/~team10/ongoingWagers.php")
}
func get_data(_ link:String)
{
let url:URL = URL(string: link)!
var request = URLRequest(url:url);
request.httpMethod = "POST";
let postString = "a=\(userUsernameLabel.text!)";
request.httpBody = postString.data(using: String.Encoding.utf8);
let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
self.extract_data(data)
}
task.resume()
}
func extract_data(_ data:Data?)
{
let json:Any?
if(data == nil)
{
return
}
do{
json = try JSONSerialization.jsonObject(with: data!, options: [])
}
catch
{
return
}
guard let data_array = json as? NSArray else
{
return
}
for i in 0 ..< data_array.count
{
if let data_object = data_array[i] as? NSDictionary
{
if let data_user1 = data_object["id"] as? String,
let data_user2 = data_object["id2"] as? String,
let data_wager = data_object["wager"] as? String,
let data_amount = data_object["amount"] as? String
{
list.append(MyStruct(data_user1, data_user2, data_wager, data_amount))
}
}
}
refresh_now()
}
func refresh_now()
{
DispatchQueue.main.async(
execute:
{
self.ongoingTable.reloadData()
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return list.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.ongoingTable.dequeueReusableCell(withIdentifier: "owcell", for: indexPath) as! OngoingTableViewCell
cell.infoLabel.text = list[indexPath.row].user1 + " " + list[indexPath.row].user2 + " " + list[indexPath.row].wager + " " + list[indexPath.row].amount
cell.user1Button.tag = indexPath.row
cell.user1Button.addTarget(self, action: #selector(OngoingViewController.user1Action), for: .touchUpInside)
cell.user2Button.tag = indexPath.row
cell.user2Button.addTarget(self, action: #selector(OngoingViewController.user2Action), for: .touchUpInside)
return cell
}
#IBAction func user1Action(sender: UIButton) {
let user1Alert = UIAlertController(title: "Wait a second!", message: "Are you sure this user has won this wager?", preferredStyle: UIAlertControllerStyle.alert)
user1Alert.addAction(UIAlertAction(title: "Yes", style: UIAlertActionStyle.default, handler: { action in
let user1ConfirmationAlert = UIAlertController(title: "Great!", message: "Please wait for the other user to confirm the winner of this wager.", preferredStyle: UIAlertControllerStyle.alert)
user1ConfirmationAlert.addAction(UIAlertAction(title: "Got It!", style: UIAlertActionStyle.default, handler: nil))
self.present(user1ConfirmationAlert, animated: true, completion: nil)
}))
user1Alert.addAction(UIAlertAction(title: "No", style: UIAlertActionStyle.default, handler: nil))
self.present(user1Alert, animated: true, completion: nil)
}
#IBAction func user2Action(sender: UIButton) {
let user2Alert = UIAlertController(title: "Wait a second!", message: "Are you sure this user has won this wager?", preferredStyle: UIAlertControllerStyle.alert)
user2Alert.addAction(UIAlertAction(title: "Yes", style: UIAlertActionStyle.default, handler: { action in
let user2ConfirmationAlert = UIAlertController(title: "Great!", message: "Please wait for the other user to confirm the winner of this wager.", preferredStyle: UIAlertControllerStyle.alert)
user2ConfirmationAlert.addAction(UIAlertAction(title: "Got It!", style: UIAlertActionStyle.default, handler: nil))
self.present(user2ConfirmationAlert, animated: true, completion: nil)
}))
user2Alert.addAction(UIAlertAction(title: "No", style: UIAlertActionStyle.default, handler: nil))
self.present(user2Alert, animated: true, completion: nil)
}
}
Here is the OngoingTableViewCell subclass:
import UIKit
class OngoingTableViewCell: UITableViewCell {
#IBOutlet weak var infoLabel: UILabel!
#IBOutlet weak var user1Button: UIButton!
#IBOutlet weak var user2Button: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
You have an array of MyStruct structures, that contain entries for user1, user1, wager, and amount. That's good.
You're using tags on the buttons as a way of figuring out the selected cell, which is not ideal. Instead I would suggest using the sender parameter to figure out the indexPath of the cell that contains the button. See the bottom of my answer for the details of a better way to do it.
In any case, once you have a row number, you can easily get to the data for that wager by indexing into your array:
#IBAction func user1Action(sender: UIButton) {
selectedRow = sender.tag
//or, using my function:
selectedRow = tableView.indexPathForView(sender)
//Get the wager for the button the user tapped on.
let thisWager = list[selectedRow]
}
If you want to take action on the wager once the user taps a button in your UIAlertController, don't use a nil handler on the your second alert controller. Instead, pass in a closure that uses the selectedRow parameter from the code above to index into the list of wagers, or even use the thisWager local variable I show in my code.
Getting the indexPath of the button the user taps on:
I created a simple extension to UITableView that lets you pass in a UIView (like the sender from a button action) and get back the indexPath that contains that view.
That extension is dirt-simple. Here's how it looks:
public extension UITableView {
/**
This method returns the indexPath of the cell that contains the specified view
- Parameter view: The view to find.
- Returns: The indexPath of the cell containing the view, or nil if it can't be found
*/
func indexPathForView(_ view: UIView) -> IndexPath? {
let origin = view.bounds.origin
let viewOrigin = self.convert(origin, from: view)
let indexPath = self.indexPathForRow(at: viewOrigin)
return indexPath
}
}
And you can call that function from your button's actions:
#IBAction func buttonTapped(_ button: UIButton) {
if let indexPath = self.tableView.indexPathForView(button) {
print("Button tapped at indexPath \(indexPath)")
}
else {
print("Button indexPath not found")
}
}
The whole project can be found on Github at this link: TableViewExtension

Why is my string becoming nil when used as the title of an action sheet?

In an app I am currently writing, I have a string named 'User' which stores the user's name for a game. The value of the string, when printed anywhere else in the Swift file, prints the value that I have set, as an optional.
If I try to use this string as the title of an action sheet action, the string is automatically set to nil, which I can see as both the title of the action and which is printed when I ask it to print(user).
If anyone could shed some light as to why this is happening, or how to prevent it, that would be great. I have also posted my Swift file below, thanks.
import UIKit
class MainViewController: UIViewController {
#IBOutlet weak var segmentedControl: UISegmentedControl!
#IBOutlet weak var firstView: UIView!
#IBOutlet weak var secondView: UIView!
var user:String!
var playerTwo:String!
var playerThree:String!
var playerFour:String!
var playerFive:String!
var playerSix:String!
var userCards = [String]()
override func viewDidLoad() {
super.viewDidLoad()
firstView?.isHidden = false
secondView?.isHidden = true
}
#IBAction func valueDidChange(_ sender: UISegmentedControl) {
switch segmentedControl.selectedSegmentIndex {
case 0:
firstView.isHidden = false
secondView.isHidden = true
case 1:
firstView.isHidden = true
secondView.isHidden = false
default:
break;
}
}
#IBAction func confirm(_ sender: UIButton) {
let alertController = UIAlertController(title: "Action Sheet", message: "What would you like to do?", preferredStyle: .actionSheet)
let userButton = UIAlertAction(title: user /* Here I have tried with putting both 'user', and "\(user)"*/, style: .default, handler: { (action) -> Void in
print("User button tapped")
})
let deleteButton = UIAlertAction(title: "Delete button test", style: .destructive, handler: { (action) -> Void in
print("Delete button tapped")
})
let cancelButton = UIAlertAction(title: "Cancel", style: .cancel, handler: { (action) -> Void in
print("Cancel button tapped")
})
alertController.addAction(userButton)
alertController.addAction(deleteButton)
alertController.addAction(cancelButton)
self.present(alertController, animated: true, completion: nil)
}
}
The value is passed into the above file directly from this code in another file:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showMainController" {
let VC = segue.destination as! MainViewController
VC.user = self.user
if playerTwo != nil {
VC.playerTwo = self.playerTwo
}
if playerThree != nil {
VC.playerThree = self.playerThree
}
if playerFour != nil {
VC.playerFour = self.playerFour
}
if playerFive != nil {
VC.playerFive = self.playerFive
}
if playerSix != nil {
VC.playerSix = self.playerSix
}
}
}
The value is, however, passed through several view controllers, and is initially set here:
if (meTextField.text?.isEmpty)! == false {
let p1 = meTextField.text!
initialPlayersDict["player1"] = "\(p1)"
if errLabelNotBlank {
errorLabel.text = ""
errLabelNotBlank = false
}
}

UIAlertController Not Within The Window Hierarchy

First of all please do not mark duplicate on this question Ive done my research on this topic, and not only have the most recent answers been from over a year ago, but they have also been in C#. Mine also differs from those because I am trying to present my UIView from what I assume to be a child of a child view. But I'm not 100% sure about this. So, here is what my code dump looks like after the suggestions.
import UIKit
import Firebase
class LoginViewController: UIViewController, UITextFieldDelegate{
#IBOutlet weak var usernameTxt: UITextField!
#IBOutlet weak var emailTxt: UITextField!
#IBOutlet weak var passwordTxt: UITextField!
#IBOutlet weak var confirmPassTxt: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func nextScreen(sender: UIButton) {
if(emailTxt.text == "" || passwordTxt.text == "" || confirmPassTxt.text == "" || usernameTxt.text == ""){
let alertController = UIAlertController(title: "Wait!", message: "You didn't fill out the required fields, please do so and try again. ", preferredStyle: .Alert)
let defaultAction = UIKit.UIAlertAction(title: "OK", style: .Default, handler: nil)
alertController.addAction(defaultAction)
self.presentViewController(alertController, animated: true, completion: nil)
}else{
if(validateEmail(emailTxt.text!)){
emailCheck(emailTxt.text!){isValid in if isValid{self.secondRound({ (goodMail, goodPassL, goodPassTxt, nameGood) in
if (goodMail && goodPassL && goodPassTxt && !nameGood){
print("good")
}else{
self.showAlert("Else", description: "Got it?") }
})}else{let alertController=UIAlertController(title: "Whoops!", message: "That email address has already been taken, please try another one", preferredStyle: .Alert)
let defaultAction = UIKit.UIAlertAction(title: "OK", style: .Default, handler: nil)
alertController.addAction(defaultAction)
alertController.parentViewController
self.presentViewController(alertController, animated: true, completion: nil)
}}
}else{
let alertController = UIAlertController(title: "Whoops!", message: "That doesnt appear to be a valid email address, please check your information and try again!", preferredStyle: .Alert)
let defaultAction = UIKit.UIAlertAction(title: "OK", style: .Default, handler: nil)
alertController.addAction(defaultAction)
alertController.parentViewController
presentViewController(alertController, animated: true, completion: nil)
}
}
}
func showAlert(title: String, description: String){
let alertController: UIAlertController = UIAlertController(title: title, message: description, preferredStyle: .Alert)
let defaultAction = UIKit.UIAlertAction(title: "OK", style: .Default, handler: nil)
alertController.addAction(defaultAction)
UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(alertController, animated: true, completion: nil)
self.presentViewController(alertController, animated: true, completion: nil)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let DestinationVC : Login2VC = segue.destinationViewController as! Login2VC
DestinationVC.prepareEmail = emailTxt.text!
DestinationVC.preparePass = passwordTxt.text!
}
func validateEmail(canidate: String) -> Bool {
let emailRegex = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}"
return NSPredicate(format: "SELF MATCHES %#", emailRegex).evaluateWithObject(canidate)
}
func nameFilter(input : String)-> Bool{
var profanity : Bool = true
let dataRef = FIRDatabase.database().reference()
dataRef.child("Profanity").observeSingleEventOfType(.Value) { (snap: FIRDataSnapshot) in
if(snap.exists()){
if(snap.value! as! NSArray).containsObject(input){
print("our ears!")
profanity = true
}else{
profanity = false
}
}
}
return profanity
}
func emailCheck(input: String, callback: (isValid: Bool) -> Void) {
FIRAuth.auth()?.signInWithEmail(input, password: " ") { (user, error) in
var canRegister = false
if error != nil {
if (error?.code == 17009) {
canRegister = false
} else if(error?.code == 17011) {
//email doesn't exist
canRegister = true
}
}
callback(isValid: canRegister)
}
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
func secondRound(callback:(goodMail:Bool, goodPassL:Bool, goodPassTxt:Bool, nameGood:Bool)->Void){
let availableEmail : Bool = true
var passwordMatch : Bool = false
var passwordLength : Bool = false
var profanity : Bool = false
if(passwordTxt.text!==confirmPassTxt.text!){passwordMatch=true}else{passwordMatch=false}
if(passwordTxt.text!.characters.count>=6&&confirmPassTxt.text!.characters.count>=6){passwordLength=true}else{passwordLength=false}
if(nameFilter(usernameTxt.text!)){profanity=true}else{profanity=false}
callback(goodMail: availableEmail, goodPassL: passwordLength, goodPassTxt: passwordMatch, nameGood: profanity)
}
}
Essentially, what I am trying to do is:
Check to see if the inputted text is formatted as an email correctly
Check to see if the name is available
Check if the username contains profanity (pulled as a json from firebase)
Check to see if the passwords match
Check too see if the passwords are at least 6 characters in length
Each false result would have its own UIAlertView that results from it, but whenever I try to add these views they do not show up, and the app returns with this error.
Note, the false event does intact appear in the first condition only. Other than that, nothing happens.
This is the error I have been getting, and while it seems pretty straight forward I cannot figure out hoe to fix it, nor find any recent information online on how to accomplish this task.
> Warning: Attempt to present <UIAlertController: 0x7fb513fb8bc0> on
> <myApp.LoginViewController: 0x7fb513c6e0a0> whose view is not in the
> window hierarchy!
The logical answer to this would be:
Hey, why don't you just add the window to the hierarchy and be done?
Well, to that question I have a question, if the window is not apart of the hierarchy, then why is the view itself even displaying. Furthermore, why would the first set of UIAlerts be displaying, but as soon as I get into a nested if they cease? Any ideas on how to fix this terrible error?
Thanks all

Don't understand why I am getting Parse 206 error (UserCannotBeAlteredWithoutSessionError)

PFCloud.callFunctionInBackground("updatePositions", withParameters: ["username" : username, "location" : locationTitle, "instructor" : instructorSwitch.on, "guard" : guardSwitch.on, "sup" : supSwitch.on]) {
(positions: AnyObject!, error: NSError!) -> Void in
if error == nil {
self.currentUser.setValue(self.instructorSwitch.on, forKey: (self.shortTitle + "Instructor"))
self.currentUser.setValue(self.guardSwitch.on, forKey: (self.shortTitle + "Guard"))
self.currentUser.setValue(self.supSwitch.on, forKey: (self.shortTitle + "Sup"))
self.currentUser.save(nil)
self.navigationController?.popToRootViewControllerAnimated(true)
}
else {
let errorAlert = UIAlertController (title: "Error", message: "There was an error while processing your request. This may be because the app could not connect to the internet. Please try again.", preferredStyle: UIAlertControllerStyle.Alert)
let actionCancel = UIAlertAction (title: "Dismiss", style: .Cancel, handler: nil)
errorAlert.addAction(actionCancel)
self.presentViewController(errorAlert, animated: true, completion: nil)
}
When I run the above code, my goal to is to update the Parse Objects in my Data Browser and at the same time edit the same objects in currentUser.
As it stands now, the Login VC is shown if no one is logged in (based on a boolean value in Core Data). When a user logs in they are taken to the apps Home Page (and the boolean value is changed). If they were to shutdown the app and restart, they would be taken to the Home Page rather than the Login VC. I have a "dummy" VC where the Core Data entity is updated (I know it sounds redundant but it's there for a purpose that is unrelated to my issue).
Login VC
#IBOutlet var fullName: UITextField!
#IBOutlet var password: UITextField!
#IBOutlet var helpButton: UIButton!
#IBOutlet var loginButton: UIButton!
var nameID: String!
var passwordStatus: Bool?
var username: String?
var userID: String?
var objectID: String!
var currentUser = PFUser.currentUser()
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.hidesBackButton = true
checkInternetConnection()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
self.view.endEditing(true)
}
#IBAction func LoginTapped(sender: AnyObject) {
if (fullName.text.isEmpty || password.text.isEmpty) {
let alert = UIAlertView()
alert.title = "Error"
alert.message = "You have not completed the required fields!"
alert.addButtonWithTitle("Dismiss")
alert.show()
}
else {
PFUser.logInWithUsernameInBackground(fullName.text, password: password.text) {
(user: PFUser!, error: NSError!) -> Void in
if (user != nil) {
self.currentUser = user
self.currentUser.save()
//println(self.currentUser.objectForKey("username") as String!)
if (self.currentUser.objectForKey("passwordChanged") as Bool! == nil || self.currentUser.objectForKey("passwordChanged") as Bool! == false) {
self.performSegueWithIdentifier("password", sender: self)
}
else {
self.performSegueWithIdentifier("skipPassword", sender: self)
}
}
else {
let errorAlert = UIAlertController (title: "Error", message: "Profile not found. Please check your username and password.", preferredStyle: UIAlertControllerStyle.Alert)
let actionCancel = UIAlertAction (title: "Dismiss", style: .Cancel, handler: nil)
errorAlert.addAction(actionCancel)
self.presentViewController(errorAlert, animated: true, completion: nil)
}
}
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if (segue.identifier == "password") {
var svc = segue.destinationViewController as AccountSetup
svc.username = fullName.text
}
else if (segue.identifier == "skipPassword") {
var svc = segue.destinationViewController as Agree
svc.password = password.text
}
}
The only place that there is a chance for the user to log out is in the app's Settings controller:
Settings VC
var choice: String!
var currentEmail: String!
var sendEmail: String!
var email: UITextField!
var listArray = NSArray (objects: "-", "-", "-", "1", "2", "3", "4", "5", "6")
var currentUser = PFUser.currentUser()
var res : NSManagedObject!
var context : NSManagedObjectContext!
override func viewDidLoad() {
super.viewDidLoad()
currentEmail = currentUser.objectForKey("email") as String!
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 4
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
switch(section) {
case 0: return 2
case 1: return 9
case 2: return 4
case 3: return 1
default: fatalError("Unknown number of sections")
}
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
choice = listArray.objectAtIndex(indexPath.row) as NSString
if (indexPath.row >= 3) {
performSegueWithIdentifier("positions", sender: self)
}
else if (indexPath.row == 1) {
var passwordAlert = UIAlertController (title: "Change Password", message: "For security reasons, you can not change your password through the app. We will send an email to " + currentEmail + ". If you rather us send it to another email, enter it below. Otherwise, click 'Send'.", preferredStyle: UIAlertControllerStyle.Alert)
let standardCancel = UIAlertAction (title: "Dismiss", style: .Cancel, handler: nil)
let actionSubmit = UIAlertAction (title: "Send", style: .Default) { (action) in
if (self.email.text.isEmpty) {
self.sendEmail = self.currentEmail
}
else {
self.sendEmail = self.email.text
}
PFUser.requestPasswordResetForEmailInBackground(self.sendEmail){
(success: Bool!, error: NSError!) -> Void in
if (success == true) {
let emailAlert = UIAlertController (title: "Password", message: "An email containing information on how to change your password has been sent to " + self.sendEmail + ".", preferredStyle: UIAlertControllerStyle.Alert)
emailAlert.addAction(standardCancel)
self.presentViewController(emailAlert, animated: true, completion: nil)
}
else {
let errorAlert = UIAlertController (title: "Error", message: "There was an error while processing your request. This may be because the email is invalid or the app could not connect to the internet. Please try again.", preferredStyle: UIAlertControllerStyle.Alert)
errorAlert.addAction(standardCancel)
self.presentViewController(errorAlert, animated: true, completion: nil)
}
}
}
let actionCancel = UIAlertAction (title: "Cancel", style: .Cancel, handler: nil)
passwordAlert.addTextFieldWithConfigurationHandler {
(textField) in textField.placeholder = "Email"
self.email = textField
}
passwordAlert.addAction(actionSubmit)
passwordAlert.addAction(actionCancel)
self.presentViewController(passwordAlert, animated: true, completion: nil)
}
}
#IBAction func logoutPressed (sender: AnyObject) {
PFUser.logOut()
//code to change Core Data boolean value to start on Login is here but there is no need to put it (unrelated)
tabBarController?.tabBar.hidden = true
performSegueWithIdentifier("goToLogin", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if (segue.identifier == "positions") {
var svc = segue.destinationViewController as UpdatePositions;
svc.locationTitle = choice
println (choice)
}
}
Like I've mentioned before, everything works up until the app is shutdown where the authentication is lost.
Ends up that there was an error in the 1.6.0 Parse SDK. Works fine with 1.6.5.

Resources