Can't get Parse.com PFUser data? - ios

I have a UITableView that is populated with games that a user is currently playing. I have a Pointer column in the Game table called Turn that points to a PFUser in the User table. Instead of setting the cell text to the objectID of the Turn value, I'd like to instead set it to the player's first name, which is stored in the User table.
The problem is that when I println the Turn pointer object, it only contains the objectID and I can't pull the first_name. Here's my code:
let object = gameResults[indexPath.row]
let user = object["turn"] as! PFUser
let userString = "\(object.first_name)"
cell.textLabel?.text = userString
return cell
The cell text just reads "nil". How do I get the user data from the User table that relates to the Turn pointer column?

The pointer column data are not included by default when using PFQuery.
Do this to tell your query to include the pointer data:
yourQuery.includeKey("turn")
This will get the user data from the user table and include it in the PFObject obtained.
UPDATE:
To get the first_name field (or any field for that matter), the syntax is:
let userSting = user["first_name"] as String

Related

Firebase query observing reshowing data

I have a firebase query that observes data from a posts child.
func fetchPosts () {
let query = ref.queryOrdered(byChild: "timestamp").queryLimited(toFirst: 10)
query.observe(.value) { (snapshot) in
for child in snapshot.children.allObjects as! [DataSnapshot] {
if let value = child.value as? NSDictionary {
let post = Post()
let poster = value["poster"] as? String ?? "Name not found"
let post_content = value["post"] as? String ?? "Content not found"
let post_reveals = value["Reveals"] as? String ?? "Reveals not found"
post.post_words = post_content
post.poster = poster
post.Reveals = post_reveals
self.postList.append(post)
DispatchQueue.main.async { self.tableView.reloadData() }
//make this for when child is added but so that it also shows psots already there something like query.observre event type of
}
}
However, when a user posts something, it creates a more than one cell with the data. For instance, if I post "hello", a two new cards show up with the hello on it. However, when I exit the view and recall the fetch posts function, it shows the correct amount of cells. Also, when I delete a post from the database, it adds a new cell as well and creates two copies of it until I reload the view, then it shows the correct data from the database.
I suspect this has something to do with the observe(.value), as it might be getting the posts from the database and each time the database changes it creates a new array. Thus, when I add a new post, it is adding an array for the fact that the post was added and that it now exists in the database, and when I refresh the view it just collects the data directly from the database.
Also, sometimes the correct amount of cells show and other times there's multiple instances of random posts, regardless of whether I have just added them or not.
How can I change my query so that it initially loads all the posts from the database, and when some post is added it only creates one new cell instead of two?
Edit: The logic seeming to occur is that when the function loads, it gets all the posts as it calls the fetchPosts(). Then, when something is added to the database, it calls the fetchPosts() again and adds the new data to the array while getting all the old data. yet again.
One thing I always do when appending snapshots into an array with Firebase is check if it exists first. In your case I would add
if !self.postList.contains(post) {
self.postList.append...
however, to make this work, you have to make an equatable protocol for what I'm guessing is a Post class like so:
extension Post: Equatable { }
func ==(lhs: Post, rhs: Post) -> Bool {
return lhs.uid == rhs.uid
}
You are right in thinking that the .value event type will return the entire array each time there is a change. What you really need is the query.observe(.childAdded) listener. That will fetch individual posts objects rather than the entire array. Call this in your viewDidAppear method.
You may also want to implement the query.observe(.childRemoved) listener as well to detect when posts are removed.
Another way would be to call observeSingleEvent(.value) on the initial load then add a listener query.queryLimited(toLast: 1).observe(.childAdded) to listen for the latest post.

Displaying data in a tableView from a filtered array

I have written code that filters events from Core Data and only prints the events that have a date attribute that is equal to a date that was selected in a Calendar. Heres the code:
let predicate = NSPredicate(format: "eventPastStringDate = %#", formatter.stringFromDate(selectedDate))
//This prints the exact data that I want
print((eventsPast as NSArray).filteredArrayUsingPredicate(predicate))
This works and it filters that data how I would like and it prints only the events that I want. The problem is that I do not know how to display this data in the tableView. Usually I can display all the data from Core Data in the tableView like this in cellForRowAtIndexPath...
let ePast = eventsPast[indexPath.row]
let cellPast = self.tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! CustomCell
// Sets labels in cell to whatever user typed in on the events page
cellPast.titlePrototypeCell!.text = ePast.eventPastTitle!
cellPast.datePrototypeCell!.text = "Date: " + ePast.eventPastDateLabel!
return cellPast
...but I am not sure how to access the new data as an array like I did above. Any ideas?
You are going to have a second array in your class. When there is no filter, the two arrays are identical. When there is a filter, then you have a master array with absolutely everything and the filtered array is a small subset.
(class var) myFilteredArray = (eventsPast as NSArray).filteredArrayUsingPredicate(predicate)
Then in your tableview methods:
let ePast = myFilteredArray[indexPath.row]
And make sure to set the table row size to your filtered array count, not the master array count. Otherwise you are going to get out of bounds crashes.

Confused on snippet of code for implementing iCloud behavior on iOS

The code is from a book. In terms of overall app architecture (MVC), it's part of the Model. The model has two main components:
An array of tags called tags
A dictionary of tag - query called searches
The app saves these pieces of data in the NSUserDefaults (iOS defaults system) and on iCloud. The following method is called when a change in iCloud is signaled. The parameter is an instance of NSNotification.userInfo
// add, update, or delete searches based on iCloud changes
func performUpdates(userInfo: [NSObject: AnyObject?]) {
// get changed keys NSArray; convert to [String]
let changedKeysObject = userInfo[NSUbiquitousKeyValueStoreChangedKeysKey]
let changedKeys = changedKeysObject as! [String]
// get NSUbiquitousKeyValueStore for updating
let keyValueStore = NSUbiquitousKeyValueStore.defaultStore()
// update searches based on iCloud changes
for key in changedKeys {
if let query = keyValueStore.stringForKey(key) {
saveQuery(query, forTag: key, saveToCloud: false)
} else {
searches.removeValueForKey(key)
tags = tags.filter{$0 != key}
updateUserDefaults(updateTags: true, updateSearches: true)
}
delegate.modelDataChanged() // update the view
}
}
My question is on the if - else inside the for loop. The for loop iterates over keys that where changed; either the user adds a new search, updates an existing search, or deletes a search. But, I don't understand the logic behind the if-else. Some clarifying thoughts would be appreciated. I've read it over and over but it doesn't tick with me.
if let query = keyValueStore.stringForKey(key)
means that if keyValueStore contains a string corresponding to key, then this string will be assigned to the constant query.
This is called "safe unwrapping":
inside the if let ... condition, the query is safely saved with saveQuery because using if let ... guarantees that the value of keyValueStore.stringForKey(key) won't be nil.
If the value is nil, then in the else branch, the filter method is used to update the tags array without the key we just processed: tags.filter{$0 != key} means "return all items in tags that are different from key" (the $0 represents the current item from the array processed by filter).

Correct way to use CoreData Entity in this example

I have a CoreDataentity called "Person" with 3 attributes
import Foundation
import CoreData
class Person: NSManagedObject {
#NSManaged var name: String
#NSManaged var image: NSData
#NSManaged var info: String
}
EDITED:
EXAMPLE: (I think my first "Table/chair" example caused more confusion then explanation.)
I have 7 UIButtons. When a UIButton is tapped, My TableView is modally pushed and TableView cells are populated with attribute values of all available Person via
var request = NSFetchRequest(entityName: "Person")
request.returnsObjectsAsFaults = false;
results = context.executeFetchRequest(request, error: nil)!
I want to be able to double-tap a cell, which will close TableView and return to mainViewController the Person selected, and attach it to the UIButton that called the TableView. This UIButton will now display SelectedPerson's image, name, and info (or have direct access to attribute values).
How should I go about making this possible?
Is there a NSFetchRequest Method that I could simply input something like...
NSFetchRequest(entityName: "Person", attributeName: "name", String == "John Doe")
If so I could just pass the "name" value from TableView to MainViewController, and store a Person reference in MainViewController via something like this...
var PassedNameString = "John Doe"
var Person1: Person = NSFetchRequest(entityName: "Person", attributeName: "name", String == PassedNameString) as! Person
This way I'll have a reference to what Person entity is filling a "seat" and have direct access to it's info to read or edit.
My CoreData understanding is very low.
I've only learned how to create/store a file. I do not have a understanding of what is needed to recall a CoreData file and Edit if needed. I also don't know what kind of demand is put on the device when Fetching Vs holding a Reference to CoreData entry.
In your view controller have an array (or a dictionary if it suits you better) for the chairs. Each slot in the array represents a chair, and the index of the array links to the buttons you have (also should be in an array). The chair array holds instances of Person and is initially empty.
The table view controller should use a fetched results controller, with a batch size on the fetch. This is used to populate the table as usual. When a table row is selected the associated Person should be passed back and stored in the chair array.
Doing a second fetch is pointless and leads to name duplication issues.
Think of CoreData as your database, that stores the data that is categorized by your model structures.
From your description, I would do a fetch request somewhere in your ViewController's loading sequence, say in ViewDidLoad, and store the results (Person objects) in an array of persons ... var persons = [Person](). This can be a class variable. Then I would access the Person objects by calling array[index].
Let me know if this helps you with your thinking, or where you are confused.
var persons = [Person]()
override func viewDidLoad() {
...do your fetch call...get back an array of objects as a result
for person:Person in result {
self.persons.append(person)
}
// now to access your Person objects anywhere in the class just call
// self.persons[index] to get back an Person object
}

Match clicked TableView row (string) to find corresponding unique ID

I am currently stuck on how to best find and pass the ID of a clicked table view row. I'd like to know what the best practice might be to accomplish this.
In an iPhone storyboard I want to create a tableview.
This tableview should get populated from a JSON web server response.
This JSON object will contain around 10 items, say name and
address plus a unique ID.
In my tableview I'd like to have in each tableview row one name
and address but I don't want to have the unique ID displayed.
Now here comes my question: If I don't have the unique ID displayed how do I find this ID again based on which row has been clicked? Is there a way of assigning hidden fields? I'd like to send the unique ID via POST back to web server.
The directions I've been investigating in so far are...
...parse JSON object into NSDictionary and match clicked tableview row back to it to get ID.
...put JSON object into SQLlite database and match clicked row against database to get ID.
...don't do any of the above and simply send a string of name and address (instead of ID) back to web server where the matching against database would take place.
What do you think will be best in terms of getting the corresponding ID from a clicked tableview row.
Thanks a lot for your time.
This is how you can accomplish this
Call your webservice and get the json. Parse the json and keep on adding object to a NSMutableArray say itemList.
Create a model class say Itemdesc which will have 3 fields name, address and ID.
When you parse your json extract each individual product into item model object defined above and add it to itemList array.
It could be like this:
NSArray *responseArray = responsefromService;
For(int i=0;i<responseArray.count;i++)
{
//Parse your object here
NSDictionary *currentItem = [responseArray objectAtIndex:i];
Itemdesc *itemObject = [[Itemdesc alloc] init];
itemObject.name = [currentItem objectForKey:#"name"];
itemObject.address = [currentItem objectForKey:#"address"];
itemObject.ID = [currentItem objectForKey:#"ID"];
[itemList addObject:itemObject];
itemObject=nil;
}
Now pass this array as object to tableView for row count and in cellForRowAtIndexPath get your row object as below:
Itemdesc *currentIndexObj = [itemList objectAtIndex:indexpath.row];
cell.name.text = currentIndexObj.name;
cell.address.text = currentIndexObj.address;
This will display your name and address in your tableViewCell.
To get the ID when a row is select, in your didSelectRowAtIndexPath write below code:
Itemdesc *currentIndexObj = [itemList objectAtIndex:indexpath.row];
NSString *ID = currentIndexObj.ID;
NSLog(#"ID value: %#", ID);
You have got your ID, perform whatever operation you want to perform.
Model Class is nothing but simply a normal Class itself.
Code to create a Model Class:
Go to File -> New -> File ->iOS-> Source -> CocoaTouch Class
select next and given Class a name ItemDesc and make it subclass of NSObject. In your project you will get to files Itemdesc.h and Itemdesc.m
Itemdesc.h file:
#import <Foundation/Foundation.h>
#interface Itemdesc : NSObject
#property(nonatomic,strong) NSString *name;
#property(nonatomic,strong) NSString *address;
#property(nonatomic,strong) NSString *itemID;
#end
No need to make any changes in .m file.
Thats it. Import the header in your class where your tableView exists and things will work.
Json structure:
[
{
"name" : "John",
"address" : "ny",
"ID" : "xyz"
},
{
"name" : "Jane",
"address" : "dc",
"ID" : "pqr"
}
]
Typically you have a collection of model objects that determine how many UI elements to show to the user. In the simple case this could be just an NSArray but could also be fetched from Core Data or a remote backend.
As the table asks for cells, use the indexPath to look up the corresponding model object in the backing collection. You can then use the information in the object to configure the cell.
When the user selects a row, you can use the reported indexPath to do a similar lookup. Once you have the correct object, you can look up its ID and perform the necessary network request.
Lets make it in steps.
Start downloading JSON
Save JSON data in some Array/Dictionary what ever you like, here i will use responseData as name of my Collection.
Your tableView rows are responseData count.
in cellForRowAtIndexPath populate you data, only that you needed, address name, but your responseData still have id for each object, right?
in didSelectRowForIndexPath, you will get the id somehow like this
Swift
resonseObject.objectAtIndex(indexPath.row).valueForKey(id)
Objective-C
[[responseObject objectAtIndex:indexPath.row] valueForKey:#"id"]
You also can make custom UITableViewCell class and put the ID as String Property and set it in cellForRowForIndexPath, but i think the first solution is better.
Greetings.
I simplest way to accomplish this is to store the objects you get into a local array (or anything that has order and matches the display order of the table). From there you can just use the index of the table cell that was selected, to determine which object was selected and grab the ID.
So, if you have an array of sampleObjects (each of a name, address and ID) populated from your web service. Use this array as the datasource for your table. Use the tableView:didSelectRowAtIndexPath: delegate method as follows...
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
int rowSelected = indexPath.row;
id sampleObject = tableDataSource[rowSelected]; // Assuming tableDataSource is an array you created to populate the table
// ...do whatever with the object that the user selected
}

Resources