App crashing because of no values in the uiTextfield - swift

When I enter a value in the UiTextField and press add button the app works perfectly fine,but when I don't enter a value in the UiTextField, then press the add button the whole app crashes. can someone please help me and show me the light.Thank you in advance and good wishes for you and your family.
This is my code
import UIKit
class ViewController: UIViewController,UITextFieldDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.textFild.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBOutlet var textFild: UITextField!
#IBAction func ButtonAwesome(sender: AnyObject) {
let value:Double = Double(textFild.text!)!
let sum:Double = value - 1;
let alert = UIAlertController(title: "km", message: "\(sum)", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Exit", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert,animated: true, completion: nil)
}
func textField(textField: UITextField,shouldChangeCharactersInRange range: NSRange,replacementString stringer: String) -> Bool
{
if (textField.text == "" && (stringer == ".")) {
return false;
}
let countdots = textField.text!.componentsSeparatedByString(".").count - 1
if countdots > 0 && stringer == "."
{
return false
}
return true
}
}

A more robust solution would be to use the nil coalescing operator to assert that the initialization of value never fails.
#IBAction func ButtonAwesome(sender: AnyObject) {
let value = Double(textFild.text ?? "0") ?? 0
let sum:Double = value - 1;
let alert = UIAlertController(title: "km", message: "\(sum)", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Exit", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert,animated: true, completion: nil)
}
This is valid for any of the following "unexpected" values of textField.text: nil, "" (empty) or some character that cannot be used to initialize a Double type variable (e.g. "a"). For all these cases, value is give a value of 0.
As an example, consider the following comparisons between a safe and an un-safe way of solving your initial runtime exception.
We first look at the dangers of forcibly unwrapping optionals - not a safe solution. What if textField.text contains nil or an non-numerical character, e.g. "a"? Result:
var myTextFieldText : String? = nil
/* Example 1: nil value causes runtime error */
if myTextFieldText != "" { // this is 'true' -> enter if closure
let value:Double = Double(myTextFieldText!)!
/* this will fail at run time since we are forcibly (un-safely)
unwrapping an optional containing 'nil' */
}
/* Example 2: non-numeric string character causes runtime error */
myTextFieldText = "a"
if myTextFieldText != "" { // this is 'true' -> enter if closure
let value:Double = Double(myTextFieldText!)!
/* this will fail at run time since we cannot initalize a
Double with a string value "a", hence 'Double(myTextFieldText!)'
returns nil, but since we've appended '!', we, like above,
forcibly tries to unwrap this optional of value nil */
}
You should, generally, always use conditional unwrapping of optionals, to avoid encountering a nil value when forcibly unwrapping optionals, the latter leading to a runtime error.
A more robust version of the example above, making use of the nil coalescing operator:
myTextFieldText = nil
let valueA = Double(myTextFieldText ?? "0") ?? 0 // 0
/* OK, Double(myTextFieldText ?? "0") = Double("0") = 0 */
myTextFieldText = "a"
let valueB = Double(myTextFieldText ?? "0") ?? 0 // 0
/* OK, Double(myTextFieldText ?? "0") = Double("a") = nil (fails)
=> use rhs of outer '??' operator: nil ?? 0 = 0 */
For an alternative method, where you extend UITextField to cover your string to numerical type conversion needs, see Leos neat answer in the following thread:
Swift - Converting String to Int
The thread also contains some other valuable insights w.r.t. reading text as numerical values from UITextField instances.
Finally, when dealing with String to Double value conversion, it might be appropriate to use a fixed precision w.r.t. number of decimal digits in your resulting Double value. For a thorough example of how to do this using NSNumberFormatter and extensions, see:
How to input currency format on a text field (from right to left) using Swift?

Please replace your buttonAwesome method with the following code where you check if textfield is not empty, this will work if textfield has a value:
#IBAction func ButtonAwesome(sender: AnyObject) {
if textFild.text != "" {
let value:Double = Double(textFild.text!)!
let sum:Double = value - 1;
let alert = UIAlertController(title: "km", message: "\(sum)", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Exit", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert,animated: true, completion: nil)
}
}

