0

Im trying to get all the imageURLs for all activities that are for a specific user (UserId). the content of the snapshot include the correct entries, however the imageURL is not appended to imageArray. What am I doing wrong?

 // Get all images for a specific user

    func getImagesForUser(userId:String, handler: @escaping (_ imageArray: [String]) -> ()) {
        var imageArray = [String]()

        REF_ACTIVITY.queryOrdered(byChild: "userId").queryEqual(toValue: userId).observe(.value, with:  { snapshot in
            dump(snapshot)
            guard let data = snapshot.value as? NSDictionary else { print("else was returned"); return }

            let imageURL = data["imageURL"] as? String ?? ""
            imageArray.append(imageURL)
            handler(imageArray)
        })

    }

Output from dump(snap)

Snap (-L2RDXTKANzD8YGpPl92) {
    gearId = asfasfsf;
    imageCount = 3;
    imageURLs =     {
        1 = "http://www.foo.com";
        2 = "http://www.foo2.com";
        3 = "http://www.foo3.com";
    };
    killCount = 10;
    kills = 12;
    likeCount = 2;
    likes =     {
        fas86q39rasf = 1;
    };
    userId = NhZZGwJQCGe2OGaNTwGvpPuQKNA2;
}

Snap (-L2RDXTKANzD8YGpPl92) {
    gearId = asfasfsf;
    imageCount = 3;
    imageURLs =     {
        1 = "http://www.foo.com";
        2 = "http://www.foo2.com";
        3 = "http://www.foo3.com";
    };
    killCount = 10;
    kills = 12;
    likeCount = 2;
    likes =     {
        fas86q39rasf = 1;
    };
    userId = NhZZGwJQCGe2OGaNTwGvpPuQKNA2;
}

Update - output of Dict

24 elements
  ▿ 0 : 2 elements
    - key : "commentCount"
    - value : 1
// removed some info
...
  ▿ 12 : 2 elements
    - key : "imageURLs"
    ▿ value : 4 elements
      - 0 : <null>
      - 1 : https://firebasestorage.googleapis.com/v0/b/shoota-179610.appspot.com/o/activity_image%2FCA6F4C93-8F5F-456C-9FD6-F2EACC444739?alt=media&token=522d957b-dedf-4bdc-9cbc-1de581284393
      - 2 : https://firebasestorage.googleapis.com/v0/b/shoota-179610.appspot.com/o/activity_image%2FCA6F4C93-8F5F-456C-9FD6-F2EACC444739?alt=media&token=522d957b-dedf-4bdc-9cbc-1de581284393
      - 3 : https://firebasestorage.googleapis.com/v0/b/shoota-179610.appspot.com/o/activity_image%2FCA6F4C93-8F5F-456C-9FD6-F2EACC444739?alt=media&token=522d957b-dedf-4bdc-9cbc-1de581284393
  ▿ 13 : 2 elements
    - key : "imageURL"
    - value : https://firebasestorage.googleapis.com/v0/b/shoota-179610.appspot.com/o/activity_image%2FCA6F4C93-8F5F-456C-9FD6-F2EACC444739?alt=media&token=522d957b-dedf-4bdc-9cbc-1de581284393
  ▿ 14 : 2 elements
    - key : "killCount"
    - value : 10
  • You should probably start with the documentation: https://firebase.google.com/docs/database/ios/lists-of-data#sorting_and_filtering_data – Doug Stevenson Jan 18 '18 at 23:53
  • Since the documentation is sometimes a bit short on examples, this is also a good place to start: https://stackoverflow.com/search?q=%5Bfirebase%5D%5Bswift%5D+query – Frank van Puffelen Jan 18 '18 at 23:58
  • In the second question: thousands is no problem, tens of thousands also typically not, when you get to hundreds of thousands/million, thing become more interesting. See https://stackoverflow.com/questions/28857905/how-many-records-rows-nodes-is-alot-in-firebase, https://stackoverflow.com/questions/39712833/firebase-performance-how-many-children-per-node – Frank van Puffelen Jan 19 '18 at 00:02
  • @FrankvanPuffelen: Thanks for those links. They were useful. I've updated my question with my new code. I have some issues still, but I'm close I think... – Chris_1983_Norway Jan 19 '18 at 22:31

1 Answers1

1

There are a few issues with the code in the question:

It appears you just want to get the data one time but that observe will stay attached and any time there's a change anywhere in that node that matches the query, you will be notified. If you just want it one time, use observeSingleEvent instead

Also, you are returning multiple nodes with that query - and those will need to be worked with one at a time via a loop to iterate over the returned data.

