Temporary file path using swift - macos

How to get a unique temporary file path using Swift/Cocoa on OS X?
Cocoa does not seem to provide a function for this, only NSTemporaryDirectory() which returns the path of the temporary directory. Using the BSD mktemp function requires a mutable C-string as argument.

Apple has been trying to move away from path-as-string and into NSURL. Here's one way:
Swift 3:
let directory = NSTemporaryDirectory()
let fileName = NSUUID().uuidString
// This returns a URL? even though it is an NSURL class method
let fullURL = NSURL.fileURL(withPathComponents: [directory, fileName])
Swift 2:
let directory = NSTemporaryDirectory()
let fileName = NSUUID().UUIDString
let fullURL = NSURL.fileURLWithPathComponents([directory, fileName])

Here is a possible method to use mkstemp() from Swift 3 and later. URL methods
are used to convert between URL instances and C strings representing the file system path:
// The template string:
let template = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("file.XXXXXX") as NSURL
// Fill buffer with a C string representing the local file system path.
var buffer = [Int8](repeating: 0, count: Int(PATH_MAX))
template.getFileSystemRepresentation(&buffer, maxLength: buffer.count)
// Create unique file name (and open file):
let fd = mkstemp(&buffer)
if fd != -1 {
// Create URL from file system string:
let url = URL(fileURLWithFileSystemRepresentation: buffer, isDirectory: false, relativeTo: nil)
print(url.path)
} else {
print("Error: " + String(cString: strerror(errno)))
}
Older code for Swift 2:
// The template string:
let template = NSURL(fileURLWithPath: NSTemporaryDirectory()).URLByAppendingPathComponent("file.XXXXXX")
// Fill buffer with a C string representing the local file system path.
var buffer = [Int8](count: Int(PATH_MAX), repeatedValue: 0)
template.getFileSystemRepresentation(&buffer, maxLength: buffer.count)
// Create unique file name (and open file):
let fd = mkstemp(&buffer)
if fd != -1 {
// Create URL from file system string:
let url = NSURL(fileURLWithFileSystemRepresentation: buffer, isDirectory: false, relativeToURL: nil)
print(url.path!)
} else {
print("Error: " + String(strerror(errno)))
}