Since textfield.text is an optional value and as in your case textfield can or cannot have text. So you should test for optional as give below.
if let textIsAvailable = textfield.text
{
print("Text \(textIsAvailable)")
}
Note : The reason for your crash is you are trying to access value which actually doesn't have any value.

Related

Swift: How to properly write UIAlertAction with textfield and textview validation?

I am creating an app with one of the tab being a guestbook where users can write something. I use an UIAlertController to pop up a window with a textfield for entering name, and a textview for entering the message. I want to make the "Post" button disabled by default, then enable it after the name field has at least 2 characters and the message has at least 3 characters. I have achieved this by
#1: declare the "Post" UIAlertAction at the top
let saveAction = UIAlertAction(title:"Post", style: .default, handler: { (action) -> Void in print(data) })
The above line gives the error (Cannot use instance member 'data' within property initializer; property initializers run before self is available.)
#2: add this "Post" button in alert and making it disabled
alert.addAction(saveAction)
saveAction.isEnabled = false
#3 add two functions to detect how many words are in the textfield and textview, and if they meet the requirement, enable the "Post" button
func textViewDidChange(_ textView: UITextView) { //Handle the text changes here
GuestbookContentWordCount = textView.text.count
data["content"] = textView.text
enableSave()
}
#objc func textFieldDidChange(_ textField: UITextField) {
GuestbookNameWordCount = textField.text?.count ?? 0
data["name"] = textField.text
enableSave()
}
func enableSave () {
if GuestbookContentWordCount >= 3 && addGuestbookNameWordCount >= 2 {
saveAction.isEnabled = true
} else {
saveAction.isEnabled = false
}
}
The ideal situation is when the requirements are met and the user clicks on the "Post" button, I will get the data["name"] and data["content"] and insert it into a database. Right now I have gotten it to work to the point that the "Post" button is enabled after the requirements are met, but when trying to get the data it gives the error "Cannot use instance member 'data' within property initializer; property initializers run before self is available.
Can you please advise how to solve this problem? Thank you.
So this is what I would do
Change your UIAlertAction reference to this outside the viewDidLoad()
var saveAction: UIAlertAction? = nil
Inside the viewDidLoad() you can instantiate it like:
saveAction = UIAlertAction(title:"Post", style: .default, handler: { [weak self] (action) -> Void in
guard let `self` = self else { return }
print(self.data) }
)
[weak self] is used so that you don't end up having retain cycles after your UIViewController is deinitialised.

How to check for quotations in swift 4?

