UITextField nil on button click, but works onchange - ios

I'm getting the following error on button click
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
following is the code
#IBOutlet weak var UserId: UITextField!{
didSet{
UserId.setBottomBorder()
UserId.delegate = self
}
}
#IBOutlet weak var Password: UITextField!{
didSet{
Password.setBottomBorder()
Password.delegate = self
}
}
#IBAction func logincta(_ sender: Any) {
guard let _ = UserId.text, UserId.text?.characters.count != 0 else {
print("test")
return
}
}
but works fine in the following onchange code
#IBAction func UserIdChanged(_ sender: Any) {
if UserId.text == "" {
UserId.setBottomBorder()
}
else{
UserId.setPurpleBottomBorder()
}
}
#IBAction func PasswordChanged(_ sender: Any) {
if Password.text == "" {
Password.setBottomBorder()
}
else{
Password.setPurpleBottomBorder()
}
}
i wonder how it worked in onchange event "if UserId.text == "" but not in button click

I've tried your code and it seems to work...
Anyway maybe try to use the guard in this way:
guard let numOfChars = UserId?.text?.count, numOfChars != 0 else {
print("test")
return
}

Related

Swift - Accessing implicitly unwrapped variable gives a nil error

I'm following a tutorial on CoreData and I've been following it exactly, yet when they run the app, everything works and saves correctly, yet I get a nil error. The tutorial is a few years old, so I'm not sure if something has been udpated in the way CoreData works. It's an app to save goals.
Here's the first view controller where you enter the text of the goal and if it is short or long term:
import UIKit
class CreateGoalViewController: UIViewController, UITextViewDelegate {
#IBOutlet weak var goalTextView: UITextView!
#IBOutlet weak var shortTermButton: UIButton!
#IBOutlet weak var longTermButton: UIButton!
#IBOutlet weak var nextButton: UIButton!
var userGoalType: GoalType = .shortTerm
override func viewDidLoad() {
super.viewDidLoad()
nextButton.bindToKeyboard()
shortTermButton.setSelectedColor()
longTermButton.setDeselectedColor()
print("\(userGoalType)")
goalTextView.delegate = self
}
#IBAction func nextButtonPressed(_ sender: Any) {
if goalTextView.text != "" && goalTextView.text != "What is your goal?" {
guard let finishVC = storyboard?.instantiateViewController(withIdentifier: "FinishVC") as? FinishGoalViewController else {return}
finishVC.initData(description: goalTextView.text!, type: userGoalType)
print("\(finishVC.goalType.rawValue) after next button pressed")
performSegue(withIdentifier: "goToFinish", sender: self)
}
}
#IBAction func longTermButtonPressed(_ sender: Any) {
userGoalType = .longTerm
longTermButton.setSelectedColor()
shortTermButton.setDeselectedColor()
print("\(userGoalType)")
}
#IBAction func shortTermButtonPressed(_ sender: Any) {
userGoalType = .shortTerm
shortTermButton.setSelectedColor()
longTermButton.setDeselectedColor()
print("\(userGoalType)")
}
#IBAction func backButtonPressed(_ sender: Any) {
dismiss(animated: true)
}
func textViewDidBeginEditing(_ textView: UITextView) {
goalTextView.text = ""
goalTextView.textColor = UIColor(ciColor: .black)
}
}
And here's the following view controller where you set the number of times you want to do that goal where the CoreData functions are:
import UIKit
import CoreData
class FinishGoalViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var createButton: UIButton!
#IBOutlet weak var pointsTextField: UITextField!
var goalDescription: String!
var goalType: GoalType!
func initData(description: String, type: GoalType) {
self.goalDescription = description
self.goalType = type
}
override func viewDidLoad() {
super.viewDidLoad()
createButton.bindToKeyboard()
pointsTextField.delegate = self
}
#IBAction func createGoalPressed(_ sender: Any) {
if pointsTextField.text != ""{
self.save { finished in
if finished {
dismiss(animated: true)
}
}
}
}
#IBAction func backButtonPressed(_ sender: Any) {
dismiss(animated: true)
}
func save(completion: (_ finished: Bool) -> ()) {
guard let managedContext = appDelegate?.persistentContainer.viewContext else {return}
let goal = Goal(context: managedContext)
goal.goalDescription = goalDescription
goal.goalType = goalType.rawValue
goal.goalCompletionValue = Int32(pointsTextField.text!)!
goal.goalProgress = Int32(0)
do{
try managedContext.save()
print("successfully saved data")
completion(true)
}catch{
debugPrint("Could not save: \(error.localizedDescription)")
completion(false)
}
}
}
I'm getting a nil error in the save function with the goalType.rawValue turning up nil. The goal type is set up in an enum file:
import Foundation
enum GoalType: String {
case longTerm = "Long Term"
case shortTerm = "Short Term"
}
I'm not sure why there's an error. Because in the CreateGoalViewController, I print the goalType.rawValue from the following view controller and it comes up with the correct string, either short or long-term. But when FinishGoalViewController loads, it is all of a sudden nil.
You are initiating and configuring your FinishGoalViewController in nextButtonPressed but you never use it. performSegue(withIdentifier: "goToFinish", sender: self) will create and push a new instance of FinishGoalViewController.
The most simple aproach would be to push your allready configured controller from your curent Controller. Remove performSegue(... and use.
self.navigationController?.pushViewController(finishVC, animated: true)
If you still want to use the segue, remove everything from the nextButtonPressed function, leaving just the performSegue(... line. After that add this function to your CreateGoalViewController controller.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToFinish" {
if let finishVC = segue.destination as? FinishGoalViewController {
// configure finshVC here
}
}
}

