User not logged out from Dropbox and Google drive when deleting app - ios

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;

Related

Permenant unique device identifier [duplicate]

This question already has answers here:
iOS unique user identifier [duplicate]
(7 answers)
Closed 5 years ago.
Is there a way to identify a device even after having uninstalled an app and reinstalling again? I found topics where it's possible to get a UUID but it seems that after uninstalling the app the value of the UUID changes
The value in this property remains the same while the app (or another
app from the same vendor) is installed on the iOS device. The value
changes when the user deletes all of that vendor’s apps from the
device and subsequently reinstalls one or more of them.
I installed an App called Jodel, you don't have to create an Account to use the app and After uninstalling it, delete iCloud data, logging out from iCloud... an reinstalling it I was still logged in in the App. I assume they use a unique device identifier? Do you have an idea how such mechanism could be implemented?
You can use Keychain Service to store data still after uninstalling app from device.
for more reference about keychain service check this
https://developer.apple.com/documentation/security/keychain_services
Yes, It's Possible
#import "UICKeyChainStore.h"
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[self getDeviceIdFromKeychain];
}
- (void)getDeviceIdFromKeychain
{
NSString *deviceID = [UICKeyChainStore stringForKey:#"KEY TO SAVE TO Keychain" service:nil];
// if vandorid from keychain is not nil
if (deviceID)
{
[[NSUserDefaults standardUserDefaults] setObject:deviceID forKey:#"deviceID"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
// else it goes for new vendorid and then stored it to keychan
else if (deviceID == (id)[NSNull null] || deviceID.length == 0 )
{
deviceID = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
[UICKeyChainStore setString:deviceID forKey:#"KEY TO SAVE TO Keychain" service:nil];
[[NSUserDefaults standardUserDefaults] setObject:deviceID forKey:#"deviceID"];
[[NSUserDefaults standardUserDefaults] synchronize];
// NSLog(#"VendorID Local - %#",deviceID);
}
}
ViewContoller.m
- (void)viewDidLoad
{
NSString *strDeviceId = [[NSUserDefaults standardUserDefaults]objectForKey:#"deviceID"];
}

Calling push notification registration from outside app delegate

Okay so I have an app where I want to implement push notifications. The problem is that I want to use the users username to help identify which device they are sending the notifications too. But when it is called in the delegate sometimes the users are not logged in yet so the app crashes. The code is in this method
- (void)applicationDidBecomeActive:(UIApplication *)application
{
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
currentInstallation[#"installationUser"] = [[PFUser currentUser]username];
// here we add a column to the installation table and store the current user’s ID
// this way we can target specific users later
// while we’re at it, this is a good place to reset our app’s badge count
// you have to do this locally as well as on the parse server by updating
// the PFInstallation object
if (currentInstallation.badge != 0) {
currentInstallation.badge = 0;
[currentInstallation saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (error) {
// Handle error here with an alert…
}
else {
// only update locally if the remote update succeeded so they always match
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
NSLog(#"updated badge");
}
}];
}
}
So I need this NOT to be called if the user has not logged in yet but then I want it to be called every time. I am getting confused about how to do this. Let me know if you have any ideas, thanks!
You wouldn't normally call this in applicationDidBecomeActive I see you've read up on the docs a little bit but it's real easy:
if (![PFUser currentUser]) { // No user logged in : this is all you need because in applicationDidRegistreForRemoteNotificationsWithDeviceToken should have set the user or installation ID already
//Enter data
} else {
// User logged in
}
NOTE : This is best to be used in your main view controllers viewDidAppear method. Review their documentation for future guidance : https://parse.com/tutorials/login-and-signup-views

Integrating Dropbox chooser in iOS

In my iOS app i am trying to fetch details of a file stored in dropbox on click of button.
I am trying to choose a file from dropbox and redirect back to my application using DBChooser. It works fine when a user is already logged in the Dropbox.
-(void)choose{
[[DBChooser defaultChooser] openChooserForLinkType:DBChooserLinkTypeDirect fromViewController:self
completion:^(NSArray *results)
{
if ([results count]) {
_result = results[0];
link = [NSString stringWithFormat:#"%#", _result.link];
name = _result.name;
size = [NSString stringWithFormat:#"%lld", _result.size];
icon = [NSString stringWithFormat:#"%#", _result.iconURL];
} else {
_result = nil;
[[[UIAlertView alloc] initWithTitle:#"CANCELLED" message:#"user cancelled!"
delegate:nil cancelButtonTitle:#"Okay" otherButtonTitles:nil]
show];
}
}];
}
Also i have following function in Appdelegate.m
-(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:
(NSString *)sourceApplication annotation:(id)annotation
{
if ([[DBChooser defaultChooser] handleOpenURL:url]) {
// This was a Chooser response and handleOpenURL automatically ran the
// completion block
return YES;
}
return NO;
}
Problem starts when user is not logged in and when i click on button it opens dropbox and asks me to enter credentials. After successful logged in, i am able to view my dropbox data, but i am not able to choose anything i.e. on clicking an image it should redirect me back to my aplication, instead it is opening that image in dropbox itself. This happens only when user is not logged in.
I have set the URL schemes for the DropIns used for chooser. I just need to verify if user is logged in the dropbox before calling the choose function. Please help i am stuck on this.

What should I do after receiving an NSUbiquityIdentityDidChangeNotification?

Apple documentation says we need to register for NSUbiquityIdentityDidChangeNotification and to compare the current iCloud token with the one previously stored in NSUserDefaults to detect if a user disabled iCloud from Documents & Data Settings or switched to another iCloud account.
I use a standard UIManagedDocument and I target iOS 7 so the fallback store is handled automatically by CoreData.
I do not understand what I should do after I find the user enabled / disabled iCloud or switched to another account. Should I migrate the persistent store? Or should I migrate it after an NSPersistentStoreCoordinatorStoresDidChangeNotification? Or should I never migrate it because everything is handled by CoreData?
After watching WWDC 2013 207 video several times I thought this would have been handled automatically by Core Data but I found that if I start with iCloud support and then I switch it off from Document & Data Settings and I insert new
data, then I switch back iCloud to on, I end with two different data sets.
I would like that if I find the user disabled iCloud then the local db should contain up to the last change made until the iCloud was enabled and only from this point everything should stop syncing until iCloud will be enabled again.
In the WWDC 2013 207 video, in the Melissa demo, I also noticed a call to a method [self migrateBack] after an NSPersistentStoreCoordinatorStoresDidChangeNotification and this confuses me because the slides just show we should save our context here and refresh the UI, they do not show we should migrate anything:
**Account Changes Now**
NSPersistentStoreCoordinatorStoresWillChangeNotification
[NSManagedObjectContext save:]
[NSManagedObjectContext reset:]
NSPersistentStoreCoordinatorStoresDidChangeNotification
[NSManagedObjectContext save:]
The NSPersistentStoreCoordinatorStoresDidChangeNotification notification has nothing to do with changing the iCloud access.
If the user turns off iCloud access or logs out of iCloud then Core Data has to use the fallback store, which is probably empty! You won't get a chance to migrate anything.
However if the app has its own Use iCloud setting then you can test for that change and if set to NO migrate any documents to local storage assuming iCloud is still available.
You can see the expected behaviour in the latest version of Pages. If you have iCloud documents then as soon as you turn off iCloud Documents & Data in the Settings App you loose all access to the Pages documents. However if you go the Pages settings in the Settings App and turn off Use iCloud and then switch back to Pages you will be prompted to Keep on My iPhone, Delete from My iPhone or Keep using iCloud. That is how Apple will expect your app to work.
When the application enters the foreground we check the app specific iCloud settings and if they have changed we take the necessary action.
If no change has been made then we keep running
If the iCloud setting has changed then:
If it is turned OFF ask the user what to do
If it has been turned ON then share all documents in iCloud
/*! The app is about to enter foreground so use this opportunity to check if the user has changed any
settings. They may have changed the iCloud account, logged into or out of iCloud, set Documents & Data to off (same effect as
if they logged out of iCloud) or they may have changed the app specific settings.
If the settings have been changed then check if iCloud is being turned off and ask the user if they want to save the files locally.
Otherwise just copy the files to iCloud (don't ask the user again, they've just turned iCloud on, so they obviously mean it!)
#param application The application
*/
- (void)applicationWillEnterForeground:(UIApplication *)application
{
//LOG(#"applicationWillEnterForeground called");
// Check if the app settings have been changed in the Settings Bundle (we use a Settings Bundle which
// shows settings in the Devices Settings app, along with all the other device settings).
[[NSUserDefaults standardUserDefaults] synchronize];
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
bool userICloudChoice = [userDefaults boolForKey:_cloudPreferenceKey];
// Now compare it with the current apps in memory setting to see if it has changed
if (userICloudChoice == useICloudStorage) {
// No change so do nothing
//LOG(#" iCloud choice has not changed");
} else {
// Setting has been changed so take action
//LOG(#" iCloud choice has been changed!!");
// iCloud option has been turned off
if (!userICloudChoice) {
//LOG(#" Ask user if they want to keep iCloud files locally ?");
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
_cloudChangedAlert = [[UIAlertView alloc] initWithTitle:#"You're not using iCloud" message:#"What would you like to do with documents currently on this phone?" delegate:self cancelButtonTitle:#"Keep using iCloud" otherButtonTitles:#"Keep on My iPhone", #"Delete from My iPhone", nil];
} else {
_cloudChangedAlert = [[UIAlertView alloc] initWithTitle:#"You're not using iCloud" message:#"What would you like to do with documents currently on this phone?" delegate:self cancelButtonTitle:#"Keep using iCloud" otherButtonTitles:#"Keep on My iPad", #"Delete from My iPad", nil];
}
[_cloudChangedAlert show];
// Handle the users response in the alert callback
} else {
// iCloud is turned on so just copy them across... including the one we may have open
//LOG(#" iCloud turned on so copy any created files across");
[[CloudManager sharedManager] setIsCloudEnabled:YES]; // This does all the work for us
useICloudStorage = YES;
}
}
}
- (void)alertView:(UIAlertView*)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
if (alertView == _cloudChoiceAlert)
{
//LOG(#" _cloudChoiceAlert being processed");
if (buttonIndex == 1) {
//LOG(#" user selected iCloud files");
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:_cloudPreferenceKey];
[[NSUserDefaults standardUserDefaults] setValue:#"YES" forKey:_cloudPreferenceSet];
useICloudStorage = YES;
[[NSUserDefaults standardUserDefaults] synchronize];
[[CloudManager sharedManager] setIsCloudEnabled:YES];
}
else {
//LOG(#" user selected local files");
[[NSUserDefaults standardUserDefaults] setBool:NO forKey:_cloudPreferenceKey];
[[NSUserDefaults standardUserDefaults] setValue:#"YES" forKey:_cloudPreferenceSet];
useICloudStorage = NO;
[[NSUserDefaults standardUserDefaults] synchronize];
[[CloudManager sharedManager] setIsCloudEnabled:NO];
}
}
if (alertView == _cloudChangedAlert)
{ //LOG(#" _cloudChangedAlert being processed");
if (buttonIndex == 0) {
//LOG(#" 'Keep using iCloud' selected");
//LOG(#" turn Use iCloud back ON");
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:_cloudPreferenceKey];
[[NSUserDefaults standardUserDefaults] synchronize];
useICloudStorage = YES;
}
else if (buttonIndex == 1) {
//LOG(#" 'Keep on My iPhone' selected");
//LOG(#" copy to local storage");
useICloudStorage = NO;
[[CloudManager sharedManager] setDeleteICloudFiles:NO];
[[CloudManager sharedManager] setIsCloudEnabled:NO];
}else if (buttonIndex == 2) {
//LOG(#" 'Delete from My iPhone' selected");
//LOG(#" delete copies from iPhone");
useICloudStorage = NO;
[[CloudManager sharedManager] setDeleteICloudFiles:YES];
[[CloudManager sharedManager] setIsCloudEnabled:NO];
}
}
}
/*! Checks to see whether the user has previously selected the iCloud storage option, and if so then check
whether the iCloud identity has changed (i.e. different iCloud account being used or logged out of iCloud).
If the user has previously chosen to use iCloud and we're still signed in, setup the CloudManager
with cloud storage enabled.
If no user choice is recorded, use a UIAlert to fetch the user's preference.
*/
- (void)checkUserICloudPreferenceAndSetupIfNecessary
{
FLOG(#"checkUserICloudPreferenceAndSetupIfNecessary called");
[[CloudManager sharedManager] setFileExtension:_fileExtension andUbiquityID:_ubiquityContainerKey ];
id currentToken = [[NSFileManager defaultManager] ubiquityIdentityToken];
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
NSString* userICloudChoiceSet = [userDefaults stringForKey:_cloudPreferenceSet];
bool userICloudChoice = [userDefaults boolForKey:_cloudPreferenceKey];
userICloudChoice = [userDefaults boolForKey:_cloudPreferenceKey];
//FLOG(#" User preference for %# is %#", _cloudPreferenceKey, (userICloudChoice ? #"YES" : #"NO"));
if (userICloudChoice) {
//LOG(#" User selected iCloud");
useICloudStorage = YES;
[self checkUbiquitousTokenFromPreviousLaunch:currentToken];
} else {
//LOG(#" User disabled iCloud");
useICloudStorage = NO;
}
// iCloud is active
if (currentToken) {
//LOG(#" iCloud is active");
// If user has not yet set preference the prompt for them to select a preference
if ([userICloudChoiceSet length] == 0) {
_cloudChoiceAlert = [[UIAlertView alloc] initWithTitle:#"Choose Storage Option" message:#"Should documents be stored in iCloud or on just this device?" delegate:self cancelButtonTitle:#"Local only" otherButtonTitles:#"iCloud", nil];
[_cloudChoiceAlert show];
}
else if (userICloudChoice ) {
[[CloudManager sharedManager] setIsCloudEnabled:YES];
}
}
else {
//LOG(#" iCloud is not active");
[[CloudManager sharedManager] setIsCloudEnabled:NO];
useICloudStorage = NO;
[[NSUserDefaults standardUserDefaults] setBool:NO forKey:_cloudPreferenceKey];
[[NSUserDefaults standardUserDefaults] synchronize];
// Since the user is signed out of iCloud, reset the preference to not use iCloud, so if they sign in again we will prompt them to move data
[userDefaults removeObjectForKey:_cloudPreferenceSet];
}
[self storeCurrentUbiquityToken:currentToken];
}

Ask for permission to allow access to Contacts iOS [duplicate]

This question already has answers here:
Programmatically Request Access to Contacts
(13 answers)
Closed 9 years ago.
I am importing all the native address book contacts into my app. Currently, the alertview to ask for permission to 'allow access to the Contacts' is done at the launch of the app.
How do I change it so permission is asked elsewhere in the app? At the click of a button, just like Instagram?
See this picture:
You can read the documentation HERE.
Here is some code to get you started:
//First of all import the AddRessBookUI
#import <AddressBookUI/AddressBookUI.h>
// Request to authorise the app to use addressbook
ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(nil, nil);
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined) {
ABAddressBookRequestAccessWithCompletion(addressBookRef, ^(bool granted, CFErrorRef error) {
if (granted) {
// If the app is authorized to access the first time then add the contact
[self _addContactToAddressBook];
} else {
// Show an alert here if user denies access telling that the contact cannot be added because you didn't allow it to access the contacts
}
});
}
else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) {
// If the user user has earlier provided the access, then add the contact
[self _addContactToAddressBook];
}
else {
// If the user user has NOT earlier provided the access, create an alert to tell the user to go to Settings app and allow access
}
Here are another couple of tutorials that you might like to see that you can find HERE and HERE.
Hope this helps!
UPDATE: You have to use didFinishLaunchingWithOptions to detect the first time your app launches:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"HasLaunchedOnce"])
{
// The app has already launched once
}
else
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"HasLaunchedOnce"];
[[NSUserDefaults standardUserDefaults] synchronize];
// This is the first time the app is launched
}
}
App asking for permission when you try retrive contacts.
Just call retriving after button touch, not after launching.

Resources