How to retrieve multiple data in one query Firebase - ios

I'm new in firebase and nosql. I followed the guide for structuring data and here is what my data look like
{"Guardians" : {
"jojo-pti-gros" : {
"-KBT2ui6wlC6Fgk1c7Xa" : {
"creationDate" : "2016-02-26 15:50:56",
"level" : "2",
"light" : "2",
"type" : "Titan"
}
}
},
"Players" : {
"jojo-pti-gros" : {
"console" : "PS4",
"creationDate" : "2016-02-26 15:50:39",
"currentGuardian" : "-KBT2ui6wlC6Fgk1c7Xa",
"email" : "toto#gmail.com",
"gamertag" : "jojo-pti-gros",
"guardians" : {
"-KBT2ui6wlC6Fgk1c7Xa" : "true"
}
}
},
"Teams" : {
"-KBhfH9U3CxQPZiCs5ru" : {
"activity" : "Raid - Normal - Crota's end",
"creationDate" : "2016-02-29 16:37:14",
"language" : "Any language",
"leader" : "jojo-pti-gros",
"maxPlayers" : "6",
"mic" : "true",
"minimumLight" : "55",
"players" : {
"jojo-pti-gros" : "true"
},
"teamDescription" : "Chaud pour un raid"
}
}}
When I try to retrieve a team I would like to have a json like this.
"-KBhfH9U3CxQPZiCs5ru" = {
activity = "Raid - Normal - Crota's end",
creationDate = "2016-02-29 16:37:14",
language = "Any language",
leader = "jojo-pti-gros",
maxPlayers = 6,
mic = true,
minimumLight = 55,
players = {
"jojo-pti-gros" = {
"creationDate" = "2016-02-26 15:50:56",
"level" = "2",
"light" = "2",
"type" = "Titan"
}
},
teamDescription = "Chaud pour un raid"
}
But I have this json instead
"-KBhfH9U3CxQPZiCs5ru" = {
activity = "Raid - Normal - Crota's end",
creationDate = "2016-02-29 16:37:14",
language = "Any language",
leader = "jojo-pti-gros",
maxPlayers = 6,
mic = true,
minimumLight = 55,
players = {
"jojo-pti-gros" = true
},
teamDescription = "Chaud pour un raid"
}
My code right now
let fb = Firebase(url:myUrl)
fb.childByAppendingPath("/Teams").observeSingleEventOfType(.Value, withBlock: { teams in
var teamsArray: [Team] = []
if (teams.value is NSNull) {
print("no teams found")
} else {
print(teams.value)
for team in teams.value as! NSDictionary {
let newTeam = Team()
newTeam.dictionaryToTeam(team.value as! NSDictionary)
newTeam.id = String(team.key)
for player in newTeam.playerDictionary as NSDictionary {
GuardianAPI.sharedInstance.getGuardianByGamertagAndID(player.key as! String, id: player.value as! String, handler: {newGuardian in
newTeam.playerDictionary.setValue(newGuardian, forKey: player.key as! String)
})
}
teamsArray.append(newTeam)
}
handler(true)
}
})
I don't know how to make a query with juncture like SQL.
Can somebody know how to resolve this ?