Although NSTemporaryDirectory() does indeed return a temporary directory path for the current user, the documentation includes the following caveat:
See the FileManager method url(for:in:appropriateFor:create:) for the preferred means of finding the correct temporary directory.
Following that link, we are presented with the following:
You can use this method to create a new temporary directory. To do so, specify FileManager.SearchPathDirectory.itemReplacementDirectory for the directory parameter, userDomainMask for the domain parameter, and a URL for the url parameter which determines the volume of the returned URL.
For example, the following code results in a new temporary directory with a path in the form of /private/var/folders/d0/h37cw8ns3h1bfr_2gnwq2yyc0000gn/T/TemporaryItems/Untitled/:
let desktop = URL(fileURLWithPath: "/Users/jappleseed/Desktop/")
do {
let temporaryDirectory = try FileManager.default.url(
for: .itemReplacementDirectory,
in: .userDomainMask,
appropriateFor: desktop,
create: true
)
print(temporaryDirectory)
} catch {
// Handle the error.
}
(Note the the create parameter is ignored when creating a temporary directory.)
So what exactly is the difference between these two approaches? Well, here's what I get when I call the two different methods from the Swift REPL:
1> import Foundation
2> NSTemporaryDirectory()
$R0: String = "/var/folders/n_/0_9q7d2d1ls5v9kx599y_tj00000gn/T/"
3> let desktop = URL(fileURLWithPath: "/Users/chris/Desktop/")
desktop: URL = "file:///Users/chris/Desktop/"
4> let temporaryDirectory = try FileManager.default.url(
5. for: .itemReplacementDirectory,
6. in: .userDomainMask,
7. appropriateFor: desktop,
8. create: true
9. )
temporaryDirectory: URL = "file:///var/folders/n_/0_9q7d2d1ls5v9kx599y_tj00000gn/T/TemporaryItems/(A%20Document%20Being%20Saved%20By%20repl_swift)/"
It appears that NSTemporaryDirectory() will always return the temporary directory path for the current user whereas FileManager's url(for:appropriateFor:create) will return a new temporary subdirectory each time it is called. For example, here are the directories returned by consecutive calls to url(for:in:appropriateFor:create:) from the Swift REPL:
file:///var/folders/n_/0_9q7d2d1ls5v9kx599y_tj00000gn/T/TemporaryItems/(A%20Document%20Being%20Saved%20By%20repl_swift)/
file:///var/folders/n_/0_9q7d2d1ls5v9kx599y_tj00000gn/T/TemporaryItems/(A%20Document%20Being%20Saved%20By%20repl_swift%202)/
file:///var/folders/n_/0_9q7d2d1ls5v9kx599y_tj00000gn/T/TemporaryItems/(A%20Document%20Being%20Saved%20By%20repl_swift%203)/
And here are the directories returned by consecutive calls to the same method from a Swift Playground:
file:///var/folders/n_/0_9q7d2d1ls5v9kx599y_tj00000gn/T/TemporaryItems/(A%20Document%20Being%20Saved%20By%20Xcode)/
file:///var/folders/n_/0_9q7d2d1ls5v9kx599y_tj00000gn/T/TemporaryItems/(A%20Document%20Being%20Saved%20By%20Xcode%202)/
file:///var/folders/n_/0_9q7d2d1ls5v9kx599y_tj00000gn/T/TemporaryItems/(A%20Document%20Being%20Saved%20By%20Xcode%203)/
The NSHipster article on temporary files seems to suggest that the FileManager method url(for:in:appropriateFor:create:) is intended to be used when staging a file to be moved to a more permanent location (such as the user's desktop in the example above), but I don't see why it couldn't also be used to simply get a unique subdirectory that will automatically be removed when you're done with it and where you shouldn't have to worry about files getting accidentally clobbered by other processes writing to the same temporary directory.

A Swift 3 one-liner inspired by the UUID based Swift 2 answer:
let url = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(UUID().uuidString)

FileManager extension in Swift to get a temporary file URL. You can pass your own file name and extension, if needed.
public extension FileManager {
func temporaryFileURL(fileName: String = UUID().uuidString) -> URL? {
return URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true).appendingPathComponent(fileName)
}
}
Usage:
let tempURL = FileManager.default.temporaryFileURL()
let tempJPG = FileManager.default.temporaryFileURL(fileName: "temp.jpg")

Use a GUID (Globally Unique Identifier):
let directory :NSString = "directory"
let randomName = NSProcessInfo().globallyUniqueString
let path = directory.stringByAppendingPathComponent(randomName)
directory/3B635E49-813A-4324-B4B8-56279B42BEAB-36687-0002D962615DAE5F

I like the idea of this article: NSTemporary​Directory - NSHipster
This uses the NSTemporaryDirectory() for the temporary folder and ProcessInfo.processInfo.globallyUniqueString to generate a unique string.
Swift 4:
func uniqueTempFolderURL() -> URL
{
let folderName = ProcessInfo.processInfo.globallyUniqueString
return URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true).appendingPathComponent(folderName)
}

Swift3
I came here looking for something like boost::filesystem::unique_path()
So I made this extension to the URL class.
extension URL {
func appendingUniquePathComponent(pathExtension: String? = nil) -> URL {
var pathComponent = UUID().uuidString
if let pathExtension = pathExtension {
pathComponent += ".\(pathExtension)"
}
return appendingPathComponent(pathComponent)
}
}
Usage:
let url0 = URL(fileURLWithPath: "/tmp/some/dir")
let url1 = url0.appendingUniquePathComponent(pathExtension: "jpg")
print("url1: \(url1)")
// url1: file:///tmp/some/dir/936324FF-EEDB-410E-AD09-E24D5EB4A24F.jpg

Related

How to wait for the last cmd command to generate a file, and then read the binary of this file, using Rust

