I am new to guard let patterns. How come my app crashes when selectedRooms.text is nil instead of doing the return part? And why is rooms of optional type when I know that numberOfRooms is not nil?
guard let numberOfRooms = selectedRooms.text else {
return selectedRooms.placeholder = "type something"
}
let rooms = Int(numberOfRooms)
x = Int(ceil(sqrt(Double(rooms!)))) //found nil value
selectedRooms.text cannot return nil.
A UITextField and UITextView always returns a String value. An empty String ("") is returned if there is no text in the UITextField and UITextView. That's the reason else part is not executing and rooms value is nil.
Now, in the below statement you're force-unwrapping(!) the rooms.
x = Int(ceil(sqrt(Double(rooms!))))
But, since the rooms is nil, so forcefully unwrapping it is throwing runtime exception.
Solution:
You need to add an empty check as well for the else part to take effect, i.e.
guard let numberOfRooms = selectedRooms.text, !numberOfRooms.isEmpty else { //here...
return selectedRooms.placeholder = "type something"
}
guard let numberOfRooms = selectedRooms.text?.trimmingCharacters(in: .whitespacesAndNewlines), !numberOfRooms.isEmpty else {
return selectedRooms.placeholder = "type something"
}
if let rooms = Int(numberOfRooms) {
x = Int(ceil(sqrt(rooms)))
}
you don't want to force unwrap value of numberOfRooms, you can use by checking if let
Related
I'd like to add new func to get info from my firebase about id, name, etc. from my firebase to add it in ProfileView, but the above error will occur. This is what happens when my ProfileView previews and crashes. I do not know why it happens after I wrote new function. Could you tell me how to solve it?
P.S. I apologize in advance for my bad, if I failed to state the problem correctly
func getProfile(completion: #escaping (Result<MWUser, Error>) -> ()) {
PROBLEM IS HERE --> usersRef.document(AuthService.shared.currentUser!.uid).getDocument { docSnapshot, error in
guard let snap = docSnapshot else { return }
guard let data = snap.data() else { return }
guard let userName = data["name"] as? String else { return }
let id = data["id"] as? String ?? ""
guard let phone = data["phone"] as? Int else { return }
guard let address = data["address"] as? String else { return }
guard let company = data["company"] as? String else { return }
let user = MWUser(id: id, name: userName, phone: phone, company: company, address: address)
completion(.success(user))
}
}
The ! Operator means: Please crash my program if the optional is nil. Apparently it is, so Swift does exactly what you asked it to do and crashed. You should only use ! If you are 100% sure that the optional cannot be nil, unless there is a bug that you are going to fix.
You can do two things: Either find out why it is nil snd prevent it from ever being nil. Or handle it by writing
if let value = optional_function() {
// Handle the case where it is not nil
} else {
// Handle the case where it is nil
}
Or:
guard let value = optional_function() else {
// Hsndle the case where it is nil
}
You could try doing something like this.
if let user = AuthService.shared.currentUser {
usersRef.document(user.uid).getDocument() { docSnapshot, error in
//...The rest of your code.
}
}
else {
// Let the user know that there is a problem
}
What this does is safely unwrap the optional value so the function only searches for a user's info in firebase if there is a current logged in user. You can then include an 'else' statement after the 'if let' to do something (like alerting the user that there is a problem) if there is not a currently logged in user.
Hopefully that helps.
When you use `currentUser!`, you are force unwrapping an optional. If the optional is nil, then your program will crash.
You should unwrap it before using it.
You can do that by using a guard let statement (as you did with snap and data):
guard let currentUser = AuthService.shared.currentUser else {
// Do something where `currentUser` is nil
return
}
usersRef.document(currentUser.uid).getDocument { docSnapshot, error in
Edit:
It's not a force unwrapped option, it is an implicitly unwrapped optional.
From this SO answer:
These variables are designed so that you can defer their assignment until later in your code. It is your responsibility to ensure they have a value before you access them. However, because they involve force unwrapping, they are still inherently unsafe – as they assume your value is non-nil, even though assigning nil is valid.
One of the values on that line is an implicitly unwrapped optional, and it is nil when you are trying to access it. You should ensure that that value is initialized before you access it.
Reading about Optional values I was sure that all the bases were covered in my code, but I still get the dreaded unexpectedly found nil while unwrapping an Optional value.
That makes sense, since I've read: What does “fatal error: unexpectedly found nil while unwrapping an Optional value” mean?. It suggests making the Int optional, which is what I want:
func myCountUpdate(mainDict: [String : NSObject]) {
let myDict = mainDict["start"] as! [String : CFString]
let myCount = subDict["count"] as? String
let myTotal = Int(myCount)? // nope, it forces me to use non-optional !
// as the other thread suggest it's easy to check for nil with an optional int.
// how the hell can you do that if it won't allow you to make it optional?
if myTotal != nil {
print(myCount!)
let label: String = "\(myCount)"
text = label
} else {
text = nil
}
}
I've tried quite a bunch of things, including using other values to check for nil, etc. The issue is that the compiler will not allow me to declare the Int as non-optional, so what are my options? Xcode shows no warnings or suggestions on this issue, so maybe someone here has one - ty.
The best approach here is to use swift guards in order to check if a value is nil.
First, in the second line, where you use the subDict, its not referenced anywhere else, should it be myDict ?
The thing here is that the cast in let myCount = subDict["count"] as? String may be returning nil or there is not "count" in subDict. Therefore, when you do Int(myCount!), the force unwrapp of myCount is throwing the exception, since its nil.
You should avoid force unwrappings as much as you can, unless you are 100% sure that the value is not nil. In other cases, you should use the setting of a variable to check if it is not nil.
With your code, an updated version using guard would be the following:
func myCountUpdate(mainDict: [String : NSObject]) {
guard let myDict = mainDict["start"] as? [String : CFString],
let myCount = myDict["count"] as? String,
let myTotal = Int(myCount) else {
text = nil
return
}
print(myTotal)
let label: String = "\(count)"
text = label
}
This is safer, because if any of the conditions in the guard fails, then it's setting the text to nil an ending the method.
First unwrap the variable optional myCount(String?) to a variable called count (String).
let myCount = mainDict["count"] as? String
if let count = myCount {
//..
}
Then try to create a Int based on the variable count (String).
Which could return a nil since you could pass Int("Hi") or Int("1").
myTotal = Int(count)
Then after that you will have a variable called myTotal (Int?) with the result that you want.
Code
func myCountUpdate(mainDict: [String : Any]) {
let myDict = mainDict["start"] as? [String : Any]
if let myCount = myDict?["count"] as? String {
if let myTotal = Int(myCount) {
print(myTotal)
}
}
if let myCount = myDict?["count"] as? Int {
print(myCount)
}
}
Example 1
let data = [
"start": [
"count": "1"
]
]
myCountUpdate(mainDict: data) // outputs 1
Example 2
let data1 = [
"start": [
"count": 1
]
]
myCountUpdate(mainDict: data1) // outputs 1
Hi all I have tried a few solutions but no luck.
I am getting the text from Data Core, but the textview has optional on it.
when it prints it shows optional in the text.
page22TextView?.text = ("\(trans.value(forKey: "page22"))")
can anyone shed light on this ! have tried to unwrap but it stillelow: shows.
the full function is below:
func getTranscriptions () {
//create a fetch request, telling it about the entity
let fetchRequest: NSFetchRequest<TextInputs> = TextInputs.fetchRequest()
do {
//go get the results
let searchResults = try getContext().fetch(fetchRequest)
//I like to check the size of the returned results!
print ("num of results = \(searchResults.count)")
//You need to convert to NSManagedObject to use 'for' loops
for trans in searchResults as [NSManagedObject] {
page22TextView?.text = ("\(trans.value(forKey: "page22"))")
//get the Key Value pairs (although there may be a better way to do that...
print("\(trans.value(forKey: "page22"))")
}
} catch {
print("Error with request: \(error)")
}
}
try to set default value of getting nil value
page22TextView?.text = (trans.value(forKey: "page22") as? String) ?? ""
It'll set your value from trans and if it retrun nill will be set by "".
Hope it'll help you.
try with if-let statement:
if let result = trans.value(forKey: "page22") {
page22TextView?.text = result
}
Or try with guard statement:
guard let result = trans.value(forKey: "page22") else { return }
page22TextView?.text = String(describing: result)
Or you can force upwrap it like:
let result = trans.value(forKey: "page22")
if result != nil {
page22TextView?.text = result! as! String
}
Or you can follow the way suggested by #MrugeshTank below in answers
try to unwrap optional using if let then assign to your textview (if necessary then downcast your value)
if let value = trans.value(forKey: "page22") {
page22TextView?.text = value
}
or
use guard for unwrap
When I am about to fire my segue for the 2nd view I also send some values like this:
if let aTime = ads[indexPath.row]["unix_t"].int {
toView.time = aTime
}
if let aTitle = ads[indexPath.row]["title"].string {
toView.title = aTitle
}
In the second VC I have declared the varibles like:
var time: Int?
var title: String?
and this is how I unwrap the values:
if time != nil {
timeLabel.text = String(time!)
}
if title != nil {
titleLabel.text = title!
}
This all works I never get any error caused by unwrapped varibles or nil values. But is there any easier way to do it?
Right now it feels like I am checking too much
I can think of three alternatives.
if/let. Very similar to your current option, but you don't have to implicitly unwrap.
if let time = time {
timeLabel.text = "\(time)"
}
if let title = title {
titleLabel.text = title
}
You can even unwrap them on the same line. The downside to this is that if one of them is nil, then neither label will be set.
if let time = time, let title = title {
timeLabel.text = "\(time)"
titleLabel.text = title
}
guard/let. If these are in a function like setupViews(), then you can one-line your unwrapping like so:
func setupViews() {
guard let time = time, let title = title else { return }
timeLabel.text = "\(time)"
titleLabel.text = title
}
You can use default values and the ?? operator to unwrap quickly.
timeLabel.text = "\(time ?? 0)"
titleLabel.text = title ?? ""
You can use the Nil Coalescing Operator too as given in the Docs:
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. The expression a is always of an optional type. The expression b must match the type that is stored inside a.
The nil coalescing operator is shorthand for the code below:
a != nil ? a! : b
if let time = time {
timeLabel.text = "\(time)"
}
if let title = title {
titleLabel.text = title
}
It's the same as with your JSON
Yes, you are checking too much (twice).
Since only non-optional values are passed you can declare the variables non-optional
var time = 0
var title = ""
and set the labels
timeLabel.text = "\(time)"
titleLabel.text = title
The strong type system of Swift highly recommends first to consider
Can I accomplish this without optionals?
and – if there is no other option – then use optionals.
You don't need to check nil condition in the first view.
Please follow below code:
toView.time = ads[indexPath.row]["unix_t"] as? Int
toView.title = ads[indexPath.row]["title"] as? String
time and title variable in second view are optionals. So, when you assign a value to toView.time and toView.title, it will either assign the value of type time and title respectively or nil.
I'm getting the unexpectedly found nil error even though I'm checking for nil before using the value. Have I missed something here?
if self.orders[indexPath.row]["distanceToHouseHold"] != nil {
cell.distanceTextField.text = "\(self.orders[indexPath.row]["distanceToHouseHold"] as! String)m"
}
The error is on the second line of course.
Probably distanceToHouseHold is not a String, and it's failing when type casting. Try using the if-let check or the new guard check.
if let distance = self.orders[indexPath.row]["distanceToHouseHold"] as? String {
cell.distanceTextField.text = "\(distance)m"
}
Do like this instead:
if let distance self.orders[indexPath.row]["distanceToHouseHold"] as? String {
cell.distanceTextField.text = distance + "m"
}
This will both check its not nil AND cast into a String
Use guard let
guard let whatever = self.orders[indexPath.row]["distanceToHouseHold"] as? String else {
return
}
cell.distanceTextField.text = whatever + "m"
}
If it is not working try this:
guard let whatever = self.orders[indexPath.row]["distanceToHouseHold"] else {
return
}
cell.distanceTextField.text = String(whatever) + "m"
}
Try this:
let whatValue = self.orders[indexPath.row]["distanceToHouseHold"]
println(" value is \(whatValue)");
This will help you see what the output. After that you can decide what's going wrong.
Cheers!!