Parsing array in Swift2.2 - swift

How to get body and title data from the below response. I'm totally stuck
[aps: {
alert = {
body = test123;
title = test123;
};
},
gcm.message_id: 0:4a]

If you are working with notification then you need to access the data like this way.
if let info = notification.userInfo as? [String: AnyObject],
let apsDic = info["aps"] as? [String: AnyObject],
let alertDic = info["alert"] as? [String: AnyObject] {
if let body = alertDic["body"] as? String {
print(body)
}
if let title = alertDic["title"] as? String {
print(title)
}
}

Related

Not able to read data from Firebase realtime database

I have stored the comments under a post in firebase realtime database. The problem i have is that when i try to parse out the data from firebase i get an error that says Unexpectedly found nil while unwrapping an Optional value. So for example if i try to pront the data stored under degree, i get this nil error. But when i print "comments" instead of the "degree" i successfully fetch the data. My database structure looks like this.
func obeserveComments() {
// get auto-id of post
let commentKey = self.keyFound
let postRef = Database.database().reference().child("posts").child(commentKey)
var tempComments = [Comments]()
postRef.observe(.value, with: {(snapshot) in
if let dict = snapshot.value as? [String:Any] {
if let comments = dict["comments"] as? [String:Any] {
let degree = comments["reply degree"] as! String
// let name = comments["reply name"] as! String
// let text = comments["reply text"] as! String
// let university = comments["reply university"] as! String
// let photoURL = comments["reply url"] as! String
// let url = URL(string: photoURL)
// let timestamp = comments["timestamp"] as! Double
print(degree)
}
}
})
}
The answer by #aytroncb is a good answer, I prefer to leave Firebase data 'Firebasy' as long as possible. In other words coverting to dictionaries looses ordering and and find code like this
[String: [String: [String: Any]]]
To be very hard to read.
I prefer
let snap = snapshot.childSnapshot("comments") //snap becomes a DataSnapshot
So my solution maintains the order and leverages .childSnapshot to leave data in it's DataSnapshot form.
func readPostComments() {
let postRef = self.ref.child("posts") //self.ref points to my firebase
postRef.observeSingleEvent(of: .value, with: { snapshot in
let allPosts = snapshot.children.allObjects as! [DataSnapshot]
for postSnap in allPosts {
print("postId: \(postSnap.key)")
let commentsSnap = postSnap.childSnapshot(forPath: "comments") //will be a DataSnapshot
let allComments = commentsSnap.children.allObjects as! [DataSnapshot]
for commentSnap in allComments {
print(" commentId: \(commentSnap.key)")
let replyDegree = commentSnap.childSnapshot(forPath: "reply_degree").value as? String ?? "No Degree"
let replyName = commentSnap.childSnapshot(forPath: "reply_name").value as? String ?? "No Name"
print(" degree: \(replyDegree) by: \(replyName)")
}
}
})
}
EDIT
For a single post, remove the top part of the code that reads in and iterates over all posts.
func readCommentsForOnePost() {
let postRef = self.ref.child("posts")
let postCommentRef = postRef.child("post_0")
postCommentRef.observeSingleEvent(of: .value, with: { snapshot in
print("postId: \(snapshot.key)")
let commentsSnap = snapshot.childSnapshot(forPath: "comments") //will be a DataSnapshot
let allComments = commentsSnap.children.allObjects as! [DataSnapshot]
for commentSnap in allComments {
print(" commentId: \(commentSnap.key)")
let replyDegree = commentSnap.childSnapshot(forPath: "reply_degree").value as? String ?? "No Degree"
let replyName = commentSnap.childSnapshot(forPath: "reply_name").value as? String ?? "No Name"
print(" degree: \(replyDegree) by: \(replyName)")
}
})
}
Its because firebase is returning your data like this
{
"MAKFW244kdL)Cw;1": [Array of data],
"LOPSw!35pa3flAL4": [Array of data],
"ALV34VR4_A6Vn1a": [Array of data]
}
So change your initial casting of snapshot.value to this:
if let dict = snapshot.value as? [String: [String: Any]]
then loop through that new dictionary like this:
for objectJson in dict.values {
if let comments = objectJson["comments"] as? [String: [String: Any]] {
for commentJson in comments.values {
let degree = commentJson["reply_degree"] as? String
}
}
}
Update
Just read through your post again and noticed your trying to access the comments directly with a key, your first going to need to provide the PostId. Then you can use the above code to loop through the objects
let postRef = Database.database().reference().child("posts").child(postID)
alternatively I believe you can have the comments returned as a normal list by doing something like this:
let postRef = Database.database().reference().child("posts").child("\(postID)/{id}")

