0

I just started working with Firebase with Swift and have a example messages node. I add messages children that hold the text and username, etc. I have security rules set to be authenticated for each .write and .read. I created a observer to watch for this even and it is firing but the snapshot it returns is null. The reason I need the snapshot data is so I can remove that exact message from the client side.

Data Node Structure:

{
"messages" : {
    "-KcM3SlAQXfjCz01waXF" : {
    "name" : "mgardner1994",
    "text" : "test23"
    },
    "-KcM3UKA_U7n2YwhlFeB" : {
    "name" : "mgardner1994",
    "text" : "test4"
    },
    "-KcMB_8Ec74HIQGL9adt" : {
    "name" : "mitchell_maler",
    "text" : "test5"
    },
    "-KcOC08kLUO-cEWLeICG" : {
    "name" : "mitchell_maler",
    "text" : "hello"
    },
    "-KcOC6ZWT6gyVi6pxGF8" : {
    "name" : "mitchell_maler",
    "text" : "test"
    }
}
}

Rules:

{
 "rules": {
   "messages": {
     ".read": "auth != null",
     ".write": "auth != null"
     }
   }
}

Swift Code:

_refHandleDel = ref.child("messages").observe(.childRemoved, with: { (snapshot) -> Void in
    // TODO: Why is snapshot returning nil
    let index = self.messages.index(of: snapshot)
    self.messages.remove(at: index!)
    self.messagesTable.deleteRows(at: [IndexPath(row: index! , section: 0)], with: .automatic)
})

Edit: Added data structure to text instead of an image.

mitchellmaler
  • 165
  • 1
  • 1
  • 9
  • 1
    Can you add *print(snapshot)* before the *let index =* line and see what prints Also, based on the rules, I assume the user that is doing the deleting is authenticated. Oh, and please include your Firebase structure as text in your question. Links tend to expire and images are not searchable or copy/pasteable so if we need to answer with structure change, we would have to retype it. – Jay Feb 07 '17 at 21:56
  • Hello Jay, thanks for taking a look at this. Thanks for the tip about the image, I figured out how to export the json from the console and added it to the question. I added the print statement and it is printing it out to the console. It makes more sense now, in the watchers it doesn't show the full snapshot object but it does when printed. This now seems like an issue with my .index(of:) method not finding it correctly. – mitchellmaler Feb 07 '17 at 23:59
  • I think this can be simplified considerably which eliminates the problem entirely. See my answer. – Jay Feb 08 '17 at 01:10

2 Answers2

0

I was able to get around the issue by creating a lookup function that looks for the key. I am still unsure why the index(of) method isn't working but will keep digging.

_refHandleDel = ref.child("messages").observe(.childRemoved) { (snapshot: FIRDataSnapshot!) in
        print(snapshot)

        // Never finds the object and returns nil
        let index = self.messages.index(of: snapshot)
        print(index ?? "no")

        // using the workaround method it finds the index using the key
        let index2 = self.indexOfSnapshotInArray(snapshot: snapshot, snapshotArray: self.messages)
        print(index2)

        if (index2 != -1) {
        self.messages.remove(at: index2)
        self.messagesTable.deleteRows(at: [IndexPath(row: index2 , section: 0)], with: .automatic)
        } else {
            print("Could not find message in array.")
        }
    }


func indexOfSnapshotInArray(snapshot: FIRDataSnapshot, snapshotArray: [FIRDataSnapshot]) -> Int {
    var index = 0
    for snap in snapshotArray {
        if (snapshot.key == snap.key) {
            return index
        }
        index += 1
    }
    return -1
}
mitchellmaler
  • 165
  • 1
  • 1
  • 9
0

You may want to go about it in another way:

Each snapshot (or the child snapshots) will have a key and that's what you should be looking up in your array (table, dataSource etc)

In other words, when the messages are initially loaded (into a dataSource for example) they will be in a key:value pair of

messages
  -KcM3SlAQXfjCz01waXF (the key)
    name : "mgardner1994", (the values which are a dictionary)
    text : "test23"

Ideally you will end up with an array of those key value pairs

a[0] = key:value = -KcM3SlAQXfjCz01waXF: dictionary of children
a[1] = key:value = -KcM3UKA_U7n2YwhlFeB: dictionary of children

Note the above is dictionary but it could be a UserClass or ItemsClass

a[0] = aUser (UserClass)
a[1] = aUser (UserClass)

When your app receives a remove event, it will be passed the key:value pair as the snapshot. We don't care about the value but we do care about the key as you can then find the index of that item in the array and remove it. (via ObjC NSPredicate to find the match) or... something like this in Swift if you are using a UserClass for example

func handleChildRemoved(_ snapshot: FDataSnapshot! ) {

  let key = snapshot.key

  if let index = self.usersArray.indexOf({$0.firebaseKey == key}) {
      self.usersArray.removeAtIndex(index)
      self.usersTableView.reloadData()
  }
}

In this example, I have UserClass

class UserClass {
   firebaseKey = ""
   userName = ""
}
Jay
  • 34,438
  • 18
  • 52
  • 81
  • Thank you so much! Yea the direction I was going was not efficient but by using the index(where:) method makes it so much simpler. Swift renamed the .indexOf to .index(where:). – mitchellmaler Feb 08 '17 at 01:23