Found nil, after nil-check - swift

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!!

Related

guard let found nil

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

Casting in swift

So my swift skills arent great... I want to try and retrieve an audio track description for a video and have come up with this method. If anything fails the method should return null
func getAudioTrackDescription(path: String) -> AudioStreamBasicDescription? {
let asset = getAssetFromPath(path: path)
guard let track = asset.tracks(withMediaType: AVMediaType.audio).first else {
return nil
}
guard let audioDesc = track.formatDescriptions.first else {
return nil
}
// let casted = audioDesc as! CMAudioFormatDescription
// THE SDF IS JUST AN EXAMPLE THAT CAUSES IT TO FAIL
// I ACTUALLY WANT TO USE THE COMMENTED LINE ABOVE
if let casted = ("Sdf" as! CMAudioFormatDescription) {
let basic = CMAudioFormatDescriptionGetStreamBasicDescription(casted)
guard let pointee = basic?.pointee else {
return nil
}
return pointee
} else {
return nil
}
}
I am really struggling to understand how the casting works in swift...
The track.formatDescriptions list holds type Any which means it could crash if the type is not what i expect it to be (CMAudioFormatDescription)
For example I have changed the if audioDesc to be the string "Sdf" and the code crashes.
I want to be able to check if audioDesc can be casted to CMAudioFormatDescription and if cant i want to return nil.
I tried using as? and this always gives me this error
Conditional downcast to CoreFoundation type 'CMAudioFormatDescription' (aka 'CMFormatDescription') will always succeed
Can someone help?
---------------------- EDIT
is below a safe approach?
func getVideoAudioChannelCount(path: String) -> Int {
guard let audioFile = getAudioInformation(path: path) else {
return DEFAULT_AUDIO_CHANNEL
}
return Int(audioFile.channelCount)
}
func getAudioInformation(path: String) -> AVAudioFormat? {
var returnVar: AVAudioFormat?;
do {
returnVar = try AVAudioFile(forReading: URL(fileURLWithPath: path)).fileFormat
} catch _ {
returnVar = nil
}
return returnVar
}
According to the documentation, the array will always contain CMFormatDescription (aka CMAudioFormatDescription), so you can safely cast with as!.
The array contains CMFormatDescriptions (see CMFormatDescription), each of which indicates the format of media samples referenced by the track.
You get the error that conditional casts always succeed because CMAudioFormatDescription is a Core Foundation type (it conforms to _CFObject). For more info, see here.
Edit:
is below a safe approach?
Yes, if by "safe" you mean it won't crash. You can simplify the code if you use try?:
func getAudioInformation(path: String) -> AVAudioFormat? {
try? AVAudioFile(forReading: URL(fileURLWithPath: path)).fileFormat
}

How do I return safely unwrapped optionals which are outside of their scope?