I am writing a program, the desired effect is to generate a lua file, then compile this lua on windows platform using luac.exe and read the binary value of the generated luac.out file.
My code is here
fn test(lua_path: PathBuf, luac_path: String) -> Result<String, Box<dyn Error>> {
let index = luac_path.rfind("/").ok_or("path fail")?;
let (path, file) = luac_path.split_at(index + 1);
let cmd_str = format!("/c cd /d {} && .\\{} {}", path, file, lua_path.display());
let child = Command::new("cmd").raw_arg(cmd_str).spawn()?;
let output = child.wait_with_output()?;
if !output.status.success() {
let output_str = String::from_utf8(output.stderr)?;
return Err(output_str.into());
}
// Without the following code, luac.out will be generated normally.
// But once the following code is added, luac.out will be generated as 0kb and receive an access denied error
let out_path = Path::new(&path).join("luac.out");
let mut out_file = File::create(&out_path)?;
let mut buf = Vec::new();
out_file.read_to_end(&mut buf)?;
let result = String::from_utf8_lossy(&buf).to_string();
Ok(result)
}
My guess is that after executing child.wait, the file is not created to completion and then the program accesses it causing an error, how do I fix this?

MacOS xcrun error preventing from invoking a shell script

I want to write a simple app that puts the result of the command
/usr/bin/strings myfile
into a label in the normal MacOS interface. I am receiving this error
xcrun: error: cannot be used within an App Sandbox.
I tried to use the info here:
https://forums.developer.apple.com/thread/73554
and here:
ANY possible way to run the clang compiler from a Sandboxed app?
The actual piece of code invoking the instruction is:
let path = "/usr/bin/strings"
let arguments = ["/path/to/my/file"]
let task = Process()
task.arguments = arguments
task.executableURL = URL(fileURLWithPath: path)
let outputPipe = Pipe()
let errorPipe = Pipe()
task.standardOutput = outputPipe
task.standardError = errorPipe
do {
try task.run()
let outputData = outputPipe.fileHandleForReading.readDataToEndOfFile()
let errorData = errorPipe.fileHandleForReading.readDataToEndOfFile()
let output = String(decoding: outputData, as: UTF8.self)
let error = String(decoding: errorData, as: UTF8.self)
testo.stringValue="OUTPUT: \(output)\nERROR: \(error)"
} catch {
lbl.stringValue="Error somewhere"
}
Anyone does know how to solve this?
The problem is not the command (/usr/bin/strings) but the access to the filesystem. In fact, replacing the first two lines with:
let path = "/sbin/ping"
let arguments = ["-c", "1", "www.google.com"]
for instance, and allowing for outbound connections, would work fine. Hence, it is important that the application is allowed to access the path of the file to be processed, by means of the Sandbox.

NSTask : Couldn't posix_spawn: error 13 when launching app

I have a sub-app in my main Swift app. I made it so it's copied automatically in the Resources folder of the main app when building it. That way, I want to be able to launch an instance of the sub-app from the main app.
The thing is, I'm having an error that is hard to debug/find answers about.
Here is my code :
let args = ["--args", "-admin_url", site.url, "-login", site.login, "-pass", site.password]
let helperPath = (NSBundle.mainBundle().pathForResource("App Helper", ofType: "app"))!
let task = NSTask.init()
task.launchPath = helperPath
task.arguments = args
task.launch()
And the error :
[56490:7218926] Couldn't posix_spawn: error 13
I have no idea where to look, what to search for. I don't know what I'm doing wrong.
I'm wondering if the issue is related to the sub-app itself. That sub-app is empty for now. I set Application is Agent to YES. And in MainMenu.xib, I set the Visible at launch option to no.
That sub-app needs to do some work in the background and doesn't need any UI at all.
Thanks !
Don't use NSTask for this, use NSWorkspace:
let helperAppURL = NSBundle.mainBundle().URLForResource("App Helper",
withExtension:"app")!
_ = try? NSWorkspace.sharedWorkspace().openURL(helperAppURL,
options:[.Default],
configuration:[NSWorkspaceLaunchConfigurationArguments :
["--args", "-admin_url", site.url, "-login",
site.login, "-pass", site.password]])
In the above code, for brevity, I ignored the result of the openURL() command, but in reality it can return an instance of NSRunningApplication which represents the task.
To keep track of the instances of your helper app you launch, you could keep references to this NSRunningApplication in an appropriate kind of collection class, and when the time comes, call its terminate() method.
the launch() function is deprecated, using run()
func shell(_ command: String) -> String {
let task = Process()
task.launchPath = "/usr/bin/"
task.arguments = ["-c", command]
let pipe = Pipe()
task.standardOutput = pipe
if #available(macOS 10.13, *) {
try? task.run()
} else {
task.launch()
}
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output: String = NSString(data: data, encoding: String.Encoding.utf8.rawValue)! as String
return output
}
or using swift-commands
import Commands
Commands.Bash.run("say hello")

