How to get authorization programmatically using Swift 4 to copy a video file to Photo Library from an application directory - swift

When we fill value string section in Info.plist for "Privacy - Photo Library Additions Usage Description" and as the application try to copy video file to Photo Library iOS automatically ask authorization and everything goes ok after that.
But in that case it is not enough. We would like to do things if user do not want it.
We are doing it for camera usage as following;
func checkCameraAuthorizations(){
// Checks privacy authorizations and change aplication behaviour accordingly.
if AVCaptureDevice.authorizationStatus(for: .video) == .authorized {
cameraUsageAuthorized = true
} else {
AVCaptureDevice.requestAccess(for: .video, completionHandler: { (granted: Bool) in
if granted {
self.cameraUsageAuthorized = true
} else {
self.cameraUsageAuthorized = false
}
})
}
}
We are using cameraUsageAuthorized variable in several place in application about camera usage.
But we could not find a similar function for video file copy from application document directory to Photo Library.
Also, we are filling Privacy values by hand in Info.List. Is there anyway to do it programmatically?

I could not find write only permission but as much as I see following code takes both write and read permission to photo library.
if PHPhotoLibrary.authorizationStatus() == .authorized {
self.photoLibraryAuthorized = true
} else {
PHPhotoLibrary.requestAuthorization { status in
if status == .authorized {
self.photoLibraryAuthorized = true
} else {
self.photoLibraryAuthorized = false
}
}
}
But you must fill Privacy - Photo Library Additions Usage Description and Privacy - Photo Library Usage Description values.
I still don't know how to fill permission value strings in Info.plist programmatically though.

Related

Unable to save images to Egnyte/Google Drive/Dropbox - Swift/Xcode

UPDATE
I tried the following code solution and it allows for me to save to Google Drive now, but Egnyte and Dropbox are still greyed out.
func exportPhotosToFileLocation() {
var fileURLArray = [URL]()
for data in reviewDataController.tableViewReviewData {
guard let imageData = data.image.jpegData(compressionQuality: 1.00) else {
print("ERROR: Unable to print convert image to jpegData in exportPhotosToFileLocation!")
return
}
let fileManager = FileManager.default
do {
let fileURL = fileManager.temporaryDirectory.appendingPathComponent("\(data.imageTitle)").appendingPathExtension("jpeg")
try imageData.write(to: fileURL)
fileURLArray.append(fileURL)
print("Successfully created file from jpegData in exportPhotosToFileLocation!")
} catch {
print("ERROR: Unable to create file from jpegData in exportPhotosToFileLocation!")
return
}
}
if #available(iOS 14, *) {
let controller = UIDocumentPickerViewController(forExporting: fileURLArray)
present(controller, animated: true)
}
else {
let controller = UIDocumentPickerViewController(urls: fileURLArray, in: .exportToService)
present(controller, animated: true)
}
}
Here is the developer documents for Egnyte. Unfortunately, none of it makes sense to me as a beginner.
Egnyte Developer Documentation
----------------------------------------------------------------------------------------------
ORIGINAL POST
In my app, I'm trying to allow the user to select a save location (so choose a folder). Whenever I use this code, Egnyte/Google Drive/Dropbox are all "greyed" out and inaccessible.
let supportedTypes : [UTType] = [UTType.folder]
let documentPickerController = UIDocumentPickerViewController(forOpeningContentTypes: supportedTypes)
documentPickerController.delegate = self
self.present(documentPickerController, animated: true, completion: nil)
If I change supportedTypes to
let supportedTypes : [UTType] = [UTType.text]
It does let me access them. Does anyone have a solution for this? I obviously need the user to be able to select a folder in these applications... you can see why that is important.
This is up to the file provider extension (Google Drive, etc.). To allow picking a folder, the file provider has to lay content in its directory in a hierarchical manner... if they do this, they need to specify NSExtensionFileProviderSupportsPickingFolders in their Info.plist to tell the system it's allowed to choose folders.
Do you need to choose a save location and persist it? If yes, then you'll be blocked on the file provider implementing the necessary API. If not, the type you pass should the type of the document you are actually saving. The document will be saved once in the chosen folder (without any additional requirements on the file provider extension), and you will have to use the document picker again to save the next document.
If you are trying to select Dropbox as a location to import files from in the Apple File Importer but it does not advance to the file selection screen I found that restarting my iPhone seemed to resolve that issue.

