How to access data in array in JSON - ios

JSON:
{
"projects":[
{
"id":113,
"name":"Mobile app Android",
"description":"",
"created_on":"2014-10-03T16:53:56+02:00",
"updated_on":"2014-12-03T16:59:45+01:00"
},
{
"id":142,
"name":"Mobile app iOS",
"created_on":"2014-12-11T18:30:55+01:00",
"updated_on":"2014-12-11T18:30:55+01:00"
},
{
"id":52,
"name":"Test project",
"identifier":"grafikr",
"description":"",
"created_on":"2013-10-14T17:21:33+02:00",
"updated_on":"2014-10-10T17:40:47+02:00"
},
{
"id":37,
"name":"Sample project",
"identifier":"grafikf",
"description":"",
"created_on":"2013-09-18T16:31:25+02:00",
"updated_on":"2013-09-26T13:11:58+02:00"
}
],
"total_count":4,
"offset":0,
"limit":25
}
It is easy to access for example name of the first project (with name Mobile app Android) by var name = json["projects"][0]["name"].stringValue
But how do I access all names in SwiftyJSON? If I make a variable var projects = json["projects"], it gives me:
[
{
"id" : 113,
"created_on" : "2014-10-03T16:53:56+02:00",
"name" : "Mobile app Android",
"description" : "",
"updated_on" : "2014-12-03T16:59:45+01:00"
},
...
Now I don't have a problem with making a NSDictionary from data anymore, but this drives me crazy.

There's a lot going on in your code sample—perhaps too much to be addressed by a single Stack Overflow question / answer.
I would strongly recommend going back to Apple's resources for Swift and iOS application patterns. Topics to revisit would include synchronous versus asynchronous programming, authentication, and using data sources with table views.

This should probably work. Your json also contains "total_count", which looking at it, I assume that's the count of the number of projects. Pull that count out, loop over till the count and fetch the name.
var names = [String]()
let count = json["total_count"].int
for index in 0..<count {
let name = json["projects"][index]["name"].string
names.append(name)
}

Why not use SwiftyJSON the Swifty way???
Try this:
let names = json["projects"].arrayValue.map {
$0["name"].stringValue
}

Related

Swift & Firestore - appending dictionary to nested array using .arrayUnion()

I am creating a chat application where a user can start multiple chats with a different person (just like the rest of the other chat apps). I'm using Swift with Cloud Firestore.
My database design is the following:
Chat collection: [
"chatMember": ["member1", "member2"],
"createdAt" : Date().timeIntervalSince1970,
"meesages" : []
]
One chat room will have 'messages' array and it will have message dictionaries aka an object.
Below code is where I am trying to append the message dictionary to the messages array.
The Firebase doc introduces .arrayUnion() <LINK to the document>.
But it gives me an error saying,
"Contextual type '[Any]' cannot be used with dictionary literal"
#IBAction func sendBtnPressed(_ sender: UIButton) {
if let messageBody = messageField.text, let messageSender = Auth.auth().currentUser?.email {
db.collection("collectionName").document("docName").updateData([
// FieldValue.arrayUnion() does not work for this case.
K.FB.messages: FieldValue.arrayUnion([
"sender": messageSender,
"content": messageBody,
"createdAt": Date().timeIntervalSince1970
])
]) { ... closure }
}
}
I can't find any information specifically related to Swift's appending Dictionary to nested array in the Cloud Firestore.
I found a very similar case on YouTube <https://youtu.be/LKAXg2drQJQ> but this is made with Angular which uses an object with { ...curly bracket}. FieldValue.arrayUnion() seems to work on other languages but not Swift.
It'd be awesome if someone who has resolved this issue would help me out.
Thank you in advance.
So basically what you're saying with this code:
K.FB.messages: FieldValue.arrayUnion([
"sender": messageSender,
"content": messageBody,
"createdAt": Date().timeIntervalSince1970
])
is that you want to append dictionary to array with name that is in K.FB.messages constant. But you don't really want to do that and it isn't even possible. You want to append array of dictionaries to array. This means that you must enclose your dictionary in a square brackets. Like shown below:
K.FB.messages: FieldValue.arrayUnion([[
"sender": messageSender,
"content": messageBody,
"createdAt": Date().timeIntervalSince1970
]])

Compare values with an array of values with Firebase. Swift

