Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Adds caching support for friend picker, and scrumptious

Summary:
Adds API and internal support for caching of the friend picker. This includes
addition of two public classes: FBCacheDescriptor, and FBFriendPickerCacheDescriptor.
Also included is a place-holder class (not yet public) for FBPlacePickerCacheDescriptor.

Test Plan: Added unit tests, ran them

Reviewers: clang, mmarucheck

Reviewed By: clang

CC: gregschechte, platform-diffs@lists, yariv

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

Task ID: 1093164
  • Loading branch information...
commit c01c7586f552abb66acb3dff9ec1a27355173c21 1 parent f2c6903
@onebit onebit authored
Showing with 714 additions and 48 deletions.
  1. +0 −2  samples/Scrumptious/{scrumptious.xcodeproj → Scrumptious.xcodeproj}/project.pbxproj
  2. +5 −0 samples/Scrumptious/scrumptious/SCAppDelegate.m
  3. +1 −1  samples/Scrumptious/scrumptious/scrumptious-Prefix.pch
  4. +40 −0 src/FBCacheDescriptor.h
  5. +26 −0 src/FBCacheDescriptor.m
  6. +91 −0 src/FBFriendPickerCacheDescriptor.h
  7. +135 −0 src/FBFriendPickerCacheDescriptor.m
  8. +33 −0 src/FBFriendPickerViewController+Internal.h
  9. +41 −0 src/FBFriendPickerViewController.h
  10. +98 −17 src/FBFriendPickerViewController.m
  11. +7 −1 src/FBGraphObjectPagingLoader.h
  12. +42 −16 src/FBGraphObjectPagingLoader.m
  13. +2 −0  src/FBGraphObjectTableCell.m
  14. +7 −3 src/FBGraphObjectTableDataSource.m
  15. +22 −0 src/FBPlacePickerCacheDescriptor.h
  16. +21 −0 src/FBPlacePickerCacheDescriptor.m
  17. +8 −3 src/FBPlacePickerViewController.m
  18. +2 −0  src/FBRequestConnection+Internal.h
  19. +5 −0 src/FBRequestConnection.m
  20. +2 −1  src/FacebookSDK.h
  21. +41 −1 src/facebook-ios-sdk.xcodeproj/project.pbxproj
  22. +2 −1  src/tests/FBCacheTests.h
  23. +59 −0 src/tests/FBCacheTests.m
  24. +3 −1 src/tests/FBRequestConnectionTests.m
  25. +7 −0 src/tests/FBTestBlocker.h
  26. +14 −1 src/tests/FBTestBlocker.m
