Permalink
Browse files

[ios-sdk] Added photo upload to Scrumptious.

Summary:
Lots of changes required to make this work:

- Enumeration of FBGraphObject was not really fixed by previous commit. Fast
enumeration worked, regular enumeration did not. (Regular enumeration checks for
collection mutation on the next iteration through -- unit test only had a single
element, so did not trigger this behavior.) Fixed by adding graphObjectifyAll to
both FBGraphObject wrappers and calling it before returning enumerators.

- A couple bugs in batch request serialization. NSString used where NSMutableString
expected. We were not specifying 'name=' along with 'filename=' on file attachments.

- Added rudimentary handling of FBGraphObjectArray objects in processGraphObject, so
we can serialize 'tags' and 'image' on an Open Graph action. These are not robust
enough to handle the general case yet.

- Some UI tweaks to Scrumptious - moved "What are you eating" first and made it the
only required field. Others are optional.

- Added some helpers to unit tests to automatically clean up test users after tests
are written, to help keep them from accumulating. Added #defines to turn on/off certain
test categories.

Test Plan:
- Ran all unit tests
- Ran Scrumptious, verified photos were uploaded and actions were created

Reviewers: jacl, mmarucheck, gregschechte, vijaye

Reviewed By: jacl

CC: caabernathy, platform-diffs@lists, yariv

Differential Revision: https://phabricator.fb.com/D475442

Task ID: 1043680
  • Loading branch information...
