I'm using Parse with Facebook login to create a new user. I get a few fields from Facebook like their name and location, request their profile image to load in the background before I segue to my main view controller which has a PFImageView for the profile image.
The problem is that the PFFile user[#"profileImage"] is still nil by the time the main view controller's viewDidLoad fires, so there's nothing to loadInBackground.
I don't want to wait for the profile image to load before going to the main view controller...that would take some random time and just be bad.
So, how to I deal with a PFFile that hasn't loaded before I give it to the PFFImageView? Some kind of placeholder?
Here's my code:
------- in LoginViewController.m
- (void) pressedFacebookLogin {
[PFFacebookUtils logInWithPermissions...
if(user.isNew) {
FBRequest *request = [FBRequest requestForMe];
[request startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
NSDictionary *userData = (NSDictionary *)result;
user[#"fullName"] = userData[#"name"];
// get location, relationship if available
[user saveInBackground];
// start getting the user's profile image before going to main view controller
NSURL *pictureURL = [NSURL URLWithString:[NSString stringWithFormat:#"https://graph.facebook.com/%#/picture?type=large&return_ssl_resources=1", facebookID]];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:pictureURL];
[NSURLConnection sendAsynchronousRequest:urlRequest queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (connectionError == nil && data != nil) {
UIImage *tmpImage = [UIImage imageWithData:data];
NSData *imageData = UIImagePNGRepresentation(tmpImage);
PFFile *imageFile = [PFFile fileWithName:#"image.png" data:imageData];
user[#"profileImage"] = imageFile;
[user saveInBackground];
}
}];
[self segueToMainViewController];
}];
}else {
// deal with existing user...
}
}];
}
-------------- in MainViewController.m
- (void) viewDidLoad {
[super viewDidLoad];
PFUser *user = [PFUser currentUser];
self.profileImageView.image = [UIImage imageNamed:#"placeholder"];
PFFile *profileImageFile = user[#"profileImage"];
// at this point profileImageFile is nil
self.profileImageView.file = profileImageFile;
[self.profileImageView loadInBackground];
}
In the place where you get the profileImageFile as empty add this line.
I guess the profileImageView you use is a ImageView.
#property (weak, nonatomic) IBOutlet UIImageView *profileImageView;
// at this line where you get profileImageFile as nil
[self.profileImageView setImageWithURL:[NSURL URLWithString:user[#"profileImage"]] placeholderImage:[UIImage imageNamed:#"default_profileImage"]];
Related
NSURL *pictureURL = [NSURL URLWithString:[NSString stringWithFormat:#"https://graph.facebook.com/%#/picture?type=large&return_ssl_resources=1", facebookID]];
NSString *urlString = [pictureURL absoluteString];
FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:#"me" parameters:nil];
[request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) {
if (!error) {
// result is a dictionary with the user's Facebook data
NSDictionary *userData = (NSDictionary *)result;
NSString *facebookID = userData[#"id"];
NSURL *pictureURL = [NSURL URLWithString:[NSString stringWithFormat:#"https://graph.facebook.com/%#/picture?type=large&return_ssl_resources=1", facebookID]];
NSString *urlString = [pictureURL absoluteString];
NSLog(#"sting=%#",urlString);
PFUser *me = [PFUser currentUser];
me[#"facebookId"] = userData[#"id"];
me["pictureURL"] = userData[urlString];
me[#"username"] = userData[#"name"];
[me saveInBackgroundWithBlock:^(BOOL succeeded, NSError * _Nullable error) {
if (error) {
NSLog(#"Error to store=%#",error.localizedDescription);
}
}];
[self presentViewController:push animated:YES completion:nil];
} else {
[self presentViewController:push animated:YES completion:nil];
}
}];
I need to store the Facebook Image or URL when any new user login into my app. AFAIK we can't save the url directly so tried to convert it into NSString but then it threw error "'Can't use nil for keys or values on PFObject. Use NSNull for values.'"
Is there any way so that I can store that link or any other alternate way to save directly the Image into Parse?
Your issue is most likely being caused by the following line:
me["pictureURL"] = userData[urlString];
You most likely want this to be:
me["pictureURL"] = urlString;
As you have it, you will setting me[#"pictureURL"] to nil because you probably don't have a value in userData with a key matching urlString.
Try this, the url should be converted to data then saved as a PFFile.
NSURL *pictureURL = [NSURL URLWithString:[NSString stringWithFormat:#"https://graph.facebook.com/%#/picture? type=large&return_ssl_resources=1", facebookID]];
NSData *data = [NSData contentsOfURL:picureURL];
PFFile *file = [PFFile fileWithData:data];
//save it
[file saveInBackground];
Hope that helps!
can i show an instagram photo on my UIImageView?
I was looking for media id and other options, but i can't find the format and the way to show this image for example:
https://www.instagram.com/p/9W-K0wtq3v/
You can get direct link to image using http://api.instagram.com/oembed?url= . After that, downloading the image from that URL & displaying it in UIImageView is fairly simple. I have edited my answer to this one as it works without integrating Instagram API or parsing the web page for URL to file.
Add the following to methods to your View Controller. I have added explanation in comments.
- (void)getDirectURLToLink:(NSString *)urlStr completionBlock:(void (^)(BOOL succeeded, NSString *imageLink))completionBlock
{
NSURL *url = [NSURL URLWithString:urlStr];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if ( !error )
{
//Convert data to dictionary as it is JSON. You can view json response for your url at http://paste.ubuntu.com/14437288/
NSError *error1;
NSMutableDictionary * innerJson = [NSJSONSerialization
JSONObjectWithData:data options:kNilOptions error:&error1
];
//Send the direct url to block
completionBlock(YES,[innerJson objectForKey:#"thumbnail_url"]);
} else{
//request failed
completionBlock(NO,nil);
}
}];
}
- (void)downloadImageWithURL:(NSString *)urlStr completionBlock:(void (^)(BOOL succeeded, UIImage *image))completionBlock
{
NSURL *url = [NSURL URLWithString:urlStr];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if ( !error )
{
//cnvert data to uiimage
UIImage *image = [[UIImage alloc] initWithData:data];
completionBlock(YES,image);
} else{
//download failed
completionBlock(NO,nil);
}
}];
}
(But since sendAsynchronusRequest is deprecated in iOS 9.0, you should use [NSURLSession dataTaskWithURL])
Now you have set up the web request part of the file. To utilize these services, Add following method to your Viewcontroller:
-(void) getImageForPostURL: (NSString *)postURL
{
NSString *baseURL = #"http://api.instagram.com/oembed?url=";
NSString *directLinkRequestURL = [NSString stringWithFormat:#"%#%#",baseURL,postURL];
//Request Direct URL to file from your post url
[self getDirectURLToLink:directLinkRequestURL completionBlock:^(BOOL succeeded, NSString *imgDirectURL) {
if (succeeded) {
//Direct link retrieved
//Get image
[self downloadImageWithURL:imgDirectURL completionBlock:^(BOOL succeeded, UIImage *image) {
if (succeeded) {
// change the image where you want, it has been downloaded
_imgView.image = image;
}
}];
}
else
{
//Error
//Link could not be retrieved
}
}];
}
All this work is not for vain. Now, You are all set to go. All you need is a URL to instagram post and you will be able to download your image just by calling this one line:
[self getImageForPostURL:#"https://www.instagram.com/p/9W-K0wtq3v/"]; //Give your post url as parameter here
I think: it will have two ways to achieve your goal
*First: parse link web Instagram. If you view source your link give: you will find direct link to image:
https://igcdn-photos-b-a.akamaihd.net/hphotos-ak-xfp1/t51.2885-15/e35/10729433_781480525295113_239273684_n.jpg
So you can parse web and find:
<meta property="og:image" content="
for direct link.
Second: You can reference here:
https://instagram.com/developer/endpoints/media/
You enroll developer instagram and learn how to use Instagram API end points.
I am using PFLogInViewController to login to facebook which works successfully the first time-
- (void)logInViewController:(PFLogInViewController *)controller
didLogInUser:(PFUser *)user {
if([PFFacebookUtils isLinkedWithUser:user]){
FBRequest *request = [FBRequest requestForMe];
[request startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
NSDictionary *userData = (NSDictionary *)result;
NSString *facebookID = userData[#"id"];
NSString *name = userData[#"name"];
NSString *location = userData[#"location"][#"name"];
NSString *gender = userData[#"gender"];
NSString *birthday = userData[#"birthday"];
NSString *relationship = userData[#"relationship_status"];
[user setObject:userData[#"id"] forKey:#"fbId"];
[user setObject:userData[#"name"] forKey:#"name"];
[user setObject:userData[#"email"] forKey:#"email"];
[user saveInBackground];
NSURL *pictureURL = [NSURL URLWithString:[NSString stringWithFormat:#"https://graph.facebook.com/%#/picture?type=large&return_ssl_resources=1", facebookID]];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:pictureURL];
// Run network request asynchronously
[NSURLConnection sendAsynchronousRequest:urlRequest
queue:[NSOperationQueue mainQueue]
completionHandler:
^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (connectionError == nil && data != nil) {
// Set the image in the header imageView
PFFile *profileImage=[PFFile fileWithData:data];
[user setObject:profileImage forKey:#"profileImage"];
[user saveInBackground];
}
}];
}];
}
[self dismissViewControllerAnimated:YES completion:nil];
}
If I then call [PFUser logOut] and open the PFLogInViewController again, when I tap the facebook icon to log in again, the facebook spinner spins indefinitely and I get the error "Attempted to change an objectId to one that's already known to the OfflineStore."
How do I log out of the current user and then login again using PFLogInViewController?
If I remove [Parse enableLocalDatastore] from the appDelegate, I no longer get this error. If I am not pinning any objects is there any disadvantage to disabling this? Will I still be able to use "saveEventually" if there is no network connection?
I think you are trying to save same object(email) again.
On first login, that email id will be stored fisrt time. But, after logout, you are again trying to store same email.
Try to search that entry is already exists in Parse. If you want to update the other details of user then you can update that using objectId.
Try unlinking the PFUser from the PFFacebookUtils and also close the active Facebook session and clear it's token.
[[FBsession activeSession]closeAndClearTokenInformation];
Hope this helps..
I'm currently having some trouble with data getting lost when transferring from a ViewController to a subclass of PFFile. The data being passed is image data to upload to a users profile. Here's the code for selecting the image:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
// Access the uncropped image from info dictionary
UIImage *image = [info objectForKey:#"UIImagePickerControllerOriginalImage"];
// Dismiss controller
[picker dismissViewControllerAnimated:YES completion:nil];
// Resize image
_focusedImage.image = image;
NSData *imageData = UIImageJPEGRepresentation(image, 0.05f);
PFFile *imageFile = [PFFile fileWithName:#"Image.jpg" data:imageData];
[[imageUpload uploadImage] setImagePFFile:imageFile];
}
The Log on imageFile in this view is printing out correctly. However, when I pass the data through to my singleton class imageUpload uploadImage This is what the data structure looks like:
+ (imageUpload *) uploadImage
{
static imageUpload*_sharedImageUpload = nil;
_sharedImageUpload = [[self alloc] init];
_sharedImageUpload.imageData = [[NSData alloc] init];
PFUser *user = [PFUser currentUser];
_sharedImageUpload.imagePFFile = [[PFFile alloc] init];
PFFile *imageFile = [PFFile fileWithName:#"Image.jpg" data:_sharedImageUpload.imageData];
[imageFile saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!error)
{
[user setObject:imageFile forKey:#"image"];
[user saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!error)
{
NSLog(#"This should be the profile image upload");
}
else
{
NSLog(#"Something went wrong: %#", error);
}
}];
}
}];
return _sharedImageUpload;
}
When I get to this point, the system just uploads a blank file (zero bytes) to Parse. The naming is right and its going in the right place on the database, but somewhere along the line the data in the file itself is being lost. I can't figure out why. Does anyone have any suggestions?
It looks like you're confusing objects and methods. What you want is a singleton object that has a method / function that uploads your image. I think this is what you're looking for:
//ImageUploader.h
#import <Foundation/Foundation.h>
#interface ImageUploader : NSObject
+ (instancetype)uploader;
- (void)uploadImageFile:(PFFile *)aFile;
#end
//ImageUploader.m
#import "ImageUploader.h"
#implementation ImageUploader
+ (instancetype)uploader {
static ImageUploader * _uploader = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_uploader = [[self alloc] init];
});
return _uploader;
}
-(void)uploadPFFile:(PFFile *)imageFile{
[imageFile saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!error)
{
[user setObject:imageFile forKey:#"image"];
[user saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!error)
{
NSLog(#"This should be the profile image upload");
}
else
{
NSLog(#"Something went wrong: %#", error);
}
}];
}
}];
}
#end
You invoke it by calling [[ImageUploader uploader]uploadImageFile:someFile].
I am writing an app that displays images from url in a view. The idea is that when the view appears, image is dowloaded and it actualizes a UIImamgeView in the view.
I am using a Asyncrounse method in this way:
-(void)downloadASyncro:(NSString*)urlLink{
NSURL * imageURL = [NSURL URLWithString:urlLink];
[self downloadImageWithURL:imageURL completionBlock:^(BOOL succeeded, UIImage *image) {
if (succeeded) {
NSLog(#"scaricaImmagineASyncro Succeded= %#",image);
picView.image = image;
}
else {
//default image
picView.image = [UIImage imageNamed:#"icon_old.jpg"];
}
}];
}
the downloadImageWithURL method is:
- (void)downloadImageWithURL:(NSURL *)url completionBlock:(void (^)(BOOL succeeded, UIImage *image))completionBlock
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if ( !error )
{
UIImage *image = [[UIImage alloc] initWithData:data];
completionBlock(YES,image);
} else{
completionBlock(NO,nil);
}
}];
}
When I call the method:
[self downloadASyncro:link];
and the operation sees end with success (NSLOG), picView.image = image; should actualize the view showing the image downloaded , should not it? But immage does not appear...
Any idea? Thanks in advance.