Update a system file from sandboxed application - macos

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.

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

NSOpenPanel/NSSavePanel crashes in Swift 3

In Swift 3/Xcode8.1/10.12.2 I am trying to use a NSOpenPanel to get a text file with the following code, written by DenBeke
#IBAction func browseFile(sender: AnyObject) {
let dialog = NSOpenPanel();
dialog.title = "Choose a .txt file";
dialog.allowedFileTypes = ["txt"];
if (dialog.runModal() == NSModalResponseOK)
{
let result = dialog.url // Pathname of the file
if (result != nil) {
let path = result!.path
print("browseFile path: \(path)")
//filename_field.stringValue = path
}
} else {
// User clicked on "Cancel"
return
}
}
The code opens a open dialog as expected and I can choose a file. Clicking the open button crashes the app. In the console I get:
FI_TFloatingInputWindowController object 0x60800009c0c0 overreleased while already deallocating; break on objc_overrelease_during_dealloc_error to debug
When I run the code and open the dialog, in the console I get
[default] [ERROR] Failed getting container for URL: file:///Users/ruediheimlicher/Documents/LoggerdataDir/Messungen/, error: Error Domain=BRCloudDocsErrorDomain Code=12 "App library not found: 'com.apple.Documents'" UserInfo={NSDescription=App library not found: 'com.apple.Documents'}
but this does not affect the app.
There are more examples for NSOpen or NSSave dialogs on the net with slightly different code, but with everyone I get the same result: Crash, and the exact same error on the console.
Is there a mistake in my code attempts, or even an example that works with Swift3/sierra?
Give your app access to user selected files under <Your Project> -> <Your Target> -> Capabilities -> App Sandbox -> File Access -> User Selected File.
[default] [ERROR] Failed getting container for URL: file:///Users/ruediheimlicher/Documents/LoggerdataDir/Messungen/, error: Error Domain=BRCloudDocsErrorDomain Code=12 "App library not found: 'com.apple.Documents'" UserInfo={NSDescription=App library not found: 'com.apple.Documents'}
To remove this output you have to enable iCloud Drive in your Finder preferences.
Open finder
Open prefences
Select third tab (Sidebar)
Check iCloud Drive
Then rerun your application

found nil while unwrapping an Optional value when trying to play a sound

I am trying to play a sound in my OS X app which has been built in Swift.
I am getting the following error:
fatal error: unexpectedly found nil while unwrapping an Optional value
The line of code in question is:
let bgMusicURL:NSURL = NSBundle.mainBundle().URLForResource("emptytrash", withExtension: "aif")!
I have the sound located here
However when I call my method to prepare and play the sound:
func playSound() {
let bgMusicURL:NSURL = NSBundle.mainBundle().URLForResource("emptytrash", withExtension: "aif")!
backgroundMusicPlayer = try! AVAudioPlayer(contentsOfURL: bgMusicURL)
backgroundMusicPlayer.numberOfLoops = -1
backgroundMusicPlayer.prepareToPlay()
backgroundMusicPlayer.play()
}
I get this error. What is going on?
I declare it with this:
var backgroundMusicPlayer:AVAudioPlayer = AVAudioPlayer()
A file that is part of your project doesn't have to be a part of your target, that means it won't get copied to the application bundle.
To see "Target membership", you can open the sidebar on the right side (File inspector) of the window. See Viewing the properties of a File for more information.

xcode 6.2 create a new simulator path every time when run the app

I used swiftData to store information on sqlite database till today it will work fine and suddenly it created a new location of database as well as simulator location
when first time run it shows
/Users/div/Library/Developer/CoreSimulator/Devices/606D7F8E-2402-4782-ADEE-12725EDB203A/data/Containers/Data/Application/2DB733AF-2544-4256-B1E5-5E8725E51CDF/Documents/DataBase.db
second time
/Users/div/Library/Developer/CoreSimulator/Devices/606D7F8E-2402-4782-ADEE-12725EDB203A/data/Containers/Data/Application/551991FA-392A-40E9-810E-31CEFCD3069A/Documents/dataBase.db
third time
/Users/div/Library/Developer/CoreSimulator/Devices/606D7F8E-2402-4782-ADEE-12725EDB203A/data/Containers/Data/Application/03E4BE03-D6E7-47BF-A98D-A129DF09DD28/Documents/DataBase.db
i used this code
let fileManager = NSFileManager()
var Sourcepath = NSBundle.mainBundle().resourcePath?.stringByAppendingPathComponent("PhotoKeeper.db");
let docsPath = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0] as String
let databaseStr = "DataBase.db"
let dbPath = docsPath.stringByAppendingPathComponent(databaseStr)
println(dbPath)
if(fileManager .fileExistsAtPath(dbPath) == false) {
var error:NSError?
fileManager.copyItemAtPath(Sourcepath!, toPath: dbPath, error: &error)
println(error)
}
my problem is every time new instance of database is created i.e. if i insert 2 row and compile and run the app it will create new database with zero row
You should save only relative path of your contents (document, db, ...) cause XCode with iOS8 (I suppose) changes application folder whenever you build and run it.
For example, you have your .db file in Documents, so just save your link as "DataBase.db". Then, when you need to access to this file, get the path as:
let documentsFolder = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0] as String
let dbPath = documentsFolder.stringByAppendingPathComponent("Database.db")

Cocoa, output current App path

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"
}

Resources