I am having trouble reading a file within a command line tool project in Xcode 11.6.
Steps:
Create a new command line tool project using the template in the mac os section.
In main.swift:
import Foundation
let fileURL = URL( fileURLWithPath: "/Users/ausom4/Desktop/myTest.txt" )
var rawDataString: String
var errorString: String?
do {
rawDataString = try String( contentsOf: fileURL, encoding: .utf8 )
print(rawDataString)
} catch let error as NSError {
errorString = error.description
rawDataString = ""
print(rawDataString)
}
This will build successfully in Xcode however will always print a null string in the console.
However if I go to the location of my product in terminal and run the build I get the contents of the file.
I do not have sandboxing enabled. Sandboxing is also not enabled by default in this xcode template. I have also given xcode full disk access.
I can run this code in a playground.
What could be the issue here?
It is not sandbox - it is macOS Security & Privacy system.
On first launch you had to get alert asking something like “TestFileReading” would like to access files in your Desktop folder. so you grand access for this application.
If you did not grant access then (or for some reason macOS forgot to ask that) there is possibility to grant access manually in System Preferences at any time:
With all this passed your code snippet works as expected - tested with Xcode 12 / macOS 10.15.6
Related
We have these environment variables within the Xcode Scheme
Which works well locally with this code
let webHost = ProcessInfo.processInfo.environment["HOST_URL"]!
let apiHost = ProcessInfo.processInfo.environment["API_URL"]!
let beamsKey = ProcessInfo.processInfo.environment["BEAMS_KEY"]!
let mixpanelKey = ProcessInfo.processInfo.environment["MIXPANEL_KEY"]!
However, when deploying using Xcode Cloud with the same environment variables.
It succeeds in building, but the app crashes with this log.
What is the right way to read these environment variables when using Xcode Cloud?
I had a similar issue, mostly i wanted to add an api-key in the project without this exist in the source code. So I had to create a ci_pre_xcodebuild.sh file
#!/bin/sh
echo "Stage: PRE-Xcode Build is activated .... "
# for future reference
# https://developer.apple.com/documentation/xcode/environment-variable-reference
cd ../ProjectName/
plutil -replace API_KEY_DEBUG -string $API_KEY_DEBUG Info.plist
plutil -replace API_KEY_RELEASE -string $API_KEY_RELEASE Info.plist
plutil -p Info.plist
echo "Stage: PRE-Xcode Build is DONE .... "
exit 0
and in the code we have
let key = config.preferences.debug ? "API_KEY_DEBUG" : "API_KEY_RELEASE"
guard let apiKey = Bundle.main.infoDictionary?[key] as? String
These variables are available and valid while the temporary environment is active to build the app only, not when the app is running on the device.
The environment variables can, however, be “captured” during the build process using shell scripts (see the Xcode "Build Phases" under the target settings or the Xcode Cloud custom build scripts).
Another good solution is to use some code generation tool like Arkana. This tool creates obfuscated code to make the variables available at the runtime eventually.
Again the tool or shell script must run in the Xcode Cloud environment. The steps to do this are out of the scope of this response.
So, this was an absolute headache but I finally figured out a satisfactory way to access and use these variables in code.
My solution uses:
A (gitignored) JSON file to store the variables locally
Xcode Cloud to send the variables in the CI
A ci_pre_xcodebuild.sh file to write the environment variables in the JSON
A Swift file that allows you to conveniently access the secrets.
Step 1: Basic JSON file
In your .gitignore file, add a new entry for the JSON file and its path
Create a JSON file through Xcode
Add your keys to this JSON file.
Secrets.json
(at: YourProject/SupportingFiles/secrets.json)
{
"STRIPE_KEY": "",
"GOOGLE_MAPS_KEY": "",
"GOOGLE_PLACES_KEY": "",
"BASE_URL": "https://dev.api.example.fr"
}
Step 2: Write the variables in Xcode Cloud
In this screenshot you can see that I've duplicated the keys for different environments. I didn't expand on this for the sake of brevity, but you can definitely have different secrets JSON files for different Xcode Scheme configurations.
Step 3: Add a ci_pre_xcodebuild.sh file
Important: the name of the files and their position matter.
The goal here is to add a script that the CI (Xcode Cloud) will execute each time it builds. In this script, we're going to create and fill our JSON.
Add a new group at the root of your project named "ci_scripts"
In this group, add a new file called ci_pre_xcodebuild.sh
Write the following:
#!/bin/sh
echo "Stage: PRE-Xcode Build is activated .... "
# Move to the place where the scripts are located.
# This is important because the position of the subsequently mentioned files depend of this origin.
cd $CI_WORKSPACE/ci_scripts || exit 1
# Write a JSON File containing all the environment variables and secrets.
printf "{\"STRIPE_KEY\":\"%s\",\"GOOGLE_MAPS_KEY\":\"%s\",\"GOOGLE_PLACES_KEY\":\"%s\",\"BASE_URL\":\"%s\"}" "$STRIPE_KEY" "$GOOGLE_MAPS_KEY" "$GOOGLE_PLACES_KEY" "$BASE_URL" >> ../Dream\ Energy/SupportingFiles/Secrets.json
echo "Wrote Secrets.json file."
echo "Stage: PRE-Xcode Build is DONE .... "
exit 0
Of course, you need to change this text depending on your keys and the location of the file. I added a few keys as an example.
In your Terminal, navigate to the new file's folder and run this command: chmod +x ci_pre_xcodebuild.sh. This fixes a warning in Xcode Cloud.
Step 4: [BONUS] A simple Swift file to access the environment variables
import Foundation
struct Secrets {
private static func secrets() -> [String: Any] {
let fileName = "Secrets"
let path = Bundle.main.path(forResource: fileName, ofType: "json")!
let data = try! Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
return try! JSONSerialization.jsonObject(with: data) as! [String: Any]
}
static var baseURL: String {
return secrets()["BASE_URL"] as! String
}
static var stripeKey: String {
return secrets()["STRIPE_KEY"] as! String
}
}
I try to start the Preview App from within my Swift-App.
I think it worked like this before updating to BigSur (unfortunately I cannot test it in a prior OS).
The App shall be able to show any file in the filesystem with the Preview-App.
What I tried:
removed Sandbox from the targets capabilities
added iCloud Documents
added com.apple.security.temporary-exception.files.absolute-path.read-write with the value "/" to the entitlements.
func openPreviewApp(path : String)
{
let url = NSURL(fileURLWithPath: "/System/Applications/Preview.app", isDirectory: true) as URL
let configuration = NSWorkspace.OpenConfiguration()
configuration.arguments = [path]
NSWorkspace.shared.openApplication(at: url,
configuration: configuration,
completionHandler: nil)
}
The function opens the Preview App but not the file given in path - instead the fileselection dialog is presented.
open -a Preview THISPATH in Terminal works fine.
Any Ideas?
I try to run AppleScript command from Swift code like this:
var appleScriptCmd = "tell application \"System Events\" to make login item at end with properties {path:\"" + appPath + "\", hidden:false, name:\"Some App\"}";
var appleScriptCmd2 = "tell application \"System Events\" to set visible of process \"Safari\" to false";
and then I have tried both:
let script = NSAppleScript(source: appleScriptCmd2)!;
var errorDict : NSDictionary?
script.executeAndReturnError(&errorDict)
if errorDict != nil { print(errorDict!) }
or older approach:
Process.launchedProcess(launchPath: "/usr/bin/osascript", arguments: ["-e", appleScriptCmd])
neither works and simultaneously both commands I have tried are working from Terminal program using osascript -e "some command" tool.
Since your app is sandboxed (Project Settings > Capabilities turn on App Sandbox) you have three options:
Add temporary entitlements for the applications you want to use.
Put your scripts in the appropriate directory in ~/Library/Application Scripts/ and use NSUserAppleScriptTask.
Implement an AppleScriptObjC bridge and run the AppleScript code from the ASOC framework (requires also an Objective-C bridging header file).
In a sandboxed app NSAppleScript refuses to work.
I try to run AppleScript command from Swift code like this:
var appleScriptCmd = "tell application \"System Events\" to make login item at end with properties {path:\"" + appPath + "\", hidden:false, name:\"Some App\"}";
var appleScriptCmd2 = "tell application \"System Events\" to set visible of process \"Safari\" to false";
and then I have tried both:
let script = NSAppleScript(source: appleScriptCmd2)!;
var errorDict : NSDictionary?
script.executeAndReturnError(&errorDict)
if errorDict != nil { print(errorDict!) }
or older approach:
Process.launchedProcess(launchPath: "/usr/bin/osascript", arguments: ["-e", appleScriptCmd])
neither works and simultaneously both commands I have tried are working from Terminal program using osascript -e "some command" tool.
Since your app is sandboxed (Project Settings > Capabilities turn on App Sandbox) you have three options:
Add temporary entitlements for the applications you want to use.
Put your scripts in the appropriate directory in ~/Library/Application Scripts/ and use NSUserAppleScriptTask.
Implement an AppleScriptObjC bridge and run the AppleScript code from the ASOC framework (requires also an Objective-C bridging header file).
In a sandboxed app NSAppleScript refuses to work.
I have a Mac OSX app that I have previously been able to test - however when I run tests now - it will work once and then fail with the error below in the console. I need to do some drastic things to get it working:
If I
change the location of my Derived Data folder
and clean build folder - then it will usually work again once or twice before but when I run tests again it will happen again.
Any ideas of what I can do to fix it permanently - below is pretty much Greek to me..
I have tried the following:
moving the derived data into Documents
installing new Xcode from Appstore.
Deleting and re-adding Certificates and profiles
2017-01-15 16:41:51.247064 XXXXXX[51736:892136] Unable to load
configuration data from specified path
/var/folders/59/7ylv57053bv3c0rbbcc1mcg40000gp/T/com.apple.dt.XCTest/FDF2A461-45D7-4E64-B650-602DF0725CA7/remote-container/tmp/XXXXXXTests-FDF2A461-45D7-4E64-B650-602DF0725CA7.xctestconfiguration;
error: You don’t have permission. 2017-01-15 16:41:51.247221
XXXXXX[51736:892136] IDEBundleInjection Arguments: (
"/Users/XXXXXX/XXXXXX/XXXXXX/XXXXXX-eghnritsumpbbqgylbzrxqfximew/Build/Products/Debug/XXXXXX.app/Contents/MacOS/XXXXXX",
"-NSTreatUnknownArgumentsAsOpen",
NO,
"-ApplePersistenceIgnoreState",
YES ) 2017-01-15 16:41:51.248336 XXXXXX[51736:892136] IDEBundleInjection Environment: {
"APP_SANDBOX_CONTAINER_ID" = "com.XXXXXX.XXXXXX";
"Apple_PubSub_Socket_Render" = "/private/tmp/com.apple.launchd.hKPiBBDAAG/Render";
"CFFIXED_USER_HOME" = "/Users/XXXXX/Library/Containers/com.XXXXXX.XXXXXX/Data";
"DTX_CONNECTION_SERVICES_PATH" = "/Applications/Xcode.app/Contents/SharedFrameworks/DTXConnectionServices.framework";
"DYLD_FRAMEWORK_PATH" = "/Users/XXXXX/XXX/XXXXXXX/XXXXXX-eghnritsumpbbqgylbzrxqfximew/Build/Products/Debug:/Users/XXXXX/XXX/XXXXXXX/XXXXXX-eghnritsumpbbqgylbzrxqfximew/Build/Products/Debug:/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks";
"DYLD_INSERT_LIBRARIES" = "";
"DYLD_LIBRARY_PATH" = "/Users/XXXXX/XXX/XXXXXXX/XXXXXX-eghnritsumpbbqgylbzrxqfximew/Build/Products/Debug:/Users/XXXXX/XXX/XXXXXXX/XXXXXX-eghnritsumpbbqgylbzrxqfximew/Build/Products/Debug:/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks";
HOME = "/Users/XXXXX/Library/Containers/com.grant.XXXXXX/Data";
LOGNAME = XXXXX;
MallocNanoZone = 1;
NSUnbufferedIO = YES;
"OS_ACTIVITY_DT_MODE" = YES;
PATH = "/Applications/Xcode.app/Contents/Developer/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin";
PWD = "/Users/XXXXX/XXX/XXXXXXX/XXXXXX-eghnritsumpbbqgylbzrxqfximew/Build/Products/Debug";
SHELL = "/bin/bash";
"SSH_AUTH_SOCK" = "/private/tmp/com.apple.launchd.dNK7oacOAX/Listeners";
TMPDIR = "/var/folders/59/7ylv57053bv3c0rbbcc1mcg40000gp/T/com.grant.XXXXXX/";
USER = XXXXX;
XCInjectBundleInto = "/Users/XXXXX/XXX/XXXXXXX/XXXXXX-eghnritsumpbbqgylbzrxqfximew/Build/Products/Debug/XXXXXX.app/Contents/MacOS/XXXXXX";
"XCODE_DBG_XPC_EXCLUSIONS" = "com.apple.dt.xctestSymbolicator";
XCTestConfigurationFilePath = "/var/folders/59/7ylv57053bv3c0rbbcc1mcg40000gp/T/com.apple.dt.XCTest/FDF2A461-45D7-4E64-B650-602DF0725CA7/remote-container/tmp/XXXXXXTests-FDF2A461-45D7-4E64-B650-602DF0725CA7.xctestconfiguration";
"XPC_FLAGS" = 0x0;
"XPC_SERVICE_NAME" = "com.apple.dt.Xcode.23100";
"__CF_USER_TEXT_ENCODING" = "0x1F6:0x0:0x2";
"__XCODE_BUILT_PRODUCTS_DIR_PATHS" = "/Users/XXXXX/XXX/XXXXXXX/XXXXXX-eghnritsumpbbqgylbzrxqfximew/Build/Products/Debug";
"__XPC_DYLD_FRAMEWORK_PATH" = "/Users/XXXXX/XXX/XXXXXXX/XXXXXX-eghnritsumpbbqgylbzrxqfximew/Build/Products/Debug";
"__XPC_DYLD_LIBRARY_PATH" = "/Users/XXXXX/XXX/XXXXXXX/XXXXXX-eghnritsumpbbqgylbzrxqfximew/Build/Products/Debug";
}
In Xcode 9 I found a solution, that worked for me.
Go to Xcode > File > Project Settings... (or Workspace Settings...)
Select New Build System (Preview) as Build System under Shared Settings.
Make sure to select Use Shared Setting under Per-User Project Settings.
I too logged a bug with Apple. Experienced it on Xcode 9. However, I then played with it some more and found that by changing the Derived Data Folder to Custom and disabling code coverage in my Test config for my scheme, the error went away. It seems that some combination of these two caused the issue.
I have logged a bug with Apple as it appears that no one else is getting this error.
Edit: Elise has filed bug Apple #34737491, if you are experiencing it - then please raise a bug and reference that ticket so Apple can see how big the impact is.