1 parent 89ec3b9 commit ef24c3dddf00e4eccc0c2129e2ff9160eff31a1b @clang13 clang13 committed May 16, 2012
@@ -33,7 +33,7 @@ @implementation SCLoginViewController
- (IBAction)performLogin:(id)sender
{
- [self.spinner stopAnimating];
+ [self.spinner startAnimating];
SCAppDelegate* appDelegate = [UIApplication sharedApplication].delegate;
FBSession *session = [appDelegate invalidateAndGetNewSession];
@@ -37,16 +37,19 @@ @interface SCViewController()<UITableViewDataSource, UIImagePickerControllerDele
@property (strong, nonatomic) NSObject<FBGraphPlace>* selectedPlace;
@property (strong, nonatomic) NSString* selectedMeal;
@property (strong, nonatomic) NSArray* selectedFriends;
-@property (strong, nonatomic) NSMutableDictionary* selectedPhoto;
+@property (strong, nonatomic) UIImage* selectedPhoto;
@property (strong, nonatomic) CLLocationManager *locationManager;
@property (strong, nonatomic) UIPopoverController *popover;
@property (strong, nonatomic) SCMealViewController *mealViewController;
+@property (strong, nonatomic) FBSession *session;
- (IBAction)announce:(id)sender;
- (void)populateUserDetails;
- (void)updateSelections;
- (void)updateCellIndex:(int)index withSubtitle:(NSString*)subtitle;
- (id<SCOGMeal>)mealObjectForMeal:(NSString*)meal;
+- (void)postPhotoThenOpenGraphAction;
+- (void)postOpenGraphActionWithPhotoURL:(NSString*)photoID;
@end
@@ -65,10 +68,12 @@ @implementation SCViewController
@synthesize menuTableView = _menuTableView;
@synthesize locationManager = _locationManager;
@synthesize popover = _popover;
+@synthesize session = _session;
#pragma mark open graph
-- (id<SCOGMeal>)mealObjectForMeal:(NSString*)meal {
+- (id<SCOGMeal>)mealObjectForMeal:(NSString*)meal
+{
// This URL is specific to this sample, and can be used to create arbitrary
// OG objects for this app; your OG objects will have URLs hosted by your server.
NSString *format =
@@ -78,7 +83,7 @@ @implementation SCViewController
@"og:image=https://s-static.ak.fbcdn.net/images/devsite/attachment_blank.png&"
@"body=%@";
- // We create an FBGraphObject object, but we can treat it as an CGOGMeal with typed
+ // We create an FBGraphObject object, but we can treat it as an SCOGMeal with typed
// properties, etc. See <FBiOSSDK/FBGraphObject.h> for more details.
id<SCOGMeal> result = (id<SCOGMeal>)[FBGraphObject graphObject];
@@ -88,20 +93,32 @@ @implementation SCViewController
return result;
}
-- (IBAction)announce:(id)sender
+- (void)postOpenGraphActionWithPhotoURL:(NSString*)photoURL
{
// First create the Open Graph meal object for the meal we ate.
id<SCOGMeal> mealObject = [self mealObjectForMeal:self.selectedMeal];
// Now create an Open Graph eat action with the meal, our location, and the people we were with.
id<SCOGEatMealAction> action = (id<SCOGEatMealAction>)[FBGraphObject graphObject];
action.meal = mealObject;
- action.place = self.selectedPlace;
- action.tags = self.selectedFriends;
+ if (self.selectedPlace) {
+ action.place = self.selectedPlace;
+ }
+ if (self.selectedFriends.count > 0) {
+ action.tags = self.selectedFriends;
+ }
+ if (photoURL) {
+ NSMutableDictionary *image = [[NSMutableDictionary alloc] init];
+ [image setObject:photoURL forKey:@"url"];
+ NSMutableArray *images = [[NSMutableArray alloc] init];
+ [images addObject:image];
+
+ action.image = images;
+ }
+
// Create the request and post the action to the "me/scrumps:eat" path.
- SCAppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
- FBRequestConnection *conn = [FBRequest connectionForPostWithSession:appDelegate.session
+ FBRequestConnection *conn = [FBRequest connectionForPostWithSession:self.session
graphPath:@"me/scrumps:eat"
graphObject:action
completionHandler:
@@ -115,16 +132,55 @@ - (IBAction)announce:(id)sender
[conn start];
}
+- (void)postPhotoThenOpenGraphAction
+{
+ FBRequestConnection *connection = [[FBRequestConnection alloc] init];
+
+ // First request uploads the photo.
+ FBRequest *request1 = [FBRequest requestForUploadPhoto:self.selectedPhoto
+ session:self.session];
+ [connection addRequest:request1
+ completionHandler:
+ ^(FBRequestConnection *connection, id result, NSError *error) {
+ if (!error) {
+ }
+ }
+ batchEntryName:@"photopost"
+ ];
+
+ // Second request retrieves photo information for just-created photo so we can grab its source.
+ FBRequest *request2 = [FBRequest requestForGraphPath:@"{result=photopost:$.id}"
+ session:self.session];
+ [connection addRequest:request2
+ completionHandler:
+ ^(FBRequestConnection *connection, id result, NSError *error) {
+ if (!error &&
+ result) {
+ NSString *source = [result objectForKey:@"source"];
+ [self postOpenGraphActionWithPhotoURL:source];
+ }
+ }
+ ];
+
+ [connection start];
+}
+
+- (IBAction)announce:(id)sender
+{
+ if (self.selectedPhoto) {
+ [self postPhotoThenOpenGraphAction];
+ } else {
+ [self postOpenGraphActionWithPhotoURL:nil];
+ }
+}
+
#pragma mark UIImagePickerControllerDelegate methods
- (void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingImage:(UIImage *)image
editingInfo:(NSDictionary *)editingInfo
{
- if (!self.selectedPhoto) {
- self.selectedPhoto = [[NSMutableDictionary alloc] init];
- }
- [self.selectedPhoto setObject:image forKey:@"image"];
+ self.selectedPhoto = image;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
[self.popover dismissPopoverAnimated:YES];
@@ -147,12 +203,12 @@ - (void)updateCellIndex:(int)index withSubtitle:(NSString*)subtitle {
- (void)updateSelections
{
- [self updateCellIndex:0 withSubtitle:(self.selectedPlace ?
- self.selectedPlace.name :
- @"Select One")];
- [self updateCellIndex:1 withSubtitle:(self.selectedMeal ?
+ [self updateCellIndex:0 withSubtitle:(self.selectedMeal ?
self.selectedMeal :
@"Select One")];
+ [self updateCellIndex:1 withSubtitle:(self.selectedPlace ?
+ self.selectedPlace.name :
+ @"Select One")];
NSString* friendsSubtitle = @"Select friends";
int friendCount = self.selectedFriends.count;
@@ -176,16 +232,13 @@ - (void)updateSelections
[self updateCellIndex:3 withSubtitle:(self.selectedPhoto ? @"Ready" : @"Take one")];
- self.announceButton.enabled = self.selectedPlace &&
- self.selectedMeal &&
- (self.selectedFriends.count > 0);
+ self.announceButton.enabled = (self.selectedMeal != nil);
}
- (void)populateUserDetails
{
- SCAppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
- if (appDelegate.session.isValid) {
- FBRequestConnection *requestConnection = [FBRequest connectionWithSession:appDelegate.session
+ if (self.session.isValid) {
+ FBRequestConnection *requestConnection = [FBRequest connectionWithSession:self.session
graphPath:@"me"
completionHandler:
^(FBRequestConnection *connection, NSDictionary<FBGraphUser> *user, NSError *error) {
@@ -231,7 +284,10 @@ -(void)viewWillAppear:(BOOL)animated
SCAppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
if (appDelegate.session && appDelegate.session.isValid) {
- [self populateUserDetails];
+ if (appDelegate.session != self.session) {
+ self.session = appDelegate.session;
+ [self populateUserDetails];
+ }
} else {
[self showLoginView];
}
@@ -282,15 +338,15 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N
switch (indexPath.row) {
case 0:
- cell.textLabel.text = @"Where are you?";
+ cell.textLabel.text = @"What are you eating?";
cell.detailTextLabel.text = @"Select one";
- cell.imageView.image = [UIImage imageNamed:@"house.png"];
+ cell.imageView.image = [UIImage imageNamed:@"food.png"];
break;
case 1:
- cell.textLabel.text = @"What are you eating?";
+ cell.textLabel.text = @"Where are you?";
cell.detailTextLabel.text = @"Select one";
- cell.imageView.image = [UIImage imageNamed:@"food.png"];
+ cell.imageView.image = [UIImage imageNamed:@"house.png"];
break;
case 2:
@@ -324,23 +380,10 @@ - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
- SCAppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
UIViewController* target;
switch (indexPath.row) {
case 0:
- if (!self.placesPickerController) {
- self.placesPickerController = [[FBPlacesPickerViewController alloc] initWithNibName:nil bundle:nil];
- self.placesPickerController.delegate = self;
- self.placesPickerController.title = @"Select a restaurant";
- }
- self.placesPickerController.locationCoordinate = self.locationManager.location.coordinate;
- self.placesPickerController.session = appDelegate.session;
- [self.placesPickerController loadData];
- target = self.placesPickerController;
- break;
-
- case 1:
if (!self.mealViewController) {
__block SCViewController* myself = self;
self.mealViewController = [[SCMealViewController alloc]initWithNibName:@"SCMealViewController" bundle:nil];
@@ -351,14 +394,26 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath
}
target = self.mealViewController;
break;
+
+ case 1:
+ if (!self.placesPickerController) {
+ self.placesPickerController = [[FBPlacesPickerViewController alloc] initWithNibName:nil bundle:nil];
+ self.placesPickerController.delegate = self;
+ self.placesPickerController.title = @"Select a restaurant";
+ }
+ self.placesPickerController.locationCoordinate = self.locationManager.location.coordinate;
+ self.placesPickerController.session = self.session;
+ [self.placesPickerController loadData];
+ target = self.placesPickerController;
+ break;
case 2:
if (!self.friendPickerController) {
self.friendPickerController = [[FBFriendPickerViewController alloc] initWithNibName:nil bundle:nil];
self.friendPickerController.delegate = self;
self.friendPickerController.title = @"Select friends";
}
- self.friendPickerController.session = appDelegate.session;
+ self.friendPickerController.session = self.session;
[self.friendPickerController loadData];
target = self.friendPickerController;
break;
@@ -405,13 +460,9 @@ - (void)placesPickerViewControllerSelectionDidChange:
{
self.selectedPlace = placesPicker.selection;
[self updateSelections];
-}
-
-#pragma mark LoginViewControllerDelegate methods
-
-- (void)loginDidSucceed:(FBSession*)session {
- [self dismissModalViewControllerAnimated:true];
- [self populateUserDetails];
+ if (self.selectedPlace.count > 0) {
+ [self.navigationController popViewControllerAnimated:true];
+ }
}
#pragma mark CLLocationManagerDelegate methods
View
@@ -28,7 +28,7 @@ fi
#
# Certain subdirs of samples are not samples to be built, exclude them from the find query
-FB_SAMPLES_EXCLUDED=(FBConnect.bundle Scrumptious)
+FB_SAMPLES_EXCLUDED=(FBConnect.bundle)
for excluded in "${FB_SAMPLES_EXCLUDED[@]}"; do
if [ -n "$FB_FIND_ARGS" ]; then
FB_FIND_ARGS="$FB_FIND_ARGS -o"
View
@@ -55,13 +55,17 @@
@interface FBGraphObjectArray : NSMutableArray
- (id)initWrappingArray:(NSArray *)otherArray;
+- (id)graphObjectifyAtIndex:(NSUInteger)index;
+- (void)graphObjectifyAll;
@end
@interface FBGraphObject ()
- (id)initWrappingDictionary:(NSDictionary *)otherDictionary;
+- (void)graphObjectifyAll;
+- (id)graphObjectifyAtKey:(id)key;
+ (id)graphObjectWrappingObject:(id)originalObject;
+ (SelectorInferredImplType)inferredImplTypeForSelector:(SEL)sel;
@@ -199,15 +203,7 @@ - (void)forwardInvocation:(NSInvocation *)invocation {
}
}
-#pragma mark -
-
-#pragma mark NSDictionary and NSMutableDictionary overrides
-
-- (NSUInteger)count {
- return _jsonObject.count;
-}
-
-- (id)objectForKey:(id)key {
+- (id)graphObjectifyAtKey:(id)key {
id object = [_jsonObject objectForKey:key];
// make certain it is FBObjectGraph-ified
id possibleReplacement = [FBGraphObject graphObjectWrappingObject:object];
@@ -219,7 +215,28 @@ - (id)objectForKey:(id)key {
return object;
}
+- (void)graphObjectifyAll {
+ NSArray *keys = [_jsonObject allKeys];
+ for (NSString *key in keys) {
+ [self graphObjectifyAtKey:key];
+ }
+}
+
+
+#pragma mark -
+
+#pragma mark NSDictionary and NSMutableDictionary overrides
+
+- (NSUInteger)count {
+ return _jsonObject.count;
+}
+
+- (id)objectForKey:(id)key {
+ return [self graphObjectifyAtKey:key];
+}
+
- (NSEnumerator *)keyEnumerator {
+ [self graphObjectifyAll];
return _jsonObject.keyEnumerator;
}
@@ -376,7 +393,7 @@ - (NSUInteger)count {
return _jsonArray.count;
}
-- (id)objectAtIndex:(NSUInteger)index {
+- (id)graphObjectifyAtIndex:(NSUInteger)index {
id object = [_jsonArray objectAtIndex:index];
// make certain it is FBObjectGraph-ified
id possibleReplacement = [FBGraphObject graphObjectWrappingObject:object];
@@ -388,6 +405,27 @@ - (id)objectAtIndex:(NSUInteger)index {
return object;
}
+- (void)graphObjectifyAll {
+ int count = [_jsonArray count];
+ for (int i = 0; i < count; ++i) {
+ [self graphObjectifyAtIndex:i];
+ }
+}
+
+- (id)objectAtIndex:(NSUInteger)index {
+ return [self graphObjectifyAtIndex:index];
+}
+
+- (NSEnumerator *)objectEnumerator {
+ [self graphObjectifyAll];
+ return _jsonArray.objectEnumerator;
+}
+
+- (NSEnumerator *)reverseObjectEnumerator {
+ [self graphObjectifyAll];
+ return _jsonArray.reverseObjectEnumerator;
+}
+
- (void)insertObject:(id)object atIndex:(NSUInteger)index {
[_jsonArray insertObject:object atIndex:index];
}
Oops, something went wrong.

0 comments on commit ef24c3d

Please sign in to comment.