Save file but hide file extension - Cocoa with Key Value Coding - swift

I'm saving some objects to a file using Key Value Coding. I'd like the extension of the saved file to be hidden (or at least be hidden unless the value in Finder → Preferences → Advanced "Show All File Extensions" is true), but I can't seem to get it working.
I'm saving the file like so:
let saveDialog = NSSavePanel()
saveDialog.allowedFileTypes = ["purr"]
saveDialog.beginWithCompletionHandler() { (result: Int) -> Void in
if result == NSFileHandlingPanelOKButton {
NSFileManager.defaultManager()
.createFileAtPath(saveDialog.URL!.path!, contents: NSData(), attributes: [NSFileExtensionHidden: NSNumber(bool: true)])
let _ = NSKeyedArchiver.archiveRootObject(safePhrases, toFile: saveDialog.URL!.path!)
}
}
return saveDialog.URL
But when viewing the saved files in Finder, the extension is always visible. How can I resolve this?

After Willeke suggestion I set the attributes after writing the file, using NSFileManager's setAttributes:ofItemAtPath:error.
do { try NSFileManager.defaultManager().setAttributes
([NSFileExtensionHidden: NSNumber(bool: true)], ofItemAtPath: saveDialog.URL!.path!) }
catch _{ Swift.print("Unable to hide extension") }

Related

MacOS how is KIND implemented

I am trying to write a piece of code that instead of checking file extensions for the many different types of image files, but instead looking at the file attributes. What I can’t figure out from searching the docs is if KIND:Image is really a file attribute or simply a construct Apple created in the FinderApp to make things easier for the user.
I wrote a snippet that pulls the attributes for files with an extension of jpeg and for each file the fileType is returned as NSFileTypeRegular.
let attr = try filemanager.attributesOfItem(atPath: pathConfig+"/"+file) as NSDictionary
if file.hasSuffix(ext) {
   print ("Adding \(file) [ \(attr.fileSize()) \(attr.fileType())]")
   print ( attr.fileModificationDate() )
}
Does anybody know if MacOS retains an attribute for the category a file falls in to. e.g. IMAGE, DOCUMENT etc.
To achieve a functionality similar to the Kind search tag in Finder you can use UTType (Link to reference).
You can get the UTType of a file by initialising it with the file extension:
let fileType = UTType(filenameExtension: fileURL.pathExtension)
The cool thing about UTTypes is that they have a hierarchy, for example, UTType.jpeg is a subtype of UTType.image, along with others like .png.
If you want to check if a file is any kind of image, you can do it like this
let isImage = fileType.conforms(to: .image)
You can check the list for the kind of types you want to support as "Kinds", and filter using those UTTypes
This was my final solution based on the information provided by #EmilioPelaez I am not completely comfortable with Swift especially the unwrapping operations so if the code looks weird that might be why.
func imagesInDir(path: String?) -> [String] {
if let path {
let filemanager: FileManager = FileManager()
let files = filemanager.enumerator(atPath: path)
var array = [String]()
var urlFile: NSURL
while let file = files?.nextObject() as? String {
urlFile = NSURL(fileURLWithPath: file, isDirectory: false)
if (urlFile.isFileURL) {
if let pathExt = urlFile.pathExtension {
if let fileType = UTType(filenameExtension: pathExt) {
let isImage = fileType.conforms(to: .image)
if (isImage){
array.append(file)
print ("\(array.count) \(fileType)")
}
}
}
}
}
}
return array
}

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 take a screenshot of your entire screen