I am trying to check for string if it contains a single quotation or a double quotation
I tried using the following code but when I test and enter in the text field a single or double quotation , it passes as if there is no quotation.
var usernameSQL = username.text?.contains("\'") as! Bool || username.text?.contains("\"") as! Bool
guard !usernameSQL else {
let alertVC = UIAlertController(title: "Invlalid symbols", message: "(') and (\") symbols are not allowed", preferredStyle: .alert)
alertVC.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.show(alertVC, sender: nil)
return
}
print("passed")
What I get is "passed" being printed instead of getting alert "Invalid symbols".
Thanks in advance.
I think you are overcomplicating things.
You can easily achieve the same result with this small refactor.
if let textValue = username.text {
if textValue.contains("\'") || textValue.contains("\"") {
print("Show Alert")
} else {
print("Passed")
}
} else {
print("username.text is nil do something or just ignore")
}
Hope it helps.
A smarter solution is to use CharacterSet and rangeOfCharacter(from:. The benefit is the optional doesn't need to be unwrapped.
If there are no quotes the expression returns nil and passes the guard.
let quoteSet = CharacterSet(charactersIn: "\'\"")
guard username.text?.rangeOfCharacter(from: quoteSet) == nil else { ...
However I would take Larme's advice and create the character set with the allowed characters.

if statement executing itself instead of others

I have a code here that, each time I run it, only the if statement which states "All fields are required" works but NOT ONLY when it must be called, it actually runs in place of the others. So whatever I do even when all the fields are complete, I have "All fields are required" as an alert message.
Here is the code, all help is appreciated, thank you in advance.
import UIKit
class RegisterPageViewController: UIViewController {
#IBOutlet weak var userEmailTextField: UITextField!
#IBOutlet weak var userPasswordTextField: UITextField!
#IBOutlet weak var repeatPasswordTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func registerButtonTapped(_ sender: AnyObject) {
let userEmail = ""
let userPassword = ""
let userRepeatPassword = ""
// Check for empty fields
if (userEmail.isEmpty || userPassword.isEmpty ||
userRepeatPassword.isEmpty)
{
// Display Alert Message
displayMyAlertMessage(userMessage:"All fields are required")
return
}
//Check if passwords match
if (userPassword != userRepeatPassword)
{
// Display an alert message
displayMyAlertMessage(userMessage:"Passwords do not match")
return
}
// Store data
UserDefaults.standard.set(userEmail, forKey:"userEmail")
UserDefaults.standard.set(userEmail, forKey:"userPassword")
UserDefaults.standard.synchronize()
// Display alert message with confirmation
_ = UIAlertController(title:"Alert", message:"Registration is
successfull. Thank you!",
preferredStyle:UIAlertControllerStyle.alert);
_ = UIAlertAction(title:"Ok", style:UIAlertActionStyle.default)
{
action in
self.dismiss(animated: true, completion:nil)
}
}
func displayMyAlertMessage(userMessage:String)
{
let myAlert = UIAlertController(title:"Alert", message: userMessage,
preferredStyle:UIAlertControllerStyle.alert);
let okAction = UIAlertAction(title:"Ok",
style:UIAlertActionStyle.default, handler:nil)
myAlert.addAction(okAction)
self.present(myAlert, animated:true, completion:nil)
}
}
Do you ever assign any values to userEmail, userPassword, userRepeatPassword? You initialize them as empty at the start of the function, and it looks like their values never change.
Instead of declaring them in the function, try using class level variables, and linking them to your textfields in Storyboard.
#IBOutlet weak var userEmail: UITextField!
#IBOutlet weak var userPassword: UITextField!
#IBOutlet weak var userRepeatPassword: UITextField!
#IBAction func registerButtonTapped(_ sender: AnyObject) {
// Check for empty fields
if (self.userEmail.text.isEmpty || self.userPassword.text.isEmpty || self.userRepeatPassword.text.isEmpty) {
// Display Alert Message
displayMyAlertMessage(userMessage:"All fields are required")
return
}
...
}
I suspect that this code is not an accurate representation of your implementation. Would you be able to copy and paste the registerButtonTapped(_:) function unedited?
If it is, I would agree with #unmarshalled: It appears that you have declared each of the variables with an empty string as their value. If the code you have posted is implemented exactly as above, that is the cause of your issue.
based on the code you have posted, I would also recommend the following alterations:
get the email, password & repeatPassword from outside the scope of the function: usually, by just pulling it directly from the UI, most commonly from text fields: i.e. userEmailTextField.text
extracting your user defaults keys, and any other string literals you have, into a constants file is good practice and avoid any unnecessary misspelling related bugs.
you don't need to add a handler to a UIAlertAction if all you want it to do is dismiss the alert. UIAlterController will automatically be dismissed automatically: the handler argument has a default value of nil and can be omitted, simply:
let okayButton = UIAlertAction(title: "Ok", style: .default)
Generally speaking, you don't want to store a reference to a shared instance. However, within small local scopes its a little cleaner to do so:
let userDefaults = UserDefaults.standard
userDefaults.set(userEmail, forKey:"userEmail")
userDefaults.set(userEmail, forKey:"userPassword")
userDefaults.synchronize()
Cheers :)
EDIT:
I would suggest extracting the conditional out to a computed property for readability and check if count == 0 rather than isEmpty. The advantage of this is that you can make the computed property more comprehensive, I.e this will check that the strings are not nil or empty. Usually checking the count is enough, but there’s no harm in covering your bases.
As it stands with the current UIKit implementation, UITextField.text can never be nil. That being said, official documentation does not make that guarantee explicitly, so the best way to handle it is to implement it like an optional, below.
So something like:
fileprivate var registrationFormCompleted: Bool {
guard username = usernameTextfield.text,
password = passwordTextField.text,
repeat = repeatPasswordTextField.text,
else {
return false
}
return username.count > 0 &&
password.count > 0 &&
repeat.count > 0
}
In use it would be:
#IBAction func registerButtonTapped(_ sender: AnyObject) {
// Check for empty fields
if !registrationFormCompleted {
// Display Alert Message
displayMyAlertMessage(userMessage:"All fields are required")
return
}
//....
}