How to unwrap an callback in swift?

I'm using FB Login for my app in Swift and when I make a graph request, it returns the following result:
Optional({
email = "arjun.ramjams#gmail.com";
id = 10218497873670001;
name = "Arjun Ram";
})
Now, how should I read, each individual value(i.e: email, id and name)?
func fetchProfile() {
let parameters = ["fields": "id,email, first_name, last_name"]
GraphRequest(graphPath: "me",parameters: parameters).start{(connection, user, Err) in
if Err != nil {
print(Err!)
return
}
let dic = user as! NSDictionary
let userID = dic["id"] as! String
let Email = dic["email"] as! String
let fname = dic["first_name"] as! String
let lname = dic["last_name"] as! String
}
}
The result is a Dictionary of type [String:Any]?.
let result:[String:Any]? = [
"email":"arjun.ramjams#gmail.com",
"id" : 10218497873670001,
"name":"Arjun Ram"
]
You can fetch the fields from result like,
let email = result?["email"] as? String
let id = result?["id"] as? Int
let name = result?["name"] as? String
You need to use if let or guard let for unwraping an object or variable
e.g.:
if let result = result as? [String: Any] {
print(result[“email”])
}
or
guard let result = result as? [String: Any] else {
// return something
}

Retrieving multiple data in Firebase with Swift