There are a couple of directions to go with this.
One option is to read in your team node, separate out the player refs into perhaps an array and then iterate over the array to get each player ref and load each player with a separate Firebase call
A better option is to store a reference back to the team in each player's node
"Players" : {
"jojo-pti-gros" : {
team: "-KBhfH9U3CxQPZiCs5ru"
}
Observe the teams node to load in the team data then then query the players node for all players that have a child: team = "-KBhfH9U3CxQPZiCs5ru"
Firebase *playersRef = [myRootRef childByAppendingPath:#"Players"];
FQuery *query1 = [playersRef queryOrderedByChild:#"team"];
FQuery *query2 = [query1 queryEqualToValue:#"-KBhfH9U3CxQPZiCs5ru"];
[query2 observeEventType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot) {
NSLog(#"key: %# %#", snapshot.key, snapshot.value);
}];
The result will be a snapshot filled with all of the players that belong to the team.

Related

how to filter swiftyjson array

I'll try every solution which I get from stack overflow but its my bad luck. I store my JSON response in [Any] array like this:
var json = JSON()
var arrClientType = [Any]()
self.json = JSON(value) //value is json data
self.arrClientType = self.json["client_type_data"].arrayValue
now, I want to filter this array and reload that filtered data in tableview.
[{
"client_type_name" : "Asset Manager",
"client_type_id" : 1
}, {
"client_type_name" : "Broker Dealer",
"client_type_id" : 5
}, {
"client_type_name" : "Corporate",
"client_type_id" : 8
}, {
"client_type_name" : "Custodian and Prime Broker",
"client_type_id" : 3
}, {
"client_type_name" : "Diversified Financial Services Firms",
"client_type_id" : 4
}, {
"client_type_name" : "Fund Administrator",
"client_type_id" : 6
}, {
"client_type_name" : "Hedge Fund Manager",
"client_type_id" : 2
}, {
"client_type_name" : "Individual",
"client_type_id" : 7
}]
I'll try this also :
let filtered = JSON(self.arrList).arrayValue.filter({
$0["client_type_name"].arrayValue.map({ $0.stringValue }).contains("Broker Dealer")
})
print ("filterdData: \(filtered)")
but it give me entry filter array.
Please help me.
try this
filtered = arrList.filter { $0["client_type_name"].stringValue.contains("Broker Dealer") }
and change your self.arrlist to array of dictiony type
var arrList: [JSON] = []
var filtered :[JSON] = []
Try this one , you will be able to filtered the data which contain the "client_type_name" as a Broker Dealer.
let arrClientType = [["client_type_name":"Asset Manager" , "client_type_id":1] , ["client_type_name":"Broker Dealer" , "client_type_id":5] , ["client_type_name":"Corporate" , "client_type_id":8]]
if let filteredData = arrClientType.filter({(($0 as? [String:Any])?["client_type_name"] as? String ?? "") == "Broker Dealer"}) as? [Any] {
print(filteredData)
}
The filteredData is the arrayOfDictionary which contain the dictionaries in which "client_type_name" as a Broker Dealer.

Firebase looping multi level nodes

I am trying to get results from a node that has one more level of nodes and cannot get through it:
I am retrieving the data like this (it works perfectly but not for the node called "items"):
func fetchMeals() {
print("start pulling data")
let user = Auth.auth().currentUser?.uid
ref.child("Users_Food_Data").child(user!).queryOrderedByKey().observeSingleEvent(of: .value, with: { snapshot in
if snapshot.value as? [String : AnyObject] != nil {
let allMeals = snapshot.value as! [String : AnyObject]
self.foodArray.removeAll()
for (_, value) in allMeals {
let foodToShow = FoodStruct()
if let calories = value["calories"] as? String,
let itemCarbs = value["energy"] as? String,
let itemProteins = value["proteins"] as? String,
let itemFat = value["fat"] as? String,
let date = value["date"] as? String,
let id = value["id"] as? String,
let name = value["name"] as? String,
let interval = value["interval"] as? Int {
foodToShow.itemKcal = calories
foodToShow.itemCarbs = itemCarbs
foodToShow.itemProteins = itemProteins
foodToShow.itemFat = itemFat
foodToShow.id = id
foodToShow.interval = interval
foodToShow.date = date
foodToShow.itemName = name
self.foodArray.append(foodToShow)
self.collectionView?.reloadData()
}
self.breakfastArray.sort(by: {$0.interval! > $1.interval!})
}
}
})
}
And the database looks like this:
"Users_Food_Data" : {
"JztkBihGgda0jtSpe6pNwt8hZu13" : {
"Breakfast:23 Sep 2017" : {
"calories" : "1145.0",
"date" : "23 Sep 2017",
"energy" : "238.8",
"fat" : "3.0",
"id" : "-Kukx_9lSpCh3lcEMzap",
"interval" : 1.506207565807117E9,
"items" : {
"-KukxKAntXDaS__v3ZLA" : {
"calories" : "30",
"date" : "23 Sep 2017",
"energy" : "6",
"fat" : "0.1",
"interval" : 1.506207500336909E9,
"itemKey" : "-KukxKAntXDaS__v3ZLA",
"mealKey" : "-KukxKAntXDaS__v3ZLB",
"name" : "Strawberries",
"proteins" : "0.8",
"quantity" : "3"
},
"-KukxLYmyg32lU1D3Wh3" : {
"calories" : "29",
"date" : "23 Sep 2017",
"energy" : "9",
"fat" : "0.5",
"interval" : 1.506207505968336E9,
"itemKey" : "-KukxLYmyg32lU1D3Wh3",
"mealKey" : "-KukxLYmyg32lU1D3Wh4",
"name" : "Lemon",
"proteins" : "1.1",
"quantity" : "1"
}
},
"name" : "Breakfast",
"proteins" : "17.0"
},
"Breakfast:24 Sep 2017" : {
"calories" : "959.0",
"date" : "24 Sep 2017",
"energy" : "106.4",
"fat" : "46.1",
"id" : "-KunWOZeSxW9eCIA6O1z",
"interval" : 1.506250519537633E9,
"items" : {
"-KulrJq6jOpsG6oiJuDM" : {
"calories" : "458",
"date" : "24 Sep 2017",
"energy" : "4.6",
"fat" : "45",
"interval" : 1.506222704055992E9,
"itemKey" : "-KulrJq6jOpsG6oiJuDM",
"mealKey" : "-KulrJq6jOpsG6oiJuDN",
"name" : "Coconut",
"proteins" : "4",
"quantity" : "1"
},
How do I do that when I know only "Users_Food_Data" and user ID. I just want to list them in the apropiate cells.
Here is my suggestion.
First looking on your database structure, you currently have something like this :
Users_Food_Data
|_userid
|_FoodStruct
|_items
|_id_1
|_data like calories, date...
|_id_2
|_data...
There is too much nodes and this kind of hierarchy doesn't respect what we call Denormalization. Remember that Firebase database is like a NoSQL database and it's a big difference compared to SQL classic databases. Here is an explanation to the best practice to denormalize your database structure : Structure your database
What i can suggest is structure your database more like this :
One node :
UsersFood
|_userid
|_breakfeast_id_1
|_breakfeast_id_2...
2nd node
Breakfeasts
|_breakfeast_id_1
|_item_id_1
|_item_id_2...
3rd node
Items
|_item_id_1
|_calories
|_date
|_energy...
Then in your code, you can :
Observe with a single event on UsersFood node all the breakfeast ids
Then with all breakfeast ids get items_id
Finally get items_id data
Hope it will help you through your code.
Edit 1 : You can also loop through a child node by using this :
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
//DO WHAT YOU NEED TO DO
}

How to apply multiple filter in Firebase query in Swift?

I am trying developing an application like Taxi booking and storing data on Firebase.
But, I am facing problem while querying data for RideDetail(History) from Firebase.
I want to fetch ride_detail for specific "customer_id" in pagination form.
My Firebase DataStructure:
{
"ride_details": {
"NuEoP2WNPwigsbY1FQy9M150131918189233": {
"customer_id": "tstebwLlf4OCRdWhNKO9XCO08xY2",
"destination_address": "New Ranip\nNew Ranip\nAhmedabad\nGujarat 380081\nIndia",
"destination_lang": 72.55924470000001,
"destination_latg": 23.0930152,
"discount": "10%",
"driver_id": "cIyZQIJ7tsdvF1a9KpRrKucF2o62",
"drop_time": "2017-07-29 09:12:21 +0000",
"fare": "13.16 Rs.",
"payment_time": 150149034812771,
"pickup_time": "2017-07-29 09:10:38 +0000",
"priceperkm": "10.00 Rs.",
"ride_confirm_time": "2017-07-29 09:06:21 +0000",
"source_address": "Vastrapur\nVastrapur\nAhmedabad\nGujarat\nIndia",
"source_lang": 72.5293244,
"source_latg": 23.0350073,
"tax": "10%"
},
"RH0oZ0Ypbkur3wJM3HMvM150147833457957": {
"customer_id": "aYQFbwLlf4OCRdWhNKO9XCO08xY2",
"destination_address": "Sarovar Park Plaza Hotels and Resorts Private Limted\nNo 1\nSector 10\nCBD Belapur\nWadala West\nWadala\nMumbai\nMaharashtra 400614\nIndia",
"destination_lang": 72.8561644,
"destination_latg": 19.0176147,
"discount": 0,
"driver_id": "cIyZQIJ7tsdvF1a9KpRrKucF2o62",
"drop_time": "",
"fare": 0,
"payment_time": 150149034812772,
"pickup_time": "",
"priceperkm": 0,
"ride_confirm_time": "2017-07-31 05:18:54 +0000",
"source_address": "Smokin Joe's Fresh Pizza\nShop No. 2\n3\nGround Floor\nAbhiman II\nWadala West\nThane West\nMumbai\nMaharashtra 400602\nIndia",
"source_lang": 72.8561644,
"source_latg": 19.0176147,
"tax": 0
}
}
}
Here "payment_time" is timestamp when payment done.
And the response I want is like:
{
"RH0oZ0Ypbkur3wJM3HMvM150147833457957": {
"customer_id": "aYQFbwLlf4OCRdWhNKO9XCO08xY2",
"destination_address": "Sarovar Park Plaza Hotels and Resorts Private Limted\nNo 1\nSector 10\nCBD Belapur\nWadala West\nWadala\nMumbai\nMaharashtra 400614\nIndia",
"destination_lang": 72.8561644,
"destination_latg": 19.0176147,
"discount": 0,
"driver_id": "cIyZQIJ7tsdvF1a9KpRrKucF2o62",
"drop_time": "",
"fare": 0,
"payment_type": 150149034812772,
"pickup_time": "",
"priceperkm": 0,
"ride_confirm_time": "2017-07-31 05:18:54 +0000",
"source_address": "Smokin Joe's Fresh Pizza\nShop No. 2\n3\nGround Floor\nAbhiman II\nWadala West\nThane West\nMumbai\nMaharashtra 400602\nIndia",
"source_lang": 72.8561644,
"source_latg": 19.0176147,
"tax": 0
},
"1trcf0Ypbkur3wJM3HMvM150147833457957": {
"customer_id": "aYQFbwLlf4OCRdWhNKO9XCO08xY2",
"destination_address": "Sarovar Park Plaza Hotels and Resorts Private Limted\nNo 1\nSector 10\nCBD Belapur\nWadala West\nWadala\nMumbai\nMaharashtra 400614\nIndia",
"destination_lang": 72.8561644,
"destination_latg": 19.0176147,
"discount": 0,
"driver_id": "cIyZQIJ7tsdvF1a9KpRrKucF2o62",
"drop_time": "",
"fare": 0,
"payment_type": 150149034812778,
"pickup_time": "",
"priceperkm": 0,
"ride_confirm_time": "2017-07-31 05:18:54 +0000",
"source_address": "Smokin Joe's Fresh Pizza\nShop No. 2\n3\nGround Floor\nAbhiman II\nWadala West\nThane West\nMumbai\nMaharashtra 400602\nIndia",
"source_lang": 72.8561644,
"source_latg": 19.0176147,
"tax": 0
}
}
I want first 10 records for specific "customer_id" that I pass in query orderedBy "payment_time". Also I want to do pagination for the same. i.e. in second query call, it must return 11-20 records and so on.
The question and comments have some different criteria but let me address it at a high level;
The first answer is: Firebase cannot be queried for the value of one child and then ordered by another.
The simple query function expresses that:
let query = ridesRef.queryOrdered(byChild: "cust_id").queryEqual(toValue: "cust id 4")
To accomplish that task, query for the child data you want, in this case all customer id 4 nodes, and then order in code. Here's an example
class RideClass {
var key = ""
var cust_id = ""
var pay_time = ""
init(key: String, cust_id: String, pay_time: String) {
self.key = key
self.cust_id = cust_id
self.pay_time = pay_time
}
}
var rideArray = [RideClass]()
func populateRideArray() {
let usersRef = self.ref.child("ride_details")
let query = usersRef.queryOrdered(byChild: "cust_id").queryEqual(toValue: "cust id 4")
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 key = snap.key
let custId = dict["cust_id"] as! String
let payTime = dict["pay_time"] as! String
let ride = RideClass(key: key, cust_id: custId, pay_time: payTime)
self.rideArray.append(ride)
}
for ride in self.rideArray { //unsorted example
print(ride.pay_time)
}
self.rideArray.sort { $0.pay_time < $1.pay_time } //sort
for ride in self.rideArray { //sorted example
print(ride.pay_time)
}
})
}
In this example, we create a RideClass which stores some info about the ride, and then an array of rides which could be used as a tableView dataSource.
Then query for all rides that are for cust id 4. We have a loop to show what was retreived unsorted and then this little gem
self.rideArray.sort { $0.pay_time < $1.pay_time }
which sorts the ride array in place by pay_time, which answers the question.
Suppose though, there are 100,000 ride child nodes. Loading in all of that data and sorting in code could be challenging memory wise. What do you do?
We leverage a compound value; along with the child nodes of cust_id and pay_time, we also include id_time. Here's a possible structure:
"ride_details" : {
"ride_0" : {
"cust_id" : "cust id 4",
"id_time" : "cust id 4_172200",
"pay_time" : "172200"
},
"ride_1" : {
"cust_id" : "cust id 2",
"id_time" : "cust id 2_165500",
"pay_time" : "165500"
},
"ride_2" : {
"cust_id" : "cust id 1",
"id_time" : "cust id 1_182300",
"pay_time" : "182300"
},
"ride_3" : {
"cust_id" : "cust id 3",
"id_time" : "cust id 3_131800",
"pay_time" : "131800"
},
"ride_4" : {
"cust_id" : "cust id 4",
"id_time" : "cust id 4_132200",
"pay_time" : "132200"
}
},
and then some code to read in the cust id 4 nodes in the correct order
let ridesRef = self.ref.child("ride_details")
let query = ridesRef.queryOrdered(byChild: "id_time")
.queryStarting(atValue: "cust id 4_")
.queryEnding(atValue: "cust id 4_\\uf8ff")
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 key = snap.key
let custId = dict["cust_id"] as! String
let payTime = dict["pay_time"] as! String
let ride = RideClass(key: key, cust_id: custId, pay_time: payTime)
self.rideArray.append(ride)
}
for ride in self.rideArray { //unsorted example
print(ride.pay_time)
}
})
Two things to note:
The snapshot must be iterated over to maintain the child sequence
"\uf8ff" is a character at a very high code level in Unicode - because of that it encompasses all of the preceeding characters.
There are two basic approaches that may work for your requirements:
construct a query that orders by payment_time, limits to include only the first 10 records, then holds a reference to the 10th record so that you can make subsequent pagination calls with the queryStartingAtValue filter.
Setup a firebase cloud function with a database trigger listening to the payment_time node, such that every time your empty sting is updated with a value in the database you transform the data so that it's organized in such a way that makes it trivial to consume for your needs here. For example - I would organize the new data at a path that looks like this: customer_ride_details/{{customer_id}}/{{ride_id}}. And since you are triggering the function when you replace the payment_time empty string with a timestamp. The keys should be ordered already for you to consume. You will still need to manage your pagination like we did with option 1.

Matching an exact key in SwiftyJson

I'm really having trouble with determining a specific value returned in SwiftyJson; hopefully, someone can help explain this to me.
I want to see if there is a match between a predetermined word, "apple" to any of the words received from the JSON responses.
If there is a match then a message is displayed, and the user either chooses to progress to the next level or the user returns to the home screen.
If there is no match then a message is displayed, and the user must either continue playing or cancel playing.
I would like to do this for multiple words across different levels of the game.
Level one: match "apple" to any of the received JSON responses.
Level two: match "computer" to any of the received JSON responses.
Level three: match "telephone" or "phone" or "iPhone" or "Android" or any or all of the above to any of the received JSON responses.
So, basically, I can get all of the JSON responses, but I'm having a hard time finding out how to set up to determine if there is a specific, predefined JSON response returned.
I have looked everywhere for weeks with another post but to no avail :(
JSON RESPONSES
{
"responses" : [
{
"labelAnnotations" : [
{
"mid" : "\/m\/01m2v",
"score" : 0.9245476,
"description" : "computer keyboard"
},
{
"mid" : "\/m\/01c648",
"score" : 0.7945268,
"description" : "laptop"
},
{
"mid" : "\/m\/01mfj",
"score" : 0.74227184,
"description" : "computer hardware"
},
{
"mid" : "\/m\/0541p",
"score" : 0.7062791,
"description" : "multimedia"
},
{
"mid" : "\/m\/07c1v",
"score" : 0.7039645,
"description" : "technology"
},
{
"mid" : "\/m\/03gq5hm",
"score" : 0.69323385,
"description" : "font"
},
{
"mid" : "\/m\/0bs7_0t",
"score" : 0.6724673,
"description" : "electronic device"
},
{
"mid" : "\/m\/01vdm0",
"score" : 0.66489816,
"description" : "electronic keyboard"
},
{
"mid" : "\/m\/0121tl",
"score" : 0.60392517,
"description" : "electronic instrument"
},
{
"mid" : "\/m\/0h8n5_7",
"score" : 0.5834592,
"description" : "laptop replacement keyboard"
}
]
}
]
}
CODE TO SHOW ALL JSON RESPONSES
// Use SwiftyJSON to parse results
let json = JSON(data: dataToParse)
let errorObj: JSON = json["error"]
// Parse the response
print(json)
let responses: JSON = json["responses"][0]
// Get label annotations
let labelAnnotations: JSON = responses["labelAnnotations"]
let numLabels: Int = labelAnnotations.count
var labels: Array<String> = []
if numLabels > 0 {
var labelResultsText:String = "Labels found: "
for index in 0..<numLabels {
let label = labelAnnotations[index]["description"].stringValue
labels.append(label)
}
for label in labels {
// if it's not the last item add a comma
if labels[labels.count - 1] != label {
labelResultsText += "\(label), "
} else {
labelResultsText += "\(label)"
}
}
self.labelResults.text = labelResultsText
} else {
self.labelResults.text = "No labels found"
}
EDIT
I'm apparently not able to answer my own question, I'll post an edit since I think it's a better solution but #pierce's was pretty decent for a single word, not many; it just wasn't applicable for a game setting application.
So, I created a new NSObject, created a
static var _words: [[String]] = [
["apple", "computer", "beer"]]
then
func checkAnnotations(annotations: [Annotation]) -> Bool {
var isMatched = false
let searchWords = self.words
for searchWord in searchWords {
for annotation in annotations {
if searchWord == annotation.descriptionString {
isMatched = true
break
}
}
if isMatched {
break
}
}
return isMatched
}
then created a function to handle the level state,
and finally compared that to the JSON response in the View Controller and advanced level if matched
// Get JSON key value
let labelAnnotations = responses["labelAnnotations"].arrayValue
let annotationObjects: [Annotation] = labelAnnotations.flatMap({ annotationDictionary in
if let mid = annotationDictionary["mid"].string,
let score = annotationDictionary["score"].double,
let description = annotationDictionary["description"].string {
let annotation = Annotation(mid: mid, score: score, descriptionString: description)
return annotation
}
return nil
})
//print(annotationObjects)
let searchString = LevelState.shared.words[0]
print("Level \(LevelState.shared.level), looking for: \(searchString)")
var isMatched = LevelState.shared.checkAnnotations(annotations: annotationObjects)
if isMatched {
LevelState.shared.advance()
}
let alertTitle = isMatched ? "Congrats! You got \(searchString)" : "Keep looking for \(searchString)"
//let translationResult = "Translated: \(levelDescription) to \(translatedText)"
let alertController = UIAlertController(title: alertTitle, message: nil, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
self.present(alertController, animated: true, completion: nil)
}
self.prepareForNewLevel()
})
}
First thing - I really don't understand why you want to append a comma at the end of each description. I really thing that's unnecessary. Are you confused about separating elements in an array? Because the actual Strings don't require that, it's only if you're manually writing out the elements of an array (i.e. let array = ["a", "b", "c"]).
So then say you setup a property for this labels array, which is an array of Strings.
var labels: Array<String> = []
Once you've gone through and appended all the description values from your JSON, you can then manipulate it.
if numLabels > 0 {
for index in 0..<numLabels {
let label = labelAnnotations[index]["description"].stringValue
labels.append(label)
}
}
Now you could create a method that would return a filtered array of Strings based on some user entered word:
func findMatches(_ userEntry: String) -> [String] {
return labels.filter { $0.contains(userEntry) }
}
Now you could use the above method to handle some sort of user entry, like say you had the text from a UITextField named textField:
// Return the filtered matches based on the textField text (unless nil)
let matches = findMatches(textField.text ?? "")
// Print the number of matches, and also show the matches
print("Found \(matches.count) matches to user input\r\(matches)")
Now if you had labels which held ["a", "aa", "ba", "b", "c", "apple"], and ran the above code where the userEntry was just the letter "a", you'd see this print out in the console window:
Found 4 matches to user input
["a", "aa", "ba", "apple"]
EDIT - You can use the findMatches method above for what you're trying to do with pre-determined words to match. I'm not sure what you're trying to do exactly, but there are a couple different ways. First, say you had an array of pre-determined words you wanted to check as an array:
let words = ["word", "verb", "noun", "adverb"]
Then you could loop through that and check each one
for word in words {
let matches = findMatches(word)
if matches.count > 0 {
print("Found \(matches.count) matches to \(word)\r\(matches)")
} else {
// Do whatever you want when there are no matches
print("No matches found")
}
}
If you want to just check for a specific word, and have a specific response you could setup a method like so:
func checkWord(word: String, noMatchResponse: String) {
let matches = findMatches(word)
if matches.count > 0 {
print("Found \(matches.count) matches to \(word)\r\(matches)")
} else {
// Do whatever with the no match response
print(noMatchResponse)
}
}
There are so many ways you could implement this. You could also use a switch statement, and then use different case statements for each pre-determined word. It's really up to you and how you want to design your game.