Passing value of a textfield to a label in swift

I have a textfield and I want it to hide and show when a button is pressed, and I also want the text in the textfield to be passed to a label just underneath the textfield. I tried this :
#IBAction func myfunction(_ sender: UIButton) {
if textfield.isHidden == true{
textfield.isHidden = false
}else{
label.text = textfield.text
textfield.isHidden = true
}
}
Apparently the hidding and showing part is working but the line
label.text = textfield.text
is not. I get an error like this "Thread 1: EXC_BREAKPOINT (code=1, subcode=0x10143fb50" and in the console I have "fatal error: unexpectedly found nil while unwrapping an Optional value"
Can someone help me please.
Based on your error, it seems that you try to affect with this line label.text = textfield.text the text from your textfield into label but the problem is that your textfield.text is nil.
That's why you have the error unexpectedly found nil while unwrapping an Optional value
Your textfield is an Optional. It can have a value or nil. If you try to unwrap an optional value having nil you have this kind of error.
The solution here is to unwrap safely the optional value with Optional Binding like this :
if let textInput = textfield.text {
//There is a text in your textfield so we get it and put it on the label
label.text = textInput
} else {
//textfield is nil so we can't put it on the label
print("textfield is nil")
}
Here is a simpler code you may try. The nil-coalescing operator (a ?? b) unwraps an optional a. if it contains a value, or returns a default value b if a is nil.
#IBAction func myfunction(_ sender: UIButton) {
label.text = textfield.text ?? ""
}

Dynamic Cast Faild - AnyObject to NSArray

I am trying to read textFields values which is coming trough alert controller but I am getting run time error on casting with Anyobject to NSArray
alertUserStatus.addAction(UIAlertAction(title: "Sign Up", style: UIAlertActionStyle.Default, handler: {
alertAction in
//let textField:NSArray=alertUserStatus.textFields as AnyObject! as NSArray
let textField=alertUserStatus.textFields! as AnyObject! as NSArray
let username:String=textField.objectAtIndex(0) as String
let password:String=textField.objectAtIndex(1) as String
var signUpUser=PFUser()
signUpUser.username=username
signUpUser.password=password
signUpUser.signUpInBackgroundWithBlock{
(success:Bool!,error:NSError!)->Void in
if success == true {
println("sign up successfully")
}
else{
println("fail to sign up")
}
}
}))
It always helps to look at the type you are getting from a function before trying to massage it into another type. In this case alertUserStatus.textFields returns [AnyObject]? – that is, an optional array of AnyObject. So there’s really no need to convert it to an AnyObject and then to an NSArray just so you can get the first two entries from it. Instead you want to unwrap it and convert it to an array of String.
It’s also best to avoid using !, even if you’re confident the optional can’t possibly be nil, because one of these days there’ll be an edge case that you miss that doesn’t hold and your code will crash with an inexplicable force-unwrap error message.
// nb you can skip the handler: argument name with trailing-closure syntax
alertUserStatus.addAction(UIAlertAction(title: "Sign Up", style: UIAlertActionStyle.Default) {
alertAction in
if let textFields = alertUserStatus.textFields as? [String] where textFields.count > 1 {
let username = textFields[0]
let password = textFields[1]
// rest of your code...
}
else {
// assert with intelligible message or log error or similar
}
})