I have a value which needs to be compared with an array of values. Basically a user needs to check if it has the same item as another user. But I am struggling to solve this as how do I get the item of an array of users? Normally when you observe a value you do something like this:
Database.database().reference(withPath: "users/\(userID)/itemList").observeSingleEvent...
However, when it comes to an array of users itemList how can this be achieved if there are multiple ID's? I'd like compare a user item or items with other users item and check if they match in order to sort an array of users that have a match.
If there is already an example for this please help direct me there.
Update
This is how my data structure looks:
{
"users": {
"EWJGFYmVTzOiHgnq42ebhrg2fj": {
"firstName": "Friederike",
"itemList": [
2: true,
3: true,
0: true
]
},
"C076OYmVTzOiHgnq4wPQtY2XpED2": {
"firstName": "Ian",
"itemList": [
0: true,
1: true,
3: true
]
},
"Juoiuf0N6qNmkm32jrtu6X6UK62": {
"itemList": [
0: true
],
"firstName": "Jack"
}
}
}
Update 2.0
With the answer below I am able to query through the items table but still unable to query the keys that match as there can be multiple arrays and therefore I cannot use it to filter or anything.
Ok so with your current data structure you'd have to query all nodes under users and then compare the arrays, which is very inefficient. There isn't a straight forward or easy way to do that without modifying your structure. So I suggest you modify your data structure so that each item has a list of all users that have it. Something like this:
{
"items": {
"0": {
"EWJGFYmVTzOiHgnq42ebhrg2fj": "Friederike",
"C076OYmVTzOiHgnq4wPQtY2XpED2": "Ian",
"Juoiuf0N6qNmkm32jrtu6X6UK62": "Jack"
},
"1": {
"C076OYmVTzOiHgnq4wPQtY2XpED2": "Ian"
},
"2": {
"EWJGFYmVTzOiHgnq42ebhrg2fj": "Friederike"
}
//....
}
}
Depending on what you want to display you might want to store more information than just the users UID and username. You can query all the users that have the same items as you using a query like this:
let ref = Database.database().reference()
// assuming you already have the current users items stored in an array
for item in items {
ref.child("items").child(String(item)).observeSingleEvent(of: .value, with: { snap in
for child in snap.children {
let child = child as? DataSnapshot
if let key = child?.key, let name = child?.value as? String {
// do something with this data
}
}
})
}
Firebase database is noSQL, so data is meant to be denormalized or duplicated so that queries can be optimized. Firebase actually recommends that you avoid nesting data. Take a look at this question for more information.
Hope that helps
Code related to question asked in comments
Assuming you are storing the UID's or names of users with the same items in a string array you can prevent duplicates using .contains()
var namesWithMatchingItems = [String]()
if !namesWithMatchingItems.contains(nameYouJustFetched) {
namesWithMatchingItems.append(nameYouJustFetched)
}

Swift : Simplifying Nested Firebase Fetching/Queries

