I'm using Firebase for my Swift iOS application. I found the retrieving data tutorial on Firebase's guide a bit confusing and I am not sure why when I try to access existing values in my database results in nil values.
This is what I have so far:
usersRef.queryOrderedByChild("fbid").queryEqualToValue(userId).observeSingleEventOfType(.Value, withBlock:{ snapshot in
print("SNAPSHOT: ",snapshot.value)
Here is the result of printing snapshot.
SNAPSHOT: {
1 = {
fbid = 1;
firstName = Michelle;
friendlist = {
9 = "Kevin C";
};
lastName = C;
profilepicurl = "https:;
uid = "facebook:1";
};
}
However, the line below results in:
fatal error: unexpectedly found nil while unwrapping an Optional value
firstName = snapshot.value.objectForKey("firstName") as! String
I would like to retrieve all the values for the user (firstName, profilepicurl, friendlist, etc) and store them in variables. It seems simple but perhaps I'm missing something. Any help would be appreciated.
Your FDataSnapshot does not contain a child firstName. It only contains a child 1.
This is because you're performing a query and then asking for a value. Since a query can have many results, it returns a list of results. Even when there's only one result, it is still a list of 1.
The solution is to loop over the children:
usersRef.queryOrderedByChild("fbid")
.queryEqualToValue(userId)
.observeSingleEventOfType(.Value, withBlock:{ snapshot in
for child in snapshot.children {
print("Loading group \(child.key!)")
}
})
Related
I have a query "items" object. The class has a "parent" key that points to _User object.
The query is successful and fetches 'parent' object and i am able to access some custom keys from this 'parent' object but email is returned as nil !!
below is stripped down code.
Could anyone suggest why is this happening? This was working earlier and i have not made any code change. Running Parse-server ver 2.5.3 on Heroku.
let query = PFQuery(className: "items")
query.includeKey("parent") // parent points to _User
do {
let results = try query.findObjects()
for result in results {
let parent = result["parent"] as! PFUser
let foundUser = User()
foundUser.user = parent // User.user is of PFUser type
//custom keys in _User
foundUser.name = parent["name"] as! String
foundUser.city = parent["city"] as! String
foundUser.geoLocation = parent["location_geopoint"] as! PFGeoPoint
foundUser.userAddress = parent["user_address"] as! String
//this is failing with nil eventhough there is value in Parse
foundUser.email = parent["email"] as! String
// also tried this. parent.objectId returns a valid value
foundUser.email = parent.email
}
} catch...
Got the answer here enter link description here
In 2.3.0 there was a breaking change introduced which restricted the fields returned from a query on _User. If I remember correctly, email is taken out by default and you can specify additional fields to be removed from a query result in a configuration setting. In order to retrieve email you either have to specify the master key or be the owner of that object.
My structure in firebase is as follows:
app name
user ID
wins = 7
losses = 8
and my code to read the wins child node
ref = Database.database().reference().child(passUserID)
ref?.child("wins").observe(.childAdded, with: { (snapshot) in
//Convert the info of the data into a string variable
let getData = snapshot.value as? String
print(getData)
})
But it prints nothing.
To read data from Firebase you attach a listener to a path which is what creates a FIRDatabase reference. A FIRDatabaseReference represents a particular location in your Firebase Database where there is a key-value pair list of children. So in your case, you have created a Firebase reference to the key "wins" which only points to a value and not a key-value pair. Your reference was valid up to this point:
ref = Database.database().reference().child(passUserID)
//did you mean FIRDatabase and not Database??
This FIRDatabaseReference points to the key passUserID which has a key-value pair list of children ["wins":"7"] and ["losses":"8"] (NOTE: a key is always a string). So from your FIRDatabase reference, you create your observer as follows and read the value of "wins":
ref?.observe(.childAdded, with: { (snapshot) in
//Convert the info of the data into a string variable
if let getData = snapshot.value as? [String:Any] {
print(getData)
let wins = getData["wins"] as? String
print("\(wins)")
}
})
The Child added event will fire off once per existing piece of data, the snapshot value will be an individual record rather than the entire list like you would get with the value event. As more items come in, this event will fire off with each item. So if "losses" is the first record you might not get the value of "wins". Is this what you are trying to achieve? If what you really wanted to know is the value of "wins" at that particular location and to know if this value has ever changed you should use the .value observer as follows:
ref?.observe(.value, with: { (snapshot) in
//Convert the info of the data into a string variable
if let getData = snapshot.value as? [String:Any] {
let wins = getData["wins"] as? String
print("\(wins)") //check the value of wins is correct
}
})
Or if you just wanted to get the know the value of wins just once and you are not worried about knowing if there any changes to it, use the "observeSingleEvent" instead of "observe".
EDIT
I saw your image and now realize you might also have a problem with your reference. Your ref should actually be something like:
ref = FIRDatabase.database().reference().child("game-").child(passUserID)
You have obscured what "game" is but a valid reference to "wins" will include it.
SECOND EDIT
I will add the following so you can properly debug the problem. Use this pattern to observe the value and see if you get an error returned and what is says:
ref.observe(.value, with: { (snapshot) in
print(snapshot)
}, withCancel: { (error) in
print(error.localizedDescription)
})
Normally it will give you an error if you cannot access that Firebase location because of a database rule. It will also be a good idea to see if print(snapshot) returns anything as above.
You need this:
ref.child("YOUR_TOP_MOST_KEY").observe(.childAdded, with: { (snapshot) in
let keySnapshot = snapshot.key
//print(keySnapshot)
self.ref.child(keySnapshot).observe(.value, with: { (snapshot2) in
//print(snapshot2)
}) { (error) in
print("error###\(error)")
}
})
I got score node in Firebase:
And under score node my data structure is as:
{
"jjTO29ziQ2SjrH5PFzY7ZMpSgjq1-Kak6FCyGtdy_kcEPd4K" = {
jjTO29ziQ2SjrH5PFzY7ZMpSgjq1 = "-Kak6FCyGtdy_kcEPd4K";
score = 5;
};
"jjTO29ziQ2SjrH5PFzY7ZMpSgjq1-KbE_pgfUsukOm4uW0bx" = {
jjTO29ziQ2SjrH5PFzY7ZMpSgjq1 = "-KbE_pgfUsukOm4uW0bx";
score = 4;
};
Question:
Can I filter the data by score ?
I tried doing :
FIRDatabase.database().reference().child("scores").queryOrdered(byChild: "score").observeSingleEvent(of: .value, with: { (snapshot) in
debugPrint(snapshot.value ?? "nil")
})
But can't get the result ?
When you execute a query against Firebase, you get a FIRDataSnapshot that contains three pieces of information for each result:
its key
its value
its position relative to other nodes
When you're calling snapshot.value the snapshot is converted into a Dictionary. Unfortunately that dictionary can only hold key-value pairs, so you lose the information on the position of the nodes. So while the data in the snapshot is actually sorted correctly, you're throwing that information away.
The solution is to use the FIRDataSnapshots built-in children property to loop over the child nodes in the correct order. The Firebase documentation on querying has this sample of how to do that:
_commentsRef.observeEventType(.Value, withBlock: { snapshot in
for child in snapshot.children {
...
}
})
I can make a Facebook SDK Graph Request to get a user's likes, but I'm having trouble taking the returned values and storing one of the keys in an array of Strings. The request returns an NSDictionary of keys/values. Then, using objectForKey I can get the data key which returns what I want: the id and name of the "liked" page on Facebook.
Data returns elements like this:
{
id = 486379781543416;
name = "Star Wars Movies";
},
I specifically want only the "name" of all of these objects and to throw them into an array [String]. I tried to loop through the objects but I'm getting error ambiguous use of subscript. Here's the relevant code:
request.startWithCompletionHandler{(connection:FBSDKGraphRequestConnection!, result:AnyObject!, error:NSError!) -> Void in
let resultdict = result as! NSDictionary
let likes = resultdict.objectForKey("data") as! NSArray
print("Found \(likes.count) likes")
print(likes)
for object in likes{
let name = object["name"] as! String //error: ambiguous use of subsript
print(name)
}
}
After doing some research it looks like the issue is with the NSArray and that I should instead use Swift data types. I tried casting it to a Swift array but I got different errors.
What's the best way to handle this error?
Thanks!
update: Here is what the facebook API request returns:
{
data = (
{
id = 111276025563005;
name = "Star Wars (film)";
},
{
id = 115061321839188;
name = "Return of the Jedi";
}
);
paging = {
cursors = {
after = MTE1MDYxMzIxODM5MTg4;
before = Mjc0NzYzODk2MTg4NjY5;
};
next = "https://graph.facebook.com/v2.5/10155262562690368/likes?access_token=<redacted>";
};
}
You should always use the native Swift collection types wherever possible as NSArray and NSDictionary are really type-inspecific, and therefore can easily trigger "ambiguous use of subscript" errors.
You'll also want to avoid force down-casting, in case you receive data that's in the wrong format, or no data at all. This situation would be more elegantly handled with a guard, in order to prevent a crash. If your program depends on the force down-casting succeeding, and therefore should crash – then you can always call fatalError in the guard, with a descriptive error message in order to assist you in debugging the problem.
If I understand your data structure correctly, the request returns an AnyObject that should be a [String:AnyObject] (A dictionary of strings to any objects). In the case of the "data" key, the AnyObject value is then a [[String:AnyObject]] (An array of dictionaries of strings to any objects).
Therefore you'll want to do your casting in two stages. First, using a conditional downcast on your result to cast it as a [String:AnyObject]. If this fails, then the else clause of the guard will be executed and the code will return. You'll then want to get out your "data" value (your 'likes' array), and conditionally downcast it to a [[String:AnyObject]]. Both of these statements will handle the possibility of resultDict or resultDict["data"] being nil.
guard let resultDict = result as? [String:AnyObject] else {return}
guard let likes = resultDict["data"] as? [[String:AnyObject]] else {return}
You can put whatever error handling logic you want in the brackets of these statements to handle cases in which the results dictionary doesn't exist, was the wrong format, or there wasn't a 'likes' array in it.
You can then get an array of 'like' names through using flatMap.
let likeNames = likes.flatMap{$0["name"] as? String}
This will create an array of the like names of each dictionary – if the like names don't exist or aren't strings, then they won't be added. Because the compiler knows for certain that likes is a [[String:AnyObject]] – there's no ambiguity in subscripting its elements.
If you want a more general approach such as you're doing in your question, you can use a guard statement within a for loop.
for object in likes {
guard let name = object["name"] as? String else {continue}
print(name)
}
Again, you can put whatever error handling you wish in the brackets of the guard.
I new to Swift programming. I am using CoreData in my app as a database option.
I am using NSManagedObject (deleteObject) to delete lastObject, firstObject and entire rows in the table.
I want to delete a specific row in the table by using a value which is stored in a variable. I am not using table view (I don't have any index path), this delete operation is the background process after I get response from the my application server.
Any links or tutorials or suggestions will be helpful.
Thanks in advance!!!
Code for deleting lastObject
if var results1 = context.executeFetchRequest(requestParams, error: nil) as? [Params] {
println("\n Params Results count : \(results1.count)")
for param in results1{
for var n = 0 ;n < results1.count; ++n
{
let lastPerson = (results1 as NSArray).lastObject as Params
context.deleteObject(lastPerson)
var savingError: NSError?
context.save(&savingError)
}
}
}
Params table consists of columns 'pname' and 'pvalues'.
I want to delete a specific param pname ='server_timeout'.
Consider if 'Params' table consist of 10 rows and 'pname'='server_timeout' might be present at index 5, 6.
How can I find the 'pname' (i.e 'server_timeout') in this list of records and delete it?
How about creating a predicate that filters out the list to those with pname value of server_timeout in the first place.
context.performBlock {
requestParams.predicate = NSPredicate(format: "pname == %#", "server_timeout")
if let results = context.executeFetchRequest(requestParams, error: nil) as? [Params] {
for param in results {
context.deleteObject(param)
}
var error: NSError?
if !context.save(&error) {
println(error)
}
}
}
As you can see, I have placed the whole search-and-delete inside a performBlock closure. I don't know how you have setup your Core Data, but you might want to consider threading to maximize perceived performance.