1

I have a function to fetch convos and it worked but then I deleted all the documents in Firebase. So now when I run it, it says "document path cannot be empty" and the app crashes. I am not very familiar with Swift but in Python I simply just use a try and except.

In the try block I can simply copy and paste all my code, and the except block I just do my error handling. I'm not sure how to do this in Swift for my entire function. How can I rearrange my function so that the function body is inside a do/try block?

Also what is the most strategic spot to do my error handling, the viewModel file or the Service file? The viewModel file inherits functions from the service file.

viewModel file:

    func fetchConvos(){
        var userList: [User] = []
        service.getConversations() { users in
            userList = users
            userList.forEach { user in
                var messList: [Message] = []
              }

        }
    }

service file:

    func getConversations(completion: @escaping([User]) -> Void){

            Firestore.firestore().collection("users")
                .document(uid)
                .collection("user-convo")
                .getDocuments { snapshot, _ in
                    guard let documents = snapshot?.documents else { return }
                    
                    documents.forEach { doc in
                        let userID = doc.documentID
                        
                        users.append(userID)
                        completion(users)
                            
                        
                    }
                }
        
    }
halfer
  • 19,824
  • 17
  • 99
  • 186
ahmed
  • 341
  • 2
  • 9
  • @loremipsum I saw that but wasnt able to fix my code with that, I simply need to use a try except block for each of my functions. Would you know how to do that? – ahmed Dec 21 '22 at 03:05
  • 1
    Firebase doesn't provide that, for this error, your `uid` is more than likely `nil` you can `throw` your own error by checking before you put it in the `document` Firebase is terrible at throwing errors. This isn't a Swift this you are attempting to `catch` from something that doesn't `throw` that is an API issue. – lorem ipsum Dec 21 '22 at 03:14
  • Something you could try is switching to the `async/await` methods, there is a little better error handling there. https://stackoverflow.com/questions/73636543/thread-1-exc-bad-instruction-when-fetching-data/73637713#73637713 – lorem ipsum Dec 21 '22 at 03:19
  • @loremipsum so should i try and change up my functions in the service file or viewModel file? or both. – ahmed Dec 21 '22 at 03:33
  • Service, once you have it in the service you can use it in the VM – lorem ipsum Dec 21 '22 at 03:34
  • @loremipsum any idea how to check if a collection exists – ahmed Dec 21 '22 at 03:49
  • No, I think it usually gets added (if adding) or the snapshot will just come up empty when retrieving. But try that other method, see if you can at least get a decent error then as a developer you can use some kind of analytics to bring it to your attention and the user will get told something useful. – lorem ipsum Dec 21 '22 at 14:13

2 Answers2

0

Trying to handle an exception as flow control is a bad idea here. See for an explanation why that is: Why not use exceptions as regular flow of control?

So instead of handling the exception that you get when uid is null or empty, check whether it has a value first and then only call the Firestore API with it if it has a value. For example:

if !(uid ?? "").isEmpty {
    Firestore.firestore().collection("users")
        .document(uid)
        .collection("user-convo")
        ...
}

There is no specific operation to check whether a collection exists.

Instead collection is comes into existence when you add the first document to it, and disappears when you remove the last document from it.

So to check whether a collection exists, the simplest way is to try and read a single document from it.

Firestore.firestore().collection("users")
    .document("theUID")
    .collection("user-convo")
    .limit(to: 1) // 
    .getDocuments { snapshot, _ in
        if !snapshot!.isEmpty ... // 
    }
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • in my case the uid is not the issue, the uid already has the right value. Your solution assumes that if there is a uid there is a collection. My problem is that I have a uid and I have to check if there is a collection. – ahmed Dec 21 '22 at 05:55
  • 1
    Given the `document path cannot be empty`, you are passing an empty value to `document()` somewhere. In the code you shared, the only option for that seems to be the `uid`. --- I updated my answer to show how to check whether a subcollection exists too. – Frank van Puffelen Dec 21 '22 at 14:16
  • thanks, if I try and read 1 document from a collection as you modeled above would that still result in the error: "document path cannot be empty" if there is no collection existing for that path. – ahmed Dec 22 '22 at 00:23
  • Basically does your solution above not cause a fatal error if there is no collection existing from which I am calling. – ahmed Dec 22 '22 at 00:24
  • "does your solution above not cause a fatal error if there is no collection" Nope it doesn't, lack of collections never causes an error like that. If you get that error **you are passing an empty value to the `document` method. There is no other way to get it (and hence me specifying a hardcoded value in my answer). Did you try the code like that, with a hard-coded value - even if that doesn't fit your use-case, it will help in figuring out where the problem comes from. – Frank van Puffelen Dec 22 '22 at 01:49
0

I would avoid "checking" to see if a collection exists or doesn't, unless if there is a good and specific reason to. If there isn't, then simply query the documents you need and if you get them you get them and if you don't you don't; springboard from there.

As to your error, a do-try-catch approach is not the solution I'd use here because that sort of error handling should be reserved for things out of your control, like network tasks. Your specific error is completely in your control because you've constructed an illegal document path. You can, and should, safeguard against this by simply ensuring the strings you pass into the path are valid, which is always best practice if there's a chance they can be nil.

Furthermore, deleting the documents from this collection and the error you're receiving appear to be coincidental, unless if there is code elsewhere that's constructing this document path based on the existence of documents in this collection. If there isn't (and there probably shouldn't) then this is just a coincidence.

trndjc
  • 11,654
  • 3
  • 38
  • 51
  • what do you mean by "if you get them you get them and if you dont you dont" my initial code doesnt check if a collection exists or not. It simply trys to get the documents from that collection as you said is a better approach. But in doing so I get a fatal error "document path cannot be empty" – ahmed Dec 22 '22 at 00:26