Skip to content
This repository
1  sample/Hackbook/Hackbook/APIResultsViewController.h
@@ -19,7 +19,6 @@
19 19
 
20 20
 @interface APIResultsViewController : UIViewController
21 21
 <FBRequestDelegate,
22  
-FBSessionDelegate,
23 22
 UITableViewDataSource,
24 23
 UITableViewDelegate>{
25 24
     NSMutableArray *myData;
57  sample/Hackbook/Hackbook/HackbookAppDelegate.m
@@ -43,37 +43,44 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
43 43
     UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:rootViewController];
44 44
     [navController.navigationBar setTintColor:[UIColor colorWithRed:0/255.0
45 45
                                                               green:51.0/255.0
46  
-                                                               blue:102.0/255.0 
  46
+                                                               blue:102.0/255.0
47 47
                                                               alpha:1.0]];
48 48
     [navController.navigationBar setBarStyle:UIBarStyleBlackTranslucent];
49 49
     self.navigationController = navController;
50 50
     [rootViewController release];
51 51
     [navController release];
52  
-    
  52
+
53 53
     // Initialize Facebook
54 54
     facebook = [[Facebook alloc] initWithAppId:kAppId andDelegate:rootViewController];
55  
-    
  55
+
  56
+    // Check and retrieve authorization information
  57
+    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  58
+    if ([defaults objectForKey:@"FBAccessTokenKey"] && [defaults objectForKey:@"FBExpirationDateKey"]) {
  59
+        facebook.accessToken = [defaults objectForKey:@"FBAccessTokenKey"];
  60
+        facebook.expirationDate = [defaults objectForKey:@"FBExpirationDateKey"];
  61
+    }
  62
+
56 63
     // Initialize API data (for views, etc.)
57 64
     apiData = [[DataSet alloc] init];
58  
-    
  65
+
59 66
     // Initialize user permissions
60 67
     userPermissions = [[NSMutableDictionary alloc] initWithCapacity:1];
61  
-    
  68
+
62 69
     // Override point for customization after application launch.
63 70
     // Add the navigation controller's view to the window and display.
64 71
     self.window.rootViewController = self.navigationController;
65 72
     [self.window makeKeyAndVisible];
66  
-    
  73
+
67 74
     // Check App ID:
68 75
     // This is really a warning for the developer, this should not
69 76
     // happen in a completed app
