How can I use optional chaining to unwrap swift variables - ios

I've received the following compiler error
"value of optional type 'String?' not unwrapped; did you mean to use
'!'or '?'?"
This is my code
#IBAction func registerButtonTapped(sender: AnyObject)
{
let userEmail = userEmailTextField.text;
let userPassword = userPasswordTextField.text;
let userConfirmPassword = confirmPasswordTextField.text;
// check for empty fields
if userEmail.isEmpty || userPassword.isEmpty || userConfirmPassword.isEmpty
{
//Display alert message
displayMyAlertMessage("All fields are required");
return;
}
The error shows at line:
if userEmail.isEmpty || userPassword.isEmpty || userConfirmPassword.isEmpty

Brush up on Swift basics, check out the Optional Chaining guide for help with this kind of problem.
But in general...
Any textField.text property is an optional, meaning it could be nil.
Therefore your variables like userEmail are optional as well, and could be nil.
You can't call a property or method on an optional variable without first unwrapping it. There's two ways to do this.
userEmail!.isEmpty says that you guarantee that userEmail is not nil
userEmail?.isEmpty says maybe you're not sure and only check isEmpty is userEmail happens to not be nil
You can also unwrap an optional using if let:
if let userEmail = userEmail, userPassword = userPassword, userConfirmPassword = userConfirmPassword where userEmail.isEmpty || userPassword.isEmpty || userConfirmPassword.isEmpty {
//do stuff
}
That's the best short explanation I can give. Study, learn, code.

You have not unwrapped the text fields in your code.
The system does not know if there is text present in your textfield. So, when you say:
let userEmail = userEmailTextField.text;
let userPassword = userPasswordTextField.text;
let userConfirmPassword = confirmPasswordTextField.text;
The values in userEmail, userPassword and userConfirmPassword are Optional Strings. To force unwrap them, you can do the following:
let userEmail = userEmailTextField.text!;
let userPassword = userPasswordTextField.text!;
let userConfirmPassword = confirmPasswordTextField.text!;
But, bear in mind that if the user does not enter any value in the textfields, you will get a crash.
Instead of force unwrapping, you should use safe unwrap, like this:
if let userEmail = userEmailTextField.text {
//Do something
} else {
//Handle case where input is nil
}
You can read more about optionals and their unwrapping here.

Related

Unexpectedly found nil while implicitly unwrapping an Optional value despite conditional binding

So I have the following lines:
let theUsername = "\(String(describing: selectedPost?.user.username!))"
if selectedPost?.user.username! != nil {
print("Contains a value!")
username.text = theUsername//Fails here
} else {
print("Doesn’t contain a value.")
username.text = "No username found: Weird"
}
The error message:
Unexpectedly found nil while implicitly unwrapping an Optional value
would make one think the value inside is nil. However when printing:
print("\(String(describing: selectedPost?.user.username!))", " LIT555")
I get:
Optional("C22AE009-8CC6-490A-9328-23A08AAD3A10") LIT555
How can I turn the value into a non optional (ie. get rid of the Optional() part), so it can work without it failing?
Try
if let res = selectedPost?.user.username { // to unwrap the optional value
// check textfield is not nil
username.text = res
}
else {
username.text = "default"
}
Or shortly
username.text = selectedPost?.user.username ?? "default"
According to the crash your textfield is nil , check the IB connection
When you do this
var selectedPost : Post? { didSet { getMediaStats() loadP3Data() } }
and assign a value to selectedPost in the prepare method of the previous vc , the destination vc outlets are still nil because it's not loaded yet and since didSet is called instantly before the load , hence the crash
So remove didSet and call what inside it in viewDidLoad
Even thought you're using ! at the username, this expression is still optional because you have the ? in selectedPost. I strongly suggest you to never use forced unwraps (!), always use if let or guard let to make sure you can unwrap an optional and avoid those crashes.
This is what your code could look like:
if let theUsername = selectedPost?.user.username {
username.text = theUsername
} else {
username.text = "No username found: Weird"
}
or
username.text = selectedPost?.user.username ?? "No username found: Weird"

Userdefault value comparison with textfield

i am trying to compare in login form password value with UserDefault value . but an error occur that bool operation cant be perform there.
Please can any one help me compare both values.
How can i compare these values?
if let savedPassword = UserDefaults.standard.string(forKey: "password") {
let enteredPassword = textField?.text
if savedPassword == enteredPassword {
// Do stuff.
}
}
else {
// Failure.
}
I wouldn't recommend storing passwords in plain text in UserDefaults.
There are a number of tutorials on this topic available, for example https://medium.com/ios-os-x-development/securing-user-data-with-keychain-for-ios-e720e0f9a8e2
Please use the below code snippet
if passwordField.text == UserDefaults.standard.string(forKey:"password"){
print("Same Password")
}
Thanks
You have to specify your object type
if UserDefaults.standard.string(forKey: "password") == passwordLabel.text {
// ...
}
I think you should cast your result from Any to String from User Defaults
if let yourString = UserDefaults.standard.string(forKey: "yourString"), yourString == passwordTextField.text {
// do something here
}

Swift optional types thoughts

I have a question about: " I guess why it work, but i should ask it here to check my thoughts :D.
I have a code:
let possibleNumber = "1232"
var somevariable : Int
if let actualNumber = Int(possibleNumber) {
somevariable = actualNumber
} else {
print("\(possibleNumber) could not be converted to an integer")
}
print(somevariable)
and mistake: Variable 'somevariable' used before being initialized
But if i had something like this:
let possibleNumber = "1232"
var somevariable : Int
if let actualNumber = Int(possibleNumber) {
somevariable = actualNumber
} else {
somevariable = 0
}
print(somevariable)
It will work without any problems.
If i delete the else block it'l make an error too. In any case, i understand it in that meaning like: i don't allow you to make a situation when somevariable can be nil because his type is not optional.
Maybe my thoughts are wrong or you have a better explanation. Thanks for any help and good links.
Good luck!
Your thought is right this
if let actualNumber = Int(possibleNumber)
has a possibility of not being true , and as else doesn't assign a value , hence the error , also you'll get the same error if you only assigned it inside else only , the compiler needs to make sure the value isn't nil because it doesn't know which block ( if or else ) will run at runtime , so depending on your conditions you have to either use Int! or Int? , but the former will cause a crash if the value is nil
The somevariable is used without initialization. So in the first code, there is a possibility to reach the else clause so the somevariable is not initialized.
But in the second case even in the else clause, somevariable is initialized and =0.
These codes work too:
let possibleNumber = "1232"
var somevariable : Int?
if let actualNumber = Int(possibleNumber) {
somevariable = actualNumber
} else {
print("\(possibleNumber) could not be converted to an integer")
}
print(somevariable)
And this:
let possibleNumber = "1232"
var somevariable : Int!
if let actualNumber = Int(possibleNumber) {
somevariable = actualNumber
} else {
print("\(possibleNumber) could not be converted to an integer")
}
print(somevariable)

How to check nil for variables in swift and avoid app crashing?

I'm having my string declared as,
var firstName = String()
and I'm assigning value from parsed JSON content like,
firstName = json["first_name"].stringValue
But sometimes, there might be empty values in the JSON response and the app is crashing, I read about guard statement and if statement to check empty values, but that requires the declaration format to be changed, couldn't find a right way to handle this error without changing the declaration format.
since I have declared all the variables in my app with similar formats, changing that requires time, I'm in the verge of uploading my app, this is my first swift app, if my declaration format is wrong please answer why it is, can someone help me out of this?
Code as of Swift 4:
Keep in mind that when you are using ".stringValue", it is almost the same as using a "!" which will force a crash on nil.
if let firstName = json["first_name"]as? String {
//do stuff like
self.firstName = firstName
}
This will unwrap it to where you can get at the value if it isn't null and can be a string.
Guard let's are really good for this though as you can account for it in the beginning and you can assume that it is not optional for the entire scope.
guard let firstName = json["first_name"]as? String else {return}
self.firstName = firstName
In addition, you could always check for nulls in one line and assign a default value if a nil value occurs.
self.firstName = (json["first_name"]as? String) ?? "Default String"
You can use next statement:
guard let firstName = json["first_name"].stringValue else { // Do sth if nil }
// Do sth if not nil
Or you could use statement, which you wrote, but you should check variable
firstName like this:
guard firstName != nil else { // Do sth if nil }
// Do sth if not nil
Or
if firstName != nil {
// Do sth if not nil
}
You can use guard statement also,
guard let firstName = json["first_name"] else {
print("FirstName is Empty")
return
}
or you can check with if also,
if let firstName = json["first_name"] {
//Your code goes here
}
You can do that the following way:
if let dictionary = json {
if let fName = dictionary["first_name"] {
firstName = fName as? String
}
}
I guest you use SwiftyJSON. The function stringValue always return String object. It's impossible to be nil. It sounds like the response data which is not valid JSON format, so it crashed.
My snippet codes.
// Alarmofire + SwiftyJSON
let request: DataRequest = ...//< Configure Alarmofire DataRequest
request.response() { (resp) in
if let error = resp.error {
// TODO: Error handle
return
}
if (response.data == nil) {
// TODO: error handle for empty data?
return
}
assert(response.data != nil, "In this place, the `data` MUST not be nil.")
let json = try? JSON(data: response.data!)
if (json == nil) {
// TODO: Error handle for invalid JSON format.
// If json is nil, then `json["first_name"]` will lead to crash.
return
}
// TODO: parse json object
let firstNameStr = json["first_name"].stringValue
}

Checking if `if let` is nil

I have an app where I'm currently using the SwiftKeychainWrapper. Below is the code I have which checks if retrievedString is nil. However I'm still getting retrievedString: nil in the console.
Shouldn't the code in the if-let statement not run, or am I using/understanding if-let incorrectly?
With the given example, what's the correct way to use if-let to unwrap my optional value?
if let retrievedString: String? = KeychainWrapper.stringForKey("username") {
print("retrievedString: \(retrievedString)")
//value not nil
} else {
//Value is nil
}
This is because you are setting the value of a optional String, String? KeychainWrapper.stringForKey("username") to another optional String retrievedString.
By trying to set a String? to another String?, the if let check always succeeds, even when the value is nil, because both the types are the same, and both can accept a nil value.
Instead, you should attempt to set the optional string, String? to a non-optional string, String. When Swift tries to set a non-optional String to nil, it will fail, because a non-optional cannot have a nil value. The code will then continue in the else statement
You should use
//notice the removal of the question mark
// |
// v
if let retrievedString: String = KeychainWrapper.stringForKey("username") {
print("retrievedString: \(retrievedString)")
//value not nil
} else {
//value is nil
}
You are setting the type of retrievedString to be optional. The whole point of the check is to remove the optional and just have a String.
if let retrievedString: String = KeychainWrapper.stringForKey("username") {
print("retrievedString: \(retrievedString)")
//value not nil
} else {
//Value is nil
}

Resources