View
2  ...ous/scrumptious.xcodeproj/project.pbxproj → ...ous/Scrumptious.xcodeproj/project.pbxproj
@@ -46,7 +46,6 @@
856DC2191577F105001FA6A6 /* Icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon@2x.png"; sourceTree = "<group>"; };
8592BE3915816CBF00056680 /* AddressBook.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AddressBook.framework; path = System/Library/Frameworks/AddressBook.framework; sourceTree = SDKROOT; };
8592BE3C15816CCC00056680 /* AddressBook.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AddressBook.framework; path = System/Library/Frameworks/AddressBook.framework; sourceTree = SDKROOT; };
- 8592BE3E158173A000056680 /* Scrumptious.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = Scrumptious.entitlements; sourceTree = "<group>"; };
85954AE1155889A200FABA9A /* SCProtocols.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SCProtocols.h; sourceTree = "<group>"; };
B9C1C35815129F0E008FA5D1 /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = System/Library/Frameworks/CoreLocation.framework; sourceTree = SDKROOT; };
B9C1C35A1512A637008FA5D1 /* SCMealViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCMealViewController.h; sourceTree = "<group>"; };
@@ -135,7 +134,6 @@
B9CD0A79150EA4DD00560A93 /* Scrumptious */ = {
isa = PBXGroup;
children = (
- 8592BE3E158173A000056680 /* Scrumptious.entitlements */,
B9CD0A98150EBFBD00560A93 /* images */,
B9CD0A7A150EA4DD00560A93 /* Supporting Files */,
B9CD0A82150EA4DD00560A93 /* SCAppDelegate.h */,
View
5 samples/Scrumptious/scrumptious/SCAppDelegate.m
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#import <FBiOSSDK/FacebookSDK.h>
#import "SCAppDelegate.h"
#import "SCViewController.h"
#import "SCLoginViewController.h"
@@ -71,6 +72,10 @@ - (void)sessionStateChanged:(FBSession *)session
if ([[topViewController modalViewController] isKindOfClass:[SCLoginViewController class]]) {
[topViewController dismissModalViewControllerAnimated:YES];
}
+
+ // fetch and cache the friends for the friend picker as soon as possible
+ FBCacheDescriptor *cacheDescriptor = [FBFriendPickerViewController cacheDescriptor];
+ [cacheDescriptor prefetchAndCacheForSession:session];
}
break;
case FBSessionStateClosed:
View
2  samples/Scrumptious/scrumptious/scrumptious-Prefix.pch
@@ -15,7 +15,7 @@
*/
//
-// Prefix header for all source files of the 'scrumptious' target in the 'scrumptious' project
+// Prefix header for all source files of the 'Scrumptious' target in the 'Scrumptious' project
//
#ifdef __OBJC__
View
40 src/FBCacheDescriptor.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2012 Facebook
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import <Foundation/Foundation.h>
+#import "FBSession.h"
+
+/*!
+ @class
+
+ @abstract
+ Base class from which CacheDescriptors derive, provides a method to fetch data for later use
+
+ @discussion
+ Cache descriptors allow your application to specify the arguments that will be
+ later used with another object, such as the FBFriendPickerViewController. By using a cache descriptor
+ instance, an application can choose to fetch data ahead of the point in time where the data is needed.
+ */
+@interface FBCacheDescriptor : NSObject
+
+/*!
+ @method
+ @abstract
+ Fetches and caches the data described by the cache descriptor instance, for the given session.
+ */
+- (void)prefetchAndCacheForSession:(FBSession*)session;
+
+@end
View
26 src/FBCacheDescriptor.m
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2012 Facebook
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import "FBCacheDescriptor.h"
+
+@implementation FBCacheDescriptor
+
+- (void)prefetchAndCacheForSession:(FBSession *)session {
+ // we are treating this method as abstract virtual here
+ [self doesNotRecognizeSelector:_cmd];
+}
+
+@end
View
91 src/FBFriendPickerCacheDescriptor.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2012 Facebook
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import <Foundation/Foundation.h>
+#import "FBCacheDescriptor.h"
+
+/*
+ @class
+
+ @abstract
+ Represents the data needed by an FBFriendPickerViewController, in order to construct
+ the necessary request to populate the view; instances of FBFriendPickerCacheDescriptor
+ are used to fetch data ahead of the point when the data is used to populate a display.
+
+ @discussion
+ A common use of an FBFriendPickerCacheDescriptor instance, is to allocate an instance
+ at the point when a session is opened, and then call prefetchAndCacheForSession. This
+ causes the API to fetch and cache the data needed by the FBFriendPickerViewController.
+ If at some point the user goes to select friends, the FBFriendPickerViewController
+ will first check the cache for a copy of the friends list, and then after displaying
+ whatever cached data is available, then it will fetch a fresh copy of the friends list.
+
+ @unsorted
+ */
+@interface FBFriendPickerCacheDescriptor : FBCacheDescriptor
+
+/*
+ @method
+
+ @abstract
+ Initializes an instance with default values for populating
+ a FBFriendPickerViewController, at some later point.
+*/
+- (id)init;
+
+/*
+ @method
+
+ @abstract
+ Initializes an instance specifying the userID to use for populating
+ a FBFriendPickerViewController, at some later point.
+*/
+- (id)initWithUserID:(NSString*)userID;
+
+/*
+ @method
+
+ @abstract
+ Initializes an instance specifying the fields to use for populating
+ a FBFriendPickerViewController, at some later point.
+*/
+- (id)initWithFieldsForRequest:(NSSet*)fieldsForRequest;
+
+/*
+ @method
+
+ @abstract
+ Initializes an instance specifying the userID and fields to use for populating
+ a FBFriendPickerViewController, at some later point.
+
+ @param userID fbid of the user whose friends we wish to display; nil='me'
+ @param fieldsForRequest set of additional fields to include in request for friends
+ */
+- (id)initWithUserID:(NSString*)userID fieldsForRequest:(NSSet*)fieldsForRequest;
+
+/*
+ @abstract
+ Fields to use when fetching data for the view
+ */
+@property (nonatomic, readonly, copy) NSSet *fieldsForRequest;
+
+/*
+ @abstract
+ Indicates the fbid of the user whose friends are being viewed
+ */
+@property (nonatomic, readonly, copy) NSString *userID;
+
+@end
View
135 src/FBFriendPickerCacheDescriptor.m
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2012 Facebook
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import "FBFriendPickerCacheDescriptor.h"
+#import "FBFriendPickerViewController+Internal.h"
+#import "FBSession.h"
+#import "FBRequest.h"
+#import "FBRequestConnection.h"
+#import "FBGraphObjectTableDataSource.h"
+#import "FBGraphObjectPagingLoader.h"
+
+@interface FBFriendPickerCacheDescriptor () <FBGraphObjectPagingLoaderDelegate>
+
+@property (nonatomic, readwrite, copy) NSSet *fieldsForRequest;
+@property (nonatomic, readwrite, copy) NSString *userID;
+@property (nonatomic, readwrite, retain) FBGraphObjectPagingLoader *loader;
+
+// these properties are only used by unit tests, and should not be removed or made public
+@property (nonatomic, readwrite, assign) BOOL hasCompletedFetch;
+@property (nonatomic, readwrite, assign) BOOL usePageLimitOfOne;
+- (void)setUsePageLimitOfOne;
+
+@end
+
+@implementation FBFriendPickerCacheDescriptor
+
+@synthesize fieldsForRequest = _fieldsForRequest,
+ userID = _userID,
+ loader = _loader,
+ hasCompletedFetch = _hasCompletedFetch,
+ usePageLimitOfOne = _usePageLimitOfOne;
+
+- (id)init {
+ return [self initWithUserID:nil
+ fieldsForRequest:nil];
+}
+
+- (id)initWithUserID:(NSString*)userID {
+ return [self initWithUserID:userID
+ fieldsForRequest:nil];
+}
+
+- (id)initWithFieldsForRequest:(NSSet*)fieldsForRequest {
+ return [self initWithUserID:nil
+ fieldsForRequest:fieldsForRequest];
+}
+
+- (id)initWithUserID:(NSString*)userID fieldsForRequest:(NSSet*)fieldsForRequest {
+ self = [super init];
+ if (self) {
+ self.fieldsForRequest = fieldsForRequest ? fieldsForRequest : [NSSet set];
+ self.userID = userID;
+ self.hasCompletedFetch = NO;
+ self.usePageLimitOfOne = NO;
+ }
+ return self;
+}
+
+- (void)dealloc {
+ self.fieldsForRequest = nil;
+ self.userID = nil;
+ self.loader = nil;
+ [super dealloc];
+}
+
+- (void)prefetchAndCacheForSession:(FBSession*)session {
+ // datasource has some field ownership, so we need one here
+ FBGraphObjectTableDataSource *datasource = [[FBGraphObjectTableDataSource alloc]
+ init];
+ datasource.groupByField = @"name";
+
+ // me or one of my friends that also uses the app
+ NSString *user = self.userID;
+ if (!user) {
+ user = @"me";
+ }
+
+ // create the request object that we will start with
+ FBRequest *request = [FBFriendPickerViewController requestWithUserID:user
+ fields:self.fieldsForRequest
+ dataSource:datasource
+ session:session];
+
+ // this property supports unit testing
+ if(self.usePageLimitOfOne) {
+ [request.parameters setObject:@"1"
+ forKey:@"limit"];
+ }
+
+ self.loader.delegate = nil;
+ self.loader = [[FBGraphObjectPagingLoader alloc] initWithDataSource:datasource];
+ self.loader.session = session;
+ [self.loader release];
+
+ self.loader.delegate = self;
+ self.loader.pagingMode = FBGraphObjectPagingModeImmediateViewless;
+
+ // make sure we are around to handle the delegate call
+ [self retain];
+
+ // seed the cache
+ [self.loader startLoadingWithRequest:request
+ cacheIdentity:FBFriendPickerCacheIdentity
+ skipRoundtripIfCached:NO];
+
+ [datasource release];
+}
+
+- (void)setUsePageLimitOfOne {
+ self.usePageLimitOfOne = YES;
+}
+
+- (void)pagingLoaderDidFinishLoading:(FBGraphObjectPagingLoader *)pagingLoader {
+ self.loader.delegate = nil;
+ self.loader = nil;
+ self.hasCompletedFetch = YES;
+
+ // this feels like suicide!
+ [self release];
+}
+
+@end
View
33 src/FBFriendPickerViewController+Internal.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2012 Facebook
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import <Foundation/Foundation.h>
+#import "FBFriendPickerViewController.h"
+#import "FBRequest.h"
+#import "FBGraphObjectTableDataSource.h"
+#import "FBSession.h"
+
+// This is the cache identity used by both the view controller and cache descriptor objects
+extern NSString *const FBFriendPickerCacheIdentity;
+
+@interface FBFriendPickerViewController (Internal)
+
++ (FBRequest*)requestWithUserID:(NSString*)userID
+ fields:(NSSet*)fields
+ dataSource:(FBGraphObjectTableDataSource*)datasource
+ session:(FBSession*)session;
+
+@end
View
41 src/FBFriendPickerViewController.h
@@ -17,8 +17,10 @@
#import <UIKit/UIKit.h>
#import "FBGraphUser.h"
#import "FBSession.h"
+#import "FBCacheDescriptor.h"
@protocol FBFriendPickerDelegate;
+@class FBFriendPickerCacheDescriptor;
/*!
@typedef FBFriendSortOrdering enum
@@ -145,6 +147,17 @@ typedef enum {
/*!
@abstract
+ Configures the properties that impact any queries made by the view controller, using a cacheDescriptor
+
+ @discussion
+ Cache descriptors are used to fetch and cache the data used by the ViewController, at some point
+ prior in the execution of the application. If the ViewController finds a cached copy of the data, it will
+ first display the cached content, and then fetch a fresh copy from the server.
+ */
+- (void)configureUsingCachedDescriptor:(FBCacheDescriptor*)cacheDescriptor;
+
+/*!
+ @abstract
Starts a query against the server for a set of friends. It is legal
to call this more than once.
*/
@@ -160,6 +173,34 @@ typedef enum {
*/
- (void)updateView;
+/*!
+ @method
+
+ @abstract
+ Creates a cache descriptor based on default settings of FBFriendPickerViewController
+
+ @discussion
+ A cacheDescriptor object may be used to fetch data ahead of use by the FBFriendPickerViewController, and
+ may also be used to configure a FBFriendPickerViewController object at time of use
+ */
++ (FBCacheDescriptor*)cacheDescriptor;
+
+/*!
+ @method
+
+ @param userID fbid of the user whose friends we wish to display; nil='me'
+ @param fieldsForRequest set of additional fields to include in request for friends
+
+ @abstract
+ Creates a cache descriptor with additional fields and a userID for use with FBFriendPickerViewController
+
+ @discussion
+ A cacheDescriptor object may be used to fetch data ahead of use by the FBFriendPickerViewController, and
+ may also be used to configure a FBFriendPickerViewController object at time of use
+
+ */
++ (FBCacheDescriptor*)cacheDescriptorWithUserID:(NSString*)userID fieldsForRequest:(NSSet*)fieldsForRequest;
+
@end
/*!
View
115 src/FBFriendPickerViewController.m
@@ -16,6 +16,8 @@
#import "FBError.h"
#import "FBFriendPickerViewController.h"
+#import "FBFriendPickerViewController+Internal.h"
+#import "FBFriendPickerCacheDescriptor.h"
#import "FBGraphObjectPagingLoader.h"
#import "FBGraphObjectTableDataSource.h"
#import "FBGraphObjectTableSelection.h"
@@ -25,8 +27,10 @@
#import "FBRequestConnection.h"
#import "FBUtility.h"
-static NSString *defaultImageName =
-@"FBiOSSDKResources.bundle/FBFriendPickerView/images/default.png";
+NSString *const FBFriendPickerCacheIdentity = @"FBFriendPicker";
+static NSString *defaultImageName = @"FBiOSSDKResources.bundle/FBFriendPickerView/images/default.png";
+
+int const FBRefreshCacheDelaySeconds = 2;
@interface FBFriendPickerViewController () <FBFriendPickerDelegate,
FBGraphObjectSelectionChangedDelegate,
@@ -39,6 +43,8 @@ @interface FBFriendPickerViewController () <FBFriendPickerDelegate,
- (void)initialize;
- (void)centerAndStartSpinner;
+- (void)loadDataSkippingRoundTripIfCached:(NSNumber*)skipRoundTripIfCached;
+- (FBRequest*)requestForLoadData;
@end
@@ -227,8 +233,47 @@ - (void)viewDidUnload
self.tableView = nil;
}
-- (void)loadData
+- (void)configureUsingCachedDescriptor:(FBCacheDescriptor*)cacheDescriptor {
+ if (![cacheDescriptor isKindOfClass:[FBFriendPickerCacheDescriptor class]]) {
+ [[NSException exceptionWithName:FBInvalidOperationException
+ reason:@"FBFriendPickerViewController: An attempt was made to configure "
+ @"an instance with a cache descriptor object that was not created "
+ @"by the FBFriendPickerViewController class"
+ userInfo:nil]
+ raise];
+ }
+ FBFriendPickerCacheDescriptor *cd = (FBFriendPickerCacheDescriptor*)cacheDescriptor;
+ self.userID = cd.userID;
+ self.fieldsForRequest = cd.fieldsForRequest;
+}
+
+- (void)loadData {
+ [self loadDataSkippingRoundTripIfCached:[NSNumber numberWithBool:YES]];
+}
+
+- (void)updateView
{
+ [self.dataSource update];
+ [self.tableView reloadData];
+}
+
+#pragma mark - public class members
+
++ (FBCacheDescriptor*)cacheDescriptor {
+ return [[[FBFriendPickerCacheDescriptor alloc] init] autorelease];
+}
+
++ (FBCacheDescriptor*)cacheDescriptorWithUserID:(NSString*)userID fieldsForRequest:(NSSet*)fieldsForRequest {
+ return [[[FBFriendPickerCacheDescriptor alloc] initWithUserID:userID
+ fieldsForRequest:fieldsForRequest]
+ autorelease];
+}
+
+
+#pragma mark - private members
+
+- (FBRequest*)requestForLoadData {
+
// Respect user settings in case they have changed.
NSMutableArray *sortFields = [NSMutableArray array];
NSString *groupByField = nil;
@@ -245,20 +290,46 @@ - (void)loadData
}
[self.dataSource setSortingByFields:sortFields ascending:YES];
self.dataSource.groupByField = groupByField;
-
- FBRequest *request = [FBRequest requestForMyFriendsWithSession:self.session];
-
- NSString *fields = [self.dataSource fieldsForRequestIncluding:self.fieldsForRequest,
- @"id", @"name", @"first_name", @"middle_name", @"last_name", @"picture", nil];
- [request.parameters setObject:fields forKey:@"fields"];
- [self.loader startLoadingWithRequest:request];
+ // me or one of my friends that also uses the app
+ NSString *user = self.userID;
+ if (!user) {
+ user = @"me";
+ }
+
+ // create the request and start the loader
+ FBRequest *request = [FBFriendPickerViewController requestWithUserID:user
+ fields:self.fieldsForRequest
+ dataSource:self.dataSource
+ session:self.session];
+ return request;
}
-- (void)updateView
-{
- [self.dataSource update];
- [self.tableView reloadData];
+- (void)loadDataSkippingRoundTripIfCached:(NSNumber*)skipRoundTripIfCached {
+ [self.loader startLoadingWithRequest:[self requestForLoadData]
+ cacheIdentity:FBFriendPickerCacheIdentity
+ skipRoundtripIfCached:skipRoundTripIfCached.boolValue];
+}
+
++ (FBRequest*)requestWithUserID:(NSString*)userID
+ fields:(NSSet*)fields
+ dataSource:(FBGraphObjectTableDataSource*)datasource
+ session:(FBSession*)session {
+
+ FBRequest *request = [FBRequest requestForGraphPath:[NSString stringWithFormat:@"%@/friends", userID]
+ session:session];
+
+ NSString *allFields = [datasource fieldsForRequestIncluding:fields,
+ @"id",
+ @"name",
+ @"first_name",
+ @"middle_name",
+ @"last_name",
+ @"picture",
+ nil];
+ [request.parameters setObject:allFields forKey:@"fields"];
+
+ return request;
}
- (void)centerAndStartSpinner
@@ -348,21 +419,31 @@ - (void)pagingLoader:(FBGraphObjectPagingLoader*)pagingLoader willLoadURL:(NSStr
}
- (void)pagingLoader:(FBGraphObjectPagingLoader*)pagingLoader didLoadData:(NSDictionary*)results {
- [self.spinner stopAnimating];
-
// This logging currently goes here because we're effectively complete with our initial view when
// the first page of results come back. In the future, when we do caching, we will need to move
// this to a more appropriate place (e.g., after the cache has been brought in).
[FBLogger singleShotLogEntry:FBLogBehaviorPerformanceCharacteristics
timestampTag:self
formatString:@"Friend Picker: first render "]; // logger will append "%d msec"
-
+
if ([self.delegate respondsToSelector:@selector(friendPickerViewControllerDataDidChange:)]) {
[self.delegate friendPickerViewControllerDataDidChange:self];
}
}
+- (void)pagingLoaderDidFinishLoading:(FBGraphObjectPagingLoader *)pagingLoader {
+ // finished loading, stop animating
+ [self.spinner stopAnimating];
+
+ // if our current display is from cache, then kick-off a near-term refresh
+ if (pagingLoader.isResultFromCache) {
+ [self performSelector:@selector(loadDataSkippingRoundTripIfCached:)
+ withObject:[NSNumber numberWithBool:NO]
+ afterDelay:FBRefreshCacheDelaySeconds];
+ }
+}
+
- (void)pagingLoader:(FBGraphObjectPagingLoader*)pagingLoader handleError:(NSError*)error {
if ([self.delegate
respondsToSelector:@selector(friendPickerViewController:handleError:)]) {
View
8 src/FBGraphObjectPagingLoader.h
@@ -24,6 +24,8 @@
typedef enum {
// Paging links will be followed as soon as one set of results is loaded
FBGraphObjectPagingModeImmediate,
+ // Paging links will be followed as soon as one set of results is loaded, even without a view
+ FBGraphObjectPagingModeImmediateViewless,
// Paging links will be followed only when the user scrolls to the bottom of the table
FBGraphObjectPagingModeAsNeeded
} FBGraphObjectPagingMode;
@@ -35,9 +37,12 @@ typedef enum {
@property (nonatomic, retain) FBSession *session;
@property (nonatomic, assign) id<FBGraphObjectPagingLoaderDelegate> delegate;
@property (nonatomic) FBGraphObjectPagingMode pagingMode;
+@property (nonatomic, readonly) BOOL isResultFromCache;
- (id)initWithDataSource:(FBGraphObjectTableDataSource*)aDataSource;
-- (void)startLoadingWithRequest:(FBRequest*)request;
+- (void)startLoadingWithRequest:(FBRequest*)request
+ cacheIdentity:(NSString*)cacheIdentity
+ skipRoundtripIfCached:(BOOL)skipRoundtripIfCached;
- (void)addResultsAndUpdateView:(NSDictionary*)results;
- (void)cancel;
@@ -49,6 +54,7 @@ typedef enum {
- (void)pagingLoader:(FBGraphObjectPagingLoader*)pagingLoader willLoadURL:(NSString*)url;
- (void)pagingLoader:(FBGraphObjectPagingLoader*)pagingLoader didLoadData:(NSDictionary*)results;
+- (void)pagingLoaderDidFinishLoading:(FBGraphObjectPagingLoader*)pagingLoader;
- (void)pagingLoader:(FBGraphObjectPagingLoader*)pagingLoader handleError:(NSError*)error;
- (void)pagingLoaderWasCancelled:(FBGraphObjectPagingLoader*)pagingLoader;
View
58 src/FBGraphObjectPagingLoader.m
@@ -17,11 +17,14 @@
#import "FBGraphObjectPagingLoader.h"
#import "FBRequest.h"
#import "FBError.h"
+#import "FBRequestConnection+Internal.h"
@interface FBGraphObjectPagingLoader ()
@property (nonatomic, retain) NSString *nextLink;
@property (nonatomic, retain) FBRequestConnection *connection;
+@property (nonatomic, copy) NSString *cacheIdentity;
+@property (nonatomic, assign) BOOL skipRoundtripIfCached;
- (void)followNextLink;
- (void)requestCompleted:(FBRequestConnection *)connection
@@ -40,6 +43,9 @@ @implementation FBGraphObjectPagingLoader
@synthesize session = _session;
@synthesize connection = _connection;
@synthesize delegate = _delegate;
+@synthesize isResultFromCache = _isResultFromCache;
+@synthesize cacheIdentity = _cacheIdentity;
+@synthesize skipRoundtripIfCached = _skipRoundtripIfCached;
#pragma mark Lifecycle methods
@@ -47,6 +53,7 @@ - (id)initWithDataSource:(FBGraphObjectTableDataSource*)aDataSource {
if (self = [super init]) {
self.dataSource = aDataSource;
self.pagingMode = FBGraphObjectPagingModeAsNeeded;
+ _isResultFromCache = NO;
}
return self;
}
@@ -57,6 +64,7 @@ - (void)dealloc {
[_nextLink release];
[_session release];
[_connection release];
+ [_cacheIdentity release];
[super dealloc];
}
@@ -100,8 +108,9 @@ - (void)addResultsAndUpdateView:(NSDictionary*)results {
[self.dataSource appendGraphObjects:nil];
[self updateView];
- if ([self.delegate respondsToSelector:@selector(pagingLoader:didLoadData:)]) {
- [self.delegate pagingLoader:self didLoadData:results];
+ // notify of completion
+ if ([self.delegate respondsToSelector:@selector(pagingLoaderDidFinishLoading:)]) {
+ [self.delegate pagingLoaderDidFinishLoading:self];
}
return;
} else {
@@ -155,11 +164,12 @@ - (void)addResultsAndUpdateView:(NSDictionary*)results {
[self.delegate pagingLoader:self didLoadData:results];
}
- // If we are supposed to keep paging, do so. But if we have lost our tableView,
- // take that as a sign to stop (probably because the view was unloaded). If
- // tableView is re-set, we will start again.
- if (self.pagingMode == FBGraphObjectPagingModeImmediate &&
- self.tableView) {
+ // If we are supposed to keep paging, do so. But unless we are viewless, if we have lost
+ // our tableView, take that as a sign to stop (probably because the view was unloaded).
+ // If tableView is re-set, we will start again.
+ if ((self.pagingMode == FBGraphObjectPagingModeImmediate &&
+ self.tableView) ||
+ self.pagingMode == FBGraphObjectPagingModeImmediateViewless) {
[self followNextLink];
}
}
@@ -180,9 +190,10 @@ - (void)followNextLink {
FBRequestConnection *connection = [[FBRequestConnection alloc] init];
[connection addRequest:request completionHandler:
^(FBRequestConnection *connection, id result, NSError *error) {
- self.connection = nil;
- [self requestCompleted:connection result:result error:error];
- }];
+ _isResultFromCache = _isResultFromCache || connection.isResultFromCache;
+ self.connection = nil;
+ [self requestCompleted:connection result:result error:error];
+ }];
// Override the URL using the one passed back in 'next'.
NSURL *url = [NSURL URLWithString:self.nextLink];
@@ -192,22 +203,37 @@ - (void)followNextLink {
self.nextLink = nil;
self.connection = connection;
- [self.connection start];
+ [self.connection startWithCacheIdentity:self.cacheIdentity
+ skipRoundtripIfCached:self.skipRoundtripIfCached];
[request release];
[connection release];
}
}
-- (void)startLoadingWithRequest:(FBRequest*)request {
+- (void)startLoadingWithRequest:(FBRequest*)request
+ cacheIdentity:(NSString*)cacheIdentity
+ skipRoundtripIfCached:(BOOL)skipRoundtripIfCached {
[self.dataSource clearGraphObjects];
[self.connection cancel];
+ _isResultFromCache = NO;
+
+ self.cacheIdentity = cacheIdentity;
+ self.skipRoundtripIfCached = skipRoundtripIfCached;
+
+ FBRequestConnection *connection = [[FBRequestConnection alloc] init];
+ [connection addRequest:request
+ completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
+ _isResultFromCache = _isResultFromCache || connection.isResultFromCache;
+ [self requestCompleted:connection result:result error:error];
+ }];
+
+ self.connection = connection;
+ [self.connection startWithCacheIdentity:self.cacheIdentity
+ skipRoundtripIfCached:self.skipRoundtripIfCached];
- self.connection = [request startWithCompletionHandler:
- ^(FBRequestConnection *connection, id result, NSError *error) {
- [self requestCompleted:connection result:result error:error];
- }];
+ [connection release];
NSString *urlString = [[[self.connection urlRequest] URL] absoluteString];
if ([self.delegate respondsToSelector:@selector(pagingLoader:willLoadURL:)]) {
View
2  src/FBGraphObjectTableCell.m
@@ -33,6 +33,8 @@ @interface FBGraphObjectTableCell()
@property (nonatomic, retain) UILabel* titleSuffixLabel;
@property (nonatomic, retain) UIActivityIndicatorView *activityIndicator;
+- (void)updateFonts;
+
@end
@implementation FBGraphObjectTableCell
View
10 src/FBGraphObjectTableDataSource.m
@@ -104,19 +104,23 @@ - (NSString *)fieldsForRequestIncluding:(NSSet *)customFields, ...
if (self.groupByField) {
[nameSet addObject:self.groupByField];
}
+
+ // get a stable order for our fields, because we use the resulting URL as a cache ID
+ NSMutableArray *sortedFields = [[nameSet allObjects] mutableCopy];
+ [sortedFields sortUsingSelector:@selector(caseInsensitiveCompare:)];
+
+ [nameSet release];
// Build the comma-separated string
NSMutableString *fields = [[[NSMutableString alloc] init] autorelease];
- for (NSString *field in nameSet) {
+ for (NSString *field in sortedFields) {
if ([fields length]) {
[fields appendString:@","];
}
[fields appendString:field];
}
- [nameSet release];
-
return fields;
}
View
22 src/FBPlacePickerCacheDescriptor.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2012 Facebook
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import <Foundation/Foundation.h>
+#import "FBCacheDescriptor.h"
+
+@interface FBPlacePickerCacheDescriptor : FBCacheDescriptor
+
+@end
View
21 src/FBPlacePickerCacheDescriptor.m
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2012 Facebook
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import "FBPlacePickerCacheDescriptor.h"
+
+@implementation FBPlacePickerCacheDescriptor
+
+@end
View
11 src/FBPlacePickerViewController.m
@@ -253,7 +253,9 @@ - (void)loadDataPostThrottle
[request.parameters setObject:fields forKey:@"fields"];
_hasSearchTextChangedSinceLastQuery = NO;
- [self.loader startLoadingWithRequest:request];
+ [self.loader startLoadingWithRequest:request
+ cacheIdentity:nil
+ skipRoundtripIfCached:NO];
[self updateView];
}
@@ -352,8 +354,6 @@ - (void)pagingLoader:(FBGraphObjectPagingLoader*)pagingLoader willLoadURL:(NSStr
}
- (void)pagingLoader:(FBGraphObjectPagingLoader*)pagingLoader didLoadData:(NSDictionary*)results {
- [self.spinner stopAnimating];
-
// This logging currently goes here because we're effectively complete with our initial view when
// the first page of results come back. In the future, when we do caching, we will need to move
// this to a more appropriate place (e.g., after the cache has been brought in).
@@ -366,6 +366,11 @@ - (void)pagingLoader:(FBGraphObjectPagingLoader*)pagingLoader didLoadData:(NSDic
}
}
+- (void)pagingLoaderDidFinishLoading:(FBGraphObjectPagingLoader *)pagingLoader {
+ // finished loading, stop spinner
+ [self.spinner stopAnimating];
+}
+
- (void)pagingLoader:(FBGraphObjectPagingLoader*)pagingLoader handleError:(NSError*)error {
if ([self.delegate respondsToSelector:@selector(placePickerViewController:handleError:)]) {
[self.delegate placePickerViewController:self handleError:error];
View
2  src/FBRequestConnection+Internal.h
@@ -18,6 +18,8 @@
@interface FBRequestConnection (Internal)
+@property (nonatomic, readonly) BOOL isResultFromCache;
+
- (void)startWithCacheIdentity:(NSString*)cacheIdentity
skipRoundtripIfCached:(BOOL)consultCache;
View
5 src/FBRequestConnection.m
@@ -126,6 +126,7 @@ @interface FBRequestConnection ()
@property (nonatomic, retain) FBRequest *deprecatedRequest;
@property (nonatomic, retain) FBLogger *logger;
@property (nonatomic) unsigned long requestStartTime;
+@property (nonatomic, readonly) BOOL isResultFromCache;
- (NSMutableURLRequest *)requestWithBatch:(NSArray *)requests
timeout:(NSTimeInterval)timeout;
@@ -220,6 +221,7 @@ @implementation FBRequestConnection
@synthesize deprecatedRequest = _deprecatedRequest;
@synthesize logger = _logger;
@synthesize requestStartTime = _requestStartTime;
+@synthesize isResultFromCache = _isResultFromCache;
- (NSMutableURLRequest *)urlRequest
{
@@ -262,6 +264,7 @@ - (id)initWithTimeout:(NSTimeInterval)timeout
_timeout = timeout;
_state = kStateCreated;
_logger = [[FBLogger alloc] initWithLoggingBehavior:FBLogBehaviorFBRequests];
+ _isResultFromCache = NO;
}
return self;
}
@@ -421,6 +424,8 @@ - (void)startWithCacheIdentity:(NSString*)cacheIdentity
self.connection = connection;
[connection release];
} else {
+ _isResultFromCache = YES;
+
// complete on result from cache
[self completeWithResponse:nil
data:cachedData
View
3  src/FacebookSDK.h
@@ -19,10 +19,11 @@
#import "FBRequest.h"
#import "FBError.h"
-// controls
+// ux
#import "FBProfilePictureView.h"
#import "FBPlacePickerViewController.h"
#import "FBFriendPickerViewController.h"
+#import "FBCacheDescriptor.h"
// graph
#import "FBGraphUser.h"
View
42 src/facebook-ios-sdk.xcodeproj/project.pbxproj
@@ -12,6 +12,8 @@
8409694C1541F41100479AD9 /* FBOpenGraphAction.h in Headers */ = {isa = PBXBuildFile; fileRef = 8409694B1541F41100479AD9 /* FBOpenGraphAction.h */; settings = {ATTRIBUTES = (Public, ); }; };
840FDCBD152B5D9200F9C927 /* FBFrictionlessRequestSettings.h in Headers */ = {isa = PBXBuildFile; fileRef = 840FDCBB152B5D9200F9C927 /* FBFrictionlessRequestSettings.h */; };
840FDCBE152B5D9200F9C927 /* FBFrictionlessRequestSettings.m in Sources */ = {isa = PBXBuildFile; fileRef = 840FDCBC152B5D9200F9C927 /* FBFrictionlessRequestSettings.m */; };
+ 841062451582501900FC561C /* FBFriendPickerCacheDescriptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D0A6531581A15E00A2FA5E /* FBFriendPickerCacheDescriptor.m */; };
+ 841062471582502B00FC561C /* FBPlacePickerCacheDescriptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D0A6551581A1A600A2FA5E /* FBPlacePickerCacheDescriptor.m */; };
84137158152B94B000B2C0E1 /* FBSessionManualTokenCachingStrategy.h in Headers */ = {isa = PBXBuildFile; fileRef = 84137156152B94B000B2C0E1 /* FBSessionManualTokenCachingStrategy.h */; };
84137159152B94B000B2C0E1 /* FBSessionManualTokenCachingStrategy.m in Sources */ = {isa = PBXBuildFile; fileRef = 84137157152B94B000B2C0E1 /* FBSessionManualTokenCachingStrategy.m */; };
843D2DD81547C33000873F7C /* FBUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = 843D2DD61547C33000873F7C /* FBUtility.h */; };
@@ -35,6 +37,14 @@
84B5F11A1552F82200A55DDC /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8446FDB3151D2674000BE007 /* UIKit.framework */; };
84B5F11C1552FD3C00A55DDC /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84B5F11B1552FD3C00A55DDC /* CoreGraphics.framework */; };
84C7F72D15806ADC00E4B78A /* FBRequestConnection+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 84C7F72C15806ADC00E4B78A /* FBRequestConnection+Internal.h */; };
+ 84C9FF3015871363000C0C97 /* FBCacheDescriptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D0A64C1581A0CF00A2FA5E /* FBCacheDescriptor.m */; };
+ 84D0A64D1581A0CF00A2FA5E /* FBCacheDescriptor.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D0A64B1581A0CF00A2FA5E /* FBCacheDescriptor.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 84D0A64F1581A0CF00A2FA5E /* FBCacheDescriptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D0A64C1581A0CF00A2FA5E /* FBCacheDescriptor.m */; };
+ 84D0A6521581A12800A2FA5E /* FBFriendPickerCacheDescriptor.h in Resources */ = {isa = PBXBuildFile; fileRef = 84D0A6511581A12800A2FA5E /* FBFriendPickerCacheDescriptor.h */; };
+ 84D0A6561581A1A600A2FA5E /* FBPlacePickerCacheDescriptor.m in Resources */ = {isa = PBXBuildFile; fileRef = 84D0A6551581A1A600A2FA5E /* FBPlacePickerCacheDescriptor.m */; };
+ 84D0A6581581A1C000A2FA5E /* FBPlacePickerCacheDescriptor.h in Resources */ = {isa = PBXBuildFile; fileRef = 84D0A6571581A1C000A2FA5E /* FBPlacePickerCacheDescriptor.h */; };
+ 84D0A6591581A20400A2FA5E /* FBFriendPickerCacheDescriptor.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D0A6511581A12800A2FA5E /* FBFriendPickerCacheDescriptor.h */; settings = {ATTRIBUTES = (); }; };
+ 84D0A65A1581A20A00A2FA5E /* FBPlacePickerCacheDescriptor.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D0A6571581A1C000A2FA5E /* FBPlacePickerCacheDescriptor.h */; settings = {ATTRIBUTES = (); }; };
84D2FC13153CC34300D7F814 /* FBGraphObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 84AE5DA2152EA02500C4DE54 /* FBGraphObject.m */; };
84E0C9F91535ED5A00778DA4 /* FBGraphUser.h in Headers */ = {isa = PBXBuildFile; fileRef = 84E0C9F81535ED5A00778DA4 /* FBGraphUser.h */; settings = {ATTRIBUTES = (Public, ); }; };
84E0CA04153618E500778DA4 /* FacebookSDK.h in Headers */ = {isa = PBXBuildFile; fileRef = 84E0CA03153618E500778DA4 /* FacebookSDK.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -46,6 +56,9 @@
84E2DA2C15353430007F886C /* FBGraphLocation.h in Headers */ = {isa = PBXBuildFile; fileRef = 84E2DA2B1535342F007F886C /* FBGraphLocation.h */; settings = {ATTRIBUTES = (Public, ); }; };
84E374BF153CC1140043B59C /* FBGraphObjectTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 84E374BE153CC1140043B59C /* FBGraphObjectTests.m */; };
84F43FFE15194E4800CEECD5 /* FBRequestConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = 84F43FFD15194E4800CEECD5 /* FBRequestConnection.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 84F9E5A315825C73001B9CF6 /* FBFriendPickerCacheDescriptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D0A6531581A15E00A2FA5E /* FBFriendPickerCacheDescriptor.m */; };
+ 84F9E5A415825CA4001B9CF6 /* FBGraphObjectPagingLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 85954B85155B452E00FABA9A /* FBGraphObjectPagingLoader.m */; };
+ 84F9E5A515825CAE001B9CF6 /* FBFriendPickerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E28B75531547D85A002E30C0 /* FBFriendPickerViewController.m */; };
84FA4261153E155E009CEEF8 /* FBRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = AEA93B0311D5293B000A4545 /* FBRequest.m */; };
84FA4266153E15A3009CEEF8 /* FBFrictionlessRequestSettings.m in Sources */ = {isa = PBXBuildFile; fileRef = 840FDCBC152B5D9200F9C927 /* FBFrictionlessRequestSettings.m */; };
84FA426A153E15A3009CEEF8 /* FBRequestConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = E29B4E64152631FB00D1BE21 /* FBRequestConnection.m */; };
@@ -131,7 +144,7 @@
E2B99CF51549E02A002AEA86 /* FBGraphObjectTableSelection.h in Headers */ = {isa = PBXBuildFile; fileRef = E2B99CF31549E02A002AEA86 /* FBGraphObjectTableSelection.h */; };
E2B99CF61549E02A002AEA86 /* FBGraphObjectTableSelection.m in Sources */ = {isa = PBXBuildFile; fileRef = E2B99CF41549E02A002AEA86 /* FBGraphObjectTableSelection.m */; };
E7BBCE2E15753C7400B6B8F6 /* FBContentLinkTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E7BBCE2D15753C7400B6B8F6 /* FBContentLinkTests.m */; };
- E7BBCE3115753C8E00B6B8F6 /* FBContentLink.h in Headers */ = {isa = PBXBuildFile; fileRef = E7BBCE2F15753C8E00B6B8F6 /* FBContentLink.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ E7BBCE3115753C8E00B6B8F6 /* FBContentLink.h in Headers */ = {isa = PBXBuildFile; fileRef = E7BBCE2F15753C8E00B6B8F6 /* FBContentLink.h */; settings = {ATTRIBUTES = (); }; };
E7BBCE3315753C8E00B6B8F6 /* FBContentLink.m in Sources */ = {isa = PBXBuildFile; fileRef = E7BBCE3015753C8E00B6B8F6 /* FBContentLink.m */; };
/* End PBXBuildFile section */
@@ -159,6 +172,7 @@
8446FDAB151CDB0B000BE007 /* FBSessionTokenCachingStrategy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSessionTokenCachingStrategy.h; sourceTree = "<group>"; };
8446FDAC151CDB0B000BE007 /* FBSessionTokenCachingStrategy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSessionTokenCachingStrategy.m; sourceTree = "<group>"; };
8446FDB3151D2674000BE007 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
+ 847BA63115823BF600C2D56E /* FBFriendPickerViewController+Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "FBFriendPickerViewController+Internal.h"; sourceTree = "<group>"; };
84AE5DA1152EA02500C4DE54 /* FBGraphObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBGraphObject.h; sourceTree = "<group>"; };
84AE5DA2152EA02500C4DE54 /* FBGraphObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBGraphObject.m; sourceTree = "<group>"; };
84B5F1131552E4AF00A55DDC /* FBSessionTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FBSessionTests.h; path = tests/FBSessionTests.h; sourceTree = "<group>"; };
@@ -166,6 +180,12 @@
84B5F11B1552FD3C00A55DDC /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
84BEDF4C151BC24F00F89C3B /* FBSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = FBSession.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
84C7F72C15806ADC00E4B78A /* FBRequestConnection+Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "FBRequestConnection+Internal.h"; sourceTree = "<group>"; };
+ 84D0A64B1581A0CF00A2FA5E /* FBCacheDescriptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBCacheDescriptor.h; sourceTree = "<group>"; };
+ 84D0A64C1581A0CF00A2FA5E /* FBCacheDescriptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBCacheDescriptor.m; sourceTree = "<group>"; };
+ 84D0A6511581A12800A2FA5E /* FBFriendPickerCacheDescriptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBFriendPickerCacheDescriptor.h; sourceTree = "<group>"; };
+ 84D0A6531581A15E00A2FA5E /* FBFriendPickerCacheDescriptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBFriendPickerCacheDescriptor.m; sourceTree = "<group>"; };
+ 84D0A6551581A1A600A2FA5E /* FBPlacePickerCacheDescriptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBPlacePickerCacheDescriptor.m; sourceTree = "<group>"; };
+ 84D0A6571581A1C000A2FA5E /* FBPlacePickerCacheDescriptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBPlacePickerCacheDescriptor.h; sourceTree = "<group>"; };
84E0C9F81535ED5A00778DA4 /* FBGraphUser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBGraphUser.h; sourceTree = "<group>"; };
84E0CA03153618E500778DA4 /* FacebookSDK.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FacebookSDK.h; sourceTree = "<group>"; };
84E0CA051536198400778DA4 /* FBConnect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBConnect.h; sourceTree = "<group>"; };
@@ -332,6 +352,8 @@
84E0CA07153619D400778DA4 /* Facebook.h */,
84E0CA08153619D400778DA4 /* Facebook.m */,
84E0CA03153618E500778DA4 /* FacebookSDK.h */,
+ 84D0A64B1581A0CF00A2FA5E /* FBCacheDescriptor.h */,
+ 84D0A64C1581A0CF00A2FA5E /* FBCacheDescriptor.m */,
B9C6E1301525219600E46808 /* FBCacheIndex.h */,
B9C6E1311525219600E46808 /* FBCacheIndex.m */,
84E0CA051536198400778DA4 /* FBConnect.h */,
@@ -345,7 +367,10 @@
E23E5A0A1521161900A011A8 /* FBError.m */,
840FDCBB152B5D9200F9C927 /* FBFrictionlessRequestSettings.h */,
840FDCBC152B5D9200F9C927 /* FBFrictionlessRequestSettings.m */,
+ 84D0A6511581A12800A2FA5E /* FBFriendPickerCacheDescriptor.h */,
+ 84D0A6531581A15E00A2FA5E /* FBFriendPickerCacheDescriptor.m */,
E28B75521547D85A002E30C0 /* FBFriendPickerViewController.h */,
+ 847BA63115823BF600C2D56E /* FBFriendPickerViewController+Internal.h */,
E28B75531547D85A002E30C0 /* FBFriendPickerViewController.m */,
84E2DA2B1535342F007F886C /* FBGraphLocation.h */,
84AE5DA1152EA02500C4DE54 /* FBGraphObject.h */,
@@ -365,6 +390,8 @@
AEA93B0611D5293B000A4545 /* FBLoginDialog.h */,
AEA93B0711D5293B000A4545 /* FBLoginDialog.m */,
8409694B1541F41100479AD9 /* FBOpenGraphAction.h */,
+ 84D0A6571581A1C000A2FA5E /* FBPlacePickerCacheDescriptor.h */,
+ 84D0A6551581A1A600A2FA5E /* FBPlacePickerCacheDescriptor.m */,
E2223AE91554573900126FD2 /* FBPlacePickerViewController.h */,
E2223AEA1554573900126FD2 /* FBPlacePickerViewController.m */,
B9DC7F3E151AB56100DF1158 /* FBProfilePictureView.h */,
@@ -514,6 +541,9 @@
E7BBCE3115753C8E00B6B8F6 /* FBContentLink.h in Headers */,
85F29E9415785D72001F0531 /* FBTestSession+Internal.h in Headers */,
84C7F72D15806ADC00E4B78A /* FBRequestConnection+Internal.h in Headers */,
+ 84D0A64D1581A0CF00A2FA5E /* FBCacheDescriptor.h in Headers */,
+ 84D0A6591581A20400A2FA5E /* FBFriendPickerCacheDescriptor.h in Headers */,
+ 84D0A65A1581A20A00A2FA5E /* FBPlacePickerCacheDescriptor.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -588,6 +618,9 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ 84D0A6521581A12800A2FA5E /* FBFriendPickerCacheDescriptor.h in Resources */,
+ 84D0A6561581A1A600A2FA5E /* FBPlacePickerCacheDescriptor.m in Resources */,
+ 84D0A6581581A1C000A2FA5E /* FBPlacePickerCacheDescriptor.h in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -666,6 +699,10 @@
85052AF9156F5E1200F8F9A5 /* FBTestSessionTests.m in Sources */,
E7BBCE2E15753C7400B6B8F6 /* FBContentLinkTests.m in Sources */,
E7BBCE3315753C8E00B6B8F6 /* FBContentLink.m in Sources */,
+ 84D0A64F1581A0CF00A2FA5E /* FBCacheDescriptor.m in Sources */,
+ 84F9E5A315825C73001B9CF6 /* FBFriendPickerCacheDescriptor.m in Sources */,
+ 84F9E5A415825CA4001B9CF6 /* FBGraphObjectPagingLoader.m in Sources */,
+ 84F9E5A515825CAE001B9CF6 /* FBFriendPickerViewController.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -705,6 +742,9 @@
85954B87155B452E00FABA9A /* FBGraphObjectPagingLoader.m in Sources */,
8525A5BB156F2049009F6F3F /* FBTestSession.m in Sources */,
85F29E9C1578782B001F0531 /* FBContentLink.m in Sources */,
+ 841062451582501900FC561C /* FBFriendPickerCacheDescriptor.m in Sources */,
+ 841062471582502B00FC561C /* FBPlacePickerCacheDescriptor.m in Sources */,
+ 84C9FF3015871363000C0C97 /* FBCacheDescriptor.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
View
3  src/tests/FBCacheTests.h
@@ -17,8 +17,9 @@
#import <SenTestingKit/SenTestingKit.h>
#import "FBCacheIndex.h"
#import "FBDataDiskCache.h"
+#import "FBTests.h"
-@interface FBCacheTests : SenTestCase<FBCacheIndexFileDelegate>
+@interface FBCacheTests : FBTests<FBCacheIndexFileDelegate>
{
NSString* _dataCachePath;
}
View
59 src/tests/FBCacheTests.m
@@ -18,6 +18,13 @@
#import "FBDataDiskCache.h"
#import "FBCacheIndex.h"
#import "FBTests.h"
+#import "FBTestBlocker.h"
+#import "FBCacheDescriptor.h"
+#import "FBFriendPickerViewController+Internal.h"
+#import "FBFriendPickerViewController.h"
+#import "FBRequest.h"
+#import "FBRequestConnection.h"
+#import "FBRequestConnection+Internal.h"
#if defined(FBIOSSDK_SKIP_CACHE_TESTS)
@@ -369,6 +376,58 @@ - (void)testDeletingUsedData
[[NSFileManager defaultManager] removeItemAtPath:tempFolder error:NULL];
}
+- (void)testBasicFriendPickerCache {
+
+ // let's get a user going with some friends
+ FBTestSession *session1 = self.defaultTestSession;
+ FBTestSession *session2 = [self getSessionWithSharedUserWithPermissions:nil
+ uniqueUserTag:kSecondTestUserTag];
+ [self makeTestUserInSession:session1 friendsWithTestUserInSession:session2];
+
+ FBTestSession *session3 = [self getSessionWithSharedUserWithPermissions:nil
+ uniqueUserTag:kThirdTestUserTag];
+ [self makeTestUserInSession:session1 friendsWithTestUserInSession:session3];
+
+ FBCacheDescriptor *cacheDescriptor = [FBFriendPickerViewController cacheDescriptor];
+
+ // set the page limit to 1
+ //[cacheDescriptor performSelector:@selector(setUsePageLimitOfOne)];
+
+ // here we actually perform the prefetch
+ [cacheDescriptor prefetchAndCacheForSession:session1];
+
+ FBTestBlocker *blocker = [[FBTestBlocker alloc] init];
+
+ [blocker waitWithPeriodicHandler:^(FBTestBlocker *blocker) {
+ // white-box, using an internal API to determine if fetch completed
+ if ([cacheDescriptor performSelector:@selector(hasCompletedFetch)]) {
+ [blocker signal];
+ }
+ }];
+ [blocker release];
+ blocker = [[FBTestBlocker alloc] init];
+
+ FBFriendPickerViewController *vc = [[FBFriendPickerViewController alloc] init];
+ vc.session = session1;
+ [vc configureUsingCachedDescriptor:cacheDescriptor];
+
+ FBRequest *request = [vc performSelector:@selector(requestForLoadData)];
+ FBRequestConnection *connection = [[FBRequestConnection alloc] init];
+ [connection addRequest:request
+ completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
+ [blocker signal];
+ STAssertTrue(connection.isResultFromCache, @"This result should have been cached");
+ }];
+
+ [connection startWithCacheIdentity:FBFriendPickerCacheIdentity
+ skipRoundtripIfCached:YES];
+
+ [blocker wait];
+
+ [blocker release];
+
+ [connection release];
+}
@end
View
4 src/tests/FBRequestConnectionTests.m
@@ -118,6 +118,8 @@ - (void)testCachedRequests
[blocker wait];
+ STAssertFalse(connection.isResultFromCache, @"Should not have cached, and should have fetched from server");
+
[connection release];
[blocker release];
@@ -136,8 +138,8 @@ - (void)testCachedRequests
// should have completed successfully by here
STAssertTrue(completedWithoutBlocking, @"Should have called the handler, due to cache hit");
+ STAssertTrue(connection.isResultFromCache, @"Should not have fetched from server");
[connection release];
-
}
- (void)testDelete
View
7 src/tests/FBTestBlocker.h
@@ -16,6 +16,10 @@
#import <Foundation/Foundation.h>
+@class FBTestBlocker;
+
+typedef void (^FBTestBlockerPeriodicHandler)(FBTestBlocker *blocker);
+
// FBTestBlocker class
//
// Summary:
@@ -29,8 +33,11 @@
- (id)init;
- (id)initWithExpectedSignalCount:(NSInteger)expectedSignalCount;
+- (id)initWithExpectedSignalCount:(NSInteger)expectedSignalCount;
- (void)wait;
- (BOOL)waitWithTimeout:(NSUInteger)timeout;
+- (void)waitWithPeriodicHandler:(FBTestBlockerPeriodicHandler)handler;
+- (BOOL)waitWithTimeout:(NSUInteger)timeout periodicHandler:(FBTestBlockerPeriodicHandler)handler;
- (void)signal;
@end
View
15 src/tests/FBTestBlocker.m
@@ -44,8 +44,18 @@ - (void)wait {
[self waitWithTimeout:0];
}
-// Note that after call to wait/waitWithTimeout the blocker resets to its original signal count.
+- (void)waitWithPeriodicHandler:(FBTestBlockerPeriodicHandler)handler {
+ [self waitWithTimeout:0
+ periodicHandler:handler];
+}
+
- (BOOL)waitWithTimeout:(NSUInteger)timeout {
+ return [self waitWithTimeout:timeout
+ periodicHandler:nil];
+}
+
+- (BOOL)waitWithTimeout:(NSUInteger)timeout
+ periodicHandler:(FBTestBlockerPeriodicHandler)handler {
NSDate *start = [NSDate date];
// loop until the previous call completes
@@ -56,6 +66,9 @@ - (BOOL)waitWithTimeout:(NSUInteger)timeout {
[self reset];
return NO;
}
+ if (handler) {
+ handler(self);
+ }
};
[self reset];
return YES;
Please sign in to comment.
Something went wrong with that request. Please try again.