I'm a noob, bear with me:
func createEmployeeCode() -> String? {
let email = UserDefaults.standard.object(forKey: "email_Saved") as? String
let employeeCode = UserDefaults.standard.object(forKey: "employeeCode_Saved") as? String
if let emailString = email,
let employeeCodeString = employeeCode {
return (emailString+employeeCodeString)
}
return (emailString+employeeCodeString) //ERROR: Use of unresolved identifier 'employeeCodeString' & Use of unresolved identifier 'emailString'
}
I understand the reason the error shows is because I'm trying to return something that is in a different scope here, but how else can I get the function to return the 2 strings together without the "Optional[...]" tag?
Here's how I'd expect it to be done in a normal production app
(You wouldn't do any of this in a normal production app! But this is the "idiom" you're looking for.)
func createCodeIfPossible() -> String? {
guard let e = UserDefaults.standard.object(forKey: "email_Saved") else {
print("serious problem, there's no email saved")
// at this point the app is completely buggered, so give up
return ""
}
guard let c = UserDefaults.standard.object(forKey: "employeeCode_Saved") else {
print("serious problem, there's no code saved")
// at this point the app is completely buggered, so give up
return ""
}
return e + c
}
Do note that the return is largely meaningless - in the app in question, if one of the guards breaks you are "totally screwed". I'd probably just return a blank string (or more likely something like "name_error") since at this point the app architecture is hopelessly broken.
(Sidenote: use UserDefaults.standard.string(forKey:).)
The issue is that you can't know if those strings DO both exist or not--if they do, you already have a great if let that returns your answer. The question now is what do you want to do if one or both are nil? Maybe you'd like to return nil from the entire function. If so,
func createEmployeeCode() -> String? {
let email = UserDefaults.standard.string(forKey: "email_Saved")
let employeeCode = UserDefaults.standard.string(forKey: "employeeCode_Saved")
if let emailString = email,
let employeeCodeString = employeeCode {
return (emailString+employeeCodeString) //successful unwrapping, let's concatenate!
}
return nil //if one or both `if let`s fail, we end up here
}
Of course, you could do whatever you'd like in that "bad" case. Maybe you'd like to show whatever string you DO have. In that case:
func createEmployeeCode() -> String {
let email = UserDefaults.standard.string(forKey: "email_Saved")
let employeeCode = UserDefaults.standard.string(forKey: "employeeCode_Saved")
return (email ?? "") + (employeeCode ?? "") //this works in the "good" case, too, and uses the nil coalescing operator `??`
}
In this case, you can see that the return value is no longer optional. This is because even if neither string exists, it'll concatenate two empty strings. If this feels icky, you could keep your optional return value and do a quick check before returning:
if email == nil && employeeCode == nil { return nil }
func createEmployeeCode() -> String {
var finalString = String()
if let email = UserDefaults.standard.string(forKey: "email_Saved"), let employeeCode = UserDefaults.standard.string(forKey: "employeeCode_Saved") {
finalString = email + employeeCode
}
return finalString
}
When assign back the values from userDefaults you've been trying to get as an object instead of string
func createEmployeeCode() -> String? {
let email:String = UserDefaults.standard.string(forKey: "email_Saved") ?? ""
let employeeCode:String = UserDefaults.standard.string(forKey: "employeeCode_Saved") ?? ""
let emailString = "\(email)\(employeeCode)"
return emialString
}
There are different ways to solve this depending on what you're trying to achieve.
If you always want to create an employeeCode (even if the code will be empty):
Try using a "nil coalescing operator".
func createEmployeeCode() -> String {
let email = UserDefaults.standard.object(forKey: "email_Saved") as? String ?? ""
let employeeCode = UserDefaults.standard.object(forKey: "employeeCode_Saved") as? String ?? ""
return (email+employeeCode)
}
To explain what's happening here:
We're unwrapping email, if we don't find email then we default the value to an empty string, "".
We do the same with employeeCode.
This isn't a way I would solve every unwrap issue though, but it suits your usecase of email and employeeCode because you're always wanting to return something based on your original question. I've also changed the return type to non-optional.
If an employee code must always contain an email and and a code then we want to return nil if one of those isn't found.
Try using the guard statement. The guard statement is perfect for validation and very readable:
func createEmployeeCode() -> String? {
guard let email = UserDefaults.standard.object(forKey: "email_Saved") as? String else { return nil }
guard let employeeCode = UserDefaults.standard.object(forKey: "employeeCode_Saved") as? String else { return nil }
return (email+employeeCode)
}
Try this function:
func createEmployeeCode() -> String {
let email = UserDefaults.standard.object(forKey: "email_Saved") as? String
let employeeCode = UserDefaults.standard.object(forKey: "employeeCode_Saved") as? String
return [email, employeeCode].compactMap { $0 }.joined(separator: "")
}
It will return email or employeeCode or email+employeeCode in case one of them is nil or both are present, or empty String in case if both are missed out!

Swift correct approach to nil value

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

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