How to relinquish MacOS Camera,Microphone

In trying to code up a prototype, it struct me there's no way revert the request?
So, you add the entitlements of interest to your app's capabilities, and here is I check:
internal func avStatus(for media: AVMediaType) -> AVAuthorizationStatus {
let status = AVCaptureDevice.authorizationStatus(for: media)
switch status {
case .authorized: // The user has previously granted access to the microphone.
return .authorized
case .notDetermined: // The user has not yet been asked for microphone access.
return .notDetermined
case .denied: // The user has previously denied access.
return .denied
case .restricted: // The user can't grant access due to restrictions.
return .restricted
default:
Swift.print("Unknown AV status \(status) for .audio")
return status
}
}
I was thinking that a user action to request the use of the entitlement, and relinquish would be needed:
#objc #IBAction func audioVideoServicesPress(_ sender: AnyObject) {
let service = sender.title.components(separatedBy: " ").last
let media : AVMediaType = service == "Audio" ? .audio : .video
let status = self.avStatus(for: media)
guard ![.denied,.restricted].contains(status) else { return }
if status == .authorized {
print("how do we relinquish need in the a/v device")
}
else
{
AVCaptureDevice.requestAccess(for: media, completionHandler: { granted in
return
})
}
}
In other user actions, if they had disabled or denied, then I would route them to the proper settings app, but before I get there, how to undo the request?
This is no way to un-request access the a/v device?
I think that suggests I'm going about this all wrong?
There is nothing to "relinquish."
The user's button press is an intent. The user wants to perform some action that can be performed only if you have authorization. So if you have it or can get, your job now is to do it.
Okay, so either you have authorization or you don't. If authorization status is .undetermined, you might get it. If it's .authorized, you already did get it. In either of those cases, do what the user intends!
At the time of your print line, you have authorization so now go ahead and do whatever the user pressed the button intending to do.
Similarly, do not return in the completion handler for requesting access; instead, check granted and if true, do whatever the user pressed the button intending to do.
In any other case, you are hosed, so do nothing. The user's intent requires an authorization you don't have and cannot get. You might put up a dialog explaining why you can't do it, or send them off to where they can access their settings, but that's all you can do from here.

iOS 13 AVAsset: mediaOptions on AVAssetCache returning empty array for audio and captions

We have an app that downloads assets and media selections using .aggregateAssetDownloadTask. In iOS 12.4 and below we have been able to access the captions tracks when offline using the asset's assetCache. On an iOS 13 device, the assetCache for the captions is now empty despite being downloaded. We can get the captions if we directly access the assets tracks using asset.mediaSelectionGroup(forMediaCharacteristic: characteristic).
We access the tracks like this:
guard let asset = currentItem.asset as? AVURLAsset else { return [] }
let cache = asset.assetCache
if let group = asset.mediaSelectionGroup(forMediaCharacteristic: characteristic), let languages = cache?.mediaSelectionOptions(in: group) {
if medium == .captions {
return languages.filter({ $0.hasMediaCharacteristic(AVMediaCharacteristic.containsOnlyForcedSubtitles) == false }).compactMap({ $0 as Language})
} else {
return languages
}
}
We've filed a radar with apple but we're wondering if anyone else has ran into this issue? If so what workaround you're using? We're hesitant to access the mediaSelectionGroup on the asset.

How to get active tab in a Safari App Extension?