I was curious to know if there is a way to simplify my Firebase DB queries.
Also, what are some best practices to follow when making pull requests.
Ive noticed the more complex my hierarchical structure gets from adding children results in extremely strenuous and long queries within my code.
Here is an example of a query within my application:
If you find yourself needing complex queries, it most likely means that your data isn't properly structured.
In your case, your structure needs to be normalized (flattened).
The following modification should make your queries simpler:
{
"listings": {
"listing_key_1": {
"timestamp": "2017 10 19 07:49:38"
},
"listing_key_2": {
"timestamp": "2017 10 19 05:30:02"
},
...
},
"AircraftHouseRules": {
"listing_key_1": {
"Availability": "",
...
},
"listing_key_2": {
"Availability": "",
...
}
},
"BasicInfo": {
"listing_key_1": {
"ModelPlane": "",
...
},
"listing_key_2": {
"ModelPlane": "",
...
}
},
...
}
I don't get what you mean by "pull requests". If you mean observing or simply reading the data, it's pretty straightforward. All you need in your case is a listing id/key to gain direct access to its data (AircraftHouseRules, BasicInfo, DetailedInformation, etc...).
If you want the reverse (to get the key of a listing through, let's say, its TypeOfPlane), you can do the following query
// NOTE: Untested code - just for illustrative purposes
let queryRef = Database().database.reference.child("BasicInfo").queryOrdered(byChild: "TypeOfPlane").queryEqual(to: "some type")
queryRef.observeSingleEvent(of: .value, with { snapshot in
if let listings = snapshot.value as? [String : Any] {
// all retrieved listings with TypeOfPlane == "some type"
}
})
Tips
You should always avoid nesting structures to avoid downloading unnecessary data and hindering performance. In the screenshot you posted, you have up to 8 levels of nested data. With the modification above you can halve those levels easily.
You should also consider replacing all array structures (like the ones under ImageRef) with key-value pairs as arrays can be evil.

Parsing JSON for iOS app in Swift Issue

First of all, this is a noob question so anyone interested can answer me , because I'm just a beginner.
So, I need to parse JSON data from an api that my college seniors created for our annual technical festival. But they forgot to put a name to the array which contains all info.
1
They forgot to put the name and the role is similar to the "loans" at the beginning 2
I am using SwiftyJSON to parse the data and here is my code.
func getJSON(){
let url = NSURL(string: baseURL)
let request = NSURLRequest(URL:url!)
let session = NSURLSession(configuration:NSURLSessionConfiguration.defaultSessionConfiguration())
let task = session.dataTaskWithRequest(request) { (data, response, error) -> Void in
if error == nil{
let swiftyJSON = JSON(data:data!)
let loan1 = swiftyJSON["loans"][0]["status"].stringValue
print(loan1)
}else{
print("There is an error")
}
}
task.resume()
}
So i would like to know that what could "loans" be replaced by to get my JSON parsing up and running. I have already tried " " and "" to be sure.
Thanks !
Based on the JSON dictionary you posted, you aren't accessing any existing keys so loan1 can't possibly be anything other than nil. Based on the response in the first image, you get back an array of dictionaries.
[
{
"eid": 98,
"ename": "Lights. Camera. Tweet",
"sname": "IEEE GTBIT",
"ebanner": "http://s10.postimg.org/a11cezdft/12045587_1594466984176579_4873790458939193947_o.jpg",
"time": "1459189800854",
"category": "Literary & Management",
"desc": "Lights. Camera. Tweet! brings you a chance to win ..."
},
{
"eid": 45,
"ename": "Hack#GTBIT",
"sname": "Android Techies",
"ebanner": "http://i.imgur.com/ANR84uk.jpg",
"time": "1459222200397",
"category": "Technical",
"desc": "Hack#GTBIT is a Software Hackathon where particip..."
},
etc...
If none of this data is what you're looking for, you're not requesting properly or the data being returned isn't being properly formatted or including all the necessary information.
It's possible you could make a new request to check the status using the ID or some other value; you would access that through swiftyJSON[0]["eid"] – that would get you the value 98. But the key–value pair you're trying to access isn't correct based on the response. The "loans" key doesn't exist at the top level and the "status" key also does not exist.

Persistent storage to store one set of key-value pairs in iOS Swift

I want persistent storage(persistent across views) for my app, I came across Realm, but now I think its just too heavy for what I am trying to do.
Basically I have data in JSON format(which I fetch every time the app starts, I want to store it in a variable(in a Dictionary) so I can access it from different screens of my iPad app. (I also don't care if the data gets lost when the app is closed, I just want the data to be available across different screens(views) of my app when app is running)
Example Data
{
"pid": 23,
"name": "some name",
"cities": [ {
"id": 1,
"name": "Bangalore"
},
{
"id": 2,
"name": "Mysore"
}
{ //continues for about 100 more city entries
}
],
"hospital": {
"id": 234,
"name": "newHorizon",
"type": "nursing home"
}
}
Is Realm a suitable option for this? If yes, how do I fetch say hospital->name from my example data?
If not, what kind of storage is suitable in this case?
I am looking for ios equivalent for localStorage of Javascript
Realm persists data even when application is not active. I guess that you do not care. I would advise to create a session object (a singleton object) with a Dictionary as one of the data property.
Convert your JSON into Dictionary and save it in your singleton session object. Fetch it like myData["Hospital"]["name"]
EDIT: On OP request
Step 1: Setting up singleton
//
// MySession.swift
//
import Foundation
class MySession {
static let sharedInstance = MySession()
var myData = Dictionary<String,AnyObject>()
}
Step 2: Save data in singleton in class 1
var dict = Dictionary<String,AnyObject>()
dict["pid"] = "123"
dict["hospital"] = ["id":"234", "name":"newHorizon", "type":"nursing home"]
MySession.sharedInstance.myData = dict
Step 3: Fetch saved data in class 2
let myData = MySession.sharedInstance.myData as Dictionary
let hospitalDict = myData["hospital"]
if let name = hospitalDict?["name"] as? String {
print(name)
}

Resources