Modeling JSON result in Core Data Editor - ios

I'm learning Core Data and I need to create an Entity called Country. I'm getting this response from my API and I'm not sure how to create and store the JSON response in my country entity.
{
"total_count":10,
"results": [
{
"name": "Spain",
"population" : 45.000,
"location" : {
"latitude" : 29.567423,
"longitude" : -5.675326
}
},
{
"name": "France",
"population" : 25.000,
"location" : {
"latitude" : 29.567423,
"longitude" : -5.675326
}
},
{
"name": "Germany",
"population" : 15.000,
"location" : {
"latitude" : 29.567423,
"longitude" : -5.675326
}
}
]
}
The parent fields (name, population) are not problem, but how can I set the child fields (location key) in the entity graph? With an NSDictionary maybe?

There are many possibilities.
You can create two separate attributes like (latitude, longitude) having type (double, double) and at fetch time create location with both values or you can create CGPoint with both of values and then create string from CGPoint and store them
CGPoint location = CGPointMake(latitude, longitude);
NSString *stringLocation = NSStringFromCGPoint(point);
store that string and fetch it and then again convert into CGpoint and use it
CGPoint myPoint = CGPointFromString(stringLocation);

Related

IndexOn on firebase Root to query entire database swift 3

I am trying to query by name inside firebase database, I want to return all the names matching the query. But I can't seem to get it to work, I am getting error Consider adding ".indexOn": "name" at / to your security rules.
Security Rules:
{
"rules": {
".read": "true",
".write": "true",
"goals_new": {
".indexOn": ["name"]
}
}
}
I Can retrieve the names for specific child like this:
let query = ref.child("goals_new").queryOrdered(byChild:"name").queryEqual(toValue: name)
query.observe(.childAdded) { (snapshot) in
// if let values =
if let values = snapshot.value as? [String:String] {
print(values)
print(values["name"]?.count ?? "")
}
}
But I want to retrieve all the names in the database that matches the query
Database Structure:
"goals_new" : [ null, {
"name" : "Eric thomas",
"pic" : “…….”,
"title" : "Be Obsessed with your Goals",
"url" : “……”
},
{
"name" : "Bob Proctor",
"pic" : “……….",
"title" : "Goal Achievement System",
"url" : “………”
},
Any help would be appreciated.
Consider adding ".indexOn": "name" at / to your security rules <- Is only a warning so you don't have to worry much about that.
I'm seeing perhaps an error. Looks like you're using an array to store your objects of 'goals_news' because of the keys are numbers 1, 2, etc... Maybe that's is messing with your query, you have to change them for string keys like every time you add one use firebase method childByAutoID().
Better use .value instead of .childAdded method so you can get all the objects that matches the name, something like this:
let query = ref.child("goals_new").queryOrdered(byChild:"name").queryEqual(toValue: name)
query.observe(.value) { (snapshot) in
guard snapshot.exists() && snapshot.hasChildren() else {return}
for snap in snapshot.children {
var currentSnapValue = (snap as! DataSnapshot).value as! [String: String]
print("Name: \(currentSnapValue["name"])")
}
}
That way you're getting all the objects that matches the name

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 display mulitple dictionary or array data in single tableview cell in swift ios

I am new to iOS development can any one give me some idea in swift. I have JSON data after parsing i am storing data in array like example.
"orderId" : 146,
"total" : 2,
"created_at" : "2016-09-19 11:08:51",
"categories" : [
{
"name" : "Bleach",
"qrCode" : "SRY-001-0098",
"weight" : 1,
"categoryId" : "57dbd9ca8e1a0919c9075d13",
"amount" : 1
},
{
"name" : "Normal (26+ lbs)",
"qrCode" : "SRY-001-0099",
"weight" : 1,
"categoryId" : "57dbd9708e1a094ecb10cfb1",
"amount" : 1
}
],
"completed_at" : "2016-09-19 00:00:00"
I have four arrays :
orderarray
totalarray
createatarrays
cartegoriesarray
completedarray
orderarray[0] = 146
totalarray[0] = 2
createatarrays[0] = "2016-09-19 11:08:51"
cartegoriesarray[0] =
[
{
"name" : "Bleach",
"qrCode" : "SRY-001-0098",
"weight" : 1,
"categoryId" : "57dbd9ca8e1a0919c9075d13",
"amount" : 1
},
{
"name" : "Normal",
"qrCode" : "SRY-001-0099",
"weight" : 1,
"categoryId" : "57dbd9708e1a094ecb10cfb1",
"amount" : 1
}
]
completedarray[0] = "2016-09-19 00:00:00"
Using indexpathrow i can able to display orderarray, totalarray, createdarray based on index path in tableview cell.
But here categoryarray how can i display inside array again two dictionary is there ?
I want to display name" : "Bleach", "name" : "Normal" in single tableview cell based on index path like categoryarray[0]
How to get these two names in single string plz give me some idea..
Thanks.
You need to loop the catergoryarray and get each dictionary out. Then you can get the individual values out of the dictionary.
var name = "";
//Loop the array of dictionaries
for dict in categoryarray as! NSDictionary {
//extract the items from the dictionary. and concatenate names
name = name + " " + dict["name"];
}
print("Name: ", name)
You can then use that information to populate your table cells as you wish. Obviously you will want to modify how the names are concatenated to suit your needs.
Hope that helps.

Least data-heavy way to retrieve 5 closest users in Firebase/Swift?

Let's imagine I have a Firebase setup like this, except with 10,000 users:
"users" : {
"user 1" : {
"latitude": 1234567,
"Longitude": 7654321,
"name": "Name 1"
},
"user 2" : {
"latitude": 1234567,
"longitude": 7654321,
"name": "Name 2"
},
"user 3" : {
"latitude": 1234567,
"Longitude": 7654321,
"name": "Name 3"
},
"user 4" : {
"latitude": 1234567,
"Longitude": 7654321,
"name": "Name 4"
},
}
What would be the least data-intensive way to generate a list of the 5 users closest to me? Very new to this, so the only solutions I can think of would require querying all 10,000 users.
I am very new to this subject, therefore I can only point you in the right direction. Essentially the way you're storing user location is not optimal. The best way to do it is to use GeoFire a newish addition to the Firebase service. How it works is you should have a large location object and you store the geolocation of those users using that users key. Here's an example.
{
Locations: {
-KH35xPkJmX0UTSG8DuM : {
"g" : "randomID",
"l" : {
"0" : "latitude",
"1" : "longitude"
}
}
}
}
{
Users: {
-KH35xPkJmX0UTSG8DuM : {
"username" : "Joe Sloan"
}
}
}
Your locations object will have 10,000 users keys and geoLocations. the value of "g" and "l" object are set when you use
geoFire.setLocation(CLLocation(latitude: 37.7853889, longitude: -122.4056973), forKey: "firebase-hq")
Google has optimized the query for users within a similar location.
let center = CLLocation(latitude: 37.7832889, longitude: -122.4056973)
// Query locations at [37.7832889, -122.4056973] with a radius of 600 meters
var circleQuery = geoFire.queryAtLocation(center, withRadius: 0.6)
The circleQuery variable should contain a Firebase dictionary of the closest users. As I said I haven't had a chance to really delve deeper but this should give you a start.

elasticsearch rails - illegal latitude value

When trying to search from latitude dynamically from records in my index I've been getting an illegal latitude value. When looking at my index I can't see any invalid latitude and longitude values, so I'm guessing it's an error in my code.
Exact error;
{"type":"query_parsing_exception","reason":"illegal latitude value [269.99999983236194] for [geo_distance]","index":"addresses","line":1,"col":172}}]},"status":400}
Model code for search;
def self.search(query)
__elasticsearch__.search(
{
query:{
multi_match: {
query: query,
fields: ['full_address']
}
},
filter:{
geo_distance:{
distance: "6miles",
location: "address.location"
}
}
}
)
end
Mapping;
{ "addresses" : {
"mappings" : {
"address" : {
"properties" : {
"county" : {
"type" : "string"
},
"full_address" : {
"type" : "string"
},
"location" : {
"type" : "geo_point"
}
}
}
}
}
}
I experienced the same error today but with longitude. Turns out Elasticsearch version 2.0 and newer only allows longitude values from range -180..180.
For example version 1.7.5 (which I had in my local environment) worked with both ranges -180..180 and 0..360.
Your error states:
illegal latitude value [269.99999983236194]
For latitude, only values from range -90..90 are allowed.
The only fix is to re-compute lat/long values before sending them to Elasticsearch. For example for longitude you could do:
((longitude.to_f + 180) % 360) - 180
It's better to clean longitude with something like this:
$adjustedLongitude = function($vongitude) {
$result = $vongitude - 360 * floor($vongitude / 360);
if (abs($result) > 180) {
$result = 360 - $result;
}
return $result;
}

Resources