When I follow the Apple example I always get a nil activeTab:
override func toolbarItemClicked(in window: SFSafariWindow) {
// This method will be called when your toolbar item is clicked.
window.getActiveTab(completionHandler: { (activeTab) in
print(activeTab)
activeTab?.getActivePage(completionHandler: { (activePage) in
print(activePage)
activePage?.getPropertiesWithCompletionHandler( { (properties) in
print(properties)
if properties?.url != nil {
let urlString = properties!.url.absoluteString
print("URL!", urlString)
let videoURL = self.youtubeDl(urlString: urlString)
if videoURL != nil {
window.openTab(with: videoURL!, makeActiveIfPossible: true, completionHandler: nil)
}
}
})
})
})
}
Yes there is a getActiveTab:
SFSafariApplication.getActiveWindow { (window) in
window?.getActiveTab { (tab) in
tab?.getActivePage(completionHandler: { (page) in
}
}
}
Your Safari App Extension can only access the tabs of sites your extension is configured to access.
In your Info.plist, verify that the SFSafariWebsiteAccess dictionary:
Has a Level equal to "Some" or "All".
Has all required domains listed in the Allowed Domains array (if Level="Some").
Be aware that your extension's website access permissions are displayed in Safari > Preferences > Extensions. You are encouraged to limit your website access to "Some" (and explicit domains) unless you really do require access to every website.
You should go to info.plist, NSExtension->SFSafariWebsiteAccess and set something ( for example * - for all sites) to Allowed domains. Without this option, I always got nil from the window. I think your extension doesn't work if the site is not allowed.

Any way to get the name of iPhone user?

Outside of asking the user to input their name, is there any way to get it off the device?
I tried this library, which attempts to extract the name from [UIDevice currentDevice] name], but that doesn't work in a lot of situations:
https://github.com/tiboll/TLLNameFromDevice
Is the user's name present in the phonebook or anywhere else that we have access to in iOS 6?
Well you could go through all the contacts in the AddressBook and see if any of them are marked with the owner flag.
Just be aware that doing this will popup the "this app wants access to the address book" message. Also Apple isn't very keen on these kind of things. In the app review guide it is specified that an app can not use personal information without the user's permission.
You could use Square's solution:
Get the device's name (e.g. "John Smith's iPhone").
Go through the contacts on the phone and look for a contact named "John Smith".
JBDeviceOwner and ABGetMe will both do this for you.
You could use CloudKit. Following a snippet in Swift (ignoring errors):
let container = CKContainer.defaultContainer()
container.fetchUserRecordIDWithCompletionHandler(
{
(recordID, error) in
container.requestApplicationPermission(
.PermissionUserDiscoverability,
{
(status, error2) in
if (status == CKApplicationPermissionStatus.Granted)
{
container.discoverUserInfoWithUserRecordID(
recordID,
completionHandler:
{
(info, error3) in
println("\(info.firstName) \(info.lastName)")
}
)
}
}
)
}
)
The above code was based on the code at http://www.snip2code.com/Snippet/109633/CloudKit-User-Info
to save folks time. in swift4:
let container = CKContainer.default()
container.fetchUserRecordID(
completionHandler: {
(recordID, error) in
guard let recordID = recordID else {
return
}
container.requestApplicationPermission(
.userDiscoverability,
completionHandler: {
(status, error2) in
if (status == CKContainer_Application_PermissionStatus.granted)
{
if #available(iOS 10.0, *) {
container.discoverUserIdentity(withUserRecordID:
recordID,
completionHandler:
{
(info, error3) in
guard let info = info else {
return
}
print("\(info.firstName) \(info.lastName)")
}
)
}
}
}
)
}
)
however: CKUserIdentity no longer exposes either first or last name
So this answer no longer works.
You can use:
NSLog(#"user == %#",[[[NSHost currentHost] names] objectAtIndex:0]);
I did receive compiler warnings that the methods +currentHost and -names were not found. Given the warning, I’m not sure of Apple’s intention to make this available (or not) as a publicly accessible API, however, everything seemed to work as expected without the need to include any additional header files or linking in additional libraries/frameworks.
Edit 1:
You may also take a look at this Link
Edit 2:
If you have integrated your app with Facebook you can easily retrieve the user info, see Facebook Fetch User Data
For SWIFT you can use
NSUserName() returns the logon name of the current user.
func NSUserName() -> String