My app only needs the 'Privacy - Photo Library Additions Usage Description' property in the plist file (app writes to Photo library, but doesn't read).
I use this code to check for permissions:
PHPhotoLibrary.requestAuthorization { status in
if status == .authorized {
print("Permission granted")
} else {
print("Unavailable")
}
}
In the iOS Settings for my app, even though the plist only needs 'Add Photos Only', 'Read and Write' is also listed (??), and the above code only gets the authorised status if 'Read and Write' is ticked (ticking 'Add Photos Only' just causes the above code to go through to the else statement).
I'm a bit confused to why 'Read & Write' is even listed as a setting for my app, when my plist doesn't include "Privacy - Photo Library Usage Description".
Any ideas?
It's because you are calling PHPhotoLibrary.requestAuthorization.
If all you need is to write blindly into the user's camera roll, do not call PHPhotoLibrary.requestAuthorization. Just go ahead and write, e.g. by calling UIImageWriteToSavedPhotosAlbum or UISaveVideoAtPathToSavedPhotosAlbum. The runtime will request authorization on your behalf.
But if you want to interact with the photo library itself, i.e. thru the Photos framework, then you need authorization and an entry under Privacy - Photo Library Usage Description, even if all you intend to do is write.
Judging by your comments, you may be confused about what requestAuthorization does. It tries to obtain permission. If all you want to know is what the current authorization status actually is, call authorizationStatus instead.
However, even then, we're talking about the photo library itself, and thus the value referred to as Read & Write in Settings.
If you want to know whether you have permission to do UIImageWriteToSavedPhotosAlbum or UISaveVideoAtPathToSavedPhotosAlbum, just go ahead and call it. If there's a permissions problem, you'll hear about it in the completion handler.
Related
I made a music app for macOS that need the access of the file on disk. I can get the proper url, but I can't access the file. To figure out that problem, here I create a concise code snippet to concentrate on the question itself.
.onAppear {
var url = FileManager.default.homeDirectoryForCurrentUser
url.appendPathComponent("Downloads/Test.txt")
var text: String = ""
do {
text = try String(contentsOf: url)
} catch {
print("ERROR: \(error.localizedDescription)")
}
}
The output would be like this
ERROR: The file “Test.txt” couldn’t be opened because you don’t have permission to view it.. So I noticed App Sandbox in Targets > Signing & Capabilities. But at the list of file access (as you can see in the following picture), there's only User Selected File, Downloads Folder, Pictures Folder, Music Folder and Movie Folder. So I come up with 3 questions:
What that User Selected File means? Is that means that the user can select some specific file for my app to access? If it is, how can user selects them?
How can I get access to other folders like Desktop Folder if I remain the App Sandbox capability here?
I also noticed the trash icon on right-upper corner. If I delete App Sandbox, I can access every folder I want with the user's agreement by a default system pop-up. But what the deletion of App Sandbox means? Does it make my app "untrusted" somehow when user install it or others? Does it cause any security problems?
And It couldn't be better if you can tell me the conventions major developers follow when meet this problem. I would highly appreciate your help.
Is it possible to access calendar events in screen saver? I am able to request permission in the application. But some API is not prompting in the case of the screensaver.
I didn't find any documentation around it.
I used the following code to requesting calendar events:
func requestPermissionForCalendar()
{
var eventStore = EKEventStore()
eventStore.requestAccess(to: EKEntityType.event) { granted, error in
if granted{
print("Permission granted")
}else{
print("The app is not permitted to access reminders, make sure to grant permission in the settings and try again")
}
}
}
Couple of things, first, a screen saver is completely passive in macOS. Any prompt for anything will be disregarded when it's running fullscreen, as a screensaver.
In general, the proper way to do this with a screensaver is to ask for permission in the configurationSheet (the screen saver options). The user will get prompted there, and can allow or deny you. I do use this for example to request the user location.
Once allowed in System Preferences, your screensaver when running in screensaver mode will get access. I suggest you test your code first in the configuration sheet as this is much easier.
Additional note, ScreenSavers on macOS since Catalina are sandboxed by an application extension provided by Apple, and that extension has a limited list of entitlements (see a list here https://github.com/glouel/ScreenSaverMinimal).
I'm not particularly familiar with EKEventStore, but if it requires a specific entitlement to make it work, you'll be stuck as you can't add any entitlement yourself, everything depends on legacyScreenSaver.appex.
When using my app, the user is asked permission to access camera and library one time (when needed), answering allow or not in the "apple alert" (requestAccess).
I need to add a view where the user can be able to change these authorizations afterwards if he wants. I added switch buttons so the user can make the choice to keep it authorized or not. But I can't find how to set the authorizationStatus.
Is there a way to change the authorizationStatus manually, from .authorized to .denied or opposite ?
I tried to prompt the apple alert using 'AVCaptureDevice.requestAccess()' but it doesn't show if the user already authorized before.
Any suggestion welcome :)
By security and privacy reasons developers have only one way - use system dialog for requesting authorization for access to protected system services.
But you can suggest for your users a short way to system settings. This code launches the Settings app and displays your app’s custom settings.
let settingUrl = URL(string: UIApplication.openSettingsURLString)
DispatchQueue.main.async {
UIApplication.shared.open(settingUrl!, options: [:], completionHandler: nil)
}
I've been using the CGWindowListCreateImage function for years to capture the desktop as an image. In Swift, I return an image (NSImage) as follows.
func makeCGWindowImage(size: CGSize) -> NSImage? {
if let imageRef = CGWindowListCreateImage(CGRect.infinite, .optionAll, kCGNullWindowID, .bestResolution) {
let image = NSImage(cgImage: imageRef, size: size)
return image
}
return nil
}
I need to use the CGWindowListCreateImage function in order to capture the desktop area involving not just my application itself but also others (folder windows, the Safari web browser window...). The thing is that I get an alert message regarding screen recording privacy if I use the window image function above.
The worst part of using the CGWindowListCreateImage function is that the reviewer rejects the application because the application unnecessarily shows the privacy message. If I ask the reviewer what screen recording privacy is for, he or she says that it's for making a video, which sounds like B.S. to me. And the reviewer suggests that I use alternatives. I've asked, but the reviewer doesn't say what those alternatives are. So are there alternatives to the CGWindowListCreateImage function for capturing the desktop area involving not just my application but also other windows.
No matter what method or API call you use; MACOS will ask for granting access to screen recording.
However this is a one time permissions and once the user has allowed access to the app then the privacy message will not show again and again.
There is no way to avoid the privacy message from being displayed at least once.
If you can elaborate more on your question that will get you a more targeted answer.
How is it possible to remove chartboost banner after in-App purchase ?
use something like this
set bool key isPurchase YES in your NSUserDefaults when you purchase App
if(![userDefaults boolForKey:#"isPurchase"])
{
NSLog(#"Enter add start ");
[[Chartboost sharedChartboost] showInterstitial];
}
[userDefaults synchronize];
}
The easiest way is to save the information in user Defaults as mentioned in above post but this won't be secure because user defaults can easily be accessed by many softwares and one can edit/add your Key unless it is kind of big one and secret OR no body post it as a Hack.
The best thing is to store this information at Server (if you have it) or store this information in Key Chain because it is secured.
How to do that easily with keychain follow below post
iOS: How to store username/password within an app?
Chartboost offers a delegate method -(BOOL) shouldDisplayInterstitial:(NSString*)location
Returning "false" in this method will prevent an ad from showing; remember to return "true" if you do want an ad to show. Just compare against a boolean stored locally to track if they bought the IAP or not.
The reason recommend this method rather than hardcoding is because you might want to leave a few locations specific for cross-promotion campaigns promoting your own apps that will still be able to show ads in the future.
This way when you release a new app you can run a limited promotion and make sure that your most loyal fans who bought the no-ads IAP can still see a cross-promo ad for your new game - a great way to funnel your highest quality users to your new app! If you make the ad art right, they won't even know it's an advertisement and you can make it so they only see it once.
Then you can add showInterstitial:#"cross-promo" wherever you might want to show this and disable all publishing campaigns just for that location. Then, in the future when you have a new app, add a new cross-promotion campaign in that location promoting your new app and EVERYONE will see it - even the people who bought IAP!
an example of this implementation:
-(BOOL) shouldDisplayInterstitial:(NSString*) location {
if(_userBoughtNoAdsIAP && location != #"cross_promo"){
return FALSE;
}
return TRUE;
}
And don't forget to set the [Chartboost sharedChartboost].delegate = self; to make sure the delegate methods function properly!
Full Disclosure: I work for Chartboost