tobeiosdev tobeiosdev - 1 year ago 616
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 Source

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.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download