Swift Parse - local datastore and displaying objects in a tableview - ios

I am building and app that saves an object in the local datastore with parse. I then run a query to retrieve the objects that are in the local datastore and it is working fine. however, I would like to grab the object, and the contents in it, and set some labels in a table view cell based on the items that are stored in the parse local data store object. for example, i make an object with attributes like "objectID", "name", "date", "location". what i'd like to do is to have a table view on the home screen that displays the name, date, location ...etc. of each item that was saved in local datastore in labels in each cell.
i know that im saving it correctly:
// parse location object
let parseLighthouse = PFObject(className: "ParseLighthouse")
parseLighthouse.setObject(PFUser.currentUser()!, forKey: "User")
parseLighthouse["Name"] = self.placeTitle.text
parseLighthouse["Note"] = self.placeNote.text
parseLighthouse["Locality"] = self.placeDisplay.text!
parseLighthouse["Latt"] = self.map.region.center.latitude
parseLighthouse["Longi"] = self.map.region.center.longitude
parseLighthouse["LattDelta"] = 0.5
parseLighthouse["LongiDelta"] = 0.5
parseLighthouse["Date"] = dateInFormat
parseLighthouse.pinInBackground()
parseLighthouse.saveInBackgroundWithBlock { (success: Bool, error: NSError?) -> Void in
println("Object has been saved. ID = \(parseLighthouse.objectId)")
}
and when i run the query, im able to access the attributes by running println(object.objectForKey("Name"))
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"))
}
}
} else {
// Log details of the failure
println("Error: \(error!) \(error!.userInfo!)")
}
}
because when running the query, i get back the object id and name as expected.
Successfully retrieved 2 lighthouses.
Optional("A3OROVAMIj")
Optional(happy)
Optional("bbyqPZDg8W")
Optional(date test)
what I would like to do is grab the name field within the parse object local data store, and that be the name of the label on a cell in a table view controller.
i dont know how to access that info from the object, and set the label correctly.
does anyone know how this is possible?

It's always a good idea to avoid pointer lol ... so why not saving the userid or username with the specific object..
so change this line:
parseLighthouse.setObject(PFUser.currentUser()!, forKey: "User")
TO
parseLighthouse["username"] = PFUser.currentUser().username
Answer
NOW let's create a struct that contains the objectID and the Name outside of your Controller Class.
struct Data
{
var Name:String!
var id:String!
}
then inside of the Controller class, declare the following line of code globally
var ArrayToPopulateCells = [Data]()
Then your query function will look like :
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.
print("Successfully retrieved \(objects!.count) lighthouses.")
// Do something with the found objects
if let light = objects as? [PFObject] {
for object in light {
print(object.objectId)
print(object.objectForKey("Name"))
var singleData = Data()
singleData.id = object.objectId
singleData.Name = object["Name"] as! String
self.ArrayToPopulateCells.append(singleData)
}
}
} else {
// Log details of the failure
print("Error: \(error!) \(error!.userInfo)")
}
}
In the tableView numberOfRowinSection()
return ArrayToPopulateCells.count
In the cellForRowAtIndexPath()
var data = ArrayToPopulateCells[indexPath.row]
cell.textlabel.text = data.objectID
cell.detailLabel.text = data.Name
VOila that should be it

Related

Issue Reloading Table View After Adding Parse Object

I have a UITableViewController that displays data from a Parse query. It get the data and displays it fine except when I create a new object and run the query again to get the new data. When I create a new object the table view keeps the existing data in my array and displays it but it appends all the data from the query to the array so the objects that already existed prior to creating the new object get displayed twice. I tried emptying the arrays at the start of the query function but since I have the skip property set on the query I can't do that because my array will only get everything after the skip if the limit is reached. So, how can I just add the new object to my array?
I should also mention that I can't simply add the new object name to the array in addCollection() because I have to add the objectId to my objectID array.
func getCollections() {
activityIndicator?.startAnimating()
// collections = [] - Can't do this because of the skip (if the skip is used)
// objectID = []
let query = PFQuery(className: "Collections")
query.whereKey("user", equalTo: PFUser.currentUser()!)
query.orderByAscending("collectionName")
query.limit = limit
query.skip = skip
query.findObjectsInBackgroundWithBlock( {
(objects, error) -> Void in
if error == nil {
if let objects = objects as [PFObject]! {
for object in objects {
let collectionName = object["collectionName"] as! String
let id = object.objectId
self.collections.append(collectionName)
self.objectID.append(id!)
}
}
if objects!.count == self.limit {
self.skip += self.limit
self.getCollections()
}
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
self.activityIndicator!.stopAnimating()
}
} else {
var errorString = String()
if let message = error!.userInfo["error"] {
errorString = message as! String
}
print(errorString)
}
})
}
func addCollection(name: String) {
let collection = PFObject(className: "Collections")
collection["user"] = PFUser.currentUser()
collection["collectionName"] = name
collection.saveInBackground()
getCollections()
}
This code is logically flawed and can be simplified:
func addCollection(name: String) {
let collection = PFObject(className: "Collections")
collection["user"] = PFUser.currentUser()
collection["collectionName"] = name
collection.saveInBackground()
getCollections()
}
problems include:
your save runs in the background and isn't complete before you try to reload
your reload doesn't update or reset the skip and limit values
Unless you need to check for updates from other users then you shouldn't make a new request to the server to get new details. Instead you should add a completion block on the save and in there:
get the name and id and add those values to your data source arrays
update the skip value by adding one

