3

High level: In Cloud Firestore, I have two collections. fl_content and fl_files. Within fl_content, I am trying to access fl_files.

Detailed: In fl_content, each document has a field called imageUpload. This is an array of Firebase Document References. (a path to fl_files that I need to access.)

Here's my query for fl_content, in which I am accessing imageUpload reference:

let docRef = Firestore.firestore().collection("fl_content").document(item.id)
docRef.getDocument { (document, error) in
    if let document = document, document.exists {
       let property = document.get("imageUpload")
       print("PROPERTY \(property!)")
    }
}

This prints the following to the console:

PROPERTY Optional(<__NSArrayM 0x60000281d530>(
<FIRDocumentReference: 0x600002826220>
)
)

With this array of Document References, I need to get to fl_files.

This is the part I am having trouble with.

Attempts:

Within the if let statement, I tried accessing fl_files by casting property as a DocumentReference.

    let docRef = Firestore.firestore().collection("fl_content").document(item.id)
     docRef.getDocument { (document, error) in
         if let document = document, document.exists {
            let property = document.get("imageUpload") as? DocumentReference
            print("PROPERTY \(property!)")
            let test = Firestore.firestore().collection("fl_files").document(property)
         }
     }

Cannot convert value of type 'DocumentReference?' to expected argument type 'String'

    let docRef = Firestore.firestore().collection("fl_content").document(item.id)
     docRef.getDocument { (document, error) in
         if let document = document, document.exists {
            let property = document.get("imageUpload") as! DocumentReference
            let test = Firestore.firestore().collection("fl_files").document(property[0].documentID)
            print("TEST \(test)")
         }
     }

Value of type 'DocumentReference' has no subscripts

    let docRef = Firestore.firestore().collection("fl_content").document(item.id)
     docRef.getDocument { (document, error) in
         if let document = document, document.exists {
            let property = document.get("imageUpload") as! DocumentReference
            let test = Firestore.firestore().collection("fl_files").document(property.documentID)
            print("TEST \(test)")
         }
     }

Could not cast value of type '__NSArrayM' (0x7fff87c50980) to 'FIRDocumentReference' (0x10f6d87a8). 2020-02-05 12:55:09.225374-0500 Database 1[87636:7766359] Could not cast value of type '__NSArrayM' (0x7fff87c50980) to 'FIRDocumentReference' (0x10f6d87a8).

Getting closer!

    let docRef = Firestore.firestore().collection("fl_content").document(item.id)

    docRef.getDocument(completion: { document, error in
        if let err = error {
            print(err.localizedDescription)
            return
        }
        let imageUpload = document?["imageUpload"] as? NSArray ?? [""]
        print("First Object \(imageUpload.firstObject!)")

    })

This prints: First Object <FIRDocumentReference: 0x600001a4f0c0>

Here are two screenshots to help illustrate what the Firestore database looks like..

Firestore 1

enter image description here

Ultimately, I need to get to the file field within fl_files. How do I access this from the imageUpload DocumentReference?

