App crashed due to implicitly unwrapped optional (SwiftUI, Firebase) - swift

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.

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!

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