Swift 3 Mac OS Copy Audio File to NSPasteboard - swift

I am trying to copy an audio file to the NSPasteboard so that it can be pasted somewhere else on the computer or into another program like Ableton or Pro Tools. Here is how I am getting the url of the file. (An example url d after casting to string is: file:///Users/ben/Music/Ableton/User%20Library/Vox.wav )
let url = directoryItems?[tableview.selectedRow].url
let urlString = (url?.absoluteString)! as String
let pb = NSPasteboard.general()
let pasted = pb.writeFileContents(urlString)
It is not being copied to the pasteboard (pasted is set to false) and I can not find any resources that talk about writing audio files to the NSPasteboard. Any help would be greatly appreciated.
Edit:
I also tried using the url instead of the string and had the same outcome
let pb = NSPasteboard.general()
let pasted = pb.writeObjects([url as! NSPasteboardWriting])

To copy a file in such a way that you can paste it in the Finder, you'd want a file URL, not a string.
As for copying the music into a music editor, presumably you'd need to load the music file yourself into some waveform format that can be pasted into that editor.

Related

Is there a way to work with FileManager and NSBundle to get all the applications that support a given type of file in the /Applications directory?

I'm creating a Commandline tool that will get an input from a user to search for a specific application that supports a certain file extension. For example, if I enter mp4, it will probably show me QuickTime. I'm looking for the specific FileManager manipulation to achieve this in Swift.
I think you are looking for NSWorkspace.urlForApplication(toOpen:). It finds the application that would be opened if you had double clicked on a file, and returns its URL. Since it requires a file to work, you need to first create a temporary empty file somewhere, with the desired extension, then call this method.
let tempFileURL = FileManager.default.temporaryDirectory.appendingPathComponent("foo.mp4")
FileManager.default.createFile(atPath: tempFileURL.path, contents: nil, attributes: nil)
// this gives me the URL for QuickTime Player:
// file:///System/Applications/QuickTime%20Player.app/
let url = NSWorkspace.shared.urlForApplication(toOpen: tempFileURL)

Mac Sandbox issues with opening embedded RTF files

My Mac programs usually ship with some internal Rich Text files containing legal details. I use the NSWorkspace openFile call to open the files within TextEdit.
The code looks something like this:
guard let aPath = Bundle.main.path(forResource: “Legal.rtf”, ofType: nil) else { return }
NSWorkspace.shared.openFile(aPath, withApplication: nil)
This has always worked, until recently when this code returns “The application can’t be opened. -50”. Is that a Sandbox issue? Accessing files within your bundle should be allowed. We do it for images and such.
What do I have set wrong?
Thank you!
Thank you for your comments.
I should have mentioned at first that parameter string I had included both filename and the file type (extension). So I would have to split them, something that was easy to do with NSString, but is not immediately available for Swift String. A bit of conversion would have given me the two strings.
However, that OpenFile has been replaced with the newer open(_:)
let name = "Legal.rtf"
guard let aURL = Bundle.main.url(forResource: name, withExtension: "") else { return }
NSWorkspace.shared.open(aURL)
This call does NOT mind if you pass it string with both parts.

Swift: How to unzip file not contained in my bundle?

I am not very familiar with Swift programming but I need to write a small tool in Swift which can unzip a file (and then launch a program). I need to unzip a file which is not contained in my app bundle. It is located in /Users/me/folder1/folder2/openjdk-11.0.2.zip
I tried the libraries "Zip", "ZipFoundation", and "SSZipArchive". From what I read so far, I think that the libraries which I tried need the zip file to be located in the app bundle but I am not sure.
With "Zip" I tried:
_ = try Zip.quickUnzipFile(URL(string: openjdkZipUrl!.relativePath)!)
With "ZipFoundation" I tried:
let fileManager = FileManager()
let archive = openjdkZipUrl
let destinationURL = openjdkFolderUrl
do {
try fileManager.unzipItem(at: archive.url, to: destinationURL)
} catch {
}
ZipFoundation told me "Value of type 'FileManager' has no member 'unzipItem'" but I imported it with import Foundation. I also have it (and the other libraries) in my Podfile.
With "SSZipArchive" I tried:
let success = SSZipArchive.unzipFile(atPath: openjdkZipUrl!.path, toDestination: openjdkFolderUrl!.path)
The used paths are
let openjdkZip = "file:///Users/" + user + "/folder1/folder2/openjdk-11.0.2.zip"
let openjdkZipUrl = URL(string: openjdkZip)
and
let openjdkFolder = "file:///Users/" + user + "/folder1/folder2/openjdk-11.0.2"
let openjdkFolderUrl = URL(string: openjdkFolder)
Is it really a problem that the zip file is not contained in my bundle? Can someone tell me what I did wrong?
Thanks in advance
#trojanfoe mentioned in the comments:
Mac apps are sandboxed by default which means they have no access to a user's files unless they ask for it by getting the user to open the file/folder. You should ensure it's turned off for your app, however you are testing if the file exists and if this is succeeding then it looks like sandboxing is not turned on?
I looked at the .entitlements file in my project and found out that "App Sandbox" was set to "YES". I set it to "NO" and it worked perfectly.
It seems that you can check for a file while in sandbox mode (as I did with my condition) but not access them when trying to unzip them.
Thanks again #trojanfoe :)

Accessing File saved in today extension from project

I am trying to access a file I saved from my today extension.In my today extenson I did this to save the file:
func startRecording() {
let audioFilename = getDocumentsDirectory().appendingPathComponent("recording.m4a")
let settings = [
AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
AVSampleRateKey: 12000,
AVNumberOfChannelsKey: 1,
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
]
do {
audioRecorder = try AVAudioRecorder(url: audioFilename, settings: settings)
audioRecorder.delegate = self
audioRecorder.record()
recordButton.setTitle("Stop", for: .normal)
} catch {
finishRecording(success: false)
}
}
func getDocumentsDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
return documentsDirectory
}
I then tried to get the data for my AVAudio Player in the main part of the project using this code:
let path = Bundle.main.path(forResource: "recording.m4a", ofType:nil)!
let url = URL(fileURLWithPath: path)
However, it gave the error: unexpectedly found nil while unwrapping an Optional value.
Thanks for the help.
Your extension saves the file to its document directory and your app code is looking for the file in the app bundle. The app bundle only contains the resources that are distributed with the app. You'll need to delve into the file system.
However, there's another problem. The extension and containing app don't share a documents directory. They each have their own container for writing data local to themselves. If you want to share data between them, it's a little more work. In summary:
Create an app group identifier for the app and the extension to share.
Use FileManager.containerURL(forSecurityApplicationGroupIdentifier:) to get the file URL for the shared container directory.
From the container URL, append the file name.
In the extension, you'll set up the AVAudioRecorder as usual and start recording.
In the main app, you'll want to use the NSFileCoordinator API to ensure that only one process is writing to the file at a time. Hopefully, the AVAudioRecorder uses the NSFileCoordinator API internally, although I didn't immediately find confirmation of this.
For more details about shared containers, see this blog post.
I just tried the same - record audio from a Today Extension. The code looks sooo familiar, so I'm taking a wild guess: you want to capture voice and send the file to the Google Speech API, correct?
Nonetheless, I think we're hitting restrictions of extensions: judging by https://developer.apple.com/library/content/qa/qa1872/_index.html extensions cannot record audio. The article has been writting for iOS 8, but I don't believe Apple ever lifted the restriction. Please correct me if I'm wrong, since I keep running into problems doing what OP does myself.
btw, check the result of audioRecorder.record(). It might be false and indicate that the capture never started (that's my error currently).

Swift - Storing data in directory using NSSearchPathForDirectoriesInDomains, but directory always changes?

I have a sound recorder app where recorded sounds are stored like this:
let dirPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory,.UserDomainMask,true)[0] as String
var pathArray = [dirPath, recordingName]
let filePath = NSURL.fileURLWithPathComponents(pathArray)
All file path URL's are stored in an array (put into NSUserDefaults) for easy access.
I can play the files if they're played on the same run as when they're recorded, but opening and closing the simulator back up will result in the file path being wrong (since the app-id folder at ~/Library/Developer/CoreSimulator/Devices/Device-ID/data/Container/Data/Application/app-id-folder/Documents/filename.wav will have changed name). How can I change the array containing the file URL's to have the updated app-id folder name each time the app is run? (currently it's just an array of strings which are converted to NSURL type when necessary). Or even simpler, is it possible to save to the users home directory or somewhere where you don't need to deal with changing folder names?
I solved it by creating an NSUserDefaults array of just the file names (like [audio1.wav,audio2.wav]) and calling the NSSearchPathForDirectoriesInDomains on each new viewdidload, then combining the two into a new string. Like this (code below would call the second audio file in the array):
let dirPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory,.UserDomainMask,true)[0] as String
var filesNamesWithWav = defaults?.objectForKey("filesNamesWithWav") as NSArray
var newpath = "\(dirPath)/" + "\(filesNamesWithWav[1])"