I am trying to retrieve the username belonging to a post. The username can be found in another class however.
These are the two classes I have:
Post
User
I am populating all the information from the Posts class in a UICollectionView but since the username is in the User class my query is not working. Here's my code...
//QUERY LOST
let querylost = PFQuery(className: "Post")
querylost.order(byDescending: "createdAt")
querylost.whereKey("lostfound", equalTo: "lost")
querylost.findObjectsInBackground { (objects, error) in
if let posts = objects {
for post in posts {
self.addresslost.append(post["address"] as! String)
self.breedlost.append(post["breed"] as! String)
self.phonelost.append(post["phone"] as! String)
self.imageFileslost.append(post["imageFile"] as! PFFile)
//HERE'S THE PROBLEM - username is in User Class
self.usernames.append(self.queryusers[post["userid"] as! String]!)
//ENDS here
self.lostCollectionView.reloadData()
self.refresherLost.endRefreshing()
}
}
}
//TO SHOW DATA
scrollView.delegate = self
lostCollectionView.delegate = self
lostCollectionView.dataSource = self
}
Instead of using the userid field, you should create a pointer to put on your Post object instead. Then you can access the User object via the Post object.
Your code would look something like this:
let querylost = PFQuery(className: "Post")
querylost.order(byDescending: "createdAt")
querylost.include("user")
querylost.whereKey("lostfound", equalTo: "lost")
querylost.findObjectsInBackground { (objects, error) in
if let posts = objects {
for post in posts {
self.addresslost.append(post["address"] as! String)
self.breedlost.append(post["breed"] as! String)
self.phonelost.append(post["phone"] as! String)
self.imageFileslost.append(post["imageFile"] as! PFFile)
//unwrap the user pointer, and get the username
if let user = post["user"] as? PFUser, let username = user.username{
self.usernames.append(username)
}
//ENDS here
self.lostCollectionView.reloadData()
self.refresherLost.endRefreshing()
}
}
}
Related
I have two versions of code. Version 1 is the code I intend on using. I am trying to look up a current user's friends' in the friend array. For every friend, I want to append certain items to each array to display in a tableview cell. Version 1 incorporates a for in loop with i friends array contains: ["magellan"]. When I use a PFQuery to lookup data, it does not execute the query. However, version two is not run in a for in loop nor does it use friend. It uses the actual string "magellan" instead and works fine. What is my problem?. Thanks
Version 1:
override func viewdidload() {
for i in 0..<friendsArray.count {
friend = friendsArray[i]
print(friend)
let query2 = PFQuery(className: "CheckPost")
query2.whereKey("Username", equalTo: friend)
query2.findObjectsInBackground (block: { (objects:[PFObject]?, error: Error?) in
if let objects = objects {
for object in objects {
self.checkArray.append(object["Checks"] as! String)
self.checkArrayobjectId.append(object.objectId!);
self.checkTimeArray.append(object["UserTime"] as! String)
//append userimage
self.checkPicArray.append(UIImage(named: "randomguy")!)
print("successfully checked for friends checks")
}
} else if error != nil {
print(error)
}
})
}
Version 2:
override func viewdidload()
for i in 0..<friendsArray.count {
friend = friendsArray[i]
print(friend)
}
let query2 = PFQuery(className: "CheckPost")
query2.whereKey("Username", equalTo: "magellan")
query2.findObjectsInBackground (block: { (objects:[PFObject]?, error: Error?) in
if let objects = objects {
for object in objects {
self.checkArray.append(object["Checks"] as! String)
self.checkArrayobjectId.append(object.objectId!);
self.checkTimeArray.append(object["UserTime"] as! String)
//append userimage
self.checkPicArray.append(UIImage(named: "randomguy")!)
print("successfully checked for friends checks")
}
} else if error != nil {
print(error)
}
})
}
It looks like you are misunderstanding the async behavior of the findObjectInBackground function. This function is async and the callbacks will only be called after the loop has ended. You code should work with the find though. Something like this:
for i in 0..<friendsArray.count {
friend = friendsArray[i]
print(friend)
let query2 = PFQuery(className: "CheckPost")
query2.whereKey("Username", equalTo: friend)
let objects = query2.find()
if objects {
for object in objects {
self.checkArray.append(object["Checks"] as! String)
self.checkArrayobjectId.append(object.objectId!);
self.checkTimeArray.append(object["UserTime"] as! String)
//append userimage
self.checkPicArray.append(UIImage(named: "randomguy")!)
print("successfully checked for friends checks")
}
}
}
I'm trying to run a PFQuery that will populate an array of custom structs.
Everything looks ok, but when I run the code the query returned is empty. I also tried this using PFUser.Query, which worked, but did not return a value for objectId, so tried to query the class instead.
Here is what I have:
var parseUsers:[ParseUsers] = []
var allUsers:[PFObject] = []
let query = PFQuery(className:"User")
let currentUser = PFUser.current()
query.whereKey("username", notEqualTo: currentUser?.username)
do {
allUsers = try (query.findObjects())
for i in allUsers{
var pImage = UIImage()
if i["profileImage"] != nil{
let imageFile = i["profileImage"] as? PFFileObject
imageFile?.getDataInBackground { (imageData: Data?, error: Error?) in
if let error = error {
print(error.localizedDescription)
} else if let imageData = imageData {
pImage = UIImage(data:imageData)!
}
}
}
let currentUserInfo = ParseUsers(objectID:i["objectId"] as! String,
username:i["objectId"] as! String,
userWorkouts:i["userWorkouts"] as! [Dictionary<String, String>],
profileImage:pImage)
parseUsers.append(currentUserInfo)
}
} catch {
print("Error")
}
Found out what the problem was!
For anyone experiencing similar issues in the future, when determining the class you want to query, you need to out _ in front. In the case above it would be let query = PFQuery(className:"_User").
Weird, as I couldn't see this mentioned in the parse documentation anywhere.
As mentioned by #William George in the Querying subsection of Users you can see how to construct a query on on the _User class.
A Below is an example of this (lifted straight from the docs):
var query = PFUser.query()
query.whereKey("gender", equalTo:"female")
var girls = query.findObjects()
You can query your Parse Server with PFUser just like with PFQuery but it can only be used on the _User class.
I am trying to read data from media when data is updated on /media node, but .observe(.childAdded is not called.
For example, I update data at /media/-LKN1j_FLQuOvnhEFfao/caption , but I never receive the event in observeNewMedia .
I can read the data with no problem the first time when ViewDidLoad completes.
The first step is to download the user data, second is to get the locality from currentUser and the last step is to attach a listener .childAdded on media.
I suspect that the event is not triggered because fetchMedia is called inside DDatabaseRReference.users(uid: uid).reference().observe(.value
media
-LKNRdP4ZsE3YrgaLB30
caption: "santa"
mediaUID: "-LKNRdP4ZsE3YrgaLB30"
locality: "barking"
users
Q6Dm3IMLNLgBH3ny3rv2CMYf47p1
media
-LKNReJCxgwtGRU6iJmV: "-LKNRdP4ZsE3YrgaLB30"
email: "john#gmail.com"
locality: "barking"
//enables the programmer to create references to different childs in Firebase
enum DDatabaseRReference {
case root
case users(uid:String)
case media //to store photos
func reference() -> DatabaseReference {
return rootRef.child(path)
}
//return root reference to our database
private var rootRef: DatabaseReference {
return Database.database().reference()
}
private var path: String {
switch self { //self is the enum DDatabaseReference
case .root:
return ""
case .users(let uid):
return "users/\(uid)"
case .media:
return "media"
}
}
}//end of enum DatabaseReference
class NewsfeedTableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
//observe ~/users/uid
DDatabaseRReference.users(uid: uid).reference().observe(.value, with: { (snapshot) in
DispatchQueue.main.async {
if let userDict = snapshot.value as? [String : Any] {
self.currentUser = UserModel(dictionary: userDict)
self.fetchMedia()
self.tableView.reloadData()
}
}
})
}
func fetchMedia() {
Media.observeNewMedia((currentUser?.locality)!) { (newMedia) in
//check if newly downloaded media is already in media array
if !self.media.contains(newMedia) {
self.media.insert(newMedia, at: 0)
self.tableView.reloadData()
}else {
//remove old media and add the newly updated one
guard let index = self.media.index(of: newMedia) else {return}
self.media.remove(at: index)
self.media.insert(newMedia, at: 0)
self.tableView.reloadData()
}
}
}
}//end of NewsfeedTableViewController
class Media {
class func observeNewMedia(_ userLocality: String, _ completion: #escaping (Media) -> Void) {
DDatabaseRReference.media.reference().queryOrdered(byChild: "locality").queryEqual(toValue: userLocality).observe(.childAdded, with: { snapshot in
guard snapshot.exists() else {
print("no snap ")
return}
print("snap is \(snapshot)")
let media = Media(dictionary: snapshot.value as! [String : Any])
completion(media)
})
}
} //end of class Media
Let's first update the structure so make it more queryable
assume a users node
users
-Q6Dm3IMLNLgBH3ny3rv2CMYf47p1 //this is each users uid
email: "john#gmail.com"
locality: "barking"
and a media node that contains media for all users
media
-abcdefg12345 //node created with childByAutoId
caption: "santa"
for_uid: -Q6Dm3IMLNLgBH3ny3rv2CMYf47p1 //matches the uid in the /users node
Then our main viewController which contains a reference to Firebase and logs the user in
class ViewController: UIViewController {
var ref: DatabaseReference!
override func viewDidLoad() {
super.viewDidLoad()
self.ref = Database.database().reference()
//log user in which will populate the Auth.auth.currentUser variable
}
.
.
.
We need an object to store the media in and then an array to hold those objects
class MediaClass {
var key = ""
var caption = ""
init(k: String, c: String) {
self.key = k
self.caption = c
}
}
var mediaArray = [MediaClass]()
then set up the observers which will add, update or remove from the array when media for this user is added, changed or removed.
let thisUid = Auth.auth().currentUser?.uid
let mediaRef = self.ref.child("media")
let queryRef = mediaRef.queryOrdered(byChild: "for_uid").queryEqual(toValue: thisUid)
queryRef.observe(.childAdded, with: { snapshot in
let dict = snapshot.value as! [String: Any]
let key = snapshot.key
let caption = dict["caption"] as! String
let m = MediaClass.init(k: key, c: caption)
self.mediaArray.append(m)
self.tableView.reloadData()
})
queryRef.observe(.childChanged, with: { snapshot in
let dict = snapshot.value as! [String: Any]
let key = snapshot.key
let caption = dict["caption"] as! String
let index = self.mediaArray.index { $0.key == key } //locate this object in the array
self.mediaArray[index!].caption = caption //and update it's caption
self.tableView.reloadData()
})
//leaving this an an exercise
queryRef.observe(.childRemoved....
Note we added .childAdded, .childChanged and .childRemoved events to the media node via a query so the only events the app will receive are the ones that pertain to this user.
Also note there's no error checking so that needs to be added.
self.messageFromId.removeAll()
self.messageFrom.removeAll()
self.taskTitle.removeAll()
self.message.removeAll()
self.taskId.removeAll()
self.messageId.removeAll()
self.messageType.removeAll()
if PFUser.current()?.objectId! != nil {
let query = PFQuery(className: "Message")
query.addDescendingOrder("createdAt")
query.limit = 10
cur = PFUser.current()!.objectId!
query.whereKey("messageTo", equalTo: cur!)
query.findObjectsInBackground(block: { (objects, error) in
if let posts = objects{
for object in posts {
if let post = object as? PFObject {
let query2 = PFUser.query()
query2?.whereKey("objectId", equalTo: post["messageFrom"] as! String)
query2?.findObjectsInBackground(block: { (objects, error) in
if let posts = objects{
for object in posts {
if let post2 = object as? PFObject {
if post2["handle"] as! String == "new$!" {
self.messageFromId.append(post["messageFrom"] as! String)
self.messageFrom.append(post["messageFrom"] as! String)
self.taskTitle.append(post["taskTitle"] as! String)
self.message.append(post["message"] as! String)
self.taskId.append(post["taskId"] as! String)
self.messageId.append(post.objectId!)
self.messageType.append(post["messageType"] as! String)
} else {
self.messageFromId.append(post["messageFrom"] as! String)
self.messageFrom.append(post2["handle"] as! String)
self.taskTitle.append(post["taskTitle"] as! String)
self.message.append(post["message"] as! String)
self.taskId.append(post["taskId"] as! String)
self.messageId.append(post.objectId!)
self.messageType.append(post["messageType"] as! String)
}
}
}
}
self.tableView.reloadData()
self.refresher.endRefreshing()
})
}
}
}
})
}
Results seems to come back in a random order, not sure why ? Am I doing the query within the query correctly ?
I did add : query.addDescendingOrder("createdAt") at the beginning of the query. It's not working
Try using: query.order(byDescending: "createdAt") instead.
I'm attempting to query for a specific user using the new Firebase like so:
DataService.sharedInstance.REF_USERS.queryOrderedByChild("username").queryEqualToValue(field.text).observeSingleEventOfType(.Value, withBlock: { (snapshot) in
if let userDoesNotExist = snapshot.value as? NSNull {
print("No User found!")
} else {
let theUsr = snapshot.value as! User
print(theUsr)
}
}, withCancelBlock: { (error) in
// Error
})
From there I was looking to store the snapshot into its own object and access its values from there. I was attempting to do so by doing this:
class User {
let key: String!
let ref: FIRDatabaseReference?
var username: String!
var email: String!
// Initialize from data snapshot
init(snapshot: FIRDataSnapshot) {
key = snapshot.key
email = snapshot.value!["email"] as! String
username = snapshot.value!["username"] as! String
ref = snapshot.ref
}
func toAnyObject() -> AnyObject {
return [
"email": email,
"username": username,
]
}
}
The problem I'm running into is a crash with the following:
Could not cast value of type '__NSDictionaryM' (0x10239efc0) to 'Scenes.User' (0x100807bf0).
How do I fix this? I don't remember running into this problem on the old Firebase. I need access to the snapshot.value's key and for some reason can't access it by using snapshot.value.key without getting a crash so I figure I'd try passing all the data into it's own object.
snapshot.value isn't a User, it's a dictionary (*)
Here's a couple of options - there are many.
let aUser = User()
aUser.initWithSnapshot(snapshot)
then, within the User class, map the snapshot.values to the user properties
or
let aUser = User()
aUser.user_name = snapshot.value["user_name"] as! String
aUser.gender = snapshot.value["gender"] as! String
Oh, and don't forget that by using .Value your snapshot may return multiple child nodes so those will need to be iterated over
for child in snapshot.children {
let whatever = child.value["whatever"] as! String
}
In your case it's just one user so it's fine they way you have it.
*It could be
NSDictionary
NSArray
NSNumber (also includes booleans)
NSString