This is what I have so far:
private let contactStore = CNContactStore()
private func requestAccessForContacts() {
switch CNContactStore.authorizationStatusForEntityType(.Contacts) {
case .Denied, .NotDetermined:
contactStore.requestAccessForEntityType(.Contacts, completionHandler: { access, error in
if access {
print("super")
} else {
print("problem")
}
})
case .Authorized:
print("ok")
case .Restricted:
print("restricted")
}
}
The problem is printed on console, but nothing is displayed on screen, no request for access. Why?
Your code works; I was able to replicate the problem by first rejecting access to contact and re-running the app.
Once you have disallowed access, subsequent running of the app would log "problem" without displaying any request window.
If one wanted to see this request window again after denying, one could,
however, go to
"Settings" -> "Reset" and click "Reset Location And Privacy" in the simulator.
After having done so, the next time you run the app, a request window would pop up again.
Related
When using Apple login, I want to know how to log out. Two revoking methods I know
[1] Upon opening your app
Open the Settings app, then tap [your name].
Tap Password & Security
Tap Apps Using Your Apple ID.
let authorizationAppleIDProvider = ASAuthorizationAppleIDProvider()
authorizationAppleIDProvider.getCredentialState(forUserID: "currentUserIdentifier") { (credentialState: ASAuthorizationAppleIDProvider.CredentialState, error: Error?) in
if let error = error {
print(error)
// Something went wrong check error state
return
}
switch (credentialState) {
case .authorized:
//User is authorized to continue using your app
break
case .revoked:
//User has revoked access to your app
break
case .notFound:
//User is not found, meaning that the user never signed in through Apple ID
break
default: break
}
}
How to know through this code when running the app afterwards
[2] While your app is running
let notificationCenter = NotificationCenter.default
let sessionNotificationName = NSNotification.Name.ASAuthorizationAppleIDProviderCredentialRevoked
appleIDSessionObserver = notificationCenter.addObserver(forName: sessionNotificationName, object: nil, queue: nil) { (notification: Notification) in
//Sign user out
}
This code notifies when a credentialState becomes revoked when a user session changes.
What I'm curious about is [2] While your app is running, in what situation is the user session revoked in the app? For example, if you press the logout button, credentialState becomes revoked, you will receive notifications and execute the logout function, right? How to revoked appleId CredentialState in the context of using the app?
After a user takes a photo in my app, there is a UIUserActivity that is presented that allows users to save and image. A pop up comes up and asks for permission to write the photo to the users photo album. However, if the user denies there is no way to prompt the user to be redirected to the photo library. PHPhoto authorization status is always returning undetermined even though permissions were already asked for. I am looking for a few things to be satisfied:
the user should be able to deny but continue to be prompted to give permission to the app if they want to save their photo.
if the user denies, I want the option of "save photo" to remain in the UIUserActivity as a potential option
How can I accomplish this?
I've tried using the PHPhotoLibrary authorization status, but it always returns undetermined. I've tried checking for .undetermined and using the PHPhotoLibrary to request access to the user's camera roll, however if the user denies at this point then the option to save photo is completely removed from the UIUserActivity pop up.
code:
activityViewController.completionWithItemsHandler = { activity, success, items, error in
if success {
if let activity = activity {
...
case .saveToCameraRoll:
handleCameraRollPermission()
func handleCameraRollPermission(status: PHAuthorizationStatus? = nil, completion: #escaping ((Bool) -> Void)) {
let authorizationStatus = status ?? PHPhotoLibrary.authorizationStatus()
switch authorizationStatus {
case .denied, .restricted:
showPermissionMissingAlert(completion: completion)
case .notDetermined:
PHPhotoLibrary.requestAuthorization { (status) in
switch(status) {
case .denied, .restricted, .notDetermined:
self.showPermissionMissingAlert(completion: completion)
default:
break
}
}
default:
completion(true)
}
}
This might be a bit overdoing it, but you could create a custom action section for the activityView. You can find more about that here and scroll down to the Adding a custom action section.
Basically you would make a custom save button that would persist regardless of permission status. When it is tapped you can check their authorization status and go from there.
I'm currently working on an app where I need to access either the photo library or the camera (or even both, it's up to the user).
If I close the app and deny the access to both camera and library for the app.
After restarting my App, it's still possible to access both, I can open and use the camera or even pick a photo from the library.
How is this possible, I thought Apple would restrict the access.
I dont think so. Apple always restrict use of camera or photo library if access is denied. Please use following code to check current status and verify it.
import Photos
if type == .CAMERA{
if AVCaptureDevice.authorizationStatus(forMediaType: AVMediaTypeVideo) == AVAuthorizationStatus.authorized {
completionHander(true) //Allowed
} else {
//Dont Know
AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeVideo, completionHandler: { (granted: Bool) -> Void in
completionHander(granted)
})
}
}else{
let status = PHPhotoLibrary.authorizationStatus()
switch status {
case .authorized:
completionHander(true)//Allowed
break
case .denied, .restricted :
completionHander(false)//Not Allowed
break
case .notDetermined:
//Dont Know
PHPhotoLibrary.requestAuthorization { status in
switch status {
case .authorized:
completionHander(true)
break
case .denied, .restricted:
completionHander(false)
break
case .notDetermined:
completionHander(false)
break
}
}
}
}
Okay I know the answer...
I just didn't get the concept.
If I deny the access to the photoLibrary it is not possible to save images but, if Camera access is granted I can still use the photoLibrary.
If camera access is denied, I still get the alert with the context from the info.plist
When requesting user's iOS location permissions, how could I know if locationManager.requestAlwaysAuthorization() has already been asked to the user?
In case the user had .AuthorizedWhenInUse status and the request for always authorization has been denied, the always-auth prompt for the next request won't be shown so I won't get any callback of this request launch.
Any ideas?
You need to check CLLocationManager.authorizationStatus() and only request authorization if the value is .notDetermined, since this is the only case when the authorization prompt will actually be shown.
You can check the Authorization status and compare if it's notDetermined it has not been asked, else - it's been asked.
You can know by using authorizationStatus() like this.
if CLLocationManager.authorizationStatus() == .notDetermined {
print("Not Determined")
}
else if CLLocationManager.authorizationStatus() == .restricted {
print("Restricted")
}
else if CLLocationManager.authorizationStatus() == .denied {
print("Denied")
}
else if CLLocationManager.authorizationStatus() == .authorizedAlways {
print("Always Authorized")
}
else if CLLocationManager.authorizationStatus() == .authorizedWhenInUse {
print("Authorized When Require")
}
If the Dialog appear for 1st time it returns .notDetermined status and if you respond to dialog than it returns status based on your selection like if you allow to access your location always that it returns .authorizedAlways.
After a lot of brainstorming this issue, I've found a solution.
Check authorization status each time the app starts, or when the app enters the foreground after the user views the Settings app.
If status is notDetermined, request authorizedWhenInUse. You can't jump straight to authorizedAlways.
If status is authorizedWhenInUse, request authorizedAlways.
The catch, as you know, is when the user goes from notDetermined to notDetermined, i.e. doesn't change their settings. We need to be able to see what the status was before they were prompted.
This is actually easy, just save the value of CLLocationManager.authorizationStatus() in a property called previousAuthStatus.
private var previousAuthStatus = CLLocationManager.authorizationStatus()
When the application enters the foreground, check the authorization state with that previous state.
Full code
Don't miss the last bit where previousAuthStatus is set
func checkStatus(_ status: CLAuthorizationStatus) {
switch status {
case .notDetermined:
// request "When in use"
locationManager.requestWhenInUseAuthorization()
case .authorizedWhenInUse:
// already tried requesting "Always"?
guard previousAuthStatus != .authorizedWhenInUse else {
<#notify the user...#>
break
}
// otherwise, request "Always"
locationManager.requestAlwaysAuthorization()
case .authorizedAlways:
// start updating location
<#dismiss any warning you may have displayed#>
locationManager.startUpdatingLocation()
case .denied:
<#notify the user...#>
case .restricted:
<#notify the user...#>
#unknown default:
break
}
// save status for next check
previousAuthStatus = status
}
I'm facing the same problem. This is not a problem for app updates (where you did not store authStatus in UserDefaults in previous version) only. It is also a problem if a user decides to uninstall and reinstall the app (user defaults are removed), or user manually change the status from 'Always' to 'When in Use' (no way of knowing that it was changed to 'When in Use' or if it was that status all along).
Btw: the user manually changing the authorisation is actually very common, because iOS now brings up an alert every once in a while "AppName has been using your location in the background... do you want to continue allowing this" (or something like that don't remember exactly). Lots of my users choose 'No' in that alert, resulting in the 'Always' location access being changed to 'When in Use' without the app getting a notification about that.
iOS however does remember if requestAlwaysAccess was already asked before, no matter what. Even after an uninstall and reinstall of the app.
So, with lack of other options I'm now using this, which honestly, is not the most user friendly, but it does work and is user friendly 'enough' (for me at least).
In stead of requesting always authorisation, I simply bring up an alert, with one of the buttons pointing to the apps Settings page, where user can then manually change the setting. I did add a userdefault storing if the app showed this alert before. If it did, I won't show it again.
switch CLLocationManager.authorizationStatus() {
case .restricted, .denied:
// show an alert with one button opening settings (see below)
case .authorizedAlways:
// already have always permission, continue with your code
case .notDetermined:
// request whenInUse authorisation (you can request always authorisation here too, but iOS won't show 'always' as a choice the first time)
case .authorizedWhenInUse:
guard !UserDefaults.showedNoAlwaysAuthorisationAlert else {
return
}
UserDefaults.showedNoAlwaysAuthorisationAlert = true
// show the alert with "no thanks" and "settings" button
// button action:
if 'settingsButtonTapped', let settingsUrl = URL(string: UIApplication.openSettingsURLString), UIApplication.shared.canOpenURL(settingsUrl) {
UIApplication.shared.open(settingsUrl, completionHandler: nil)
}
}
I'm updating my app to use CNContacts instead of AB. I've noticed that I am not getting prompted for granting permission to my Contacts. In the below switch, it is correctly telling me I am denied access - but then it doesn't prompt me to give it access. Furthermore, it just displays the picker and even stores the chosen properties into the tableview I am populating...
Is it not required to get permission to grab phone numbers or emails out of Contacts? I am confused why my code seems to be working if I am ".Denied"
//This code is called when you hit the "add a contact" button on my UI
switch CNContactStore.authorizationStatusForEntityType(.Contacts){
case .Authorized:
print("Already authorized")
presentPicker()
/* Access the address book */
case .Denied:
print("Denied access to address book")
store.requestAccessForEntityType(.Contacts){succeeded, err in
guard err == nil && succeeded else{
return
}
self.presentPicker()
}
case .NotDetermined:
store.requestAccessForEntityType(.Contacts){succeeded, err in
guard err == nil && succeeded else{
return
}
self.presentPicker()
}
default:
print("Not handled")
}
You do not need authorization to use CNContactsPickerViewController. It is "out of process"; it just works. In effect, it is the Contacts app sitting inside your app — and the user doesn't need permission to use the Contacts app.