Here is the database
I'm trying to append isDisable to an array
What i've come so far:
func retrieving(){
Database.database().reference().child("ServingHours/\(choosenDate)").observeSingleEvent(of: .value, with: {(snapshot) in
if let eachDict = snapshot.value as? NSDictionary{
for each in eachDict{
print(each.value)
}
}
}, withCancel: {(Err) in
})
}
with the result in console:
{
isDisable = false;
numberOfRegistration = 0;
}
{
isDisable = false;
numberOfRegistration = 0;
}
{
isDisable = false;
numberOfRegistration = 0;
}
{
isDisable = false;
numberOfRegistration = 0;
}
From this, I don't know what to do to get a specific value from each.value
You're retrieving your specific dates objects which have 2 items within them (isDisable and numberOfRegistration). When you retrieve them from Firebase, you're type casting them into NSDictionary using "as?".
Your values seem to be in String format, therefore, you can retrieve them from the NSDictionary using:
let isDisabled = eachDict["isDisable"] as? String
let numberOfRegistration = eachDict["numberOfRegistration"] as? String
You can also directly set your values in Firebase to be a Bool or Int and you can typecast your retrieved objects into Bool (isDisable) and Int (numberOfRegistration). But it looks like they are currently Strings, so your code would like this:
func retrieving(){
Database.database().reference().child("ServingHours/\(choosenDate)").observeSingleEvent(of: .value, with: {(snapshot) in
if let eachDict = snapshot.value as? NSDictionary{
let isDisabled = eachDict["isDisable"] as? String
let numberOfRegistration = eachDict["numberOfRegistration"] as? String
print(isDisabled)
print(numberOfRegistration)
}
}, withCancel: {(Err) in
})
}
Also, if you want to retrieve a value directly, then you don't need to use an NSDictionary. You can just directly cast that value you retrieve into whatever type of object you have. For example, say you want to retrieve your "isDisable" value directly:
func retrieving(){
Database.database().reference().child("ServingHours/\(choosenDate)").child("isDisable").observeSingleEvent(of: .value, with: {(snapshot) in
if let isDisable = snapshot.value as? String{
print(isDisable) }
Check out the Firebase official documentation for Swift:
https://firebase.google.com/docs/database/ios/read-and-write
You need to cast to a dictionary once again (and don't use NSDictionary)
if let eachDict = snapshot.value as? [String: AnyObject]{
for each in eachDict {
if let innerDict = each as? [String: AnyObject] {
//innerDict now contains isDisable and numberOfRegistration
if let isDisable = innerDict["isDisable"] as? String
print(isDisable)
}
if let numberOfRegistration = innerDict["numberOfRegistration"] as? String {
print(numberOfRegistration)
}
}
}
}

Code only retrieving one value from data in Firebase

As the title suggests, I'm trying to retrieve some data from firebase database, but my code's not working. I have three children (I guess that's how you call them) inside "Posts" called "title", "description" and "username" and I'm trying to get all of them and append them into a variable to use them later, but it only retrieves the first value of each of them, despite the fact that there are like 5 values. Anyone knows why?
By the way, I'm calling upon this function on my ViewDidLoad.
let postDB = Database.database().reference().child("Posts")
postDB.queryOrderedByKey().observeSingleEvent(of: .value) { (snapshot) in
if let snapshotValue = snapshot.value as? NSDictionary {
let postTitle = snapshotValue["title"] as? String
let postUsername = snapshotValue["username"] as? String
let postDesc = snapshotValue["description"] as? String
let postArray = postStruct(title: postTitle, description: postDesc, username: postUsername)
self.newPost.insert(postArray, at: 0)
}
import UIKit
import FirebaseDatabase
class HomeViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
// MARK: - variables
var postDB: DatabaseReference!
override func viewDidLoad() {
super.viewDidLoad()
// getting a node from database //
postDB = Database.database().reference().child("Posts")
// observing data changes //
postDB.observe(DataEventType.value) { (dataSnapshot) in
self.postArray.removeAll()
if dataSnapshot.childrenCount > 0 {
for post in dataSnapshot.children.allObjects as! [DataSnapshot] {
let object = post.value as! [String: Any]
let description = object["description"] as! String
let title = object["title"] as! String
let userName = object["username"] as! String
let model = postStruct(title: title, description: description, username: userName))
self.postArray.append(model)
}
}
self.tableView.reloadData()
}
}
}
Try this – the code replaces what you currently have in the snapshot handler.
if let firebaseList = snapshot.children.allObjects as? [FIRDataSnapshot] {
if let swiftList = snapshot.value as? [String:AnyObject] {
for firebaseItem in firebaseList {
let childID = firebaseItem.key as String
let swiftItem = swiftList[childID]
let postTitle = swiftItem?["title"] as? String
let postUsername = swiftItem?["username"] as? String
let postDesc = swiftItem?["description"] as? String
let postArray = postStruct(title: postTitle, description: postDesc, username: postUsername)
self.newPost.insert(postArray, at: 0)
}
}
}
}
Worked for me. It gets all the values now you just have to put them in an array
postDB.get().addOnCompleteListener(new OnCompleteListener<DataSnapshot>() {
#Override
public void onComplete(#NonNull #NotNull Task<DataSnapshot> task) {
if(!task.isSuccessful()){
Log.e("firebase", "Error getting data; ", task.getException());
}else{
Log.d("firebase", String.valueOf(task.getResult().getValue()));
}
}
});

Return from initializer without initializing all stored properties error - yet everything is initialized

I'm running into this weird error. it was working fine until I added the image and imageString values and then this error happened:
Return from initializer without initializing all stored properties
I thought I initialized all the properties, not sure why this error is happening. Here is the custom object class
class JSONObject {
private let baseImageURL = "https://website.com"
var airbnbUS: Int
var airbnbLocal: Int
var imageString: String
var image: URL
init(airbnbUS: Int, airbnbLocal: Int, imageString: String, image: URL ){
self.airbnbUS = airbnbUS
self.airbnbLocal = airbnbLocal
self.imageString = imageString
self.image = image
}
init(resultsDictionary:[String: Any]){
guard let cost = resultsDictionary["cost"] as? [String: Any],
let airbnb = cost["airbnb_median"] as? [String : Any],
let usd = airbnb["USD"] as? Int,
let chf = airbnb["CHF"] as? Int
else {
airbnbUS = 0
airbnbLocal = 0
return
}
airbnbUS = usd
airbnbLocal = chf
guard let media = (resultsDictionary["media"] as? [String: Any]),
let imageDictionary = media["image"] as? [String: Any],
let image1000 = imageDictionary["1000"] as? String
else {
imageString = ""
image = URL(string: "\(baseImageURL)")!
return
}
imageString = image1000
image = URL(string: "\(baseImageURL)\(imageString)")!
}
}
The issue is in your resultsDictionary initializer. The return in your first guard statement could return from the initializer early, and the following guard statement (and the code where you assign the image and imageString properties) might not execute.
One solution is to change the first guard statement to an if-let statement.
init(resultsDictionary:[String: Any]){
if let cost = resultsDictionary["cost"] as? [String: Any],
let airbnb = cost["airbnb_median"] as? [String : Any],
let usd = airbnb["USD"] as? Int,
let chf = airbnb["CHF"] as? Int
{
airbnbUS = usd
airbnbLocal = chf
} else {
airbnbUS = 0
airbnbLocal = 0
}
guard let media = (resultsDictionary["media"] as? [String: Any]),
let imageDictionary = media["image"] as? [String: Any],
let image1000 = imageDictionary["1000"] as? String
else {
imageString = ""
image = URL(string: "\(baseImageURL)")!
return
}
imageString = image1000
image = URL(string: "\(baseImageURL)\(imageString)")!
}