tobeiosdev tobeiosdev - 5 months ago 241
iOS Question

Swift - How Can I use 'shouldChangeTextInRange' with Firebase for real time searching?

I wonder that how creating Firebase matchesRegex query and listing current results using shouldChangeTextInRange.

I have did it before in Parse for searching username or user fullname in Parse Cloud. If someone shed light my path to do it in Firebase, it would be great.

// search updated
func searchBar(searchBar: UISearchBar, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {

// find by username
let usernameQuery = PFQuery(className: "_User")
usernameQuery.whereKey("username", matchesRegex: "(?i)" + searchBar.text!)
usernameQuery.findObjectsInBackgroundWithBlock { (objects:[PFObject]?, error:NSError?) -> Void in

if error == nil {

// if no objects are found according to entered text in username column, find by fullname
if objects!.isEmpty {

let fullnameQuery = PFUser.query()
fullnameQuery?.whereKey("fullname", matchesRegex: "(?i)" + self.searchBar.text!)
fullnameQuery?.findObjectsInBackgroundWithBlock({ (objects:[PFObject]?, error:NSError?) -> Void in

if error == nil {

// clean up
self.usernameArray.removeAll(keepCapacity: false)
self.avaArray.removeAll(keepCapacity: false)

// found related objects
for object in objects! {
self.usernameArray.append(object.objectForKey("username") as! String)
self.avaArray.append(object.objectForKey("avatar") as! PFFile)
}

// reload
self.tableView.reloadData()

}

})

}

}

// clean up
self.usernameArray.removeAll(keepCapacity: false)
self.avaArray.removeAll(keepCapacity: false)

// found related objects
for object in objects! {
self.usernameArray.append(object.objectForKey("username") as! String)
self.avaArray.append(object.objectForKey("avatar") as! PFFile)
}

// reload
self.tableView.reloadData()

}

return true

}



Update 1:


I couldn't do it using same parent name, snap.value didn't give me response, even not compile. So I have tried to do two separate parent node like that: firebase screen shot link

I'm getting indexOn security warning for each letter stroke first_name subnodes on xcode console:


[Firebase] Using an unspecified index. Consider adding ".indexOn": "first_name/T" at /people_spell to your security rules for better performance

[Firebase] Using an unspecified index. Consider adding ".indexOn": "first_name/Te" at /people_spell to your security rules for better performance

... much more times


I have tried these security rules it didn't solve these problems. It doesn't encompass each subnodes. How Can I provide this?

"people_spell": {
".read": "auth !== null",
".write": "auth !== null",
".indexOn": "first_name"
},
"people": {
".read": "auth !== null",
".write": "auth !== null",
".indexOn": "first_name"
}


I'm using these lines of codes:

let peopleSpellRef = Firebase(url: "https://sampleproject.firebaseio.com/people_spell")
let peopleRef = Firebase(url: "https://sampleproject.firebaseio.com/people")

func searchBar(searchBar: UISearchBar, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {

self.usernameArray.removeAll(keepCapacity: false)

peopleSpellRef.queryOrderedByChild("first_name/\(searchBar.text!)").queryEqualToValue(true)
.observeEventType(.Value, withBlock: { snapshot in

let enumerator = snapshot.children
while let rest = enumerator.nextObject() as? FDataSnapshot {

peopleRef.childByAppendingPath("\(rest.key)").observeEventType(.Value, withBlock: { snapshot in

self.usernameArray.removeAll(keepCapacity: false)

let str: String = (snapshot.value.objectForKey("first_name")) as! (String)
print(str)
self.usernameArray.append(str)
self.userkeyArray.append(snapshot.key)

self.tableView.reloadData()
})
}


self.usernameArray.removeAll(keepCapacity: false)
self.tableView.reloadData()

})

return true
}

Jay Jay
Answer

This is not how Firebase works and you can't directly do a 'real time search' in Firebase for a substring. But, you can format your Firebase data to be searched and there are a couple of other methods which will also accomplish the goal.

One is straightforward: load your data from Firebase into an array and NSPredicate search (or other methods) through the array.

Another is that query'ing for full strings is easy; as long as the text is broken up into the chunks you want to search for:

people
  person_0
    first_name: "Ted"
    last_name: "Stryker"
    position: "sitting down and facing front"
  person_1
    first_name: "Teddy"

You can search for 'Ted' in first_name or 'Stryker' in last_name but you cannot search for 'front' in position.

Now the workaround and keep in mind disk space is cheap.

A node can be created that will let you search for stuff in a variety of ways

people
  person_0
   first_name //First name is Ted from above
    T: true
    Te: true
    Ted: true
    ed: true
    d: true
    e: true
  person_1
   first_name  //First name is Teddy from above
    T: true
    Te: true
    Ted: true
    etc

With this structure we can find all first names that start with Ted

    peopleRef.queryOrderedByChild("first_name/Ted").queryEqualToValue(true)
         .observeEventType(.Value, withBlock: { snapshot in

        for child in snapshot.children {
          print(child.value)  
        }
    })

Other searches can be performed; searching for all first names that contain e or Te etc.

If this is purely a situation where you are searching to perform an autofill in a text field (for example) pinging firebase on a keystroke by keystroke basis isn't going to work well.

Loading the data from Firebase into an array, or dictionary (or whatever) will probably be your best solution.