Disabling the auto request for Photo Library access - ios

I am referring to this request:
The first time your app uses PHAsset, PHCollection, PHAssetCollection, or PHCollectionList methods to fetch content from the library, or uses one of the methods listed in Applying Changes to the Photo Library to request changes to library content, Photos automatically and asynchronously prompts the user to request authorization.
Is there any way to disable this and do it manually instead?

You can't "disable" the automatic request per se, if the authorisation status is undetermined the API will do the request automatically.
You can, however, request authorisation manually using PHPhotoLibrary.requestAuthorization
Which might look something like...
let status = PHPhotoLibrary.authorizationStatus()
switch status {
case .notDetermined:
PHPhotoLibrary.requestAuthorization({ (status) in
// Check the status and deal with it
})
case .restricted: fallthrough
case .denied:
// Deal with it
break
case .authorized:
// All is good
break
}

Related

PHPhotoLibraryPreventAutomaticLimitedAccessAlert still shows select photos option while presenting photo library

As per the quick read the purpose of this key
"PHPhotoLibraryPreventAutomaticLimitedAccessAlert" is to prevent limited library access. I have added this option in info.plist but it still shows "Select photos" option in permissions dialogue.
The code of getting permissions is following.
func checkPhotoLibraryPermission(completionBlock completion: #escaping ()->Void) {
let status = PHPhotoLibrary.authorizationStatus()
switch status {
case .authorized:
completion()
break
case .notDetermined,.denied,.restricted:
// ask for permissions
PHPhotoLibrary.requestAuthorization { (status) in
switch status {
case .authorized:
completion()
break
case .notDetermined,.denied,.restricted:
self.alertToEncouragePhotoLibraryAccessWhenApplicationStarts()
break
case .limited:
break
}
}
break
case .limited:
break
}
}
Anyone please mention what is the exact purpose of this key and is it possible to even force drop this option ?
As far as i know the purpose of this key "PHPhotoLibraryPreventAutomaticLimitedAccessAlert" is to prevent limited library access.
Then you "know" wrong. You cannot prevent use of limited authorization. It is built into the system and the user can always specify it.
So what is this key for? Well, if the user does specify limited access, the system may put up the Select Photos interface again from time to time to see whether the user wants to change what photos your app can access. PHPhotoLibraryPreventAutomaticLimitedAccessAlert prevents that. It does not prevent the user from specifying limited access in the initial authorization request alert or the Settings app.

How to force 'always' location access in iOS app