Swift - Execute performSegue under conditions

My intention is to run performSegue solely if all textfields are filled. If not, the button is not supposed to work - to be exact, performSegue shall not be executed.
My approach was to put performSegue inside of an if statement, but somehow it's being ignored and performSegue is being executed anyways, even though both fields are empty. Is there another more successful approach?
#IBAction func buttonAdd(_ sender: Any) {
if (addKmInput.text! != "" && addPriceInput.text != "") {
...
performSegue(withIdentifier: "goBackToSecond", sender: self)
}
}
#IBOutlet weak var addKmInput: UITextField!
#IBOutlet weak var addPriceInput: UITextField!
New version:
#IBAction func buttonAdd(_ sender: Any) {
performSegue(withIdentifier: "goBackToSecond", sender: self)
}
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
switch identifier {
case "goBackToSecond":
return shouldGoBackToSecond()
default:
return true
}
}
func shouldGoBackToSecond() -> Bool {
guard let kmInput = addKmInput.text, let priceInput = addPriceInput.text else { return false }
return !kmInput.isEmpty && !priceInput.isEmpty
}
Try following solution:
#IBAction func buttonAdd(_ sender: Any) {
if shouldGoBackToSecond() {
performSegue(withIdentifier: "goBackToSecond", sender: self)
}
}
func shouldGoBackToSecond() -> Bool {
guard let kmInput = addKmInput.text, let priceInput = addPriceInput.text else { return false }
return !kmInput.isEmpty && !priceInput.isEmpty
}
use str.isEmpty to check if the string int he textField is empty :
if let t1 = addKmInput?.text, let t2 = addPriceInput?.text, !t1.isEmpty, !t2.isEmpty {
...
performSegue(withIdentifier: "goBackToSecond", sender: self)
}
and you should ideally use this delegate to deny segue performance:
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
return true //return true/false conditionally. returning false will prevent segue performance.
}

Unexpectedly found nil while unwrapping an Optional value / Swift

Building the ToDo app. The app crashes when the new todo task is created.
The breakpoint stops the code and returns:
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
#IBAction func doneButton(_ sender: UIButton) {
guard let title = textView.text, !title.isEmpty else {
return
}
let todo = Todo(context: managedContext)
todo.title = title
todo.priority = Int16(segmentedControl.selectedSegmentIndex)
todo.date = Date()
do {
try managedContext.save()
dismiss(animated: true)
textView.resignFirstResponder()
} catch {
print("Error saving todo: \(error)")
}
}
#IBAction func cancelButton(_ sender: UIButton) {
dismiss(animated: true)
textView.resignFirstResponder()
}
Any ideas what could have caused the app crash? Thanks
UISegmentedControlSegment is the public enum and UISegmentedControl is the UIControl
As per your comment, it seems that you have mistaken UISegmentedControl for UISegmentedControlSegment, so connect UISegmentedControl like below:
#IBOutlet weak var segmentedControl: UISegmentedControl!

firebase analytics error when preforming segue