Here's a very simplified example of that technique. Note there is no error checking; for example if one of the returned nodes doesn't contain an imageURL key that should be handled with a guard or perhaps a nil-coalescing operator (??) to assign a default in that case.

let activitesRef = self.ref.child("activities")
let query = activitesRef.queryOrdered(byChild: "userId").queryEqual(toValue: userId)
query.observeSingleEvent(of: .value, with: { snapshot in
    for child in snapshot.children {
        let snap = child as! DataSnapshot
        let dict = snap.value as! [String: Any]
        let url = dict["imageURL"] as? String ?? "no url"
        print(url)
    }
})

Edit:

This code assumes a structure like this

activities
  activity_0
     userId: "uid_0"
     imageURL: "www.someurl.com"
  activity_1
     userId: "uid_1"
     imageURL: "www.anotherurl.com"
  activity_2
     userId: "uid_0"
     imageURL: "www.coolurl.com"

in the above case two nodes will be returned in the snapshot and the above code will iterate over those nodes and print the url from each.

www.someurl.com
www.coolurl.com

Another Edit:

Based on some follow up comments, it appears there are multiple urls for each user. Here's an updated Firebase structure to match:

activities
  activity_0
     userId: "uid_1"
     urls
       url_0: "www.someurl.com"
       url_1: "www.anotherurl.com"
       url_2: "www.coolurl.com"
  activity_1
     userId: "uid_1"
     urls
       url_0: "www.thisurl.com"
       url_1: "www.thaturl.com"
       url_2: "www.ou812url.com"

Note that arrays should be avoided in NoSQL databases so we're using url_0, url_1 but in reality those node keys should be created with childByAutoId.

And then the code to query for all nodes for uid_1 and print their url's

let activitesRef = self.ref.child("activities")
let query = activitesRef.queryOrdered(byChild: "userId").queryEqual(toValue: userId)
query.observeSingleEvent(of: .value, with: { snapshot in
    for child in snapshot.children {
        let snap = child as! DataSnapshot
        print(snap.key)
        let urlSnap = snap.childSnapshot(forPath: "urls")
        for url in urlSnap.children {
            let aUrlSnap = url as! DataSnapshot
            let key = aUrlSnap.key
            let val = aUrlSnap.value as! String
            print("  key: \(key)  url: \(val)")
        }
    }
})

and finally the output

activity_0
  key: url_0  url: www.someurl.com
  key: url_1  url: www.anotherurl.com
  key: url_2  url: www.coolurl.com
activity_1
  key: url_0  url: www.thisurl.com
  key: url_1  url: www.thaturl.com
  key: url_2  url: www.ou812url.com
Jay
  • 34,438
  • 18
  • 52
  • 81
  • Thanks for the input. I've been playing around with it for a while now but without success. It fails on the let dict row... Swift also indicate that this will always fail... I've not managed to fix it.. – Chris_1983_Norway Jan 21 '18 at 13:45
  • @Chris_1983_Norway OOPS! I had a typo in the code; needed snap.value instead of just snap. Snap is a snapshot and snap.value is the data we are after. Try it now - super sorry about that. I should have tested it before posting. It's now been tested and will iterate and print all url's where the userId is equal to the uid you are looking for. I also added a nil coalescing operator to protect the code in case there was no imageURL node within one of the snapshots. – Jay Jan 21 '18 at 14:16
  • No worries. It is me that is thankful for you even taking the time to help. – Chris_1983_Norway Jan 21 '18 at 15:58
  • @Chris_1983_Norway I hope the answer helps! If it does, be sure to accept it so it can help others as well. – Jay Jan 22 '18 at 18:19
  • Im still struggling with this.. I've also changed the imageURLs to be an dict of multiple images. See updated question for print out of the "dict". I guess I need to cycle through this to add it to a local array? – Chris_1983_Norway Jan 23 '18 at 21:36
  • @Chris_1983_Norway Not sure specifically what you are struggling with but I added a little more info to my answer. The code is copied and pasted from a working project and I added a structure similar to yours and the output which I think matches what you are looking for. The dictionary that was added to your question is confusing as it doesn't match your Firebase structure and appears to not be directly related. Take another look at my answer and let us know where the difficulty is. – Jay Jan 24 '18 at 18:15
  • You are right, I had forgotten to update the output from the snap variable. Basically the imageURL is not imageURLs and contains 1 or more URLs. So basically I need to cycle through another layer to get all the URLs per activity, and across multiple activities... – Chris_1983_Norway Jan 26 '18 at 19:12
  • @Chris_1983_Norway ok, I got you. Check the updated answer. – Jay Jan 26 '18 at 20:51
  • Thanks that worked like a charm - very much appreciate the help – Chris_1983_Norway Jan 27 '18 at 17:09