The app that I'm building needs always location access to work properly. The app basically tracks location and puts it on a map and stuff (details not important, lol).
My goal is this:
Prompt user to enable "always" location access
If always location access has been requested but the user said no, make the app unusable - basically just show a little button that redirects them to settings where they can change that setting.
My AppDelegate.swift is implementing CLLocationManagerDelegate, and the code is as follows:
alreadyRequestedLocationWhenInUse = UserDefaults.standard.bool(forKey: "alreadyRequestedLocationWhenInUse")
alreadyRequestedLocationAlways = UserDefaults.standard.bool(forKey: "alreadyRequestedLocationAlways")
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
switch CLLocationManager.authorizationStatus() {
case .notDetermined:
if (!alreadyRequestedLocationWhenInUse) {
print("Requesting location access 'while in use'.")
self.locationManager.requestWhenInUseAuthorization();
UserDefaults.standard.set(true, forKey: "alreadyRequestedLocationWhenInUse")
alreadyRequestedLocationWhenInUse = true
} else {
promptToChangeLocationSettings()
}
case .restricted, .denied:
print("No Location access")
promptToChangeLocationSettings()
break;
case .authorizedWhenInUse:
if (!alreadyRequestedLocationAlways) {
print("Requesting location access 'Always'.")
self.locationManager.requestAlwaysAuthorization()
UserDefaults.standard.set(true, forKey: "alreadyRequestedLocationAlways")
alreadyRequestedLocationAlways = true
} else {
promptToChangeLocationSettings()
}
break;
case .authorizedAlways:
self.startLocationMonitoring();
break;
default:
self.locationManager.requestAlwaysAuthorization();
return
}
}
where promptToChangeLocationSettings() is a properly working function that takes the user to the settings page for my app.
The problem is that the user isn't prompted to enable "Always location tracking" until they exit the app and come back. They are asked for 'while in use' permissions (and I know that the way it works is they have to say yes to that first), but I want the always prompt to happen right away! In theory, the locationManagerDidChangeAuthorization function should be called AGAIN after the 'while use' authorization is granted, but this does not happen! Why does this not happen? Instead, promptUserToChangeLocationSettings() runs and makes the app unusable BEFORE the user gets the little poup that asks whether they want to enable 'always' location access.
Can someone help me fix this?
By the way, I am using UserDefaults to keep track of whether we have done a location permission request (as the request can only be done once).
A few observations about this flow where we request “when in use” first, and when that's granted, only then request “always” (as discussed in WWDC 2020 What's New in Location):
Make sure you run this on a device, not the simulator. You may not see the subsequent “upgrade ‘when-in-use’ to ‘always’” permission alert when using a simulator.
This feature was introduced in iOS 13.4. Make sure you are not attempting this on an earlier iOS 13 version. On those earlier versions, you won’t see the second alert to upgrade to “always”.
Make sure you don’t have a lingering requestAlwaysAuthorization elsewhere in your code-base that might have put the app in a “provisional always” state. Once in provisional state, you are locked into the provisional flow of 13.0.
I know it isn’t what you’re looking for, but for the sake of future readers, the alternative to the above is the simpler “provisional always” flow introduced in iOS 13.0 (outlined in WWDC 2019's What's New in Core Location). You just call requestAlwaysAuthorization (never calling requestWhenInUseAuthorization). Apple's intent here was to let the user better reason about what’s going on, showing the “when in use” alert while the app is in use and automatically showing the “always” upgrade alert when location services are used while the app isn't running.
This is a solution that got the result I desired:
Firstly: Make a call to locationManagerDidChangeAuthorization(locationManager) in the AppDelegate.swift didFinishLaunchingWithOptions function. I also called it in applicationWillEnterForeground just so that it rechecks every time the app opens.
Secondly, this is my new locationManagerDidChangeAuthorization function. Just need to remove the return/break statements, but I'm just gonna answer this now before I forget:
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
switch CLLocationManager.authorizationStatus() {
case .notDetermined:
UserDefaults.standard.set(false, forKey: "alreadyRequestedAlwaysLocationAccess")
alreadyRequestedAlwaysLocationAccess = false
DispatchQueue.main.async{
self.coreLocationManager.requestWhenInUseAuthorization()
self.locationManagerDidChangeAuthorization(manager)
}
break;
case .restricted, .denied:
print("No Location access")
promptToChangeLocationSettings()
break;
case .authorizedWhenInUse:
if (!alreadyRequestedAlwaysLocationAccess) {
print("Requesting location access 'Always'.")
UserDefaults.standard.set(true, forKey: "alreadyRequestedAlwaysLocationAccess")
alreadyRequestedAlwaysLocationAccess = true
DispatchQueue.main.async{
self.coreLocationManager.requestAlwaysAuthorization()
self.locationManagerDidChangeAuthorization(manager)
}
} else {
promptToChangeLocationSettings()
}
break;
case .authorizedAlways:
self.startLocationMonitoring();
break;
default:
return
}
}

PhotoLibrary access .notDetermined, when denying then enabling

