On Parse I have users with Facebook profile and Email login profile. So I want to bury for users data in my twitter-like app.
In my "messages" class on Parse I have column "sender" that contains pointers to parse users.
I just want to retrive and show the name of users in class "messages" contained in the column "sender" wich contains pointers to PFUsers of which I need data for keys
"first_name"
"last_name"
"profile_picture"
How can I retrive their data like name and image in order to show them in a tableview?
these are the declarations of my arrays:
var sendersArray : [String] = []
var picturesArray : [NSData] = []
maybe I could use something like this tuple, but I can't understand how to grab data from pointers
for user in list {
let firstName = "fist_name"
let lastName = "last_name"
let oProfileImage = NSData() //"image_profile" as! NSData
otherUsers.append((oName: firstName, oLastName: lastName, oImageProfle: oProfileImage))
}
version - 1:
I started with printing the whole pf object
//******************************************************
func theSearch() {
let theSearchQuery = PFQuery(className: "Messages")
theSearchQuery.findObjectsInBackgroundWithBlock({
(objects : [AnyObject]?, error : NSError?) -> Void in
for object in objects! {
let theName = object.sender!
print(object)
print(theName)
sendersArray.append(theName)
let profilePicture = object["profile_pic"] as! PFFile
picturesArray.append(profilePicture)
}
self.tableView.reloadData()
})
}
//*******************************************************
version - 2:
then, found this solution, but still, doesn't
func theSearch() {
let theSearchQuery = PFQuery(className: "Messages" )
theSearchQuery.includeKey("sender")
theSearchQuery.findObjectsInBackgroundWithBlock({
(objects : [AnyObject]?, error : NSError?) -> Void in
for object in objects! {
let theName = object.sender!["first_name"] as? String
print(object)
print(theName)
sendersArray.append(theName)
let profilePicture = object["profile_pic"] as! PFFile
picturesArray.append(profilePicture)
}
self.tableView.reloadData()
})
}
errors:
seems to be a problem with sender, maybe I shouldn't use it
thanks in advance
let theName = object.objectForKey("sender")!.objectForKey("first_name") as! String
Complete Code:
func theSearch() {
let theSearchQuery = PFQuery(className: "Messages")
theSearchQuery.includeKey("sender")
theSearchQuery.findObjectsInBackgroundWithBlock({
(objects : [AnyObject]?, error : NSError?) -> Void in
for object in objects! {
let theName = object.objectForKey("sender")!.objectForKey("first_name") as! String
print(object)
print(theName)
self.sendersArray.append(theName)
let profilePicture = object["profile_picture"] as! PFFile
self.picturesArray.append(profilePicture)
}
self.tableView.reloadData()
})
}
Also, your picturesArray should be of type PFFile, like this:
var picturesArray = [PFFile]()
NOT NSData. change that at the top of your class.
-----EDIT------:
If you want to retrieve an image from a parse query, do this:
1) at the top of your class, declare the following arrays to store the results:
// your images will be stored in the file array
var fileArray = [PFFile]()
// your first and last names will be stored in String Arrays:
var firstNameArray = [String]()
var lastNameArray = [String]()
2) perform the query:
let query1 = PFQuery(className: "_User")
query1.orderByDescending("createdAt")
query1.findObjectsInBackgroundWithBlock({
(objects : [AnyObject]?, error : NSError?) -> Void in
if error == nil {
for x in objects! {
let firstName = x.objectForKey("first_name") as! String
let lastName = x.objectForKey("last_name") as! String
self.firstNameArray.append(firstName)
self.lastNameArray.append(lastName)
if x.objectForKey("profile_picture") as? PFFile == nil {
print("do nothing cause it's nil")
}
else {
let file:PFFile = x.objectForKey("profile_image") as! PFFile
self.fileArray.append(file)
}
}
self.tableView.reloadData()
}
})
Note I am using Swift 2 and Xcode 7. Syntax is slightly different in Xcode 6.4 and Swift 1.2.
Related
I have a chat filled with users and of course a username array. I want to get the profile picture associated with the username in order for each user in the username array. Parse, however, can only sort by ascending/descending order that I am aware of.
Therefore, I need to figure out how to sort the data once received.
I am ultimately appending a url to be used as the pic.
func getPics(_ completionHandler: #escaping () -> Void) {
let query = PFQuery(className: "_User")
var dictionary: [String : Int] = [:]
var unit = 0
for username in usernameArray {
unit += 1
dictionary[username] = unit
}
query.findObjectsInBackground(block: { (objects: [PFObject]?, error: Error?) in
if let objects = objects {
for object in objects {
if error == nil {
for user in self.usernameArray {
let pfuser = object["username"] as! String
if pfuser == user {
let imageFile = object["profilePic"] as? PFFileObject
let imageFileString = imageFile?.url as! String
if let url = URL(string: imageFileString) {
let replacedImageUrlString = imageFileString.replacingOccurrences(of: "[removed for privacy]", with: "removed for privacy")
let url = replacedImageUrlString as NSString
self.urlArray.append(url)
}
}
}
}
}
completionHandler()
}
})
}
I am not aware of Parse server, So I dont really know if there exists provision to get the response in specific order, if it exists that should be the optimal solution. But there is a generic issue with your solution, its the time complexity.
You have two nested for loops which makes its worst case complexity to be O(n^2), I guess the least you can do is to reduce its complexity to O(n)
func getPics(_ completionHandler: #escaping () -> Void) {
let query = PFQuery(className: "_User")
var dictionary: [String : Int] = [:]
var unit = 0
for username in usernameArray {
unit += 1
dictionary[username] = unit
}
query.findObjectsInBackground(block: { (objects: [PFObject]?, error: Error?) in
if let objects = objects, error == nil {
let objectsDict = Dictionary(grouping: objects, by: { $0["username"] as! String /* typically you should be accessing $0.username, but again am not aware of PFObject */})
for user in self.usernameArray {
if let pfuser = objectsDict[user]?[safe: 0] as? PFObject {
let imageFile = pfuser["profilePic"] as? PFFileObject
let imageFileString = imageFile?.url as! String
if let url = URL(string: imageFileString) {
let replacedImageUrlString = imageFileString.replacingOccurrences(of: "[removed for privacy]", with: "removed for privacy")
let url = replacedImageUrlString as NSString
self.urlArray.append(url)
}
}
}
completionHandler()
}
})
}
Once you get the array of PFObject, you can create a dictionary with username as the key and PFObject as value, once you have the dictionary you can get the PFObject for specific username in O(1), so you can run a single for loop which reduces your code's complexity to O(n)
P.S If you are wondering what [safe: 0] is you can add this handy extension to safely access object at specific index in an array
link: Safe (bounds-checked) array lookup in Swift, through optional bindings?
extension Collection {
subscript (safe index: Index) -> Element? {
return indices.contains(index) ? self[index] : nil
}
}
P.P.S: My answer is completely ignoring the complexity of Dictionary(grouping: API itself, I tried to look for the info, but couldnt find. But I think its O(n) not really sure though, whatever it is if its not O(n^2) you will still be benefited
I have a query that gets objects from the server I'm then reducing the number of objects by matching "packName" to "className" which should just give me the children of "packName".
from this i am populating an array of struct items and pulling out the data for the first index of the array.
this is fine but I'm just a bit concerned that if the number of children increases this may slow processing down. so i was wondering if there was a way to just retrieve the first item of the for loop, which is all I'm after as the query has been sorted in ascending order.
this is the function code below.
class func createHistory(packName: String, completeBlock: ((Bool) -> Void)? = nil) {
struct initialDataStruct {
var packNameStruct : String
var packIdStruct : String
var partNameStruct : String
var partIdStruct : String
var partIndexStruct : Int
}
var initialDataArray = [initialDataStruct]()
let historyClass = PFObject(className: packName)
let query = PFQuery(className: "Part")
query.includeKey("fromPack")
query.order(byAscending: "partName")
query.fromLocalDatastore()
query.findObjectsInBackground { (objects, error) in
if error != nil {
print(error!)
}
else if let parts = objects {
for object in parts {
// if the fromPack column has data
if let fromPack = object.object(forKey: "fromPack") as? PFObject {
// create the class name from the pack name
if let className = (fromPack.object(forKey: "packName") as? String) {
// packName was sent from JVC
// this will limit array items to how ever many children packName has
if packName == className {
// because its sorted could probably just get the first item here
let packName = fromPack.object(forKey: "packName") as! String
let packId = fromPack.objectId as String!
let partName = object.object(forKey: "partName") as! String
let partId = object.objectId as String!
let partIndex = 0
initialDataArray.append(initialDataStruct(packNameStruct: packName,
packIdStruct: packId!,
partNameStruct: partName,
partIdStruct: partId!,
partIndexStruct: partIndex))
}
}
}
} // for
historyClass.add(initialDataArray[0].packNameStruct, forKey: "packName")
historyClass.add(initialDataArray[0].partIdStruct, forKey: "packId")
historyClass.add(initialDataArray[0].partNameStruct, forKey: "partName")
historyClass.add(initialDataArray[0].partIndexStruct, forKey: "partIndex")
print(historyClass)
PFObject.pinAll(inBackground: [historyClass])
}
} // query
}
I have a query that retrieve current user and their friend list post on my feed page, i tried to order the post by it's creation date or the latest date. Here is the result that I got from my code when I print the "createAt":
The result is very random.
And here is my code
for friend in friendsArray {
let postQuery = PFQuery(className:"Post")
postQuery.whereKey("createdBy", equalTo: friend)
postQuery.includeKey("Song")
postQuery.includeKey("createdBy")
postQuery.orderByDescending("createdAt")
postQuery.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
// The find succeeded.
//print("Successfully retrieved \(objects!.count) scores.")
// Do something with the found objects
if let objects = objects as? [PFObject] {
for object in objects {
let songObject : PFObject = object["Song"] as! PFObject
let songTitle: AnyObject? = songObject.objectForKey("songTitle")
let songArtist: AnyObject? = songObject.objectForKey("songArtist")
let albumCover: AnyObject? = songObject.objectForKey("albumCover")
let previewUrl: AnyObject? = songObject.objectForKey("previewUrl")
let trackviewUrl : AnyObject? = songObject.objectForKey("trackViewUrl")
let timeAgo = object.createdAt
let postCaption: AnyObject? = object.objectForKey("postCaption")
let postId: AnyObject? = object.objectId
let recentTotalComment: AnyObject? = object.objectForKey("recentTotalComment")
let recentTotalLike: AnyObject? = object.objectForKey("recentTotalLike")
let posterObject = object["createdBy"] as! PFObject
let posterUser = object["createdBy"] as! PFUser
let posterImage: AnyObject? = posterObject.objectForKey("profilePicture")
let posterUsername: AnyObject? = posterObject.objectForKey("username")
self.postDictionary = ["title" : songTitle!, "artist" : songArtist!, "previewUrl" : previewUrl! , "caption" : postCaption!, "timeAgo" : self.timeAgoSinceDate(timeAgo!, numericDates: true), "cover" : albumCover!, "postId" : postId!, "posterImage" : posterImage!, "posterUsername" : posterUsername!, "likeCount" : recentTotalLike!, "commentCount" : recentTotalComment! , "otherUser" : posterUser , "trackViewUrl" : trackviewUrl!]
self.postArray.addObject(self.postDictionary!)
self.likeArray.addObject(recentTotalLike as! String)
self.commentArray.addObject(recentTotalComment as! String)
print("created at : \(object.createdAt)")
self.tbl.reloadData()
self.tbl.setContentOffset(CGPointMake(0, self.verticalContentOffset), animated: true)
self.refreshControl.endRefreshing()
}
}
} else {
// Log details of the failure
print("Error: \(error!) \(error!.userInfo)")
}
}
}
How can I order my post query by it's created at date?
The postQuery is wrapped in a loop to iterate through all of friends in friendsArray. Since the query is executed in the background, the loop will continue while the query is being executed and still waiting for a response.
This will result in multiple queries and responses being executed simultaneously, and my guess is that you are viewing the print statements from multiple queries which is giving the appearance of being out of order.
To make sure that the query is ordered properly, execute just one of the queries and print out the results.
Lastly, you should avoid executing a loop of queries at all costs because it will not be scalable at all.
For example, instead of wrapping the query in a loop and using
postQuery.whereKey("createdBy", equalTo: friend)
you may be able to refactor your code to utilize whereKey:containedIn:
postQuery.whereKey("createdBy", containedIn: friendsArray)
I have PFUser saved as a pointer in this class. I'd like to retrieve the user's first name and corresponding "point value"
My attempt below to append that data to it's cell value, but it is only returning the last retrieved object for that key value.
var innerQuery : PFQuery = PFUser.query()!
innerQuery.whereKeyExists("objectId")
let query = PFQuery(className: "myClass")
query.whereKey("userId", matchesQuery: innerQuery)
query.whereKey("points", greaterThan: 1000)
query.findObjectsInBackgroundWithBlock{ (objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
if let objects = query.findObjects() as? [PFObject]{
for object in objects {
if let listPoints = object.objectForKey("points") as? Int {
var temp = String(listPoints)
cell.pointStatus.text = temp
}
}
}
}
else{
println(error?.description)
}
}
I retrieve the users first name and profile picture in a separate call. Everything is functional aside from the query for points.
if let pfuser = userProfile["first_name"] as? String{
if let pfimage = userProfile["profile_picture"] as? PFFile{
pfimage.getDataInBackgroundWithBlock({
(result, error) in
cell.userIcon.image = UIImage(data: result!)
cell.userName.text = username
})
}
}
It appears that you are only writing the value of listPoints to one cell in
var temp = String(listPoints)
cell.pointStatus.text = temp
If you want to have multiple point values to be displayed, the cell reference will need to be changed.
This is a followup of my question from yesterday yesterdays post
I have successfully saved and object in the local datastore using parse, and and trying to add that object to an array to store them so i can display the contents in a table view. the query is running fine, but it appears to me that nothing is being appended into the array, so nothing shows in the table view. here's my code.
localData.swift file
import Foundation
struct localData {
var date: String!
var latt: NSNumber!
var lattDelta: NSNumber!
var locality: String!
var longi: NSNumber!
var longiDelta: NSNumber!
var name: String!
var note: String!
}
I then declare this globally:
var arrayToPopulateCells = [localData]()
this is my parse query:
func performQuery() {
let query = PFQuery(className: "ParseLighthouse")
query.fromLocalDatastore()
query.whereKey("User", equalTo: PFUser.currentUser()!)
query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if error == nil {
// The find succeeded.
println("Successfully retrieved \(objects!.count) lighthouses.")
// Do something with the found objects
if let light = objects as? [PFObject] {
for object in light {
// println(object.objectId)
// println(object.objectForKey("Name"))
// println(object.objectForKey("Locality"))
var singleData = localData()
singleData.name = object["Name"] as! String
singleData.note = object["Note"] as! String
singleData.date = object["Date"] as! String
singleData.latt = object["Latt"] as! NSNumber
singleData.longi = object["Longi"] as! NSNumber
singleData.lattDelta = object["LattDelta"] as! NSNumber
singleData.longiDelta = object["LongiDelta"] as! NSNumber
singleData.locality = object["Locality"] as! String
self.arrayToPopulateCells.append(singleData)
}
}
} else {
// Log details of the failure
println("Error: \(error!) \(error!.userInfo!)")
}
}
}
in my table code:
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrayToPopulateCells.count
}
and
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// var lighthouse = self.lighthouses[indexPath.row]
var data = self.arrayToPopulateCells[indexPath.row]
//setting the prototype cell to link with the identifier set in attributes earlier.
let cell = tableView.dequeueReusableCellWithIdentifier("locationCell") as! lighthouseCell
let row = indexPath.row
cell.cellName.text = data.name
cell.cellPlace.text = data.locality
// cell.cellCoordinates.text = "\(lighthouse.latt)" + ", " + "\(lighthouse.longi)"
// cell.cellNote.text = lighthouse.note
cell.cellDate.text = "\(data.date)"
return cell
}
so im not sure what i'm doing wrong, but it seems that the query is working but nothing is going into the array. any ideas?
i do want to note that the parse object is created on lets say viewcontroller #2, and the query is run on viewcontroller #1 where the table view is. does this make a difference? should i run the query and try to append right after the object is made on the same controller?
I think your problem is you need to call
self.tableView.reloadData()
outside the for object in light { loop
I think your data is being added to the array ok, its just the table view needs to know when its done.
EDIT***
func performQuery() {
let query = PFQuery(className: "ParseLighthouse")
query.fromLocalDatastore()
query.whereKey("User", equalTo: PFUser.currentUser()!)
query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if error == nil {
// The find succeeded.
println("Successfully retrieved \(objects!.count) lighthouses.")
// Do something with the found objects
if let light = objects as? [PFObject] {
for object in light {
var singleData = localData()
singleData.name = object["Name"] as! String
singleData.note = object["Note"] as! String
singleData.date = object["Date"] as! String
singleData.latt = object["Latt"] as! NSNumber
singleData.longi = object["Longi"] as! NSNumber
singleData.lattDelta = object["LattDelta"] as! NSNumber
singleData.longiDelta = object["LongiDelta"] as! NSNumber
singleData.locality = object["Locality"] as! String
self.arrayToPopulateCells.append(singleData)
}
self.tableView.reloadData()
}
} else {
// Log details of the failure
println("Error: \(error!) \(error!.userInfo!)")
}
}
}