I have created an Xcode swift based software that is menu based. One of the buttons I have created is intended to capture a screenshot and save the file to a specific location.
I have found sources explaining how to do this on iOS, but I'm looking for macOS functionality. The article: Programmatically Screenshot | Swift 3, macOS has responses that have gotten me close but I think some of it is deprecated.
How can I implement this in a software developed for macOS with Xcode & Swift 5.
Here is the code for the function:
#objc func TakeScreenshot(_ sender: Any){
func CreateTimeStamp() -> Int32
{
return Int32(Date().timeIntervalSince1970)
}
var displayCount: UInt32 = 0;
var result = CGGetActiveDisplayList(0, nil, &displayCount)
if (result != CGError.success) {
print("error: \(result)")
return
}
let allocated = Int(displayCount)
let activeDisplays = UnsafeMutablePointer<CGDirectDisplayID>.allocate(capacity: allocated)
result = CGGetActiveDisplayList(displayCount, activeDisplays, &displayCount)
if (result != CGError.success) {
print("error: \(result)")
return
}
for i in 1...displayCount {
let unixTimestamp = CreateTimeStamp()
let fileUrl = URL(fileURLWithPath: "~/Documents" + "\(unixTimestamp)" + "_" + "\(i)" + ".jpg", isDirectory: true)
let screenShot:CGImage = CGDisplayCreateImage(activeDisplays[Int(i-1)])!
let bitmapRep = NSBitmapImageRep(cgImage: screenShot)
let jpegData = bitmapRep.representation(using: NSBitmapImageRep.FileType.jpeg, properties: [:])!
do {
try jpegData.write(to: fileUrl, options: .atomic)
}
catch {print("error: \(error)")}
}
}
menu.addItem(NSMenuItem(title: "Take Screenshot", action:
#selector(AppDelegate.TakeScreenshot(_:)), keyEquivalent: ""))
The second portion of code is the menu item that is a button. I want this button to take a screenshot of the screen and then save the file to a location I specify.
I get this error when I use the button on my application:
Error
The error has to do with saving the file. You are constructing the URL badly.
First, expanding ~ to the home directory is generally a shell feature, not an OS feature. The underlying file path APIs (e.g. open()) just treat that as a normal character in a path. Foundation does support expanding ~ in path strings (not URLs), but you have to specifically request it with expandingTildeInPath. It's never automatic and it's never meaningful in URLs.
Next, I suspect you were trying to build a URL to a file within the Documents directory. However, you did not put a path separator (/) between the name of the directory and the name of the file. In other words, you constructed ~/Documents10989439875_1.jpg, not ~/Documents/10989439875_1.jpg.
You should use FileManager().urls(for:.downloadsDirectory, in:.userDomainMask)[0] to get the URL to the Downloads folder and then append a path component to that using appendingPathComponent(_:isDirectory:).

URL bookmark data when moved to trash

I use URL bookmark data (with security scope). After a file was moved to the trash (presumably when the App was not running), the bookmark is updated. That is generally good, but when a file was moved to the trash, I really want to reflect that and remove my bookmark.
The only way I can think to get around it is to store the original URL and compare their absoluteString or check for .Trash.
Is there a (simple?) way to check that bookmark data is now pointing to the Trash?
FileManager methods can be used if an URL refers to a file in a
trash, this avoids to hard-code the trash folder path. Here is
a translation of the Objective-C code in
Detect if file is in iCloudDrive Trash
to Swift, as an extension of URL:
extension URL {
func inTrashFolder() -> Bool {
do {
let fm = FileManager.default
let trashFolder = try fm.url(for: .trashDirectory, in: [], appropriateFor: self, create: false)
var relationShip = FileManager.URLRelationship.other
try fm.getRelationship(&relationShip, ofDirectoryAt: trashFolder, toItemAt: self)
return relationShip == .contains
} catch {
return false
}
}
}

macOS - How to have NSSavePanel to add a file extension in the file name?

I'm using this code to give the user the choice to specify a name and a location where to save a plain text file on disk. All seems to work but the saved file hasn't any extension. Actually I have not specify an extension in any part of my code, I read NSSavePanel documentation without notice the part where explained this option.
Here is the code I'm using:
let textToExport = mainTextField.textStorage?.string
if textToExport != "" {
let mySave = NSSavePanel()
mySave.begin { (result) -> Void in
if result == NSFileHandlingPanelOKButton {
let filename = mySave.url
do {
try textToExport?.write(to: filename!, atomically: true, encoding: String.Encoding.utf8)
} catch {
// failed to write file (bad permissions, bad filename etc.)
}
} else {
NSBeep()
}
}
}
Add the line
mySave.allowedFileTypes = ["txt"]
before presenting the panel.
From the documentation:
The value of this property specifies the file types the user can save
the file as. A file type can be a common file extension, or a UTI. The
default value of this property is nil, which indicates that any file
type can be used. (Note that if the array is not nil and the array
contains no items, an exception is raised.)
If no extension is given by the user, the first item in the
allowedFileTypes array will be used as the extension for the save
panel. If the user specifies a type not in the array, and
allowsOtherFileTypes is true, they will be presented with another
dialog when prompted to save.