If I deny at first, then go to settings and allow in settings, in both cases the status is notDetermined, instead of denied then authorized.
Why is that happening?
It doesn't save the image when i click "Don't allow", but status becomes .notDetermined not .denied .
It saves, after i go to settings->Photos, uncheck "Never" and check "Add Photos Only". But the status stays .notDetermined, does not become .authorized
func save(){
guard let image = imageView.image else {return}
UIImageWriteToSavedPhotosAlbum(image, self, nil, nil)
let status = PHPhotoLibrary.authorizationStatus()
switch status {
case .authorized:
print("authorized")
return
case .notDetermined:
print("not determined")
case .denied, .restricted:
print("denied or restricted")
//please go to settings and allow access
promptToSettings()
}
}
I am asking permission to save an image to photo library.
When the first time the user tries to save, he gets asked: "App would like to add to Photos" "Don't Allow" "Ok"
If the user denied then tried to save again,i want to check and if the status is .denied, prompt the user to go to settings and allow.
But the code goes to .notDetermined block when the user does not give access the first time. It stays .notDetermined even after in settings the user allows access.
I downloaded your code and ran it. I was able to experience whatever you said. It always returned Not Determined status.
I did a little bit analysis on your code further. Please find my observation below.
In your current code, "PHPhotoLibrary.requestAuthorization" method is not called before reading the authorization status using "PHPhotoLibrary.authorizationStatus" method.
Though "UIImageWriteToSavedPhotosAlbum" in Save method triggers a Photos app permission pop up, it does not help here completely.
So, I called "askForAccessAgain" method in ViewDidLoad method in order to get the permission using "PHPhotoLibrary.requestAuthorization" method first.
After that, whenever i saved the photos using Save method, it returned the correct status, let it be either "Authorized" or "Denied".
If I choose "Never" and "Allow Photos Only", it returned "Denied or Restricted" status. When "Read and Write" is chosen, "authorized" is returned.
So, it looks like, We need to call "PHPhotoLibrary.requestAuthorization" method to get the permission first instead of relying on other Photo access methods ( like UIImageWriteToSavedPhotosAlbum) for getting the permission. Only then, correct status is returned by "PHPhotoLibrary.authorizationStatus".
One more addition info:
App Permissions are retained even after deleting the app and reinstalling it. Just take a look on this as well. It might help you in troubleshooting similar issues in future.
iPad remembering camera permissions after delete—how to clear?
Please let me know if this has resolved your issues.

Fetching data from Facebook page's events page

I would like to pull data from a Facebook event page that would fetch all the events listed by a Facebook page. I have to up the iOS SDK, but i am now unsure how to call the Graph API. I found the PAGE ID for a Facebook group page i want to take the events from by looking at the page's source code and finding "PAGEID".
I have set it up all in my dashboard along with my Bundle ID. The only left is to call the Graph API, but i am unsure and left confused when trying to following their doc.
Show us the code you tried already and what is not working.
Have you tried this already? (where {page-id} is your page's ID):
let connection = GraphRequestConnection()
connection.add(GraphRequest(graphPath: "/{page-id}/events")) { httpResponse, result in
switch result {
case .success(let response):
print("Graph Request Succeeded: \(response)")
case .failed(let error):
print("Graph Request Failed: \(error)")
}
}
connection.start()

How to request for permission before the actual object is initialized?

How can I request for permissions to location, camera, bluetooth etc. without initializing proper object instances?
I want to ask for permissions/authorizations during app's onboarding, then I'd like to initialize CLLocationManager etc afterwards.
I tried Googling it, but failed to find anything relevant. Is it actually possible?
For permissions like location access ,we need a manager instance to show the location access prompt ( refer :http://nshipster.com/core-location-in-ios-8/). These instances can be used only to request access (if you want them to only request for access) and in the future if you want to access the resource or data we can again use these manager instances.
For Example:
CLLocation manager should be used to access user's location, so in first screen if you just want to ask location permission then can use following code
CLLocationManager().requestAlwaysAuthorization() //Requesting always permission
And if you want to access user's location in some other screen you can access it as:
locationManager.startUpdatingLocation() // use delegate methods to handle the values.
So these kind of managers can be initialised only for requesting permission, then can be reinitialised when required.
Here is an article about best way to ask location permissions (same can be applied for other types of permission requests) https://techcrunch.com/2014/04/04/the-right-way-to-ask-users-for-ios-permissions/
For each action do the similar approach
let status = PHPhotoLibrary.authorizationStatus()
switch status {
case .authorized:
case .denied, .restricted :
//handle denied status
case .notDetermined:
// ask for permissions
PHPhotoLibrary.requestAuthorization() { (status) -> Void in
switch status {
case .authorized:
// as above
case .denied, .restricted:
// as above
case .notDetermined: break
// won't happen but still
}
}
}

Resources