Cocoa, output current App path - macos

I am working on OS X application. In the code, I would like to output a path of the current Mac OS App to a variable for future use. So later I could read the files in the same folder. Could anyone tell me the command/method in the Xcode ?
Thanks very much.
UPDATE
To be more clear, I am using xcode and create a cocoa-application.
My application is connected with applescript to control Mac software read files on the Mac. So I have to return the files' directory and name.
Actually I have no idea about what to do. So got stuck here.

To get the URL of your application, you can use:
[[NSBundle mainBundle] bundleURL]
See the NSBundle Class Reference for further information.

my 2 cents for swift 5,x:
let path = Bundle.main.bundlePath
print(path)
If You need a path to REAL exacutable: (i.e. you mast pass to shell or similar..)
let appName = AppName()
let exePath = path + "/Contents/MacOS/" + appName
print(exePath)
where AppName() is:
func AppName()->String{
let bund = Bundle.main
//print(bund)
if let displayName = bund.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String {
if displayName.count>0{
return displayName
}
}
if let name = bund.object(forInfoDictionaryKey: "CFBundleName") as? String {
return name
}
return "no AppName"
}

Related

File access permission in Swift on OSX

I'm using:
let dialog = NSOpenPanel()
to get a file URL.
I'm then reading in the contents of the text file with:
let content = try String( contentsOf: dialog.url)
This works!
I'm then trying to read in another text file in the same directory with a different extension:
let b = dialog.url?.deletingPathExtension()
// Add the new file extension
let c = b?.appendingPathExtension("TSN")
let content2 = try String( contentsOf: c)
With this I get:
"The file “FLO5.TSN” couldn’t be opened because you don’t have permission to view it."
If I try and open the .tsn file with a URL from a NSOpenPanel() dialog result it works. I need to open several data files from this same directory with different extensions. Is there a way to do this?
set off sandbox!!)
Xcode 9 and later enables sandboxing by default, which severely limits interactions with the system.
Select your target, then Capabilities and set Downloads Folder to Read/Write:
In my case, solve it's startAccessingSecurityScopedResource
Example:
let selectedFile = try result.get() // get path to file
do {
// get access to open file
if selectedFile.startAccessingSecurityScopedResource() {
let path = selectedFile.path
let data = NSData(contentsOfFile: path)
print(data) // array bytes
selectedFile.stopAccessingSecurityScopedResource()
}
} catch {
// Couldn't read the file.
print(error.localizedDescription)
}
Apple wiki about this feature https://developer.apple.com/documentation/foundation/nsurl/1417051-startaccessingsecurityscopedreso

Update a system file from sandboxed application

