I'm new to Firebase, and I have been following their documentation here.
However, nothing I have tried seems to work.
What I am trying to do:
1) Register a user -Works
2) Have the user choose a profile picture during the registration process -Doesn't work.
The code:
- (void) registerNewUser:(FIRUser *)user
{
FIRUser *currentUser = [FIRAuth auth].currentUser;
NSString *email = emailAddressTxtField.text;
NSString *password = passwordTxtField.text;
NSString *username = usernameTxtField.text;
[[FIRAuth auth]
createUserWithEmail:email
password:password
completion:^(FIRUser *_Nullable user,
NSError *_Nullable error)
{
if (error)
{
NSLog(#"%#", error.localizedDescription);
return;
}
else
{
////ASSIGN NEW USER THEIR NAME////
self.databaseRef = [[FIRDatabase database] reference];
[[[databaseRef child:#"users"] child:user.uid]
setValue:#{#"name": username}];
}
}];
////ESTABLISHED A USER, SO LET'S ASSIGN THEIR PIC TO THEM////
if (profilePicImageView.image)
{
FIRStorageReference *profilePicRef = [[storageRef child:#"images/profilePicture.jpg"] child:currentUser.uid];
FIRStorageMetadata *metadata = [[FIRStorageMetadata alloc] init];
metadata.contentType = #"image/jpeg";
NSData *imageData = UIImageJPEGRepresentation(self.profilePicImageView.image, 0.8);
NSLog(#"metadata from image: %#", metadata);
[profilePicRef putData:imageData metadata:metadata completion:^(FIRStorageMetadata *metadata, NSError *error)
{
if (error != nil)
{
NSString *profileImageURL = metadata.downloadURL.absoluteString;
NSLog(#"Profile Image URL from image: %#", profileImageURL);
[ProgressHUD showSuccess:[NSString stringWithFormat:#"%# Successfully Registered!!", username]];
[self.segmentedLoginRegister setSelectedSegmentIndex:0];
[self checkSegmentedControl];
[ProgressHUD showSuccess:[NSString stringWithFormat:#"Welcome %#!", username]];
}
else
{
NSLog(#"Failed to Register User with profile image");
}
}];
}
}
Additional Information:
Photos are coming only from the camera roll of the user's device
Debug area prints:
[Generic] Creating an image format with an unknown type is an error
My guess is that profilePicImageView.image doesn't actually contain a valid image object.
HOW TO REGISTER A USER
Let's assume that you have 3 textfields to register a user. You have a usernameTextfield, an emailTextField, and a passwordTextfield. We also want to have a profilePicture associated with this user. So we first:
Established a method to save our values for our User to the Firebase Database:
-(void) saveValuesForUser:(FIRUser *) user
{
NSString *username = usernameTxtField.text;
self.databaseRef = [[FIRDatabase database] reference];
[[[databaseRef child:#"users"] child:user.uid]
setValue:#{#"Name": username, #"Profile Picture": profileImageURL}];
}
The profileImageURL above is an NSString that you can create above your implementation of your view controller, but we also should create a UIImage that we can access throughout the ViewController:
#import "ViewController.h"
#import Photos;
#interface ViewController ()
#end
NSString *profileImageURL;
UIImage *profileImage;
#implementation ViewController
I'm guessing that you know how to launch the camera roll off from a UIButton but in case you do not, let's call these methods so that when our user taps the button to choose their image that it get's set to our profilePicture.imageView:
- (IBAction)chooseImageAction:(id)sender
{
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.allowsEditing = YES;
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
[self presentViewController:picker animated:YES completion:NULL];
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info
{
picker.delegate = self;
profileImage = info[UIImagePickerControllerEditedImage];
profilePicImageView.image = profileImage;
[self dismissViewControllerAnimated:NO completion:nil];
}
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
[picker dismissViewControllerAnimated:YES completion:NULL];
}
Now we're cooking with fire! Now that we have the image the user wants to upload for their profilePicture, let's create a method to save it as an NSURL to our Firebase Database:
- (void) saveProfileImage
{
FIRUser *currentUser = [FIRAuth auth].currentUser;
NSString *username = usernameTxtField.text;
if (profilePicImageView.image != nil)
{
FIRStorage *storage = [FIRStorage storage];
storageRef = [storage referenceForURL:#"gs://PUTYOURFIREBASEHERE.appspot.com"];
NSString *imageID = [[NSUUID UUID] UUIDString];
NSString *imageName = [NSString stringWithFormat:#"Profile Pictures/%#.jpg",imageID];
FIRStorageReference *profilePicRef = [storageRef child:imageName];
FIRStorageMetadata *metadata = [[FIRStorageMetadata alloc] init];
metadata.contentType = #"image/jpeg";
NSData *imageData = UIImageJPEGRepresentation(self.profilePicImageView.image, 0.8);
[profilePicRef putData:imageData metadata:metadata completion:^(FIRStorageMetadata *metadata, NSError *error)
{
if (!error)
{
profileImageURL = metadata.downloadURL.absoluteString;
[self saveValuesForUser: currentUser];
}
else if (error)
{
NSLog(#"Failed to Register User with profile image");
}
}];
}
}
IMPORTANT: Make sure you insert your gs:// reference for the value of storageRef! You can find this on your Firebase Console under Storage.
Now that we have the method to save the user's profile picture, let's create a method to Register the new user that we can call it on our IBAction:
- (void) registerNewUser
{
NSString *email = emailAddressTxtField.text;
NSString *password = passwordTxtField.text;
[[FIRAuth auth]
createUserWithEmail:email
password:password
completion:^(FIRUser *_Nullable user,
NSError *_Nullable error)
{
if (error)
{
NSLog(#"%#", error.localizedDescription);
return;
}
else
{
[self saveProfileImage];
}
}];
}
Oh thats nice! Now, let's call that method on our button, so that when the user taps the Register button on our UI, that it get's called:
- (IBAction)registerUserAction:(id)sender
{
[self registerNewUser];
}
Note: Make sure that in your ViewController.h file that you are setting the appropriate delegates:
#interface ViewController : UIViewController <UIImagePickerControllerDelegate, UINavigationControllerDelegate, UITextFieldDelegate>
Related
I am new to iOS. I have a UITextfield and a Keyword Search Button. When ever I want to search a keyword from a service and press enter. Tt should display the related searched keyword from a service. Please help me to fix this issue? TIA!
- (IBAction)KeywordSearchClicked:(id)sender {
NSMutableDictionary *dict=[[NSMutableDictionary alloc] init];
[self KeywordcallSignupProfileService:dict];
}
-(void)KeywordcallSignupProfileService:(NSMutableDictionary *)dict
{
[SVProgressHUD showWithStatus:#"" maskType:SVProgressHUDMaskTypeBlack]; // Progress
NSString * post = [[NSString alloc]initWithFormat:#"userId=%#&key_word%#",UserId,[dict objectForKey:#"key_word"]];
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"http://www.amarbiyashaadi.com/service/amarbiya-service.svc/userKeywordSearch/"]];
RBConnect = [[RBConnection alloc]init];
RBConnect.delegate = self;
[RBConnect postRequestForUrl:url postBody:post];
}
#pragma mark - MRConnection Delegate Methods
- (void)jsonData:(NSDictionary *)jsonDict
{
[SVProgressHUD dismiss];
NSMutableArray *jsonArr;
NSMutableDictionary *userDict,*dict;
NSArray *arr=[jsonDict allKeys];
jsonArr=[jsonDict objectForKey:#"DataTable"];
if (jsonArr.count>0) {
// Save credentials in user defaults
matchesProfileArr=[jsonArr mutableCopy];
DisplayTableViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"DisplayTableViewController"];
[self presentViewController:vc animated:YES completion:nil];
}
else
{
NSString *error=#"Somthing Went Wrong";
[SVProgressHUD showErrorWithStatus:error];
}
}
I integrated iCloud into iOS app using raywenderlich https://www.raywenderlich.com/6015/beginning-icloud-in-ios-5-tutorial-part-1
But iam unable to show all the files from iCloud to our iOS app and also need specific type of files like pdf, doc and docx
Can any one suggest me.
Follow below steps to integrate iCloud in iOS app and retrieve files.
1. Enable iCloud from your developer account.
2. Create iCloud containers entitlement at developer account.
3. Then just use below code where you want to integrate your iCloud integration.
First of all import #import and add iCloudDelegate delegate then set delegate:
// Setup iCloud
[[iCloud sharedCloud] setDelegate:self];
[[iCloud sharedCloud] setVerboseLogging:YES];
[[iCloud sharedCloud] setupiCloudDocumentSyncWithUbiquityContainer:nil];
[self showiCloudFiles];
then implementation of method showiCloudFiles below
-(void) showiCloudFiles{
BOOL cloudAvailable = [[iCloud sharedCloud] checkCloudAvailability];
if (cloudAvailable && [[NSUserDefaults standardUserDefaults] boolForKey:#"userCloudPref"] == YES) {
UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:#[#"public.content"]
inMode:UIDocumentPickerModeImport];
documentPicker.delegate = self;
documentPicker.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentViewController:documentPicker animated:YES completion:nil];
}
else if ([[NSUserDefaults standardUserDefaults] boolForKey:#"userCloudPref"] == NO) {
UIAlertController * alert = SIMPLE_ALERT_VIEW(#"iCloud Disabled", #"You have disabled iCloud for this app. Would you like to turn it on again?");
UIAlertAction* cancelButton = [UIAlertAction actionWithTitle:#"Cancel" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action){}];[alert addAction:cancelButton];
UIAlertAction* deleteButton = [UIAlertAction actionWithTitle:#"Turn On iCloud"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action){
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"userCloudPref"];
[[NSUserDefaults standardUserDefaults] synchronize];
BOOL cloudAvailable = [[iCloud sharedCloud] checkCloudAvailability];
if (cloudAvailable && [[NSUserDefaults standardUserDefaults] boolForKey:#"userCloudPref"] == YES) {
UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:#[#"public.content"]
inMode:UIDocumentPickerModeImport];
documentPicker.delegate = self;
documentPicker.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentViewController:documentPicker animated:YES completion:nil];
}
}];
[alert addAction:deleteButton];
[self presentViewController:alert animated:YES completion:nil];
} else {
UIAlertController * alert = SIMPLE_ALERT_VIEW(#"Setup iCloud", #"iCloud is not available. Sign into an iCloud account on this device and check that this app has valid entitlements.");
UIAlertAction* cancelButton = [UIAlertAction actionWithTitle:#"Okay" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action){}];[alert addAction:cancelButton];
}];
[self presentViewController:alert animated:YES completion:nil];
}
}
After that for downloading file use UIDocumentPickerDelegate method:
#pragma mark - UIDocumentPickerDelegate
-(void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url{
if (controller.documentPickerMode == UIDocumentPickerModeImport) {
//NSLog(#"%#",url);
[url startAccessingSecurityScopedResource];
NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] init];
NSError *error;
__block NSData *fileData;
[coordinator coordinateReadingItemAtURL:url options:NSFileCoordinatorReadingForUploading error:&error byAccessor:^(NSURL *newURL) {
// File name for use in writing the file out later
NSString *fileName = [newURL lastPathComponent]; NSString *fileExtension = [newURL pathExtension]; if([fileExtension isEqualToString:#"zip"]) {if([[[newURL URLByDeletingPathExtension] pathExtension] isEqualToString:#"pages"] ||
[[[newURL URLByDeletingPathExtension] pathExtension] isEqualToString:#"numbers"] ||
[[[newURL URLByDeletingPathExtension] pathExtension] isEqualToString:#"key"] ) {
// Remove .zip if it is an iWork file
fileExtension = [[newURL URLByDeletingPathExtension] pathExtension];
fileName = [[newURL URLByDeletingPathExtension] lastPathComponent];
}
}
NSError *fileConversionError;fileData = [NSData dataWithContentsOfURL:newURL options:NSDataReadingUncached error:&fileConversionError];
// Do further code using fileData
}
}];
[url stopAccessingSecurityScopedResource];
}
}
For UIDocumentPicker visit this link iOS-8-UIDocumentPicker
Follow this guide
https://www.raywenderlich.com/12779/icloud-and-uidocument-beyond-the-basics-part-1
Download sample code at
https://github.com/rwenderlich/PhotoKeeper
Check if iCloud available
- (void)initializeiCloudAccessWithCompletion:(void (^)(BOOL available)) completion {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
_iCloudRoot = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
if (_iCloudRoot != nil) {
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"iCloud available at: %#", _iCloudRoot);
completion(TRUE);
});
}
else {
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"iCloud not available");
completion(FALSE);
});
}
});
}
Query type of flies like pdf, doc and docx
- (NSMetadataQuery *)documentQuery {
NSMetadataQuery * query = [[NSMetadataQuery alloc] init];
if (query) {
// Search documents subdir only
[query setSearchScopes:[NSArray arrayWithObject:NSMetadataQueryUbiquitousDocumentsScope]];
// Add a predicate for finding the documents
NSString * filePattern = [NSString stringWithFormat:#"*.%#", PTK_EXTENSION];
[query setPredicate:[NSPredicate predicateWithFormat:#"%K LIKE %#",
NSMetadataItemFSNameKey, filePattern]];
}
return query;
}
Looking to get some help implementing QuickBlox into our iOS app. We can’t seem to get around an issue that after sending a message QuickBlox returns all the messages that has been previously sent when the block is called by the timer ticks event. Any help would be much appreciated!
QBResponsePage *page = [QBResponsePage responsePageWithLimit:10 skip:0];
[QBRequest dialogsForPage:page extendedRequest:nil successBlock:^(QBResponse *response, NSArray *dialogObjects, NSSet *dialogsUsersIDs, QBResponsePage *page)
{
QBChatDialog *dialog=[dialogObjects objectAtIndex:0];
[QBRequest messagesWithDialogID:dialog.ID extendedRequest:nil forPage:page successBlock:^(QBResponse *response, NSArray *messages, QBResponsePage *responsePage)
{
[self.items removeAllObjects];
[self.items addObjectsFromArray:messages];
[self finishSendingMessageAnimated:YES];
} errorBlock:^(QBResponse *response) {
NSLog(#"error: %#", response.error);
}];
} errorBlock:^(QBResponse *response) {
}];
Here are the steps we have taken:
1) Imported all of the QuickBlox Classes into our project.
2) In My App Delegate Class set all credentials of QuickBlox
[QBApplication sharedApplication].applicationId = (removed);
[QBConnection registerServiceKey:#“(removed)”];
[QBConnection registerServiceSecret:#“(removed)”];
[QBSettings setAccountKey:#“(removed)”];
3) Then created the subClass of QMChatViewController that was imported
4) Imported NMPaginatorDelegate and Cretae UsersPaginator object, (gives me list of users)
5) On Clicking on Particular user invokes the subClass we created and on its viewDidLoad wrote below code:
QBUUser *currentUser = [QBUUser user];
currentUser.ID = LoginUser.ID;
currentUser.password = LoginUser.login;
[[QBChat instance] addDelegate:(id)self];
[[QBChat instance] loginWithUser:currentUser];
After loginWithUser called
BOOL chlLogion =[[QBChat instance]isLoggedIn];
NSLog(#"Loggin IN Status :- %hhd”,chlLogion);
Log Print :- 2015-09-22 16:04:31.911 AppName[2263:96572] [ChatService] Connecting to Chat, host: chat.quickblox.com, user JID: 5434136-28329#chat.quickblox.com/7DE2CD1E-481F-4922-B21D-8EB14BF8B55F
2015-09-22 16:04:42.978 AppName[2263:96225] Loggin IN Status :- 0
Note :- Deprication
[[QBChat instance] sendMessage:message];
That Code is Depricated and and your sdks shows it option but that well also not working.
and set its delegate Methods.
(void)chatDidConnect
{
}
(void)chatDidAccidentallyDisconnect{
}
(void)chatDidReconnect{
}
(void)chatDidReceiveMessage:(QBChatMessage *)message{
NSLog(#"Received Message %#",message);
}
(void)chatDidNotSendMessage:(QBChatMessage *)message error:(NSError *)error{
}
(void)chatDidDeliverMessageWithID:(NSString *)messageID
{
}
However, it just called 1 delegate “chatDidConnect” after that NO another delegate is called.
And in Send Message I am setting:
QBChatDialog *chatDialog = [[QBChatDialog alloc]initWithDialogID:[NSString stringWithFormat:#"%lu",(unsigned long)LoginUser.ID] type:QBChatDialogTypePrivate];
chatDialog.occupantIDs=#[#(SelectedChatUser.ID)];
chatDialog.name = #"school friends";
[QBRequest createDialog:chatDialog successBlock:^(QBResponse *response, QBChatDialog *createdDialog) {
_dialog=createdDialog;
QBChatMessage *message = [QBChatMessage message];
message.text = text;
message.senderID = senderId;
message.recipientID=createdDialog.recipientID;
message.deliveredIDs=[createdDialog.occupantIDs objectAtIndex:1];
message.dialogID=[NSString stringWithFormat:#"%#",createdDialog.ID];
message.senderNick=#"Nick Simulator";
message.dateSent = [NSDate date];
[self.items addObject:message];
[createdDialog sendMessage:message];
[self finishSendingMessageAnimated:YES];
} errorBlock:^(QBResponse *response) {
}];
But threw that code messages are not sent and we don’t get any messages threw delegate.
6) After that I switched to new methods to send a message. Wrote the following code to send a message:
- (void)didPressSendButton:(UIButton *)button
withMessageText:(NSString *)text
senderId:(NSUInteger)senderId
senderDisplayName:(NSString *)senderDisplayName
date:(NSDate *)date {
QBChatDialog *chatDialog = [[QBChatDialog alloc]initWithDialogID:[NSString stringWithFormat:#"%lu",(unsigned long)LoginUser.ID] type:QBChatDialogTypePrivate];
chatDialog.occupantIDs=#[#(SelectedChatUser.ID)];
chatDialog.name = #"school friends";
[QBRequest createDialog:chatDialog successBlock:^(QBResponse *response, QBChatDialog *createdDialog) {
_dialog=createdDialog;
QBChatMessage *message = [QBChatMessage message];
message.text = text;
message.senderID = senderId;
message.recipientID=createdDialog.recipientID;
message.deliveredIDs=[createdDialog.occupantIDs objectAtIndex:1];
message.dialogID=[NSString stringWithFormat:#"%#",createdDialog.ID];
message.senderNick=#"Nick Simulator";
message.dateSent = [NSDate date];
[self.items addObject:message];
[QBRequest createMessage:message successBlock:^(QBResponse *response, QBChatMessage *createdMessage) {
NSLog(#"success: %#", createdMessage);
} errorBlock:^(QBResponse *response) {
NSLog(#"ERROR: %#", response.error);
}];
[self finishSendingMessageAnimated:YES];
} errorBlock:^(QBResponse *response) {
}];
The dialogsForpage of QBResponsePage will return all the previous messages. So skip the message count that you already have will return you only latest messages.
let page = QBResponsePage.(limit:10, skip: message count that you already have)
let parameters = ["sort_desc" : "date_sent"]
QBRequest.messagesWithDialogID(currentDialog.ID!,
extendedRequest: parameters,
forPage: resPage,
successBlock:{ (response: QBResponse, messages: [QBChatMessage]?, page: QBResponsePage?) -> Void in
/**your code**/
}
So I am using Parse to link a user with their twitter account. In the app delegate I have the following:
[PFTwitterUtils initializeWithConsumerKey:CONSUMER_KEY consumerSecret:CONSUMER_SECRET];
Then the button which the user clicks to link the user to facebook calls the following:
-(IBAction)twitterConnectPressed{
NSLog(#"twitter");
[PFTwitterUtils linkUser:[PFUser currentUser] block:^(BOOL succeeded, NSError* error){
NSLog(#"haha");
if(succeeded){
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:#"Done!" message:#"Connected with Twitter!" delegate:self cancelButtonTitle:#"okay" otherButtonTitles: nil];
[alert show];
self.fbButton.backgroundColor = [TGAPublic grey];
}else{
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:#"Oops" message:error.userInfo[#"error"] delegate:self cancelButtonTitle:#"okay" otherButtonTitles: nil];
[alert show];
}
}];
}
However even though linkUser:block: is called it doesn't do anything at all. It doesn't create a pop up to log in to twitter like [PFFacebookUtils linkUser:] does and therefore doesn't end up calling the block either
PFTwitterUtils does not appear to handle all cases on iOS. In particular, if you do not have an account setup (Settings->Twitter) it does not fire up a web view and attempt to used web oauth. Conversely if you have multiple Twitter accounts configured (again in Settings) then it doesn't appear to fire up an action sheet to allow you to select which account you'd like to link.
There's a great tutorial on how to do these things which exposes an extension to PFFacebookUtils here: http://natashatherobot.com/ios-twitter-login-parse/
It does not do linking though, just login, but should be a good basis to add linking.
I've got similar problem with link/unlink methods for both PFFacebookUtils and PFTwitterUtils (v. 1.7.4).
The only way I managed to make it work was to replace them by, unfortunately, messing with internal Parse implementation of authData:
#import "TwitterAuthProvider.h"
#import "PFTwitterUtils.h"
#import "PFUser.h"
static NSString * const kTwitterKey = #"XXX";
static NSString * const kTwitterSecret = #"XXX";
#implementation TwitterAuthProvider
- (instancetype)init {
if ((self = [super init])) {
[PFTwitterUtils initializeWithConsumerKey:kTwitterKey consumerSecret:kTwitterSecret];
}
return self;
}
- (void)setAuthData:(id)twAuthData forUser:(PFUser *)user {
static NSString * const kParseAuthDataKey = #"authData";
static NSString * const kParseLinkedServiceNamesKey = #"linkedServiceNames";
static NSString * const kParseAuthProviderName = #"twitter";
NSMutableDictionary *authData = [[user valueForKey:kParseAuthDataKey] mutableCopy] ?: [NSMutableDictionary dictionary];
authData[kParseAuthProviderName] = twAuthData ?: [NSNull null];
[user setObject:authData forKey:kParseAuthDataKey];
[user setValue:authData forKey:kParseAuthDataKey];
NSMutableSet *linkedServices = [[user valueForKey:kParseLinkedServiceNamesKey] mutableCopy] ?: [NSMutableSet set];
if (twAuthData) {
[linkedServices addObject:kParseAuthProviderName];
} else {
[linkedServices removeObject:kParseAuthProviderName];
}
[user setValue:linkedServices forKey:kParseLinkedServiceNamesKey];
}
- (void)linkWithCompletion:(PFBooleanResultBlock)completion {
NSParameterAssert(completion != nil);
PFUser *user = [PFUser currentUser];
__weak typeof(self) weakSelf = self;
PF_Twitter *twitter = [PFTwitterUtils twitter];
[twitter authorizeWithSuccess:^(void) {
[weakSelf setAuthData:[self twitterAuthData] forUser:user];
[user saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!succeeded) {
//revert
[weakSelf setAuthData:nil forUser:user];
}
completion(succeeded, error);
}];
} failure:^(NSError *error) {
completion(NO, error);
} cancel:^(void) {
completion(NO, nil);
}];
}
- (void)unlinkWithCompletion:(PFBooleanResultBlock)completion {
NSParameterAssert(completion != nil);
PFUser *user = [PFUser currentUser];
[self setAuthData:nil forUser:user];
[user saveInBackgroundWithBlock:completion];
}
- (NSDictionary *)twitterAuthData {
PF_Twitter *twitter = [PFTwitterUtils twitter];
return #{
#"auth_token" : twitter.authToken,
#"auth_token_secret": twitter.authTokenSecret,
#"consumer_key": kTwitterKey,
#"consumer_secret": kTwitterSecret,
#"id": twitter.userId,
#"screen_name": twitter.screenName,
};
}
#end
I'm writing an application that allows the users to take and store images on Parse. Thus far I've managed to accomplish saving the image array to Parse by using the following logic:
Take Image
Add Object to Array
Convert array to NSData
Convert NSData to PFFile
Set file upload destination (via unique objectId)
Upload PFFile to Parse
This is what the code looks like; please forgive the fact that it's in dismissViewController for now, I'm only trying to get it to save successfully:
- (void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
_takenImage = (UIImage *) [info objectForKey:UIImagePickerControllerOriginalImage];
[self dismissViewControllerAnimated:YES completion:^
{
// Add object to array: Working
[_tankImagesArray addObject:_takenImage];
NSLog(#"Number of images taken: %lu", (unsigned long)_tankImagesArray.count);
// Convert array to NSData Object
NSData *imageData = [NSKeyedArchiver archivedDataWithRootObject:_tankImagesArray];
// Convert NSData Object to PFFile
PFFile *imageFile = [PFFile fileWithData:imageData];
PFQuery *tankQuery = [PFQuery queryWithClassName:#"SavedTanks"];
_tankObject = [tankQuery getObjectWithId:_passedValue];
[_tankObject setObject:imageFile forKey:#"tankImages"];
[_tankObject save];
}];
}
Now, my question is: How exactly would I go about retrieving that file? My ultimate goal here is to allow the user to see images they've taken in the past and add to the list of pictures in the collection and upload them to the server. I'm just unsure of how to retrieve the file once its been uploaded and make sure the integrity is maintained.
Did you try:
PFQuery *query = [PFQuery queryWithClassName:#"SavedTanks"];
[query whereKey:#"tankImages" equalTo:#"your_image.jpg"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// The find succeeded.
NSLog(#"Successfully retrieved %d images.", objects.count);
// Do something with the found objects
for (PFObject *object in objects) {
NSLog(#"%#", object.objectId);
}
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
PFQuery *query = [PFQuery queryWithClassName:#"SavedTanks"];
// Add constraints here to get the image you want (like the objectId or something else)
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
for (PFObject *object in objects) {
PFFile *imageFile = object[#"tankImages"];
[imageFile getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
if (!error) {
UIImage *image = [UIImage imageWithData:imageData]; // Here is your image. Put it in a UIImageView or whatever
}
}];
}
} else {
// Log details of the failure
}
}];
In the .h file of your collection view you need to have something like below. Note that the one I built you could like an image and then sort liked images using a segment controller.
#import <UIKit/UIKit.h>
#import "UICollectionCell.h"
#import <Parse/Parse.h>
#interface ParseViewController : UIViewController {
NSArray *imageFilesArray;
NSMutableArray *imagesArray;
}
#property (weak, nonatomic) IBOutlet UICollectionView *imagesCollection;
- (IBAction)segmentSelected:(id)sender;
#property (weak, nonatomic) IBOutlet UISegmentedControl *segmentedController;
#end
Then in the .m file of your collection view
#interface ParseViewController ()
#end
#implementation ParseViewController
#synthesize imagesCollection;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[self queryParseMethod];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
// code to add the number of images etc as per table view
-(void) queryParseMethod {
NSLog(#"start query");
PFQuery *query = [PFQuery queryWithClassName:#"collectionView"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
imageFilesArray = [[NSArray alloc] initWithArray:objects];
NSLog(#"%#", imageFilesArray);
[imagesCollection reloadData];
}
}];
}
#pragma mark - UICollectionView data source
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
// number of sections
return 1;
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
// number of items
return [imageFilesArray count];
}
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
// the custom cell we named for the reusable identifier
static NSString *cellIdentifier = #"imageCell";
UICollectionCell *cell = (UICollectionCell *)[collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
PFObject *imageObject = [imageFilesArray objectAtIndex:indexPath.row];
PFFile *imageFile = [imageObject objectForKey:#"imageFile"];
// show loading spinner
[cell.loadingSpinner startAnimating];
cell.loadingSpinner.hidden = NO;
[imageFile getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
if (!error) {
NSLog(#"%#", data);
cell.parseImage.image = [UIImage imageWithData:data];
[cell.loadingSpinner stopAnimating];
cell.loadingSpinner.hidden = YES;
}
}];
return cell;
}
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
[self likeImage:[imageFilesArray objectAtIndex:indexPath.row]];
}
-(void) likeImage:(PFObject *)object {
[object addUniqueObject:[PFUser currentUser].objectId forKey:#"favorites"];
[object saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!error) {
NSLog(#"liked picture!");
[self likedSuccess];
}
else {
[self likedFail];
}
}];
}
-(void) likedSuccess {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Success" message:#"You have succesfully liked the image" delegate:self cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
}
-(void) likedFail {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Unsuccesfull" message:#"You have been unable to like the image" delegate:self cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
- (IBAction)segmentSelected:(id)sender {
if (_segmentedController.selectedSegmentIndex == 0) {
[self queryParseMethod];
}
if (_segmentedController.selectedSegmentIndex == 1) {
[self retrieveLikedImages];
}
}
-(void) retrieveLikedImages {
PFQuery *getFavorites = [PFQuery queryWithClassName:#"collectionView"];
[getFavorites whereKey:#"favorites" equalTo:[PFUser currentUser].objectId];
[getFavorites findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
imageFilesArray = [[NSArray alloc] initWithArray:objects];
[imagesCollection reloadData];
}
}];
}
#end
Hope this is of some help to you.
All the above solutions are correct, but want to add one another way to have support of Image Caching of SDWebImage or any library like that.
On Successful completion you will be have PFFile, whose property "url" will return you actuall URL of Image where it is saved. You can use that to load image. Using this approach I was able to have Image Cacheing based on key as URL.
...
NSString *strUrl = pfFileObject.url;
...
...
[img sd_setImageWithURL:[NSURL URLWithString:strUrl]];
Why would you want to download photos from parse that the user already has them locally..?
I recommend you to use : https://github.com/AFNetworking/AFNetworking
you can also save local photos to cache so you access them easily so you dont need any downloading from parse...
now if you still want to download the photos from parse just make a normal query and download all the photos parse object and you will get in the PFObject the PFFile of your photos.
Example:
PFQuery *query = [PFQuery queryWithClassName:#"SavedTanks"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
for(PFObject *obj in objects){
PFFile *file = [obj objectForKey:#"tankImages"];
// now you can use this url to download the photo with AFNetwork
NSLog(#"%#",file.url);
}
}
}];