The box.com SDK for iOS has an object called sharedSDK that holds another object called OAuth2Session. OAuth2Session has a property called isAuthorized. On each application launch this property is set to NO. Even if I keep the refreshToken inside the system Keychain, and assign it at launch like so:
//...applicationDidFinisLaunching...
NSString *token = [controllerObject fetchFromKeychainForKey:#"com.box.token"];
[BoxSDK sharedSDK].OAuth2Session.refreshToken = token;
if ([BoxSDK sharedSDK].OAuth2Session.isAuthorized) {
//Not until signing in
NSLog(#"Authorized.)";
} else {
NSLog(#"Not Authorized.");
}
What should I be doing differently to check auth status? The Dropbox SDK has a method to determine if the session is linked, persists through launches.
I'm the author of the iOS SDK. The isAuthorized method is only a best guess of whether or not the current OAuth2 tokens are valid. From the documentation:
Compares accessTokenExpiration to the current time to determine if an access token may be valid. This is not a guarantee that an access token is valid as it may have been revoked or already refreshed.
Because accessTokenExpiration is not stored anywhere by the Box iOS SDK, this field will be nil following initialization, even if the refresh token is loaded.
The Box iOS SDK takes the stance that the Box API is the source of truth about state and does not attempt to perform client side checks that can be handled more reliably by the server.
The recommended way of reloading the OAuth2 session is to set the refresh token from the keychain as you have done and then issue a "heartbeat" API call to trigger an autorefresh or fail if the refresh token is invalid.
An example of this can be found in the Box iOS SDK sample app
- (void)viewDidLoad
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(boxAPIAuthenticationDidSucceed:)
name:BoxOAuth2SessionDidBecomeAuthenticatedNotification
object:[BoxSDK sharedSDK].OAuth2Session];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(boxAPIAuthenticationDidFail:)
name:BoxOAuth2SessionDidReceiveAuthenticationErrorNotification
object:[BoxSDK sharedSDK].OAuth2Session];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(boxAPIInitiateLogin:)
name:BoxOAuth2SessionDidReceiveRefreshErrorNotification
object:[BoxSDK sharedSDK].OAuth2Session];
// attempt to heartbeat. This will succeed if we successfully refresh
// on failure, the BoxOAuth2SessionDidReceiveRefreshErrorNotification notification will be triggered
[self boxAPIHeartbeat];
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)boxAPIHeartbeat
{
[[BoxSDK sharedSDK].foldersManager folderInfoWithID:BoxAPIFolderIDRoot requestBuilder:nil success:nil failure:nil];
}
#pragma mark - Handle OAuth2 session notifications
- (void)boxAPIAuthenticationDidSucceed:(NSNotification *)notification
{
NSLog(#"Received OAuth2 successfully authenticated notification");
BoxOAuth2Session *session = (BoxOAuth2Session *) [notification object];
NSLog(#"Access token (%#) expires at %#", session.accessToken, session.accessTokenExpiration);
NSLog(#"Refresh token (%#)", session.refreshToken);
[self dismissViewControllerAnimated:YES completion:nil];
BOXAssert(self.viewControllers.count == 1, #"There should only be one folder in the hierarchy when authentication succeeds");
BoxFolderViewController *rootVC = (BoxFolderViewController *)self.topViewController;
[rootVC fetchFolderItemsWithFolderID:BoxAPIFolderIDRoot name:#"All Files"];
}
- (void)boxAPIAuthenticationDidFail:(NSNotification *)notification
{
NSLog(#"Received OAuth2 failed authenticated notification");
NSString *oauth2Error = [[notification userInfo] valueForKey:BoxOAuth2AuthenticationErrorKey];
NSLog(#"Authentication error (%#)", oauth2Error);
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)boxAPIInitiateLogin:(NSNotification *)notification
{
NSLog(#"Refresh failed. User is logged out. Initiate login flow");
dispatch_sync(dispatch_get_main_queue(), ^{
[self popToRootViewControllerAnimated:YES];
NSURL *authorizationURL = [BoxSDK sharedSDK].OAuth2Session.authorizeURL;
NSString *redirectURI = [BoxSDK sharedSDK].OAuth2Session.redirectURIString;
BoxAuthorizationViewController *authorizationViewController = [[BoxAuthorizationViewController alloc] initWithAuthorizationURL:authorizationURL redirectURI:redirectURI];
BoxAuthorizationNavigationController *loginNavigation = [[BoxAuthorizationNavigationController alloc] initWithRootViewController:authorizationViewController];
authorizationViewController.delegate = loginNavigation;
loginNavigation.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentViewController:loginNavigation animated:YES completion:nil];
});
}
This view controller registers for OAuth2 notifications which are triggered in the event of a successful refresh or a logout. In the selectors you register for these callbacks, you can load a view controller in your app or load the BoxAuthorizationViewController to log a user in.
Related
I am using Dropbox and Google Drive integration in my iOS app. I can fetch files from both drives and view listing in tableview. However, when I delete the app on my iPhone without logout from these drives, it still shows logged in when I install new app. How to logout user when I delete the app or remove session?
For Dropbox i am using ObjectiveDropboxOfficial apiV2 and for Google Drive i am using GoogleAPIClientForREST, GTMSessionFetcher etc libraries.
My code:
[DBClientsManager setupWithAppKey:#"my-key"];
[DBClientsManager authorizeFromController:[UIApplication sharedApplication]
controller:self openURL:^(NSURL *url) {
[[UIApplication sharedApplication] openURL:url];
}];
//AppDelegate
if ([DBClientsManager handleRedirectURL:url])
{
if (DBClientsManager.authorizedClient || DBClientsManager.authorizedTeamClient) {
// NSLog(#"App linked successfully!");
// At this point you can start making API calls
NSNotification *notification = [NSNotification notificationWithName:#"DropboxLoggedIn" object:nil];
[[NSNotificationCenter defaultCenter] postNotification:notification];
}
return YES;
}
If these services are designed this way I assume they save credentials in keychain which persists data and your application is already logged in when reinstalled or keychain is anyhow transfered.
If this is not your desired effect I can only assume you will need to log out from these services manually. This means you will need to track these logins and logouts and then when the app starts simply log out from all services which have not been tracked as logged in by you.
It is an ugly thing to do but it is a solution:
When a service is logged in save a value in user defaults
- (void)serviceDidLogin:(ServiceType)type {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:[self keyForServiceType: type]];
}
Then when it is logged out you need to clear it
- (void)serviceDidLogout:(ServiceType)type {
[[NSUserDefaults standardUserDefaults] removeObjectForKey:[self keyForServiceType: type]];
}
Then when app starts you need to log out from all of the services that you have no recording of being logged into:
- (void)logOutFromAllUnusedService {
for(int i=0; i<ServiceTypeCount; i++) {
ServiceType type = i;
if([[NSUserDefaults standardUserDefaults] boolForKey:[self keyForServiceType: type]] == NO) {
[self logoutFromService:type];
}
}
}
No matter how you do this but my situation assumes ServiceType is an enum like so:
typedef enum {
// Never assign values to enums
ServiceTypeDropbox,
ServiceTypeGoogleDrive,
ServiceTypeCount, // Always last of the valid
// Move deprecated services here
ServiceTypeDeprecated1 // TODO: remove at some point
} ServiceType;
I have an app that has a "Home Screen" with login and register options. When a user completes one of the above the data is stored in a shared instance. Now for security reasons I was looking at using the applicationDidBecomeActive to periodically check to make sure the user is still active on the server or not blocked by calling a method in the shared instance. If the user is not active the app kicks them to the home screen with a prompt.
My issue is however that when the app loads for the first time `applicationDidBecomeActive is called and because the user is not logged in you end up with a loop.
What is the correct approach for dealing with this issue? Ideally I want to use applicationDidBecomeActive but I only want to perform the check whilst in the account section of the app.
Any help would be great.
Thanks.
There are several ways of implementing this.
Way 1: Fire a Notification using NotificationCenter.default and implement a listener in your Accounts-ViewController.
Way 2: In your AppDelegate, get active ViewController (depends on which rootViewController you are using) and if that ViewController has the type AccountVC trigger a public function.
Way 3: Combine or use Way1 and Way2 in a different object and notify your ViewController in your preferred way.
And many more.
Post some code for more specific help :)
You can use either of the following methods to deal with the issue.
Approach 1
- (void)checkWhetherAppIsActive {
UIApplicationState appState = [[UIApplication sharedApplication] applicationState];
if (appState == UIApplicationStateActive) {
}else if (appState == UIApplicationStateInactive) {
}else if (appState == UIApplicationStateBackground) {
}
}
Approach 2
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillResignActiveNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
}];
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillEnterForegroundNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
}];
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillTerminateNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
}];
When the user logs in , generate an expiryTimeStamp. And whenever the app is active, you can check the currentTimestamp to expiryTimeStamp . If the currentTimestamp is before the expiryTimeStamp, then consider it as active session!
- (void)applicationDidBecomeActive:(UIApplication *)application {
//compare current NSDate with expiryDate
if(current date is before expiry date){
//active session
}else{
//log out the user
}
}
You can generate expiry date as:
-(void)generateExpiryTimeStamp{
[[NSUserDefaults standardUserDefaults]setObject:[NSDate dateWithTimeIntervalSinceNow:900] forKey:#"tokenExpiry"]; //Expiry date Set to 15mins
[[NSUserDefaults standardUserDefaults]synchronize];
}
I am using Sinch SDK version is: 3.7.1-0313526 - with Xcode 6.3 - IOS SDK 8.3 and I would like to know what is the best way to let the app know that there was a missed call when it was in background or not running so I can update UI and badges for missed calls.
The behaviour which I am looking for it's pretty standard. The app is in background or not running, a call arrives. If it is a missed call, the app badge will be update and the notification shown will change to 'Missed call'. Like all the other calling apps.
So far I have tried the following to try to get the missed call from sinch:
- (void)handleRemoteNotification:(NSDictionary *)userInfo {
// Extract the Sinch-specific payload from the Apple Remote Push Notification
NSString* SIN = [userInfo valueForKey:#"sin"];
// Get previously initiated Sinch client
id<SINClient> client = _client;
id<SINNotificationResult> result = [client relayRemotePushNotificationPayload: SIN];
if ([result isCall] && [[result callResult] isTimedOut]) {
//Let set the badge number
[self setTheCallBadgeValue];
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:#"Missed call"
message:[NSString stringWithFormat:#"Missed call from %#", callerName]
delegate:nil
cancelButtonTitle:nil
otherButtonTitles:#"OK", nil];
[alert show];
}
}
I call the above method from:
#pragma mark - SINManagedPushDelegate
- (void)managedPush:(id<SINManagedPush>)unused
didReceiveIncomingPushWithPayload:(NSDictionary *)payload
forType:(NSString *)pushType {
// NSLog(#"Incoming push - This is the payload: %#", payload);
[self handleRemoteNotification:payload];
}
Unfortunately, that only seems to work on the following cases:
User needs to tap on the notification alert after a few second (around 10s)
The app will open, then it will show the alert for missed call
If user doesn't tap on the notification alert after waiting a few seconds or just ignore the notification alert and just open the app by tapping on the app icon, the app will never know that there was a missed call as "[[result callResult] isTimeout] will not become true", therefore the app will never update UI and the user won't never know that there was a missed call.
For full disclosure, I am using Parse SDK version is 1.7.4 and only added Push notification as a mean to inform the client of new calls:
#pragma Mark - Let Instantiate Sinch!
- (void)initSinchClientWithUserId:(NSString *)userId {
// NSLog(#"Sinch client has started with user id: %#", userId);
if (!_client) {
_client = [Sinch clientWithApplicationKey: SINCH_APPLICATION_KEY
applicationSecret: SINCH_APPLICATION_SECRET
environmentHost: SINCH_ENVIRONMENT_HOST
userId:userId];
_client.delegate = self;
_client.callClient.delegate = self;
[_client setSupportCalling:YES];
[_client enableManagedPushNotifications];
[_client setSupportActiveConnectionInBackground:NO];
[_client start];
// [_client startListeningOnActiveConnection];
NSString *currUsrName = [userDefaults objectForKey:#"currentUserFullName"];
[_client setPushNotificationDisplayName:currUsrName];
}
}
Thank you very much in advance for any help.
You could send a push via parse when you recieve calldid end with reason noanswer.
FBSDKSharingDelegate callback is not working. I can post to facebook fine with the ios SDK, but I'd like to detect if the post was successful and take additional action to notify the user. However, the callback is not working for me. The delegate methods are not being called and I don't know why.
Using ios8, Parse as my backend. In Parse, the user is linked to FB. I'm using the IOS simulator.
What I've tried:
I've ensured that publish permissions are granted, saved, and linked to the Parse user. I've run a check and "publish_actions" are detected OK. The posting works fine as I can see the post on the facebook account. It's just the callback that is not working. I've checked my fb setup and it looks fine. For good measure at the very bottom I've included that relevant code from my app delegate. I've blocked out confidential keys with XXXX.
Code:
1st: See if user is logged in to Parse, if not, send to sign in and link to facebook account. Once that is done, I request "publish" permissions and link that additional permission to the Parse user. I know this works b/c when I recompile, it remembers the "publish" permissions and goes right to into the post.
#interface FacebookAPIPost () <FBSDKSharingDelegate>
#end
#implementation FacebookAPIPost
-(void)shareSegmentFacebookAPI { //if statement below
//1) logged in?, if not send to sign up screen
//2) else if logged in, link account to facebook account, then send post
//3) else send to post b/c signed up and linked already.
PFUser *currentUser = [PFUser currentUser];
if(!currentUser) {
[self pushToSignIn];
} else if(![PFFacebookUtils isLinkedWithUser:currentUser]){
[self linkUserToFacebook:currentUser];
NSLog(#"user account not linked to facebook");
} else {
[self shareSegmentWithFacebookComposer];
}
}
-(void)linkUserToFacebook:currentUser{
[PFFacebookUtils linkUserInBackground:currentUser withPublishPermissions:#[#"publish_actions"] block:^(BOOL succeeded, NSError *error) {
if(error){
NSLog(#"There was an issue linking your facebook account. Please try again.");
}
else {
NSLog(#"facebook account is linked");
//Send the facebook status update
[self shareSegmentWithFacebookComposer];
}
}];
}
-(void)shareSegmentWithFacebookComposer{
if ([[FBSDKAccessToken currentAccessToken] hasGranted:#"publish_actions"]) {
[self publishFBPost]; //publish
} else {
NSLog(#"no publish permissions"); // no publish permissions so get them, then post
[PFFacebookUtils linkUserInBackground:[PFUser currentUser]
withPublishPermissions:#[ #"publish_actions"]
block:^(BOOL succeeded, NSError *error) {
if (succeeded) {
NSLog(#"User now has read and publish permissions!");
[self publishFBPost];
}
}];
Here is where the post gets made:
-(void) publishFBPost{
FBSDKShareLinkContent *content = [FBSDKShareLinkContent new];
content.contentURL = [NSURL URLWithString:[self.selectedSegment valueForKey:#"linkToContent"]];
content.contentTitle = [self.selectedProgram valueForKey:#"programTitle"];
content.contentDescription = [self.selectedSegment valueForKey:#"purposeSummary"];
PFFile *theImage = [self.selectedSegment valueForKey:#"segmentImage"];
NSString *urlString = theImage.url;
NSURL *url = [NSURL URLWithString:urlString];
content.imageURL = url;
FBSDKShareDialog *shareDialog = [FBSDKShareDialog new];
[shareDialog setMode:FBSDKShareDialogModeAutomatic];
// [FBSDKShareDialog showFromViewController:self.messageTableViewController withContent:content delegate:self];
[shareDialog setShareContent:content];
[shareDialog setDelegate:self];
[shareDialog setFromViewController:self.messageTableViewController];
[shareDialog show];
}
Delegate methods below are not working. Meaning after the post is complete, I can see it on the FB account, but none of these delegate methods execute.
#pragma mark - delegate methods
- (void)sharer:(id<FBSDKSharing>)sharer didCompleteWithResults:(NSDictionary *)results {
// if ([sharer isEqual:self.shareDialog]) {
NSLog(#"I'm going to go crazy if this doesn't work.%#",results);
// Your delegate code
// }
}
- (void)sharer:(id<FBSDKSharing>)sharer didFailWithError:(NSError *)error
{
NSLog(#"sharing error:%#", error);
NSString *message = error.userInfo[FBSDKErrorLocalizedDescriptionKey] ?:
#"There was a problem sharing, please try again later.";
NSString *title = error.userInfo[FBSDKErrorLocalizedTitleKey] ?: #"Oops!";
[[[UIAlertView alloc] initWithTitle:title message:message delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil] show];
}
- (void)sharerDidCancel:(id<FBSDKSharing>)sharer
{
NSLog(#"share cancelled");
}
Console output:
The only message I get back after posting is after a few seconds this message appears:
plugin com.apple.share.Facebook.post invalidated
Please help!
Footnote: appDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Initialize Parse.
[Parse enableLocalDatastore];
[Parse setApplicationId:#"XXXX"
clientKey:#"XXX"];
[PFFacebookUtils initializeFacebookWithApplicationLaunchOptions:launchOptions];
//Initialize Facebook
[FBSDKAppEvents activateApp];
return [[FBSDKApplicationDelegate sharedInstance] application:application didFinishLaunchingWithOptions:launchOptions];
}
//Method added for facebook integration
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation {
return [[FBSDKApplicationDelegate sharedInstance] application:application
openURL:url
sourceApplication:sourceApplication
annotation:annotation];
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
[FBSDKAppEvents activateApp];
}
I think I am too late answering this question but someone else could trap into this as well, that's why sharing my knowledge.
As per Facebook API documentation
sharer:didCompleteWithResults: Sent to the delegate when the share completes without error or cancellation.
The results from the sharer. This may be nil or empty.
its probably because this delegate method is only get called when the post is successfully shared. In case of failure the other delegate method sharer:didFailWithError:get called. I think Facebook API should not need to add the result parameter is that case.
So in my experience if sharer:didCompleteWithResults whenever this is called that would mean success.
I have seen a lot of different posts about how to solve this problem, but I have had no luck. I have tried the heartbeat solution, and it does nothing. I know that my keychain is storing my refresh token, but it is not serving any use.
Steps:
Start app
Go to load directory (root in this case)
Get this error:
EDIT: First I get a 20000 error. It seems my authentication tokens are not refreshing.
Error Domain=com.box.sdk.errordomain Code=20002 "The operation
couldn’t be completed. (com.box.sdk.errordomain error 20002.)"
Go through Box login process again.
Reload tableview
Works.
I am using this code to refresh my access tokens (I think it is supposed to)
if (storedRefreshToken)
{
[BoxSDK sharedSDK].OAuth2Session.refreshToken = storedRefreshToken;
}
I feel like I am missing something here also.
I need my user to stay logged in for the allowed 14 days. How can I get the app login state to survive app restarts?
I am using the latest V2 SDK.
EDIT:
I have tried everything, from refreshing the refreshtoken in the keychain on each ViewController to referencing the AppDelegate. I can't get it to stay logged in and just keep getting the 20002 error when I start the app again (not resume, but cold start). I don't want to use the Box filepicker, but I want to make my own tableview. Any other ideas out there?
AppDelegate:
in didFinishLaunching:
[BoxSDK sharedSDK].OAuth2Session.clientID = #"XXXXXXXXXX";
[BoxSDK sharedSDK].OAuth2Session.clientSecret = #"XXXXXXX";
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(boxAPITokensDidRefresh:) name:BoxOAuth2SessionDidBecomeAuthenticatedNotification object:[BoxSDK sharedSDK].OAuth2Session];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(setRefreshTokenInKeychain:) name:BoxOAuth2SessionDidRefreshTokensNotification object:[BoxSDK sharedSDK].OAuth2Session];
// set up stored OAuth2 refresh token
_keychain = [[KeychainItemWrapper alloc] initWithIdentifier:REFRESH_TOKEN_KEY accessGroup:nil];
id storedRefreshToken = [_keychain objectForKey:(__bridge id)kSecValueData];
if (storedRefreshToken)
{
[BoxSDK sharedSDK].OAuth2Session.refreshToken = storedRefreshToken;
}
listener methods
- (void)boxAPITokensDidRefresh:(NSNotification *)notification
{
BoxOAuth2Session *OAuth2Session = (BoxOAuth2Session *) notification.object;
[self setRefreshTokenInKeychain:OAuth2Session.refreshToken];
_isBox = YES;
[self removeBoxLoginViewController];
}
- (void)setRefreshTokenInKeychain:(NSString *)refreshToken
{
[_keychain setObject:#"MyApp" forKey: (__bridge id)kSecAttrService];
[_keychain setObject:refreshToken forKey:(__bridge id)kSecValueData];
NSLog(#"refreshToken: %#", refreshToken);
}
Main ViewController:
ViewDidLoad
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(boxAPIAuthenticationDidSucceed:) name:BoxOAuth2SessionDidBecomeAuthenticatedNotification object:[BoxSDK sharedSDK].OAuth2Session];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(boxAPIAuthenticationDidFail:) name:BoxOAuth2SessionDidReceiveAuthenticationErrorNotification object:[BoxSDK sharedSDK].OAuth2Session];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(boxAPIAuthenticationRefreshToken:) name:BoxOAuth2SessionDidReceiveRefreshErrorNotification object:[BoxSDK sharedSDK].OAuth2Session];
[self boxAPIHeartbeat];
Heartbeat:
- (void)boxAPIHeartbeat
{
[[BoxSDK sharedSDK].foldersManager folderInfoWithID:#"0" requestBuilder:nil success:nil failure:nil];
}
ListenerMethods after hearbeat:
- (void)boxAPIAuthenticationDidSucceed:(NSNotification *)notification
{
NSLog(#"Received OAuth2 successfully authenticated notification");
BoxOAuth2Session *session = (BoxOAuth2Session *) [notification object];
NSLog(#"Access token (%#) expires at %#", session.accessToken, session.accessTokenExpiration);
NSLog(#"Refresh token (%#)", session.refreshToken);
//[self.tableView reloadData];
}
- (void)boxAPIAuthenticationDidFail:(NSNotification *)notification
{
NSLog(#"Received OAuth2 failed authenticated notification");
NSString *oauth2Error = [[notification userInfo] valueForKey:BoxOAuth2AuthenticationErrorKey];
NSLog(#"Authentication error (%#)", oauth2Error);
//[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)boxAPIAuthenticationRefreshToken:(NSNotification *)notification
{
BoxOAuth2Session *OAuth2Session = (BoxOAuth2Session *) notification.object;
[self setRefreshTokenInKeychain:OAuth2Session.refreshToken];
NSLog(#"REFRESH TOKEN: %#", OAuth2Session.refreshToken);
}
//trying this out????
- (void)setRefreshTokenInKeychain:(NSString *)refreshToken
{
[_keychain setObject:#"MyApp" forKey: (__bridge id)kSecAttrService];
[_keychain setObject:refreshToken forKey:(__bridge id)kSecValueData];
NSLog(#"refreshToken: %#", refreshToken);
}
I can't use the Box SDK if I can't get this figured out this weekend. I would think Box would want their SDK to be used by developers, but the documentation is so poor. What am I missing? I just want the app to stay logged in through cold starts!
It turns out, that the issue was with the ARC version of Keychain. I noticed this when I started placing NSLogs all over the place and noticed that the refreshToken getting returned at app launch, was not the refreshToken that was getting encoded into the Keychain. I replaced the ARC Keychain files with the ones from the sample app and put the ARC flag in, and it is working perfectly.