The app crashes when I perform the segue. I checked if there was any sigbrt errors, but there was not. I think it is firebase analytics from the log. This error was from the log :
terminating with uncaught exception of type NSException.
The code
#IBOutlet weak var email: UITextField!
#IBOutlet weak var password: UITextField!
#IBOutlet weak var adduser: UIButton!
#IBOutlet weak var errormessege: UILabel!
var databaseref = FIRDatabase.database().reference()
override func viewDidLoad() {
super.viewDidLoad()
password.isSecureTextEntry = true
adduser.isEnabled = false
}
#IBAction func didtapcancel(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
#IBAction func didtapadd(_ sender: Any) {
adduser.isEnabled = false
FIRAuth.auth()?.createUser(withEmail: email.text!, password: password.text!, completion: {(user,error) in
if error != nil {
if error!._code == 17999 {
self.errormessege.text = "Invalid email address" }
else {
self.errormessege.text = error?.localizedDescription
}
}
else
{
FIRAuth.auth()?.signIn(withEmail: self.email.text!, password: self.password.text!, completion: {(user,error) in
if (error == nil) {
self.databaseref.child("users").child(user!.uid).child("email").setValue(self.email.text!)
self.performSegue(withIdentifier: "hi", sender: nil)
}
else {
self.errormessege.text = error?.localizedDescription
}
})
}
}
)
}
#IBAction func didtextchange(_ sender: Any) {
if((email.text?.characters.count)!>0){
adduser.isEnabled = true}
else{
adduser.isEnabled = false}
}
#IBAction func did4(_ sender: Any) {
if((password.text?.characters.count)!>0){
adduser.isEnabled = true}
else{
adduser.isEnabled = false}
}
Without more code and the entire scenario in front of me it is hard to specifically nail down what is happening between your code and Firebase. One thing that will significantly help you though in tracking down this error is to validate the data you are pulling out of your #IBOutlets each steps of the way. That way your program is not force unwrapping these objects and leaving opening up your code to risk.
#IBOutlet weak var email: UITextField!
#IBOutlet weak var password: UITextField!
#IBOutlet weak var adduser: UIButton!
#IBOutlet weak var errormessege: UILabel!
var databaseref = FIRDatabase.database().reference()
override func viewDidLoad() {
super.viewDidLoad()
password.isSecureTextEntry = true
adduser.isEnabled = false
}
#IBAction func didtapcancel(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
#IBAction func didtapadd(_ sender: Any) {
adduser.isEnabled = false
guard let emailText = email.text,
let passwordText = password.text else {
// Handle error safely
print("Error unrapping email text or password text")
return
}
FIRAuth.auth()?.createUser(withEmail: emailText, password: passwordText, completion: { (user,error) in
if error != nil {
if error!._code == 17999 {
self.errormessege.text = "Invalid email address"
} else {
self.errormessege.text = error?.localizedDescription
}
} else {
FIRAuth.auth()?.signIn(withEmail: emailText, password: passwordText, completion: { (user, error) in
if (error == nil) {
guard let uid = user.uid else {
// handle error safely
print("Error with user uid")
return
}
self.databaseref.child("users").child(uid).child("email").setValue(emailText)
self.performSegue(withIdentifier: "hi", sender: nil)
}
else {
self.errormessege.text = error?.localizedDescription
}
})
}
})
}
#IBAction func didtextchange(_ sender: Any) {
guard let emailText = email.text else {
// Return error safely
print("Error unrapping email text")
return
}
if emailText.characters.count> 0 {
adduser.isEnabled = true
} else{
adduser.isEnabled = false
}
}

Cannot convert value of type 'FriendTableViewCell.Type' to expected argument type 'FriendTableViewCell'

So I'm trying to set up a friends list for my app and I am running into an error where I am being told that the compile cannot convert value of type 'FriendTableViewCell.Type' to expected argument type 'FriendTableViewCell'. This confuses me because its seemingly the same. Maybe I am missing something?
The Code I am having an issue with is:
#IBAction func followButtonTap(_ sender: Any) {
if let canFollow = canFollow, canFollow == true {
delegate?.cell(cell: FriendTableViewCell, didSelectFollowUser: PFUser)
self.canFollow = false
} else {
delegate?.cell(cell: FriendTableViewCell, didSelectUnfollowUser: PFUser)
self.canFollow = true
}
}
My Full Code is:
import Foundation
protocol FriendTableViewCellDelegate: class{
func cell(cell: FriendTableViewCell, didSelectFollowUser user: PFUser)
func cell(cell: FriendTableViewCell, didSelectUnfollowUser user: PFUser)
}
class FriendTableViewCell: UITableViewCell{
#IBOutlet weak var friendName: UILabel!
#IBOutlet weak var followButton: UIButton!
weak var delegate: FriendTableViewCellDelegate?
var user: PFUser? {
didSet {
friendName.text = user?.username
}
}
var canFollow: Bool? = true {
didSet {
if let canFollow = canFollow {
followButton.isSelected = !canFollow
}
}
}
#IBAction func followButtonTap(_ sender: Any) {
if let canFollow = canFollow, canFollow == true {
delegate?.cell(cell: FriendTableViewCell, didSelectFollowUser: PFUser)
self.canFollow = false
} else {
delegate?.cell(cell: FriendTableViewCell, didSelectUnfollowUser: PFUser)
self.canFollow = true
}
}
}
The error is saying you need to provide an object of type FriendTableViewCell not the FriendTableViewCell type itself.
Just replace FriendTableViewCell with self in your function:
#IBAction func followButtonTap(_ sender: Any) {
if let canFollow = canFollow, canFollow == true {
delegate?.cell(cell: self, didSelectFollowUser: PFUser)
self.canFollow = false
} else {
delegate?.cell(cell: self, didSelectUnfollowUser: PFUser)
self.canFollow = true
}
}
I think you just want to say self which would pass along that specific cell, as opposed to just the generic type.
#IBAction func followButtonTap(_ sender: Any) {
if let canFollow = canFollow, canFollow == true {
delegate?.cell(cell: self, didSelectFollowUser: PFUser)
self.canFollow = false
} else {
delegate?.cell(cell: self, didSelectUnfollowUser: PFUser)
self.canFollow = true
}
}
As the other poster says, your delegate function is expecting you to pass in a cell, but you're passing in the CLASS of the cell in the call.
Passing in a cell is a bad idea. You should probably refactor your code to pass in the indexPath of the selected cell, not the cell itself.

Resources