Logging in Swift 2.0

I am new to XCode and work on Android Studio previously. In Android Studio, there is log cat to log different types of messages for debugging purposes.
Is this available in XCode?
All I found is NSLog which prints the date and the statement without coloring like in log cat. Is there an easier way ?
You can use the print method.
Check out these handy Apple docs.
https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html
using XCodeColors Library https://github.com/robbiehanson/XcodeColors you can log different types of messages each in a unique color so that you can find error logs faster
also i customized the code like this to get coloring, which class, function, and line number did the call
struct RZLog
{
static let ESCAPE = "\u{001b}["
static let RESET_FG = ESCAPE + "fg;" // Clear any foreground color
static let RESET_BG = ESCAPE + "bg;" // Clear any background color
static let RESET = ESCAPE + ";" // Clear any foreground or background color
static let A = "fg255,0,0;"
static let B = "fg0,0,255;"
static let C = "fg16,128,0;"
static func Error<T>(object: T, filename: String = FILE, line: Int = LINE, funcname: String = FUNCTION) {
let ClassName = NSURL(string: filename)!
print("\(ESCAPE)\(A)**ERROR \(ClassName.lastPathComponent!)(\(line)) Func: \(funcname.uppercaseString): \(object) **\(RESET)")
}
static func Debug<T>(object: T, filename: String = FILE, line: Int = LINE, funcname: String = FUNCTION) {
let ClassName = NSURL(string: filename)!
print("\(ESCAPE)\(B)**DEBUG \(ClassName.lastPathComponent!)(\(line)) Func: \(funcname.uppercaseString): \(object) **\(RESET)")
}
static func VIP<T>(object: T, filename: String = FILE, line: Int = LINE, funcname: String = FUNCTION) {
let ClassName = NSURL(string: filename)!
print("\(ESCAPE)\(C)**VIP \(ClassName.lastPathComponent!)(\(line)) Func: \(funcname.uppercaseString): \(object) **\(RESET)")
}
}
If you want to use different CocoaLumberjack:
https://github.com/CocoaLumberjack/CocoaLumberjack
Which provides some more advantages over simple logging. And it can also be used with colors:
http://code.tutsplus.com/tutorials/cocoalumberjack-logging-on-steroids--mobile-15287
You can use Printer a new logging experience in Swift 3.x.
It has many functions to add logs in various ways.
Usage:
To log a success message:
Printer.log.success(details: "This is a Success message.")
Output:
Printer ➞ [✅ Success] [⌚04-27-2017 10:53:28] ➞ ✹✹This is a Success message.✹✹
[Trace] ➞ ViewController.swift ➞ viewDidLoad() #58
Disclaimer: This library has been created by me.

How to set default file browse location with firefox addon sdk

Im new Firefox addon programming.
I want set default file browse location with firefox addon sdk.
Thank you so much.
open scratchpad copy and paste this:
const nsIFilePicker = Components.interfaces.nsIFilePicker;
var fp = Components.classes["#mozilla.org/filepicker;1"]
.createInstance(nsIFilePicker);
var startDir = FileUtils.File('C:\\');
fp.displayDirectory = startDir;
fp.init(window, "Dialog Title", nsIFilePicker.modeOpen);
fp.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterText);
var rv = fp.show();
if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace) {
var file = fp.file;
// Get the path as string. Note that you usually won't
// need to work with the string paths.
var path = fp.file.path;
// work with returned nsILocalFile...
}
if thats what you want let me know, then ill put it in a default location

Resources