iOS - programatically rejecting permission to contacts (after user has accepted) - ios

Is there a way to programatically reject/disable/disallow/reject permissions to access the calendar?
I'm using the following code to ask for / determine permissions:
//request access
[[MyCalendarUtil sharedManager] requestAccess:^(BOOL granted, NSError *error) {
/* This code will run when uses has made his/her choice */
if(error)
{
// display error message here
_calendarLabel.text = #"Calendar OFF";
}
else
if(!granted)
{
// display access denied error message here
_calendarLabel.text = #"Calendar OFF";
}
else
{
// access granted
_calendarLabel.text = #"Calendar ON";
}
}];
Can I disable the permission later on upon a user pressing a button ... ?

No way, its impossible. Take it granted. I will give 1 Million dollars even if you find a private API. :)
Its a privacy concern.
All you can do is, programmatically launching your application's preferences in the Settings App, and again its users wish to disable or enable the access.

Related

Upload Youtube video with "fixed" token in Google APIs Client Library for Objective-C for REST

I'm trying to create an app that upload video on a specified channel, without prompt a login page. I'll try to explain better what i need.
I'm using Google APIs Client Library for Objective-C for REST, with this library i can use a "standard" upload flow :
user record a video -> he press an upload button -> Safari open the login google page -> user login in his own account and give permission to the app -> Safari redirect back to the ios app -> the upload process begin -> the video will be uploaded on the personal user channel.
Instead this is the desired workflow of my ios app:
user record a video -> he press an upload button -> the video will be uploaded in the app's youtube channel.
The only help i had find is this article ,it explain a way to obtain a refreshable app token to upload video in an app channel. It is exactly what i need. Anyway this is a web example, it uploads videos that are in a server. My videos are in the phone, so i think that i have to modify the flow of this article in this way:
obtain token the first time by login as channel owner -> create a token.txt and save it in my server -> create a page called get_token.php that print the content of token.txt and refresh it if the token expire.
With this flow in my app i need this other flow:
user record a video -> press an upload button -> i made a call to get_token.php and retrive the actual token -> i made a call by library with the token retrived to upload the video on the app's youtube channel.
Here i have found some problems, this is my authentication methods :
#pragma mark - Sign In
- (void)authNoCodeExchange {
[self verifyConfig];
NSURL *issuer = [NSURL URLWithString:kIssuer];
[self logMessage:#"Fetching configuration for issuer: %#", issuer];
// discovers endpoints
[OIDAuthorizationService discoverServiceConfigurationForIssuer:issuer
completion:^(OIDServiceConfiguration *_Nullable configuration, NSError *_Nullable error) {
if (!configuration) {
[self logMessage:#"Error retrieving discovery document: %#", [error localizedDescription]];
return;
}
[self logMessage:#"Got configuration: %#", configuration];
if (!kClientID) {
[self doClientRegistration:configuration
callback:^(OIDServiceConfiguration *configuration,
OIDRegistrationResponse *registrationResponse) {
[self doAuthWithoutCodeExchange:configuration
clientID:registrationResponse.clientID
clientSecret:registrationResponse.clientSecret];
}];
} else {
[self doAuthWithoutCodeExchangeCri:configuration clientID:kClientID clientSecret:nil];
}
}];
}
/////////////////
- (void)doAuthWithoutCodeExchangeCri:(OIDServiceConfiguration *)configuration
clientID:(NSString *)clientID
clientSecret:(NSString *)clientSecret {
NSURL *redirectURI = [NSURL URLWithString:kRedirectURI];
OIDTokenRequest *tokenExchangeRequest =
[_authState.lastAuthorizationResponse tokenExchangeRequest];
[OIDAuthorizationService performTokenRequest:tokenExchangeRequest
callback:^(OIDTokenResponse *_Nullable tokenResponse,
NSError *_Nullable error) {
if (!tokenResponse) {
[self logMessage:#"Token exchange error: %#", [error localizedDescription]];
} else {
[self logMessage:#"Received token response with accessToken: %#", tokenResponse.accessToken];
}
[_authState updateWithTokenResponse:tokenResponse error:error];
GTMAppAuthFetcherAuthorization *gtmAuthorization =
[[GTMAppAuthFetcherAuthorization alloc] initWithAuthState:authState];
// Sets the authorizer on the GTLRYouTubeService object so API calls will be authenticated.
self.youTubeService.authorizer = gtmAuthorization;
// Serializes authorization to keychain in GTMAppAuth format.
[GTMAppAuthFetcherAuthorization saveAuthorization:gtmAuthorization
toKeychainForName:kGTMAppAuthKeychainItemName];
[self uploadVideoFile];
}];
}
i have also something like:
NSString * RetrivedToken = #"ya29xxxxxxx3nJxxxxxxx6qqQ-FxxxxxxxdGH";
How i can modify those methods to accept my retrivedtoken instead the one they retrive from the standard auth workflow?
This isn't really an answer but its going to be to big for a comment.
So you want to create an app that will allow others to upload to your youtube channel. Normally i would say use a service account which would allow you to do this much easier. However the YouTube api does not support service accounts.
You are going to need to authenticate the app once save your refresh token then embed this refresh token in your app.
Why this is not a good idea with a mobile app.
Refresh tokens can expire so if it does expire or break then your going to have to authenticate the app again and embed the new refresh token into your app and release a new version to your users.
An alternative would be to set up a web service with the refresh token on that and have your app access the web service for upload. Then if the refresh token breaks you will only have to fix it on your web service.
I have done this before its messy but there is really no other way of doing it.
You can see from your code
if (!tokenResponse) {
[self logMessage:#"Token exchange error: %#", [error localizedDescription]];
} else {
[self logMessage:#"Received token response with accessToken: %#", tokenResponse.accessToken];
}
your are using OIDTokenResponse object tokenResponse in your further code.
First make an object of Class OIDTokenResponse like *oidTokenResponse and assign it your access token from your server like
oidTokenResponse.accessToken=YOUR_TOKEN_FROM_SERVER;
and then use
[_authState updateWithTokenResponse:oidTokenResponse error:error];
In other way you can also do that and you won't need to change your code much you can do it with one line of code but i'm not sure you are allow to change to access token here but you can try it.
Leave the other code and just add one more line in your else
if (!tokenResponse) {
[self logMessage:#"Token exchange error: %#", [error localizedDescription]];
} else {
[self logMessage:#"Received token response with accessToken: %#", tokenResponse.accessToken];
tokenResponse.accessToken=YOUR_TOKEN_FROM_SERVER;
}

Firebase automatically signs out from the user session on iOS app

I am struggling with Firebase authentication in my app. I have enabled the database persistence using:
[FIRDatabase database].persistenceEnabled = YES;
After the user logs in (email/password authentication), I am expecting the session to remain active until I explicitly signout:. To handle the changes in authorization, I set up the following (with some debugging code to check out the behavior based on the application state changes):
[[FIRAuth auth] addAuthStateDidChangeListener:^(FIRAuth * _Nonnull auth, FIRUser * _Nullable user) {
if (user != nil) {
ZLog(YES, #"Signed in %#\n\n\n>>>>\n\n\n", user.uid);
[user getTokenWithCompletion:^(NSString * _Nullable token, NSError * _Nullable error) {
ZLog(NO, #"Token %#\n\n\n####\n\n\n", token);
}];
} else {
ZLog(YES, #"Not Signed In\n\n\n>>>>\n\n\n");
FIRUser *user = [[FIRAuth auth] currentUser];
ZLog(YES, #"The user is %#", (user != nil) ? user : #"nil");
}
}];
everything seems to be ok even after reinstalling the app from Xcode while debugging. However as soon as the app goes in "Inactive state" (e.g. by pressing the Home button). I get the "Sign out" in my listener block. This does not happen if I just switch to another app when my app goes to "background" as observed by applicationDidEnterBackground.
What could be wrong? The documentation (https://firebase.google.com/docs/database/ios/offline-capabilities) suggests that the session should be saved across app restarts (which implies the cases like applicationWillResignActive and applicationDidBecomeActive, I assume).
I would not like to re-authenticate (with the safely saved email/password credentials) on every such change (and it would also defeat the 'offline' capabilities claim). Or would the re-authentication also work in 'offline' mode?
Going crazy trying to solve this - any clues would be helpful! Thx.

How do I store & access a Twitter Fabric login session (iOS/Swift)?

I'm able to log in to Twitter through my app using this Twitter Fabric code:
let logInButton = TWTRLogInButton(logInCompletion: {
(session: TWTRSession!, error: NSError!) in
// play with Twitter session
if (session != nil) {
println("signed in as \(session.userName)");
self.TWUsernameLabel.text = "Logged in as #" + session.userName
} else {
println("error: \(error.localizedDescription)");
}
})
When I click the login button, it prompts me to approve the login and then logs me in, or it knows I already approved the login and it logs me in. This works like a charm and took all of ten minutes to set up. Amazing.
I already have an email-based login to access the app. I'd like to store a user's logged in Twitter account in that same database, so when a user logs in with their email, I already know their Twitter (if they have logged in before) and they don't need to log in again. The reason I do the email login is because Twitter is an important feature in my app, but not a total requirement.
The issue is that I have no idea how to access session outside of when the button is clicked and logInCompletion fires, and I don't know what variables to store upon initial login/check upon using the app.
I've read through the Twitter Fabric documentation numerous times, but it isn't written in swift, so it's pretty confusing. Any ideas? Thanks
If there is currently an active session you should be able to access it just like the docs say to
Twitter.sharedInstance().session()
If the user isn't logged in that method will return nil. If you want to know if someone is already authenticated just check to see if that method returns a value or not.
Twitter kit automatically present a login screen if user has not logged in (no session exist..).
But, before calling any future API, just check whether session is valid or not. In some APIs, you may required to pass this session too.
if ([[Twitter sharedInstance] session]) {
TWTRShareEmailViewController *shareEmailViewController =
[[TWTRShareEmailViewController alloc]
initWithCompletion:^(NSString *email, NSError *error) {
NSLog(#"Email %# | Error: %#", email, error);
}];
[self presentViewController:shareEmailViewController
animated:YES
completion:nil];
} else {
// Handle user not signed in (e.g. attempt to log in or show an alert)
}
}
You even don't need to worry about storing and managing access token and access token secret.
To access the authToken, userName or the userID for the logged in user session in Swift 4 is:
Twitter.sharedInstance().logIn(completion: { (session, error) in
if (session != nil) {
print(session!.authToken)
print(session!.userName)
print(session!.userID)
} else {
print(error?.localizedDescription)
}
})

Check for permissions without being asked by the system

I want my app to check if it has permissions to use the device contacts or not.
This is the code i'm using:
- (void)isContactsAutherizedWithCompletion:(void(^)(BOOL granted))completion {
self.addressBookRef = ABAddressBookCreateWithOptions(NULL, NULL);
ABAddressBookRegisterExternalChangeCallback(self.addressBookRef, MyAddressBookExternalChangeCallback, (__bridge_retained void *)self);
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined) {
ABAddressBookRequestAccessWithCompletion(self.addressBookRef, ^(bool granted, CFErrorRef error) {
if (granted) {
completion(YES);
} else {
completion(NO);
}
});
} else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) {
completion(YES);
} else {
completion(NO);
}
}
But with this code i'm automatically being asked if I want to give permissions. I don't want to be asked, I just want to know if it's has permissions or not.
Anyone has a solution?
Thanks
There are three states for permission based access.
You have been granted access.
You have been denied access.
The user hasn't been asked yet.
The first time you open the app and try to access something that requires permission it will ask. No way around this.
If you are given permission then it will never ask again and it will work.
If you are denied access then it will also never ask again and the request will fail.
The user will only be asked once for each system. That is how it has to work.
In this case it is in state 3. So it neither has not doesn't have permission. So the check cannot complete. It has to fall into either state 1 or 2 to be able to work out if the app has permission or not.
i.e. Everything seems to be working correctly here.
well from the doc:
typedef CF_ENUM (CFIndex, ABAuthorizationStatus ) {
kABAuthorizationStatusNotDetermined = 0,
kABAuthorizationStatusRestricted,
kABAuthorizationStatusDenied,
kABAuthorizationStatusAuthorized
};
So check if the status is kABAuthorizationStatusAuthorized
and you are sure that you have the permission, in all the other cases you have no permission either because the user did not accept or because you did not ask for the permission yet

m7/m8 detect if motion activity authorization was denied

I am using M7/M8 chip's MotionActivity in a variety of ways, including for step counting. For step counting, I both query for the day's steps, and request ongoingly the step count as they occur realtime.
Currently before I do this I check [CMStepCounter isStepCountingAvailable], as well as a local override flag, before proceeding with this code. I assumed isStepCountingAvailable would return FALSE if authorization for motionActivity was not granted. This does not seem to be the case, it appears to be more of a hardware detection only. I cannot seem to find other methods that detect whether authorization was granted for this.
What this means is that startStepCountingUpdatesToQueue and queryStepCountStartingFrom both run, and return blocks, but always return an error code. Specifically CMErrorDomain code 105.
Is there a better way for me to determine if motionActivity has not been authorized? I've got some fallback code but I'd prefer a boolean check beforehand, instead of an error code in the return block.
if (self.useM7IfAvailable && [CMStepCounter isStepCountingAvailable]){
self.cmStepCounter = [[CMStepCounter alloc] init];
[self.cmStepCounter startStepCountingUpdatesToQueue:self.operationQueue updateOn:1.0 withHandler:^(NSInteger numberOfSteps, NSDate *timestamp, NSError *error){
if(!error){
// do something with numberOfSteps
} else {
// not authorized: CMErrorDomain code 105
}
}];
}
[self.cmStepCounter queryStepCountStartingFrom:dayStart to:dayEnd toQueue:_operationQueue withHandler:^(NSInteger numberOfSteps, NSError *error) {
if(!error){
// do something with numberOfSteps
} else {
// not authorized: CMErrorDomain code 105
}
}];
You're doing it correctly by checking for the error. Per the docs (https://developer.apple.com/library/ios/documentation/coremotion/reference/cmmotionmanager_class/index.html#//apple_ref/c/tdef/CMError) you'll receive back CMErrors with Error Code 105 like you've seen.
Unfortunately there's no way to check ahead-of-time to see if you're authorized or not, but this follows Apple's paradigms with other Core-level frameworks that require authorization, like CoreLocation. The reasoning is you can be in the middle of getting motion steps while in the background, and the user can then disable your motion access, which you'll have to react to that event in probably the same way you'd react to not being authorized in the first place.

Resources