Swift correct approach to nil value - swift

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

Related

Why is this constant from a Dictionary not unwrapped?

I'm using some base swift functionality, the fact that you can unwrapped Optional values with a if. I am working on swift 4 and Xcode 10.3
let xyz: String? = nil
if let unwrappedValue = xyz {
print("This is unwrapped")
} else {
print("This is not unwrapped") // Program goes there
}
I have made a dictionary with an Optional value as an element, and when I try to unwrap it, it doesn't unwrap the variable, but keeps the Optional value.
var referenceDatesIdentifiers: [String: Date?] =
["ReferenceDateIdentifier": nil]
if let referenceDate = referenceDatesIdentifiers["ReferenceDateIdentifier"] {
referenceDateLabel.text = otherDateFormater.string(from: referenceDate!) // ReferenceDate is Optional and not unwrapped
}
Why isn't referenceDate unwrapped ?
I tried to export the elem of the Dictionary in a constant, but same problem, it's not unwrapped
let refDateOpt = referenceDatesIdentifiers["ReferenceDateIdentifier"]
if let referenceDate = refDateOpt {
referenceDateLabel.text = otherDateFormater.string(from: referenceDate!) // Reference date is also Optional and not unwrapped
}
What is happening ?
You should required again optionally unwrap date with type casting will fix your issue. Update code as follow:
if let referenceDate = referenceDatesIdentifiers["ReferenceDateIdentifier"] as? Date {
print(referenceDate)
}
The problem here is that it doesn't makes any sense to create a dictionary with a nil value. So instead of checking if the key exists before adding a value, I just add the value and check the value with a if let
In the end, this was the answer
var referenceDatesIdentifiers: [String : Date] = [:]
if let referenceDate = referenceDatesIdentifiers["ReferenceDateIdentifier"] {
referenceDateLabel.text = otherDateFormater.string(from: referenceDate)
}
You could use below way. It will work perfectly.Update code as follow:
var referenceDatesIdentifiers: [String: Date?] =
["ReferenceDateIdentifier": nil]
if let refDateOpt = referenceDatesIdentifiers["ReferenceDateIdentifier"] as? Date {
print(refDateOpt)
}
or
if let refDateOpt = referenceDatesIdentifiers["ReferenceDateIdentifier"]{
if let referenceDate = refDateOpt {
print(referenceDate)
}
}

Guard let construction, still getting fatal error: Index out of range

I am trying to parse some data with this code:
func findDate(data: String?) -> String {
guard let date: String? = (data!.componentsSeparatedByString("T"))[0] else{
return "20000101"
}
return date!
}
I tried the guard structure to prevent errors when there is no data found, or it has a different structure, but I still get the error when I run it:
fatal error: Index out of range
Does somebody know how to fix this?
The access of the element at index zero always happens. If the result of the call to components(separatedBy:) returns an empty array, your code crashes. Also, you should avoid force unwrapping data.
A solution to these crashes is to use the first property of the array, which is optional, so you can safely unwrap it.
guard let date = data?.components(separatedBy: "T").first else {
return "20000101"
}
return date.
Also, this could then be simplified using the nil coalescing operator:
return data?.components(separatedBy: "T").first ?? "20000101"
Please check out this code:
func findDate(data: String?) -> String {
guard let date: String? = (data?.componentsSeparatedByString("T"))?[0] else{
return "20000101"
}
return date!
}
findDate(nil)
findDate("")
findDate("98588T99")
There are two issues here:
1) Trying to access an index of an array that isn't large enough doesn't produce nil, it just crashes. For example, this code will crash:
let strings: [String] = []
guard let firstString = strings[0] else {
// couldn't find firstString
}
Instead, use the first method:
let strings: [String] = []
guard let firstString = strings.first else {
// couldn't find firstString
}
first attempts to access the first element of the array, and it returns nil if the array doesn't have a first element.
2) The purpose of guard let is to guarantee that the value you get out isn't nil. So your date value should be of type String, not String?, and you shouldn't have to force-unwrap it. For example:
let strings: [String] = ["one", "two", "three"]
guard let firstString = strings.first else {
// no first string
}
print(firstString) // firstString is not nil, no need to unwrap it

Optional in text view showing when printing

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

Comparing non optional value leads to error

I am on point where I gotta compare non optional value with nil. But I can't do it because Xcode says:
Comparing non-optional value of type 'Int' to nil always returns false
So I created Struct and then made variable: var products: [Product] = []
How I am able to compare it with nil?:
if products[indexPath.row].snusPortions == nil
{
cell.snusPortionsAmountLabel.text = "N/A"
}else
{
cell.snusPortionsAmountLabel.text = String(products[indexPath.row].snusPortions)
}
I've assigned values to them like this:
let ref = FIRDatabase.database().reference().child("Snuses").queryOrdered(byChild: "Brand").queryEqual(toValue: brandName)
ref.observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.exists(){
let enumerator = snapshot.children
while let thisProduct = enumerator.nextObject() as? FIRDataSnapshot
{
print(thisProduct.value) // So I may see what the data is like and know how to extract it
// Chances are you'd have to create a dictionary
let thisProductDict = thisProduct.value as! [String:AnyObject]
let productName = thisProductDict["Products"] as! String
let snusPortions = thisProductDict["PortionsCan"] as? Int
let productObject = Product(snusProductTitle: productName, snusNicotine: snusNicotine, snusPortions: snusPortions!, snusFlavor: snusFlavor, snusWeight: snusWeight!, snusShippingWeight: snusShippingWeight, snusProductImageURL: productURL)
self.products.append(productObject)
print(self.products)
}
self.tableView.reloadData()
}
})
This is Product struct:
struct Product {
var snusProductTitle: String
init()
{
snusProductTitle = ""
}
init(snusProductTitle: String){
self.snusProductTitle = snusProductTitle
}
}
While testing it says snusPortions is nil but I said to make it "N/A" if it is nil, why?
It sounds like you are confusing yourself between the local variable snusPortions and the Product property snusPortions.
In your Product definition, the property snusPortions is an Int. It can never be nil. Hence, in this code:
if products[indexPath.row].snusPortions == nil
... this Product's snusPortions will never be nil, and we will never set the text to "N/A".
Now let's look at your other code:
let snusPortions = thisProductDict["PortionsCan"] as? Int
This is a completely different snusPortions. It can be nil, namely, if thisProductDict lacks a "PortionsCan" key or if its value is not castable to Int.

How to use guard to unrwap optionals in a dictionary?

I am parsing a dictionary with stuff to generate a Rocket object. In the parsing method i use guard to do my checks. Is it possible to use guard to both check for existing value in a dictionary, and unwrap it at the same time?
let rocketDictionary : [String : String?] = [ "name" : nil, "numberOfThrusters" : nil ]
func generateRocketSchematics(rocketDictionary : [String : String?]) {
guard let rocketName = rocketDictionary["name"] as? String else {
print("no rocket name")
return
}
print(rocketName)
}
This code won't compile because of the as? String. If i remove that, the rocketName will be a String? Is it possible to write the guard statement so that it will return a String
You can use pattern matching with guard/case:
func generateRocketSchematics(rocketDictionary : [String : String?]) {
guard case let rocketName?? = rocketDictionary["name"] else {
print("no rocket name")
return
}
print(rocketName) // rocketName is a String
}
rocketName?? is a synonym for .Some(.Some(rocketName)), so
case let rocketName?? = rocketDictionary["name"]
matches the case where rocketDictionary["name"] (which has the
type String??) is not nil, and the unwrapped value (which has
the type String?) is also not nil. The doubly-unwrapped value
(of type String) is then assigned to rockedName.