Query Parse for all the data in a table and placing into array

I am making a running app and would like to have a viewController in which the user has running tips and facts randomly display on the field. I would like to query parse for the objectIds to then reference the id and assign the label the tip or fact. Currently I have hard coded the Ids into the app but I would like that array to contain the results from the query the code is as follows:
func GetObjectID(){
ObjectIDs = ["id1","id2","id3","id4","id5","id6","id7","id8"]
RandomID = Int(arc4random_uniform(UInt32(ObjectIDs.count)))
}
func TheInfo(){
GetObjectID()
var query : PFQuery = PFQuery(className: "FactsandTips")
query.getObjectInBackgroundWithId(ObjectIDs[RandomID]){
(ObjectHolder : PFObject?, error: NSError?) -> Void in
if (error == nil) {
self.fact = ObjectHolder!.valueForKey("Fact") as? String
self.tips = ObjectHolder!.valueForKey("Tips") as? Array
if(self.tips.count > 0){
self.factLabel.text = self.fact
self.Button1.setTitle(self.tips[0], forState: UIControlState.Normal)
self.Button2.setTitle(self.tips[1], forState: UIControlState.Normal)
self.Button3.setTitle(self.tips[2], forState: UIControlState.Normal)
self.Button4.setTitle(self.tips[3], forState: UIControlState.Normal)
}
} else {
print("There is something wrong!")
}
}
}
I am using swift, Xcode7, and parse as my backend
Below is the code I use to query a Parse table, retrieve all results and add it all into an array. I then use the array as the source for a pickerView.
var query:PFQuery = PFQuery(className: "Drivers")
query.addAscendingOrder("Name")
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
if let objects = objects as? [PFObject] {
for object in objects {
self.astrDrivers.append(object["Name"]! as! String)
}
}
self.pkvDriverTrack.reloadAllComponents()
} else {
print("Error: \(error) \(error!.userInfo)")
}
}
Note the line self.astrDrivers.append(object["Name"]! as! String). This is adding the Name column of each record to my self.astrDrivers array.
If you wanted to do retrieve multiple columns, your best bet is to create a custom object like below:
class ObjectNewFact:NSObject {
var column1:String = String() // You might want to choose more descriptive variable names (I just don't know what your column names are).
var column2:Int = Int()
// etc.
}
You could then create an array of ObjectNewFacts with a line like
var aFacts:[ObjectNewFact] = [ObjectNewFact]()
Then you could amend your routine to retrieve the data from Parse to:
var query:PFQuery = PFQuery(className: "Drivers")
query.addAscendingOrder("Name")
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
if let objects = objects as? [PFObject] {
for object in objects {
var NewFact:ObjectNewFact = ObjectNewFact()
NewFact.column1 = object["Column1"] as! String
NewFact.column2 = object["Column2"] as! Int
self.aFacts.append(NewFact)
}
}
self.pkvDriverTrack.reloadAllComponents()
} else {
print("Error: \(error) \(error!.userInfo)")
}
}
Now you have an array full of facts. You might want to go down this custom object approach because you can also include things like the Fact ID or how many times the fact has been shown (if you're keeping track of that sort of thing). It provides a more flexible solution for any changes in the future.
I hope this helped.

Make a Query with a Pointer in Parse and Swift

I'm trying to make a Query with a Pointer in Parse.
Basically I have two classes "commentsTable" and "_User", I want to get the comment of the user from class "commentsTable" on a determined post, and then get the username and the profile_pic from the class "_User"
_User Class
commentsTable Class
func loadAndShowComments(){
let query2 = PFQuery(className: "commentsTable")
query2.orderByDescending("createdAt")
query2.whereKey("newsColumns", equalTo: printteste!)
query2.includeKey("username")
query2.findObjectsInBackgroundWithBlock {
(objects: [PFObject]?, error: NSError?) -> Void in
if let objects = objects as [PFObject]? {
for object in objects {
print(object["commentColumn"])
}
}
for cardset in objects! {
var lesson = cardset["username"] as! PFObject
var name = lesson["username"] as! String
print("By user: \(name)")
}
I'm able to see the query, I print the result an I have the following output:
This is a post!
This is a test post!
By user: teste#teste.com
By user: mmachado
And in my app I display this informations inside a TableView, I'm successfully can show the results for the Query in the func cellForRowAtIndexPath:
if let usuarioComentario = object?["commentColumn"] as? String {
cell?.usuarioComentario?.text = usuarioComentario
}
But I'm no able to return the values of my other class, _User
I think I misunderstood some concept but at this point I don't know what concept, any ideas?
Thanks.
By using query2.includeKey("username") you are already retrieving all of the User data associated with each commentsTable object.
You can access the related User data using the following.
if let commentUser = object["username"] as? PFUser {
let name = commentUser["username"] as! String
let profilePicture = commentUser["profile_pic"] as! PFFile
}
You need to store the query results to an array for later use if you aren't already. If you are using Parse's provided PFQueryTableViewController this will be handled for you by implementing the queryForTable() method and the results are automatically stored in an array of dictionaries called objects.
It is also worth noting that you will have to still have to load the PFFile because they are not included in query results. You will want to assign the PFFile to a PFImageView and then call loadInBackground. See the example below.
let imageView = PFImageView()
// Set placeholder image
imageView.image = UIImage(named: "placeholder")
// Set remote image
imageView.file = profilePicture
// Once the download completes, the remote image will be displayed
imageView.loadInBackground { (image: UIImage?, error: NSError?) -> Void in
if (error != nil) {
// Log details of the failure
println("Error: \(error!) \(error!.userInfo!)")
} else {
println("image loaded")
}
}
Lastly, I'd recommend changing the name of the User pointer within commentsTable from "username" to "user" so there is no confusion with the username field of the user class. Here's a link to a great tutorial which you may also find helpful

Retrieve the userInfo using Pointer. Parse

I have a class called Posts in which i've postedBy column where i am saving the PFUser.currentUser() (pointer). so i want to retrieve the username, profile picture and stuff from the _User class using postedBy in the Posts class. What is the shortest and efficient way to achieve this? i am not much familiar with relation queries.
I believe that instead of saving the user pointer, you should save the user's username then it comes easier for you to retrieve everything.
var query = PFQuery(className:"Posts")
var username = PFUser.currentUser()?.username
query.whereKey("username", equalTo: username!)
query.findObjectsInBackgroundWithBlock { (objects:[AnyObject]?, error:NSError?) -> Void in
if error == nil
{
if let objects = objects as? [PFObject]
{
for one in objects {
var pictureImage = one["theFile"] as! PFFile
pictureImage.getDataInBackgroundWithBlock({ (dataToget:NSData?, error:NSError?) -> Void in
if error == nil {
if let Image = UIImage(data: dataToget!){
// then you have the image
// save the image to array
// reload the tableview
}
}
})
}
}
}
}

findObjectInBackgroundWithBlock nested ios

I am developing app using ios, swift and parse.com as backend.
My problem is I need one query object result in second query object like below code. but when i use below code GUI become unresponsive for some time because of findObjects() method. I have used findObjectsInBackgroundWithBlock() instead but than tableview self.posts display only one record in tableview. I have 10 record in post table.
Can you guide me proper way how to resolve below issue.Actually I does not want to use findObjects() method.
var query = PFQuery(className:"Post")
var fquery = PFQuery(className: "Friends")
query.findObjectsInBackgroundWithBlock { (objects: [AnyObject]!, error: NSError!) -> Void in
if error == nil {
let user = PFUser.currentUser()
if let objects = objects as? [PFObject] {
for object in objects {
friendArray.removeAll(keepCapacity: false)
fquery.whereKey("whosefriend", equalTo: object["postusername"])
var fobjects = fquery.findObjects()
for fobject in fobjects {
friendArray.append(fobject["friendname"] as String)
}
if (contains(friendArray, user["fullname"] as String)) {
let post = Post(.......)
self.posts.append(post)
}
}
}
self.tableView.reloadData()
} else {
println("Error: \(error) \(error.userInfo!)")
}
}
One option is to make your "postusername" a pointer column in class Post that points to Friends class and then you would only need one query that would go something like:
var query = PFQuery(className:"Post")
query.includeKey("postusername") //this would include the object that it points to i.e. the Friends object you saved there
... then in your for loop ...
for object in objects! {
let friend = object["postusername"] // now friend is the Friends object
let friendName:String = friend["friendname"] as? String
friendArray.append(friendName)
}
Note: this requires you saving "postusername" as a PFObject of Class Friends. Parse iOS docs explain this well.
https://parse.com/docs/ios/guide
I have resolve the issue by using relational query.
var query = PFQuery(classWithName: "Post")
var fQuery = PFQuery(className:"Friends")
fQuery.whereKey("friendname", equalTo: cuser["fullname"])
query.whereKey("postusername", matchesKey:"whosefriend", inQuery:fQuery)

Resources