I am developing an app that updates the file /Library/Preferences/SystemConfiguration/com.apple.airport.preferences.plist. This file is owned by root. I have am currently updating it with the /usr/libexec/authopen tool, running the tool with NSTask and NSPipe. Here is the code:
func saveAs(fname:String) {
// Create a dictory to write back out, and replace the knownNetworks and the preferredOrder
let d = NSMutableDictionary(dictionary:airport_preferences)
d[KnownNetworks] = networks
d[PreferredOrder] = preferred_order
let tempFileName = NSTemporaryDirectory() + "/preferences.new"
d.writeToFile(tempFileName,atomically:true)
let data = NSData(contentsOfFile: tempFileName)
let task = NSTask()
task.launchPath = "/usr/libexec/authopen"
task.arguments = ["-c","-w",fname]
let pipe = NSPipe()
task.standardInput = pipe
task.launch()
let old_signal = signal(SIGPIPE,SIG_IGN)
pipe.fileHandleForWriting.writeData(data!)
pipe.fileHandleForWriting.closeFile()
task.waitUntilExit()
signal(SIGPIPE,old_signal)
do {
try NSFileManager.defaultManager().removeItemAtPath(tempFileName)
} catch let error as NSError {
print(error.description)
}
}
To move to the App Sandbox, I am able to open the file by having the user select it in the browser. That's not hard, because I can pre-position the NSOpenPanel. Mac App Sandboxing- Updating files outside the sandbox describes how to update a file outside the sandbox, but not a file owned by root.
When I run my code under sandbox, I get this error:
tempfile: /Library/Preferences/SystemConfiguration/com.apple.airport.preferences.plist length: 339710
arguments: Optional(["-c", "-w", "/Library/Preferences/SystemConfiguration/com.apple.airport.preferences.plist"])
2015-09-26 11:46:07.699 WiFi Editor[94175:2887221] An uncaught exception was raised
2015-09-26 11:46:07.700 WiFi Editor[94175:2887221] Couldn't posix_spawn: error 1
2015-09-26 11:46:07.703 WiFi Editor[94175:2887221] (
(That code still has my debugging print statements in it.)
So how does one update a system file from the sandbox?
I'm afraid that's something that is not possible at all for an app that is sandboxed. Making the user paste text into a Terminal window isn't forbidden.

Download Image Using Alamofire and Save to Application Support on Mac

The Alamofire readme indicates that you can download and save a file like this:
let destination = Alamofire.Request.suggestedDownloadDestination(directory: .DocumentDirectory, domain: .UserDomainMask)
Alamofire.download(.GET, "http://httpbin.org/stream/100", destination: destination)
...but I want to change the location where it is saved. How do I alter .DocumentDirectory to be my app's Application Support path on Mac OS X?
I can generate my app's local NSURL path like this:
let path = NSFileManager.defaultManager().URLsForDirectory(.ApplicationSupportDirectory, inDomains: .UserDomainMask)[0] as NSURL
let newPath = path.URLByAppendingPathComponent("MyApp/user-profile.png")
I'm unclear on how to use this with Alamofire's suggestedDownloadDestination.
Any ideas?
suggestedDownloadDestination will try to find an available directory, but in your case you already know the full path so you don't need it.
Just create a closure with your path:
let path = NSFileManager.defaultManager().URLsForDirectory(.ApplicationSupportDirectory, inDomains: .UserDomainMask)[0] as NSURL
let newPath = path.URLByAppendingPathComponent("MyApp/user-profile.png")
Alamofire.download(.GET, "http://httpbin.org/stream/100", { _ in newPath })

Full path of project

I want to open a text file in Swift and I managed to do it passing the full path as parameter:
let dados = String.stringWithContentsOfFile("/Users/aczuquim/Google Drive/Swift/Verdadeiro ou Falso/Verdadeiro ou Falso/Dados.txt", encoding: NSUTF8StringEncoding, error: nil)
Since, I added the file to the project, is there any way to use relative path?
When I use the relative path, a nil is returned.
UPDATE
In the playground, the line bellow works fine:
let dados = String.stringWithContentsOfFile("/Users/aczuquim/Google Drive/Swift/Verdadeiro ou Falso/Verdadeiro ou Falso/dados.txt", encoding: NSUTF8StringEncoding, error: nil)
But not the following ones (path is nil):
let bundle = NSBundle.mainBundle()
let path = bundle.pathForResource("dados", ofType: "txt")
Bundle resource:
Swift 3.0
if let bundle = Bundle.main().urlForResource("Info", withExtension: "plist") {
print("Path: \(bundle)")
}
Swift 2.0
let bundleURL = NSBundle.mainBundle()!.URLForResource("dados", withExtension: "txt")
println("\(bundleURL)")
If a nil is returned then the resource is not found and an error reported (note the exclamation mark after mainBundle). Check Build Phases - Copy Bundle Resources if the resource is being included.
App and Documents Folder
Get the documents folder from an array of [AnyObject]! which casts the first object to the NSURL type:
let docFolderURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0] as NSURL
println("DocumentFolderURL: \(docFolderURL)")
Then get the App folder, by stepping up to the parent folder and delete the last path component:
let appFolderURL = docFolderURL.URLByDeletingLastPathComponent
println("AppFolderURL: \(appFolderURL)")
Other directories may be accessed by using .URLByAppendingPathComponent(pathComponent: String?, isDirectory:Bool) etc.
Temporary Directory
var tempURL = NSURL.fileURLWithPath(NSTemporaryDirectory(), isDirectory: true).URLByDeletingLastPathComponent
Note that Apple now prefers the use of URL´s to access folders and files and new methods use them exclusively. To use the path in older methods just call: tempURL.path

Getting Desktop Background in Cocoa

I'm needing to do something full-screen app, which would usually not be the problem. The problem now is that I need to have the user's desktop, but without icons, as the background of my full screen window, much like Launchpad in 10.7. I've gotten a reference to the desktop background in AppleScript:
tell application "Finder"
set a to desktop picture
end tell
This gives me something like this: document file "100930-F-7910D-001.jpg" of folder "Pictures" of folder "Fighter Jet Stuff" of folder "Desktop" of folder "tristan" of folder "Users" of startup disk of application "Finder" which I just could not figure out to get into a regular path.
I tried doing set a to desktop picture as POSIX path but that throws up on me. Any idea of how I could do this in Cocoa, using the above Applescript to get the path, or even better, without an Applescript? I'd like to not rely on the specific format of any plist that might store this info, as it has the potential to break later on. I'm thinking there might be a framework that I just don't know about...
The methods you are looking for are available in NSWorkspace.
– desktopImageURLForScreen:
– setDesktopImageURL:forScreen:options:error:
– desktopImageOptionsForScreen:
Please take a look at the documentation here: NSWorkspace Class Reference
If you needs just the current wallpaper, you can take a screenshot of it:
extension NSImage {
static func desktopPicture() -> NSImage {
let windows = CGWindowListCopyWindowInfo(
CGWindowListOption.OptionOnScreenOnly,
CGWindowID(0))! as NSArray
var index = 0
for var i = 0; i < windows.count; i++ {
let window = windows[i]
// we need windows owned by Dock
let owner = window["kCGWindowOwnerName"] as! String
if owner != "Dock" {
continue
}
// we need windows named like "Desktop Picture %"
let name = window["kCGWindowName"] as! String
if !name.hasPrefix("Desktop Picture") {
continue
}
// wee need the one which belongs to the current screen
let bounds = window["kCGWindowBounds"] as! NSDictionary
let x = bounds["X"] as! CGFloat
if x == NSScreen.mainScreen()!.frame.origin.x {
index = window["kCGWindowNumber"] as! Int
break
}
}
let cgImage = CGWindowListCreateImage(
CGRectZero,
CGWindowListOption(arrayLiteral: CGWindowListOption.OptionIncludingWindow),
CGWindowID(index),
CGWindowImageOption.Default)!
let image = NSImage(CGImage: cgImage, size: NSScreen.mainScreen()!.frame.size)
return image
}
}

Resources