70 77
     if (!kAppId) {
71  
-        UIAlertView *alertView = [[UIAlertView alloc] 
72  
-                                  initWithTitle:@"Setup Error" 
73  
-                                  message:@"Missing app ID. You cannot run the app until you provide this in the code." 
74  
-                                  delegate:self 
75  
-                                  cancelButtonTitle:@"OK" 
76  
-                                  otherButtonTitles:nil, 
  78
+        UIAlertView *alertView = [[UIAlertView alloc]
  79
+                                  initWithTitle:@"Setup Error"
  80
+                                  message:@"Missing app ID. You cannot run the app until you provide this in the code."
  81
+                                  delegate:self
  82
+                                  cancelButtonTitle:@"OK"
  83
+                                  otherButtonTitles:nil,
77 84
                                   nil];
78 85
         [alertView show];
79 86
         [alertView release];
@@ -83,7 +90,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
83 90
         NSString *url = [NSString stringWithFormat:@"fb%@://authorize",kAppId];
84 91
         BOOL bSchemeInPlist = NO; // find out if the sceme is in the plist file.
85 92
         NSArray* aBundleURLTypes = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleURLTypes"];
86  
-        if ([aBundleURLTypes isKindOfClass:[NSArray class]] && 
  93
+        if ([aBundleURLTypes isKindOfClass:[NSArray class]] &&
87 94
             ([aBundleURLTypes count] > 0)) {
88 95
             NSDictionary* aBundleURLTypes0 = [aBundleURLTypes objectAtIndex:0];
89 96
             if ([aBundleURLTypes0 isKindOfClass:[NSDictionary class]]) {
@@ -91,7 +98,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
91 98
                 if ([aBundleURLSchemes isKindOfClass:[NSArray class]] &&
92 99
                     ([aBundleURLSchemes count] > 0)) {
93 100
                     NSString *scheme = [aBundleURLSchemes objectAtIndex:0];
94  
-                    if ([scheme isKindOfClass:[NSString class]] && 
  101
+                    if ([scheme isKindOfClass:[NSString class]] &&
95 102
                         [url hasPrefix:scheme]) {
96 103
                         bSchemeInPlist = YES;
97 104
                     }
@@ -101,21 +108,29 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
101 108
         // Check if the authorization callback will work
102 109
         BOOL bCanOpenUrl = [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString: url]];
103 110
         if (!bSchemeInPlist || !bCanOpenUrl) {
104  
-            UIAlertView *alertView = [[UIAlertView alloc] 
105  
-                                      initWithTitle:@"Setup Error" 
106  
-                                      message:@"Invalid or missing URL scheme. You cannot run the app until you set up a valid URL scheme in your .plist." 
107  
-                                      delegate:self 
108  
-                                      cancelButtonTitle:@"OK" 
109  
-                                      otherButtonTitles:nil, 
  111
+            UIAlertView *alertView = [[UIAlertView alloc]
  112
+                                      initWithTitle:@"Setup Error"
  113
+                                      message:@"Invalid or missing URL scheme. You cannot run the app until you set up a valid URL scheme in your .plist."
  114
+                                      delegate:self
  115
+                                      cancelButtonTitle:@"OK"
  116
+                                      otherButtonTitles:nil,
110 117
                                       nil];
111 118
             [alertView show];
112 119
             [alertView release];
113 120
         }
114 121
     }
115  
-    
  122
+
116 123
     return YES;
117 124
 }
118 125
 
  126
+- (void)applicationDidBecomeActive:(UIApplication *)application {
  127
+    // Although the SDK attempts to refresh its access tokens when it makes API calls,
  128
+    // it's a good practice to refresh the access token also when the app becomes active.
  129
+    // This gives apps that seldom make api calls a higher chance of having a non expired
  130
+    // access token.
  131
+    [[self facebook] extendAccessTokenIfNeeded];
  132
+}
  133
+
119 134
 - (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
120 135
     return [self.facebook handleOpenURL:url];
121 136
 }
41  sample/Hackbook/Hackbook/RootViewController.m
@@ -103,7 +103,7 @@ - (void)showLoggedOut {
103 103
     nameLabel.text = @"";
104 104
     // Get the profile image
105 105
     [profilePhotoImageView setImage:nil];
106  
-    
  106
+
107 107
     [[self navigationController] popToRootViewControllerAnimated:YES];
108 108
 }
109 109
 
@@ -245,19 +245,11 @@ - (void)viewWillAppear:(BOOL)animated {
245 245
     [super viewWillAppear:animated];
246 246
 
247 247
     HackbookAppDelegate *delegate = (HackbookAppDelegate *)[[UIApplication sharedApplication] delegate];
248  
-    // Check and retrieve authorization information
249  
-    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
250  
-    if ([defaults objectForKey:@"FBAccessTokenKey"]
251  
-        && [defaults objectForKey:@"FBExpirationDateKey"]) {
252  
-        [delegate facebook].accessToken = [defaults objectForKey:@"FBAccessTokenKey"];
253  
-        [delegate facebook].expirationDate = [defaults objectForKey:@"FBExpirationDateKey"];
254  
-    }
255 248
     if (![[delegate facebook] isSessionValid]) {
256 249
         [self showLoggedOut];
257 250
     } else {
258 251
         [self showLoggedIn];
259 252
     }
260  
-
261 253
 }
262 254
 
263 255
 - (void)viewWillDisappear:(BOOL)animated {
@@ -315,6 +307,13 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath
315 307
 
316 308
 }
317 309
 
  310
+- (void)storeAuthData:(NSString *)accessToken expiresAt:(NSDate *)expiresAt {
  311
+    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  312
+    [defaults setObject:accessToken forKey:@"FBAccessTokenKey"];
  313
+    [defaults setObject:expiresAt forKey:@"FBExpirationDateKey"];
  314
+    [defaults synchronize];
  315
+}
  316
+
318 317
 #pragma mark - FBSessionDelegate Methods
319 318
 /**
320 319
  * Called when the user has logged in successfully.
@@ -323,16 +322,16 @@ - (void)fbDidLogin {
323 322
     [self showLoggedIn];
324 323
 
325 324
     HackbookAppDelegate *delegate = (HackbookAppDelegate *)[[UIApplication sharedApplication] delegate];
  325
+    [self storeAuthData:[[delegate facebook] accessToken] expiresAt:[[delegate facebook] expirationDate]];
326 326
 
327  
-    // Save authorization information
328  
-    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
329  
-    [defaults setObject:[[delegate facebook] accessToken] forKey:@"FBAccessTokenKey"];
330  
-    [defaults setObject:[[delegate facebook] expirationDate] forKey:@"FBExpirationDateKey"];
331  
-    [defaults synchronize];
332  
-    
333 327
     [pendingApiCallsController userDidGrantPermission];
334 328
 }
335 329
 
  330
+-(void)fbDidExtendToken:(NSString *)accessToken expiresAt:(NSDate *)expiresAt {
  331
+    NSLog(@"token extended");
  332
+    [self storeAuthData:accessToken expiresAt:expiresAt];
  333
+}
  334
+
336 335
 /**
337 336
  * Called when the user canceled the authorization dialog.
338 337
  */
@@ -359,13 +358,13 @@ - (void)fbDidLogout {
359 358
 /**
360 359
  * Called when the session has expired.
361 360
  */
362  
-- (void)fbSessionInvalidated {   
  361
+- (void)fbSessionInvalidated {
363 362
     UIAlertView *alertView = [[UIAlertView alloc]
364  
-                              initWithTitle:@"Auth Exception" 
365  
-                              message:@"Your session has expired." 
366  
-                              delegate:nil 
367  
-                              cancelButtonTitle:@"OK" 
368  
-                              otherButtonTitles:nil, 
  363
+                              initWithTitle:@"Auth Exception"
  364
+                              message:@"Your session has expired."
  365
+                              delegate:nil
  366
+                              cancelButtonTitle:@"OK"
  367
+                              otherButtonTitles:nil,
369 368
                               nil];
370 369
     [alertView show];
371 370
     [alertView release];
24  src/Facebook.h
@@ -25,7 +25,7 @@
25 25
  * and Graph APIs, and start user interface interactions (such as
26 26
  * pop-ups promoting for credentials, permissions, stream posts, etc.)
27 27
  */
28  
-@interface Facebook : NSObject<FBLoginDialogDelegate>{
  28
+@interface Facebook : NSObject<FBLoginDialogDelegate,FBRequestDelegate>{
29 29
   NSString* _accessToken;
30 30
   NSDate* _expirationDate;
31 31
   id<FBSessionDelegate> _sessionDelegate;
@@ -35,6 +35,8 @@
35 35
   NSString* _appId;
36 36
   NSString* _urlSchemeSuffix;
37 37
   NSArray* _permissions;
  38
+  BOOL _isExtendingAccessToken;
  39
+  NSDate* _lastAccessTokenUpdate;
38 40
 }
39 41
 
40 42
 @property(nonatomic, copy) NSString* accessToken;
@@ -51,6 +53,12 @@
51 53
 
52 54
 - (void)authorize:(NSArray *)permissions;
53 55
 
  56
+- (void)extendAccessToken;
  57
+
  58
+- (void)extendAccessTokenIfNeeded;
  59
+
  60
+- (BOOL)shouldExtendAccessToken;
  61
+
54 62
 - (BOOL)handleOpenURL:(NSURL *)url;
55 63
 
56 64
 - (void)logout;
@@ -93,8 +101,6 @@
93 101
  */
94 102
 @protocol FBSessionDelegate <NSObject>
95 103
 
96  
-@optional
97  
-
98 104
 /**
99 105
  * Called when the user successfully logged in.
100 106
  */
@@ -106,13 +112,23 @@
106 112
 - (void)fbDidNotLogin:(BOOL)cancelled;
107 113
 
108 114
 /**
  115
+ * Called after the access token was extended. If your application has any
  116
+ * references to the previous access token (for example, if your application
  117
+ * stores the previous access token in persistent storage), your application
  118
+ * should overwrite the old access token with the new one in this method.
  119
+ * See extendAccessToken for more details.
  120
+ */
  121
+- (void)fbDidExtendToken:(NSString*)accessToken
  122
+               expiresAt:(NSDate*)expiresAt;
  123
+
  124
+/**
109 125
  * Called when the user logged out.
110 126
  */
111 127
 - (void)fbDidLogout;
112 128
 
113 129
 /**
114 130
  * Called when the current session has expired. This might happen when:
115  
- *  - the access token expired 
  131
+ *  - the access token expired
116 132
  *  - the app has been disabled
117 133
  *  - the user revoked the app's permissions
118 134
  *  - the user changed his or her password
112  src/Facebook.m
@@ -30,6 +30,10 @@
30 30
 static NSString* kSDK = @"ios";
31 31
 static NSString* kSDKVersion = @"2";
32 32
 
  33
+// If the last time we extended the access token was more than 24 hours ago
  34
+// we try to refresh the access token again.
  35
+static const int kTokenExtendThreshold = 24;
  36
+
33 37
 static NSString *requestFinishedKeyPath = @"state";
34 38
 static void *finishedContext = @"finishedContext";
35 39
 
@@ -93,10 +97,11 @@ - (id)initWithAppId:(NSString *)appId
93 97
 - (id)initWithAppId:(NSString *)appId
94 98
     urlSchemeSuffix:(NSString *)urlSchemeSuffix
95 99
         andDelegate:(id<FBSessionDelegate>)delegate {
96  
-  
  100
+
97 101
   self = [super init];
98 102
   if (self) {
99 103
     _requests = [[NSMutableSet alloc] init];
  104
+    _lastAccessTokenUpdate = [[NSDate distantPast] retain];
100 105
     self.appId = appId;
101 106
     self.sessionDelegate = delegate;
102 107
     self.urlSchemeSuffix = urlSchemeSuffix;
@@ -111,6 +116,7 @@ - (void)dealloc {
111 116
   for (FBRequest* _request in _requests) {
112 117
     [_request removeObserver:self forKeyPath:requestFinishedKeyPath];
113 118
   }
  119
+  [_lastAccessTokenUpdate release];
114 120
   [_accessToken release];
115 121
   [_expirationDate release];
116 122
   [_requests release];
@@ -125,11 +131,11 @@ - (void)dealloc {
125 131
 - (void)invalidateSession {
126 132
   self.accessToken = nil;
127 133
   self.expirationDate = nil;
128  
-    
  134
+
129 135
   NSHTTPCookieStorage* cookies = [NSHTTPCookieStorage sharedHTTPCookieStorage];
130 136
   NSArray* facebookCookies = [cookies cookiesForURL:
131 137
                                 [NSURL URLWithString:@"http://login.facebook.com"]];
132  
-    
  138
+
133 139
   for (NSHTTPCookie* cookie in facebookCookies) {
134 140
     [cookies deleteCookie:cookie];
135 141
   }
@@ -160,6 +166,8 @@ - (FBRequest*)openUrl:(NSString *)url
160 166
     [params setValue:self.accessToken forKey:@"access_token"];
161 167
   }
162 168
 
  169
+  [self extendAccessTokenIfNeeded];
  170
+
163 171
   FBRequest* _request = [FBRequest getRequestWithParams:params
164 172
                                              httpMethod:httpMethod
165 173
                                                delegate:delegate
@@ -221,7 +229,7 @@ - (void)authorizeWithFBAppAuth:(BOOL)tryFBAppAuth
221 229
   if (_urlSchemeSuffix) {
222 230
     [params setValue:_urlSchemeSuffix forKey:@"local_client_id"];
223 231
   }
224  
-  
  232
+
225 233
   // If the device is running a version of iOS that supports multitasking,
226 234
   // try to obtain the access token from the Facebook app installed
227 235
   // on the device.
@@ -319,6 +327,56 @@ - (void)authorize:(NSArray *)permissions {
319 327
 }
320 328
 
321 329
 /**
  330
+ * Attempt to extend the access token.
  331
+ *
  332
+ * Access tokens typically expire within 30-60 days. When the user uses the
  333
+ * app, the app should periodically try to obtain a new access token. Once an
  334
+ * access token has expired, the app can no longer renew it. The app then has
  335
+ * to ask the user to re-authorize it to obtain a new access token.
  336
+ *
  337
+ * To ensure your app always has a fresh access token for active users, it's
  338
+ * recommended that you call extendAccessTokenIfNeeded in your application's
  339
+ * applicationDidBecomeActive: UIApplicationDelegate method.
  340
+ */
  341
+- (void)extendAccessToken {
  342
+    if (_isExtendingAccessToken) {
  343
+        return;
  344
+    }
  345
+    _isExtendingAccessToken = YES;
  346
+    NSMutableDictionary* params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
  347
+                                   @"auth.extendSSOAccessToken", @"method",
  348
+                                   nil];
  349
+    [self requestWithParams:params andDelegate:self];
  350
+}
  351
+
  352
+/**
  353
+ * Calls extendAccessToken if shouldExtendAccessToken returns YES.
  354
+ */
  355
+- (void)extendAccessTokenIfNeeded {
  356
+  if ([self shouldExtendAccessToken]) {
  357
+    [self extendAccessToken];
  358
+  }
  359
+}
  360
+
  361
+/**
  362
+ * Returns YES if the last time a new token was obtained was over 24 hours ago.
  363
+ */
  364
+- (BOOL)shouldExtendAccessToken {
  365
+  if ([self isSessionValid]){
  366
+    NSCalendar *calendar = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
  367
+    NSDateComponents *components = [calendar components:NSHourCalendarUnit
  368
+                                              fromDate:_lastAccessTokenUpdate
  369
+                                                toDate:[NSDate date]
  370
+                                                options:0];
  371
+
  372
+    if (components.hour >= kTokenExtendThreshold) {
  373
+      return YES;
  374
+    }
  375
+  }
  376
+  return NO;
  377
+}
  378
+
  379
+/**
322 380
  * This function processes the URL the Facebook application or Safari used to
323 381
  * open your application during a single sign-on flow.
324 382
  *
@@ -408,7 +466,7 @@ - (BOOL)handleOpenURL:(NSURL *)url {
408 466
  */
409 467
 - (void)logout {
410 468
   [self invalidateSession];
411  
-    
  469
+
412 470
   if ([self.sessionDelegate respondsToSelector:@selector(fbDidLogout)]) {
413 471
     [self.sessionDelegate fbDidLogout];
414 472
   }
@@ -623,6 +681,7 @@ - (void)dialog:(NSString *)action
623 681
     if ([self isSessionValid]) {
624 682
       [params setValue:[self.accessToken stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]
625 683
                 forKey:@"access_token"];
  684
+      [self extendAccessTokenIfNeeded];
626 685
     }
627 686
     _fbDialog = [[FBDialog alloc] initWithURL:dialogURL params:params delegate:delegate];
628 687
   }
@@ -648,6 +707,8 @@ - (BOOL)isSessionValid {
648 707
 - (void)fbDialogLogin:(NSString *)token expirationDate:(NSDate *)expirationDate {
649 708
   self.accessToken = token;
650 709
   self.expirationDate = expirationDate;
  710
+  [_lastAccessTokenUpdate release];
  711
+  _lastAccessTokenUpdate = [[NSDate date] retain];
651 712
   if ([self.sessionDelegate respondsToSelector:@selector(fbDidLogin)]) {
652 713
     [self.sessionDelegate fbDidLogin];
653 714
   }
@@ -663,4 +724,45 @@ - (void)fbDialogNotLogin:(BOOL)cancelled {
663 724
   }
664 725
 }
665 726
 
  727
+#pragma mark - FBRequestDelegate Methods
  728
+// These delegate methods are only called for requests that extendAccessToken initiated
  729
+
  730
+- (void)request:(FBRequest *)request didFailWithError:(NSError *)error {
  731
+  _isExtendingAccessToken = NO;
  732
+}
  733
+
  734
+- (void)request:(FBRequest *)request didLoad:(id)result {
  735
+  _isExtendingAccessToken = NO;
  736
+  NSString* accessToken = [result objectForKey:@"access_token"];
  737
+  NSString* expTime = [result objectForKey:@"expires_at"];
  738
+
  739
+  if (accessToken == nil || expTime == nil) {
  740
+   return;
  741
+  }
  742
+
  743
+  self.accessToken = accessToken;
  744
+
  745
+  NSTimeInterval timeInterval = [expTime doubleValue];
  746
+  NSDate *expirationDate = [NSDate distantFuture];
  747
+  if (timeInterval != 0) {
  748
+    expirationDate = [NSDate dateWithTimeIntervalSince1970:timeInterval];
  749
+  }
  750
+  self.expirationDate = expirationDate;
  751
+  [_lastAccessTokenUpdate release];
  752
+  _lastAccessTokenUpdate = [[NSDate date] retain];
  753
+
  754
+  if ([self.sessionDelegate respondsToSelector:@selector(fbDidExtendToken:expiresAt:)]) {
  755
+    [self.sessionDelegate fbDidExtendToken:accessToken expiresAt:expirationDate];
  756
+  }
  757
+}
  758
+
  759
+- (void)request:(FBRequest *)request didLoadRawResponse:(NSData *)data {
  760
+}
  761
+
  762
+- (void)request:(FBRequest *)request didReceiveResponse:(NSURLResponse *)response{
  763
+}
  764
+
  765
+- (void)requestLoading:(FBRequest *)request{
  766
+}
  767
+
666 768
 @end

0 notes on commit 4aa2a66

Please sign in to comment.
Something went wrong with that request. Please try again.