Here is what it looks like in the Mainviewcontroller.
I have two separate classes, LinkedInUser and _User. The accessToken is stored in the LinkedInUser not in the _User. There is a pointer in the LinkedInUser class called "user" which points to the "objectID" in _User. I want to retrieve the accessToken from LinkedInUser but overtime I try a query it returns no results and when I run the code below, it results in a null. What am I doing wrong and how can I fix it?
[PFLinkedInUtils logInWithBlock:^(PFUser *user, NSError *error) {
NSLog(#"User: %#, Error: %#", user, error);
PFObject *linkedin = [PFObject objectWithClassName:#"LinkedInUser"];
NSString *accessToken = linkedin[#"accessToken"];
[PFLinkedInUtils.linkedInHttpClient GET:[NSString stringWithFormat:#"https://api.linkedin.com/v1/people/~:(blahblahblah)?oauth2_access_token=%#&format=json", accessToken] parameters:nil success:^(AFHTTPRequestOperation *operation, id result)
This line:
PFObject *linkedin = [PFObject objectWithClassName:#"LinkedInUser"];
is creating a new instance of a LinkedInUser object. It will not have a value for the accessToken property. Instead you should query Parse for the existing object associated with the user.
Additionally, if you are able to change your data model slightly, it might be better to have a pointer on _User that references the LinkedInUser. That way, you can just get that object directly without having to make another query.
Related
i have a pointer key in PFUser and I'm trying to retrieve the object it's pointing to. I've seen many examples about querying for it but there shouldn't be any need for that, since parse has the fetch method and it's a pointer of PFUser class, so I'm using this:
PFObject *menu = [[[PFUser currentUser] objectForKey:#"menuID"] fetchIfNeeded];
I know my current user has an object being pointed to in that key but i get a null menu object all the time
Wain was correct in saying that you need to fetch the currentUser. However, you have to keep in mind that we're working with multiple threads if you want to use fetchInBackground. To keep in on a single thread, simply use [[PFUser currentUser] fetch], but keep in mind that this can cause hanging or blocking for your user when internet connection is bad. Below is a sample of how to use this more effectively. (Same issue with the fetch vs. fetchInBackground for the menu) We have to fetch the menu as well, since it is a pointer and so the currentUser will only fetch the pointer and not the whole object.
[[PFUser currentUser] fetchInBackgroundWithBlock:^(PFObject *object, NSError *error) {
if(!error){
PFObject *menu = object[#"menuID"];
[menu fetch];
// Execute any code that needs the menu here, or store it in your viewController (or other class variable) if you need to save it for later
// Alternately, you could use this:
dispatch_async(dispatch_get_main_queue(), ^{
[menu fetchInBackgroundWithBlock:^(PFObject *fetchedMenu, NSError *menuError) {
if(!menuError){
// Execute any code that needs the menu here, or store it
} else {
NSLog(#"%#", menuError);
}
}];
});
} else {
NSLog(#"%#", error);
}
}];
By default currentUser doesn't have any custom added columns of data populated. You need to fetch the user to download that data and then you can use it locally.
Alternatively your oils us cloud code and save a network request.
Edit wording..
I am using a 3rd party library called Drupal-IOS-SDk to connect my Drupal website with my under development IOS app. I use a method to index nodes on my website and I am just wondering if anyone has any knowledge about how to deal with the response from my website. To give a little context if I run a similar bit of code to my index method (a getNode method) which works fine and I am able to access my response perfectly as shown below:
//code for correctly working nodeGet method
NSMutableDictionary *nodeData = [NSMutableDictionary new];
[nodeData setValue:#"650" forKey:#"nid"];
[DIOSNode nodeGet:nodeData success:^(AFHTTPRequestOperation *operation, id responseObject) {
//print out responseObject
//NSLog(#"%#", responseObject);
//pull data from the responseObject
NSInteger testNumber = [responseObject objectForKey:#"changed"];
NSLog(#"%ld", (long)testNumber);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//we failed, uh-oh lets error log this.
NSLog(#"%#, %#", [error localizedDescription], [operation responseString]);
}];
This is the what gets printed by response object(I didnt include the whole thing but youll get the point):
//printed statement for correctly working nodeGet method
{
changed = 1390534644;
comment = 0;
created = 1390534644;
data = "b:0;";
"ds_switch" = "";
"field_additional_pictures" = (
);
"field_author" = {
und =
The above code gets node data and calling the "objectforkey" method on responseObject lets me access numbers or whatever else is stored in my responseObject. Where I have commented pull data from response object I get back the integer "1390534644" which correctly corresponds to the "changed" variable as you can see from the printed response above. The above section works fine. It is the next step where I get confused:
//code for index method that is in question
NSMutableDictionary *paramsDictionary = [NSMutableDictionary new];
[paramsDictionary setValue:#"books_e_books_other_books" forKey:#"type"];
[DIOSNode nodeIndexWithPage:#"0" fields:#"nid" parameters:paramsDictionary pageSize:#"20"
success:^(AFHTTPRequestOperation *operation, id responseObject) {
//print response object
NSLog(#"%#", responseObject);
NSLog(#"GREAT SUCCESS");
//HERE IS MY PROBLEM!!!
//what do I do with response object?
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//code
NSLog(#"Oh no failed when trying to get the index");
NSLog(#"%#, %#", [error localizedDescription], [operation responseString]);
}];
In this section I index all of the nodes and get fields from each instead of getting all of the information from only one node. I get a response which shows things are working correctly thus far because the response has the correct info. I am confused though because I am not sure what my response object is exactly. It is a collection of nodes each with a "nid" and "uri" as shown from the index method responseObject below. If I wanted to get the value "650" for example from my first "nid" in the below printed area how would I go about doing this? I dont think I can call "objectForKey" as I did in the first working example because each node has a "nid". If I told my app to look for a key named nid it doesnt know which one to look for. With no unique keys how can I access the number 650? I have printed my index method responseObject below so you can see what I am talking about.
//printed statement from index method, this is what i am confused about, there are no unique keys how do I access the value 650 or the first nid
{
nid = 650;
uri = "http://www.domain.com/domain_endpoint/node/650";
},
{
nid = 649;
uri = "http://www.domain.com/domain_endpoint/node/649";
},
{
nid = 647;
uri = "http://www.domain.com/domain_endpoint/node/647";
},
It's been a few months since you asked this question, so I'm not sure if you're still looking for an answer, but in case anyone in the future needs it.
The responseObject is an array of NSDictionary objects. You can access the responseObject items just like an array and do whatever you need to with them depending on what you need the data for.
For example:
NSArray *responseArray = responseObject;
for (NSDictionary *item in responseArray)
{
// Do something with your item here
// For example:
NSString *uriStr = [item valueForKey:#"uri"];
}
So, I'm trying to get all the facebook pictures I'm tagged so I can see them on the iPad, but I wanted to make this function so I can call it everytime I would need to get the url's. The problem is, after I call this function, the array is nil, because the values I get are inside a block. How do I make an array to store the data I get for later use?
-(NSArray *)getFacebookTaggedPictures
{
__block NSArray *taggedPictures = [[NSArray alloc]init];
[FBRequestConnection startWithGraphPath:#"me/photos" completionHandler:^(FBRequestConnection *connection, id result, NSError *error)
{
if(!error)
{
taggedPictures = [(NSArray *)[result data]copy];
//NSLog(#"the tagged pictures are: %#",result);
}
}];
return taggedPictures;
}
As you have correctly noticed, the array that you return is empty because your method returns before the response from the API has been received and parsed into taggedPictures inside the completionHandler block.
You must save the array inside that block. I'd recommend changing your method as follows:
-(NSArray *)getFacebookTaggedPicturesWithCompletion:(void (^)(NSArray* photos))completion;
where the calling methods would pass the appropriate completion block to handle the obtained pictures. (i.e. save them to disk, display them, do some processing on them etc.):
void (^loggerBlock)(NSArray* photos) = ^(NSArray *array) {
NSLog(#"obtained photos: %#",[array description]);
//save here instead of logging
};
[self getFacebookTaggedPicturesWithCompletion:loggerBlock];
Hope this helps.
I could not figure out how to change the value of results inside the success block. I use __block like some post suggests but results is forever nil. I set breakpoint inside of block and make sure that JSON is not nil, which download data as I expected.
I am using AFNetworking library if that's relevant.
+(NSArray *)eventsByCityID:(NSString *)cityID startIndex:(NSUInteger)start count:(NSUInteger)count
{
__block NSArray *results = nil;
[[DoubanHTTPClient sharedClient] getPath:#"event/list" parameters:#{#"loc":dataSingleton.cityID} success:^(AFHTTPRequestOperation *operation, id JSON) {
results = [JSON valueForKey:#"events"];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"download events error: %# \n\n",error);
}];
return results;
}
More likely than not, that [very poorly named] method getPath:parameters:success:failure: is asynchronous.
Thus, you need to tell something in the success block that the value has changed. I.e.
^{
[something yoManGotEvents:[JSON valueForKey:#"events"]];
}
(Methods shouldn't be prefixed with get outside of very special circumstances. Third party libraries with lots of API using that prefix outside of said circumstances raise question as to what other system specific patterns they may not be following.)
Inside my user object I have the following code to generate a new 'session' or continue the existing session if one exists.
Strangely it will keep other properties but just loses the 'user' property... user is in a one to many relationship with session, 1 user can have many sessions. (or will do, for the following test I am simply checking for any previous session and using it if it exists)
-(void)setupSessionStuff
{
// Create new Core Data request
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Session" inManagedObjectContext:[self managedObjectContext]];
[request setEntity:entity];
// Create Sort Descriptors for request
NSSortDescriptor *startTimeSort = [[NSSortDescriptor alloc] initWithKey:#"startTime" ascending:NO selector:nil];
[request setSortDescriptors:[NSArray arrayWithObjects:startTimeSort, nil]];
[startTimeSort release];
[request setFetchLimit:1]; // Only get the most recent session
// Execute request
NSError *error = nil;
NSArray *results = [[self managedObjectContext] executeFetchRequest:request error:&error];
if (results == nil) {
// Something went horribly wrong...
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
exit(-1);
}
[request release];
Session *theSession = nil;
if ([results count] == 1) {
NSLog(#"existing session");
// Use existing Session
theSession = [results objectAtIndex:0];
NSLog(#"session.user: %#", [theSession valueForKey:#"user"]); // this is always null!
} else {
NSLog(#"new session");
// Create new Sesson
theSession = (Session *)[NSEntityDescription insertNewObjectForEntityForName:#"Session" inManagedObjectContext:[self managedObjectContext]];
// Add the Session to the User
NSLog(#"before: session.user: %#", theSession.user); // null
theSession.user = self;
NSLog(#"after: session.user: %#", theSession.user); // looks good
}
...
NSLog(#"before.save: session.user: %#", theSession.user); // good
// Save everything
error = nil;
if (![[self managedObjectContext] save:&error]) {
// Something went horribly wrong...
NSLog(#"Unresolved error: %#, %#, %#", error, [error userInfo],[error localizedDescription]);
exit(-1);
}
NSLog(#"after.save: session.user: %#", theSession.user); // still there..
}
Additionally I have opened up the Core Data sqlite file and examined with SQLite Manager. It looks like the relationship has been correctly saved, as I can see the userID stored in the session table.
-
Just added this at the start of my method as another test.
NSSet *set = self.session;
for(Session *sess in set) {
NSLog(#"startTime %#", sess.startTime);
NSLog(#"user %#", sess.user);
}
Strangely enough the user is set in this case!? So set here then not set a few lines later when I do the fetch request... ?
-
In response to feedback below
Have added this code after assigning session.user = self and both return the expected output. So it does look like the problem is with the subsequent fetch.
NSLog(#"self.session: %#", self.session);
NSLog(#"self.session: %#", [self valueForKey:#"session"]);
Also I agree that accessing my session's through self.session will let me work around my issue, but it doesn't solve what is going on here.
In other places I surely won't be able to walk from one entity to the other so need to confidence the fetch is going to pull everything in correctly.
Firstly, I would check that the relationship is set from the other side by logging self.session. If that shows as null then you have no reciprocal relationship set in the model.
I think that your fetch is poorly constructed. You are relying on the sort descriptor to provide the last used session but you only have a fetch limit of 1. You seem to think that the fetch will find all existing Session objects, sort them and then return the first one. However, sorts execute last so the fetch will find a single session object (because of the fetch limit) and will then apply the sort to that single object. This makes it likely that you will be dealing with a more or less random Session object.
You probably don't need a fetch at all because you are only interested in the Session objects in a relationship with the User object which is self. If you already have the object in one side of a relationship, you don't need to fetch, you just need to walk the relationship. All you really need to do is:
if ([self.session count]==0){
//...create new session and set relationship
}else{
//... find latest session
}
I think your problem with this particular chunk of code is in the reporting. When you get a return value, you use the self.user notation but where you get a null return you use valueForKey:.
While in theory both return the same value, they don't have to because they don't use the same mechanism to return the value. The self-dot notation calls an accessor method while valueForKey: may or may not depending on the specifics of a class' implementation. I would test if the self.session returns a value where valueForKey: does not.
Well I found the problem and solved my issue...
After examining the memory address of my session entity I noticing that it was changing between runs. Investigating further I discovered that where I had been testing some code earlier in another class, creating a new session entity, but not saving it, well, it was being saved after all - when I issued the save in my code above!