Firebase in Swift nested query not working properly

I have a JSON structure like the following:
{
"groups" : {
"-KAv867tzVgIghmr15CM" : {
"author" : "ruben",
"name" : "Item A"
},
"-KAv87nqLEG1Jtc04Ebn" : {
"author" : "ruben",
"name" : "Item B"
},
"-KAv88yZe8KTfkjAE7In" : {
"author" : "ruben",
"name" : "Item C"
}
},
"users" : {
"rsenov : {
"avatar" : "guest",
"email" : "ruben#ruben.com",
"groups" : {
"-KAv867tzVgIghmr15CM" : "true",
"-KAv87nqLEG1Jtc04Ebn" : "true",
"-KAv88yZe8KTfkjAE7In" : "true"
}
}
}
}
Every user has the element "groups" with a childByAutoId() key. Then I have the list of all the groups that exists in the app.
Every time that I run the app, I get the current user logged url reference, and I get the list of the groups of that user (in this case, the logged in user is "rsenov" that has 3 groups).
For every group that this user belongs to, I iterate through the groups url reference, looking for getting the information of that 3 groups.
I do this like this:
func loadTable() {
self.groups = []
var counter = 0
self.meses = []
var tempItems = [String]()
DataService.dataService.CURRENT_USER_GROUPS_REF.observeEventType(.Value, withBlock: { snapshot in
if let snapshots = snapshot.children.allObjects as? [FDataSnapshot] {
tempItems = []
for snap in snapshots {
DataService.dataService.GROUPS_REF.childByAppendingPath(snap.key).queryOrderedByChild("name").observeEventType(.Value, withBlock: { snapshot in
if let postDictionary = snapshot.value as? Dictionary<String, AnyObject> {
tempItems.append(snapshot.value.objectForKey("name") as! String)
let key = snapshot.key
let group = Group(key: key, dictionary: postDictionary)
self.groups.insert(group, atIndex: 0)
}
counter++
if (counter == snapshots.count) {
self.meses = tempItems
self.miTabla.reloadData()
}
})
}
}
})
}
I think this is not a good idea of iterating in that way. For example, if there is a change of some child in the GROUPS_REF url, the code only runs in that nested code, and since it doesn't have the "snap.key" value got from the for loop, it doesn't work.
Which is the best way to do a good query in this case?
Phew, that took some time to write. Mostly because I don't iOS/Swift a lot:
let ref = Firebase(url: "https://stackoverflow.firebaseio.com/35514497")
let CURRENT_USER_GROUPS_REF = ref.childByAppendingPath("users/rsenov/groups")
let GROUPS_REF = ref.childByAppendingPath("groups")
var counter: UInt = 0
var groupNames = [String]()
CURRENT_USER_GROUPS_REF.observeEventType(.Value, withBlock: { groupKeys in
for groupKey in groupKeys.children {
print("Loading group \(groupKey.key)")
GROUPS_REF.childByAppendingPath(groupKey.key).observeSingleEventOfType(.Value, withBlock: { snapshot in
print(snapshot.value)
if (snapshot.childSnapshotForPath("name").exists()) {
groupNames.append(snapshot.value.objectForKey("name") as! String)
}
counter++
if (counter == groupKeys.childrenCount) {
print(groupNames)
}
})
}
})
By the way, this is how you create a minimal, complete verifiable example. The code has no external dependencies (such as Group and DataService in your code) and only contains what's relevant to the answer.
The important bits:
I used observeSingleEventOfType to get each group, since I don't want to get more callbacks if a group changes
I use snapshot.childSnapshotForPath("name").exists() to check if your group has a name. You probably want to either ensure they all have names or add them to the list with some other property in the real app.
Frank's answer is on-point. I wanted to throw in an alternative that may or may not work for your situation as it requires a slight alteration to the database.
groups
gid_0
author: "ruben"
name: "Item A"
users
uid_0: true
gid_1
author: "ruben"
name: "Item B"
users
uid_1: true
gid_2
author: "ruben"
name: "Item C"
users
uid_0: true
And then some ObjC Code for a Deep Query
Firebase *ref = [self.myRootRef childByAppendingPath:#"groups"];
FQuery *query1 = [ref queryOrderedByChild:#"users/uid_0"];
FQuery *query2 = [query1 queryEqualToValue:#"true"];
[query2 observeSingleEventOfType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot) {
NSLog(#"key: %# value: %#", snapshot.key, snapshot.value);
}];
This code does a deep query on the /groups for all groups that have a /users/uid_0 = true. In this case it returns gid_0 and gid_2
It eliminates the need for iterations and multiple calls to the database.
Adding a /users/ node to each group with a list of the uid's may offer some additional flexibility.
Just a thought.

Resources