I'm new to parse and i've just implemented a facebook log in using parse. In my app i need the facebook profile picture of the person who is logged in. i've created that method, but dont know if there is a simpler way.
This method takes time to load which is not very nice. Can i load the profile picture somehow when the person is logged in and then use it in another viewcontroller?
here is my code at the moment:
// After logging in with Facebook
[FBRequestConnection
startForMeWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if (!error) {
NSString *facebookId = [result objectForKey:#"id"];
image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:#"https://graph.facebook.com/%#/picture?type=large", facebookId ]]]];
[self.tableView reloadData];
}
}];
This isn't actually anything to do with Parse. It's just Facebook SDK.
What you need to do is store the image somewhere so that you can get it if it already exists.
The easiest (but arguably not best) way is to use a singleton.
Create a singleton object with a property...
#interface MySingleton : NSObject
#property (nonatomic, strong) UIImage *userImage;
+ (instancetype)sharedInstance;
#end
Then you can just check if it exists before downloading...
UIImage *theImage = [[MySingleton sharedInstance] userImage];
if (!theImage) {
// download the image from Facebook and then save it into the singleton
}
Once it is downloaded you can then just get it from the singleton without having to download it every time.
Related
My app is a messaging style app and in it you can "tag" another user. (A bit like twitter).
Now, when this message is displayed, the avatar belonging to the person(s) who was tagged is displayed with that message.
The avatar of the user is stored as a PFFile against the PFUser object.
I'm loading it something like this...
PFImageView *parseImageView = ...
[taggedUser fetchIfNeededInBackgroundWithBlock:^(PFObject *user, NSError *error) {
parseImageView.file = user[#"avatar"];
[parseImageView loadInBackground];
}];
This all works fine.
The load if needed part of the code will most of the time not touch the network as for the majority of the time it has the user data cached.
However, the load in background part that gets the image and puts it into the image view runs every single time. There doesn't seem to be any caching on the PFFile data at all.
Even after downloading the same user's avatar numerous times it still goes to the network to get it.
Is there a way to get this data to cache or is this something I'll have to implement myself?
PFFile will automatically cache the file for you, if the previous PFQuery uses caching policy such as:
PFQuery *query = [PFQuery queryWithClassName:#"MyClass"];
query.cachePolicy = kPFCachePolicyCacheThenNetwork;
To check whether the PFFile is in local cache, use:
#property (assign, readonly) BOOL isDataAvailable
For example:
PFFile *file = [self.array objectForKey:#"File"];
if ([file isDataAvailable])
{
// no need to do query, it's already there
// you can use the cached file
} else
{
[file getDataInBackgroundWithBlock:^(NSData *data, NSError *error)
{
if (!error)
{
// use the newly retrieved data
}
}];
}
Hope it helps :)
In the end I created a singleton with an NSCache and queried this before going to Parse.
Works as a quick stop for now. Of course, it means that each new session has to download all the images again but it's a lot better now than it was.
You can cache result of PFQuery like below code..And need to check for cache without finding objects in background everytime..while retrieving the image.It has some other cache policies also..Please check attached link also..
PFQuery *attributesQuery = [PFQuery queryWithClassName:#"YourClassName"];
attributesQuery.cachePolicy = kPFCachePolicyCacheElseNetwork; //load cache if not then load network
if ([attributesQuery hasCachedResult]){
NSLog(#"hasCached result");
}else{
NSLog(#"noCached result");
}
Source:https://parse.com/questions/hascachedresult-always-returns-no
Hope it helps you....!
I have an iOS app that integrated with FBLoging. And I know it can assign the profile picture into a UIView like this.
// This method will be called when the user information has been fetched
- (void)loginViewFetchedUserInfo:(FBLoginView *)loginView
user:(id<FBGraphUser>)user {
self.profilePictureView.profileID = user.id;
}
But what I wanna do is to get that profile picture into my already available UIImageView. In order to do this I have a Singleton class. I want to get that image as a UIImage and assign to the variable inside that Singleton class. When another Viewcontroller load I want to assign that singleton class's UIImage into my Viewcontroller's UIImageView
How can I do this. please help me.
Thank you
If you have Facebook user.id with you, you can create the imageURL like this
NSString * url = [NSString stringWithFormat:#"https://graph.facebook.com/%#/picture?type=large",user.objectID];
Then save this url to your singleton class. And you can download like this if you need.(You can download each time you required or download once and save in to a file, if required ,fetch from file).
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]]];
UIImage *userImage = [UIImage imageWithData:data];
You can get the image from the profilePictureImageView using this answer .
Once you have the image, then you can save it in your Singleton class as is or you can assign it to your imageView.
The method in the link only works once the image has been downloaded into the profilePictureImageView. So you need to take care of that, maybe by waiting. Or keep checking it for non nil value after some time in succession.
you can try following method
[[FBRequest requestForMe] startWithCompletionHandler:^(FBRequestConnection *connection, NSDictionary<FBGraphUser> *FBuser, NSError *error) {
if (error) {
// Handle error
}
else {
NSString *userName = [FBuser name];
NSString *userImageURL = [NSString stringWithFormat:#"https://graph.facebook.com/%#/picture?type=large", [FBuser id]];
}
}];
you have to store this url into your singleton class
I'm using Parse.com as a backend for my iOS app.
I have a class called "User".
In this class I have a flied called "picture" with the users profile picture.
At the moment im kinda stuck on how i can fetch this picture for my currentUser and show it within an UIView (not UIImage) within my storyboard viewcontroller.
Maybe you guys have some sample code for that?
Thanks in Advance!
Amit, sorry your Code didnt work for me, my app crashed.
Here is my solution:
PFUser *cUser = [PFUser currentUser];
PFFile *pictureFile = [cUser objectForKey:#"picture"];
[pictureFile getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
if (!error){
[_currentUserImage setImage:[UIImage imageWithData:data]];
}
else {
NSLog(#"no data!");
[_currentUserImage setImage:[UIImage imageNamed:#"profile"]]; //Set Custom Image if there is no user picture.
}
}];
}
I have been pouring over the internet for days now trying to figure out how to implement this.
I need to request the access token and secret from twitter in order to pass this to a server that will process the users tweets for my application.
I have been following this link https://dev.twitter.com/docs/ios/using-reverse-auth
The problem is step 1. They dont give you an example of step 1.
Here is my code:
NSURL *url = [NSURL URLWithString:TW_OAUTH_URL_REQUEST_TOKEN];
NSDictionary *parameters = #{TW_X_AUTH_MODE_KEY:TW_X_AUTH_MODE_REVERSE_AUTH};
SLRequest *getTwitterAuth = [SLRequest requestForServiceType:SLServiceTypeTwitter requestMethod:SLRequestMethodGET URL:url parameters:parameters];
// Assume that we stored the result of Step 1 into a var 'resultOfStep1'
NSString *S = resultOfStep1;
NSDictionary *step2Params = [[NSMutableDictionary alloc] init];
[step2Params setValue:#"kfLxMJsk7fqIuy8URhleFg" forKey:#"x_reverse_auth_target"];
[step2Params setValue:S forKey:#"x_reverse_auth_parameters"];
NSURL *url2 = [NSURL URLWithString:#"https://api.twitter.com/oauth/access_token"];
SLRequest *stepTwoRequest =
[SLRequest requestForServiceType:SLServiceTypeTwitter requestMethod:SLRequestMethodPOST URL:url2 parameters:step2Params];
// You *MUST* keep the ACAccountStore alive for as long as you need an ACAccount instance
// See WWDC 2011 Session 124 for more info.
self.accountStore = [[ACAccountStore alloc] init];
// We only want to receive Twitter accounts
ACAccountType *twitterType =
[self.accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
// Obtain the user's permission to access the store
[self.accountStore requestAccessToAccountsWithType:twitterType
withCompletionHandler:^(BOOL granted, NSError *error) {
if (!granted) {
// handle this scenario gracefully
} else {
// obtain all the local account instances
NSArray *accounts =
[self.accountStore accountsWithAccountType:twitterType];
// for simplicity, we will choose the first account returned - in your app,
// you should ensure that the user chooses the correct Twitter account
// to use with your application. DO NOT FORGET THIS STEP.
[stepTwoRequest setAccount:[accounts objectAtIndex:0]];
// execute the request
[stepTwoRequest performRequestWithHandler:
^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
NSString *responseStr =
[[NSString alloc] initWithData:responseData
encoding:NSUTF8StringEncoding];
// see below for an example response
NSLog(#"The user's info for your server:\n%#", responseStr);
}];
}
}];
I have been trying to figure out how I process the SLRequest in oder to pass it to step 2 from the twitter docs.
I have also used this here: https://github.com/seancook/TWReverseAuthExample
This code is great but very complex. Any help would be greatly appreciated! Thanks!
The reason step one doesn't have any code is that they assume you will do this on your server or before hand or something like that. Basically you need to generate a key that your app will use to convert iOS tokens to normal tokens.
There is a script that will make the request for you here: http://www.ananseproductions.com/twitter-reverse-auth-headaches/ Its written in ruby so you could use something similar if you have a ruby server.
Personally I would have my app request this token from my server, then make the request to twitter, then post the new token back to my server.
Here is a class to help accomplish just this with a single method call that returns a dictionary with the token and token secret.
https://github.com/kbegeman/Twitter-Reverse-Auth
Hope this helps others out!
As of this code https://github.com/seancook/TWReverseAuthExample , it's fairly simple to implement in your own application. I prefer to create reusable classes, so I don't have to implement the same code multiple times. Normally you would create some singleton and work with it on the following tutorial. However the point of this instruction is not to teach you how to create singletons, so for the simplicity sake, we will use AppDelegate.h/m which is easily accessible from all over the application.
All you have to do is the following:
Open yours and Sean Cook's project (the one which URL is above)
Drag and copy Source->Vendor->ABOauthCore group into your project
Select TWAPIManager.h/m, TWSignedRequest.h/m and copy them into your project
Add the below code into your AppDelegate.h file
#property (nonatomic, strong) ACAccountStore* store;
#property (nonatomic, strong) TWAPIManager *apiManager;
#property (nonatomic, strong) NSArray *accounts;
-(void)storeAccountWithAccessToken:(NSString *)token secret:(NSString *)secret;
-(void)performReverseAuth:(id)sender inView:(UIView*)viewToDisplaySheet;
-(void)_refreshTwitterAccounts;
Now paste the following methods into your AppDelegate.m file
-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex;
-(void)_refreshTwitterAccounts;
-(void)_obtainAccessToAccountsWithBlock:(void (^)(BOOL))block;
-(void)performReverseAuth:(id)sender inView:(UIView*)viewToDisplaySheet;
In some initialization method of your file, or as of this example in: `application: didFinishLaunchingWithOptions' paste the following code:
_store = [[ACAccountStore alloc] init];
_apiManager = [[TWAPIManager alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(_refreshTwitterAccounts) name:ACAccountStoreDidChangeNotification object:nil];
Remember to remove observer using the following code. Paste it in AppDelegate.m:
-(void)dealloc{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
Open your app-Info.plist file and add 2 string keys. Take their values from: https://apps.twitter.com/
TWITTER_CONSUMER_KEY
TWITTER_CONSUMER_SECRET
In the View Controller that you want to use to implement twitter features, in the viewDidLoad method, add the following code:
AppDelegate* appDelegate = [UIApplication sharedApplication].delegate;
[appDelegate _refreshTwitterAccounts];
OK, finally you are ready to start the whole machine. In the View Controller that you want to use to implement twitter features, create UIButton called _reverseAuthBtn and create an IBAction to it. Then in your IBAction paste the following code:
AppDelegate* appDelegate = [UIApplication sharedApplication].delegate;
[appDelegate performReverseAuth:sender inView:self.view];
Whew, I guess that's it! If I haven't forgotten about anything, you have got Twitter Reverse Oauth implementation, and if you want to use it in multiple view controllers, all you have to do is do steps 1-8, and then paste the code from the steps 9 and 10 into your view controller.
Best regards!
Use this lib, it works perfectly!
https://github.com/nst/STTwitter
Info how to implement: https://github.com/nst/STTwitter#reverse-authentication
:)
I'm using the new facebook ios sdk. I request for friends data using the new function showed below. However, since it is a function with a block as a parameter I lost these data outside the function. How can I preserve the data (i.e. store in a global variable) so that I can use it in another function?
Thanks in advance.
code:
-(void)requestFriends {
[FBRequestConnection startForMyFriendsWithCompletionHandler:^(FBRequestConnection* connection, id data, NSError *error) {
if(error) {
[self printError:#"Error requesting /me/friends" error:error];
return;
}
NSArray* friends = (NSArray*)[data data];
}];
Just store it on a property, and refresh the UI after that.
// in .h or class extension
#property(nonatomic, strong) NSArray *friends;
-(void)requestFriends {
[FBRequestConnection startForMyFriendsWithCompletionHandler:^(FBRequestConnection* connection, id data, NSError *error) {
if(error) {
[self printError:#"Error requesting /me/friends" error:error];
return;
}
self.friends = (NSArray*)[data data];
}];