Joe
  • 3,772
  • 3
  • 33
  • 64
  • You're getting array. You'll have to iterate it or index into it to get the path string to work with. – Doug Stevenson Feb 04 '20 at 20:08
  • @DougStevenson - getting "Value of type 'Any' has no subscripts" - really struggling with this – Joe Feb 05 '20 at 02:45
  • Cast it to the expected type before doing that. – Doug Stevenson Feb 05 '20 at 02:46
  • "Value of type 'DocumentReference' has no subscripts" – Joe Feb 05 '20 at 03:10
  • What if you try accessing by document id, which is the property of [DocumentReference](https://firebase.google.com/docs/reference/js/firebase.firestore.DocumentReference#properties_1)? Like this `let test = Firestore.firestore().collection("fl_files").document(property.id)` – Emil Gi Feb 05 '20 at 10:10
  • @EmilGi - Tried it.. produced an error, I edited my question to show you the error – Joe Feb 05 '20 at 17:56
  • I tossed out an answer but a couple of things about the question *Within fl_content, I am trying to access fl_files* ...but... *fl_content*, has no *fl_files* **within** it ; *fl_files* and *fl_content* are at the same level. Secondly, in *imageUpload* you're including a path to fl_files and that's not needed. Just store the collection docId alone; e.g. change this *0: /fl_files/docId* to just this *0: docId* as you know the documentId will always be stored in *fl_files*. Last thing: arrays are somewhat painful in NoSQL databases so you may want to consider another structure. – Jay Feb 05 '20 at 18:50

2 Answers2

3

Finally got it, thanks to the help of @Jay and @Emil Gi.

The "A-HA" moment came from Emil Gi's comment: All I can say is that if you successfully get element of DocumentReference type, then it must have an id property, which you can extract and query collection by document id.

    let imageUploadReference = item.imageUpload.first as? DocumentReference
    let docRef = Firestore.firestore().collection("fl_files").document(imageUploadReference!.documentID)
    docRef.getDocument(completion: { document, error in
        if let err = error {
            print(err.localizedDescription)
            return
        }
        let fileNameField = document?.get("file") as! String

        print("File name from fl_files \(fileNameField)")


    })

Once I finally had access to the corresponding "file", it was very simple to download the full URL of the image to the imageView.

I appreciate all of your help!!!

Joe
  • 3,772
  • 3
  • 33
  • 64
  • Great! Just a note though. *query collection by document id.* is not needed as if you have the document id, you won't need to query at all, you just access it directly. You may already know that. – Jay Feb 06 '20 at 19:48
  • @Jay - I'm not too sure I follow. If there's a more efficient way to get to "file" in "fl_files" - I'd love to know how! – Joe Feb 07 '20 at 20:51
  • 1
    You mentioned querying for a document that you know the id of *query collection by document id.*. If you know the document ID of a document, you don't need to do a query for it - queries have a lot more overhead than accessing it directly. In other words, if I have *users/uid_0* and I know I want to access uid_0, I would not need to query the *users* collection for uid_0, I could just access it at *users/uid_0*. – Jay Feb 07 '20 at 22:14
2

Here's some sample code that shows how to read and print out any of the fields values and also how to read the imageUpload field (an array) and print out the first element's value.

I've included both ways to read data from a document because I believe it answers both parts of the question: how to get the array field imageUpload and then access the first element within that array and how to get the value of the file field within fl_files.

Assuming this points to the correct document:

let docRef = Firestore.firestore().collection("fl_content").document(item.id)

this code will read two fields from the document being pointed to: imageUpload (an array) and fl_id (a String)

docRef.getDocument(completion: { document, error in
    if let err = error {
        print(err.localizedDescription)
        return
    }

    print( document!.data() ) //for testing to see if we are getting the fields

    //how to read an array and access it's elements
    let imageUploadArray = document?["imageUpload"] as? Array ?? [""]
    if let url = imageUploadArray.first {
        print(url)
    }

    //this is also valid for reading the imageUpload field array
    //let anArray = document?.get("imageUpload") as? Array ?? [""]
    //if let url = anArray {
    //    print(url)
    //}

    //how to read a single field, like file within fl_files
    let someId = document?.get("fl_id") as! String //example of reading a field
    print(someId)

})

Edit:

It appears the objects stored in the imageUpload array are references not strings. As an example of how to access them... here's the code

let refArray = document?.get("imageUpload") as? [DocumentReference] ?? []
for docRef in refArray {
   print(docRef.path) //prints the path to the document at this ref
}
Jay
  • 34,438
  • 18
  • 52
  • 81
  • Appreciate your help, @Jay. let imageUploadArray = document?["imageUpload"] as? Array ?? [""] is just returning an empty array (as noted with the ?? [""] ). – Joe Feb 05 '20 at 22:04
  • @Joe That's a nil coelescing operator. It will return the imageUpload as an Array unless it doesn't exist. If it doesn't exist it returns an empty array to protect your code. If that's what's happening, then either I have a typo in my code (my code runs against my Firestore), or you have a typo or possibly the docRef is not pointing to the correct location. I just added a print statement to my answer right after the `if let err` closure. That should print all of the field names to the console - see if *imageUpload* is one of them. If not, then your not pointing the correct document. – Jay Feb 05 '20 at 23:40
  • So close! (I think...) Looks like I had to cast it to an NSArray; not Array. If you see the last part of my edited question, you can see what I'm getting in the console. That seems to be a FIRDocumentReference of the first item in the imageUpload array. I just need to use that to get to fl_files > file. So that I can populate this imageView! I don't know what to do with FIRDocumentReference: 0x600001a4f0c0 to get to fl_files > file. – Joe Feb 06 '20 at 01:46
  • 1
    @Joe, I am not really familiar with swift. All I can say is that if you successfully get element of [DocumentReference](https://firebase.google.com/docs/reference/js/firebase.firestore.DocumentReference#properties_1) type, then it must have an id property, which you can extract and query collection by document id. – Emil Gi Feb 06 '20 at 12:46
  • @Joe No. Do not use NSArray. Please use Swift constructs `Array` or cast to specific type of Array (read on). So here's the issue, your imageUpload value is not a string, it's a *reference* e.g. the Array contains DocumentReference objects. I would suggest making it a string and once that's done, the code in my answer will work. However, since it's a reference, when you read it, it's a DocumentReference (which complicates it a bit). See the Edit to my answer. – Jay Feb 06 '20 at 17:04
  • @Jay and Emil Gi - thank you so much for your help! I finally got this working, after what you both said finally clicked :) I posted an answer showing the code that I used to get this working. – Joe Feb 06 '20 at 18:01