How to include file as resource in Xcode programmatically during build? - xcode

I have a file that is created by a script that is run during a build phase. Consequently, the file does not exist until build time.
How can I add the file to the app/project so that my code can access it at run time?
I know what the filename will be.
Normally, if I created the file by hand, I would just drag/drop it into Xcode, and I can instantly access it by using the filename in code. Since I am generating the file during build, I want to know how I can add it to my project.
EDIT: The file that I am adding is an Image file, that is created by a script during a build phase. I want to be able to access this file at runtime in my product.

I ended up using build references.

You could create the file and add it to the project and then during the build phase, your script could replace the existing file with the generated one....
The other option is to try manipulating the project file itself but that's quite complicated. Here are some resources on the subject...

You can copy the generated file to the build output using a script like this one I use to copy a plist with values maintained outside version control, as anything copied to "${BUILT_PRODUCTS_DIR}/${FULL_PRODUCT_NAME}/" is available using NSBundle APIs.
SECRETS="${SRCROOT}/../../secrets/Secrets.plist"
APP="${BUILT_PRODUCTS_DIR}/${FULL_PRODUCT_NAME}/Secrets.plist"
if [ -f "$SECRETS" ]; then
cp -f "$SECRETS" "$APP"
echo "copied Secrets.plist: $APP"
else
echo "Secrets.plist not found at $SECRETS, all secrets will be 'MISSING'"
fi
For instance, that plist of secrets is defined as the Decodable struct SecretsFile and loaded with
private static var file: SecretsFile? = {
guard let url = Bundle.main.url(forResource: "Secrets", withExtension: "plist"),
let data = try? Data(contentsOf: url) else { return nil }
return try? PropertyListDecoder().decode(SecretsFile.self, from: data)
}()

Related

To check whether a file has been downloaded or not via protractor

Need help to check whether a file has been successfully downloaded or not in a specified folder.
The file name dynamically changes every time a new file is downloaded but the initial part of the file "Pivot_Report" always stays the same
The actual file gets downloaded in the mentioned folder but protractor is not able to find it with just the initial part of the full name
This is the code I'm using (filenamePath is '/Users/Shubh/Documents/')
browser.driver.wait(function() {
        var fileName = filenamePath+"*.csv"
        var filesArray = glob.sync(fileName)
        if (typeof filesArray !== 'undefined' && filesArray.length > 0){
          return filesArray
        }
      }, 10000).then(function(filesArray) {
        var fileWithPath = filesArray[0]
        var temp = fileWithPath.indexOf("Pivot_Report")
        expect(fileWithPath.indexOf("Pivot_Report") >= 0).toBe(true,'Pivot Download is not succesfull')
        if(fs1.existsSync(fileWithPath)){
          fs1.unlinkSync(fileWithPath)
        }
      })
Getting the timeout error
It happens, because your path is incorrect.
I think easiest way is to use path.resolve() :
var path = require("path");
var filenamePath = path.resolve("Users/Shubh/Documents");
and then you will have (notice that you missed a / before *.csv)
var fileName = filenamePath+"/*.csv"

Visual Studio Setup Project - How to Obtain the Directory Path from a File-Search Launch Condition

I am looking for a way to add File(s) to an existing directory that has a random name as part of a Visual Studio Setup Project and I hoped someone might be able to help me solve this puzzle please.
I have been attempting to obtain the discovered path property of the directory using a Launch Condition; Unfortunately this method returns the full file path including the filename, which cannot be used as a directory property.
The directory in question takes the form [AppDataFolder]Company\Product\aaaaaaaaaaaa\
where aaaaaaaaaaaa is a random installation string.
Within the Launch Condition Setup I check for the directory's existence by searching for a file that would appear inside it,
Search Target Machine
(Name): File marker
Filename: sample.txt
Folder: [AppDataFolder]Company\Product\
Property: DIRFILE
Launch Condition
(Name): File marker exists
Condition: DIRFILE
In the Setup Project I add the file I wish to insert, with the details
Condition: DIRFILE
Folder: 'Installation folder'
Then in File System Setup I add a new folder entry for the random directory aaaaaaaaaaaa
(Name): Installation folder
Condition: DIRFILE
DefaultLocation: [DIRFILE]\..\ *Incorrect*
Property [DIRLOCATION]
As you can see the installer detects the existence of the marker file but, instead of placing my file at the same location, when using [DIRFILE] the installer would incorrectly try and insert it INTO the file;
This is because the file path was returned
[AppDataFolder]Company\Product\aaaaaaaaaaaa\sample.txt
where I instead need the directory path
[AppDataFolder]Company\Product\aaaaaaaaaaaa
Therefore I was wondering if it was possible to return the directory the file was found in from Search Target Machine (as opposed to the file location of the file), if I could extract the directory path by performing a string replace of the filename on the file location DIRFILE within the DefaultLocation field in File System Setup, or if perhaps there is even another method I am missing?
I'm also very interested in a simple solution for this, inside the setup project.
The way I did solve it was to install the files to a temporary location and then copy them to the final location in an AfterInstall event handler. Not a very elegant solution! Since it no longer care about the user selected target path I removed that dialog. Also I needed to take special care when uninstalling.
public override void OnAfterInstall(IDictionary savedState)
{
base.OnAfterInstall(savedState);
// Get original file folder
string originDir = Context.Parameters["targetdir"];
// Get new file folder based on the dir of sample.txt
string newDir = Path.GetDirectoryName(Context.Parameters["dirfile"]);
// Application executable file name
// (or loop for all files on the path instead)
string filename = "ApplicationName.exe";
// Move or copy the file
File.Move(Path.Combine(originDir, filename), Path.Combine(newDir, filename)));
}

How do I get the full local path of a file uploaded with $_FILES

I am uploaded a file through the file input. If I print_r($_FILES), I can get several pieces of info, but it doesn't give me the full LOCAL path of the file (the path on my computer, not the server). How do I get this?
I need this to use the FTP library in CodeIgniter. Documentation is here on how to use it to upload a file.
As you can see, it requires the full local path, although I'm not sure why.
As file upload path is a variable which changes from server to server, I would suggest you to use move_uploaded_file() function:
$target = "uploads/myfile.something";
if(move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target)) {
// do something
}
that way it will always work no matter what the path is even if it changes. The target can be anything you wish, it's relative to the current folder where script is being executed.
I solved this.
public function upload($file, $folder) {
$this->CI->ftp->upload($_FILES['file']['tmp_name'], '/public_html/rest/of/path/' . $_FILES['file']['name'], 'ascii', 0775);
}
The file is being uploaded, so you can access it with $FILES['file']['tmp_name']. This is from within a class, so that's why I'm using $this->CI. Otherwise, I would just use $this.

How do you a configure a Visual Studio custom build tool to depend on a lot of files?

I need to make sure a custom build tool that operates on a lot of files is always run when one of those files are changed.
I know you can specify "Additional Dependencies" for the custom build tool, but is there a better way than specifying one file per line?
Just made the build tool depend on one file. Created another custom build tool that runs before it and that touches this file if any of the real dependencies have changed. The advantage is that I now have quite a lot of flexibility and don't need to change any of the project settings if the dependencies change - that's taken care of via a database that the new custom build tool accesses.
"Additional Dependencies" is the correct and only documented way. You could adjust the contents of this field for the build tool in the Project file using an external tool to save on the troubles of doing a copy & paste & typo-adjust.
It won't be quite as reliable, but you could put all files in a subfolder and just make the folder a dependency.
A little bit hack:
(1) group the dependency files into a separate folder
(2) create a jscript file detect.js as follow:
var output = WScript.arguments(0);
var folder = WScript.arguments(1);
var fso = new ActiveXObject("Scripting.FileSystemObject");
var objOutput = fso.GetFile(output);
var objFolder = fso.GetFolder(folder);
// if the output file is older than input folder,
// delete output file to force regenerate
if (objOutput.DateLastModified < objFolder.DateLastModified) {
fso.DeleteFile(objOutput);
} else {
// if the output file is older than one of files in the input folder,
// delete output file to force regenerate
var e = new Enumerator(objFolder.Files);
for (; !e.atEnd(); e.moveNext()) {
if (objOutput.DateLastModified < e.item().DateLastModified)
fso.DeleteFile(objOutput);
break;
}
}
}
(2) Add command lines to Pre-Build Event as follow:
cscript.exe /nologo detect.js $(Output) $(InputFolder)
(3) Setup the Custom Buld Step to force the Pre-Build Event to occur, i.e.
Command Line: echo --------------
Outputs: echo.fake
Execute After: PreBuildEvent

How can I set up an Xcode build rule with a variable output file list?

Build Rules are documented in the Xcode Build System Guide
They are well adapted to the common case where one input file is transformed into a fixed number (usually one) of output files.
The output files must be described in the "Output Files" area of the build rule definition; one line per output file. Typically the output files have the same name as the input file but have different extensions.
In my case, one single input file is transformed into a variable number of files with the same extensions. The number and the names of the output files depend on the content of the input file and are not known in advance.
The output files will have to be further processed later on (they are in this case C files to be compiled).
How can I set up a build rule for such a case?
Any suggestions welcome.
(I asked the same question on the Apple developer forum, but I figured it'd be a good idea to ask here too).
I dealt with this by, instead of generating multiple C files, just concatenating them all together into one file (e.g. "AUTOGENERATED.c"), and specifying that as the output file.
So long as your output files don't contain anything that will conflict (static functions with the same name, conflicting #defines etc.) this works well.
See this article on Cocoa With Love:
http://cocoawithlove.com/2010/02/custom-build-rules-generated-tables-and.html
This has an example of generating custom C code and using that as input to the normal build process. He's using ${} variable syntax in the output
The best way I found to add any number of files to my xcode project (and make some processing) is to write a little php script. The script can simply copy files into the bundle. The tricky part is the integration with xcode. It took me some time to find a clean way. (You can use the script language you like with this method).
First, use "Add Run Script" instead of "Add Copy File"
Shell parameter:
/bin/sh
Command parameter:
${SRCROOT}/your_script.php -s ${SRCROOT} -o ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}
exit $?
(screenshot in xcode)
${SRCROOT} is your project directory.
${CONFIGURATION(...) is the bundle directory. Exactly what you need :)
This way, your script return code can stop xcode build (use die(0) for success and die(1) for failures) and the output of script will be visible in xcode's build log.
Your script will look like that: (don't forget chmod +x on it)
#!/usr/bin/php
<?php
error_reporting(E_ALL);
$options = getopt("s:o:");
$src_dir = $options["s"]."/";
$output_dir = $options["o"]."/";
// process_files (...)
die(0);
?>
BONUS: here my 'add_file' function.
Note the special treatment for PNG (use apple's png compression)
Note the filemtime/touch usage to prevent copy files each times.
l
define("COPY_PNG", "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/copypng -compress");
function add_file_to_bundle($output_dir, $filepath) {
// split path
$path_info = pathinfo($filepath);
$output_filepath = $output_dir.$path_info['basename'];
// get file's dates of input and output
$input_date = filemtime($filepath);
$output_date = #filemtime($output_filepath);
if ($input_date === FALSE) { echo "can't get input file's modification date"; die(1); }
// skip unchanged files
if ($output_date === $input_date) {
//message("skip ".$path_info['basename']);
return 0;
}
// special copy for png with apple's png compression tool
if (strcasecmp($path_info['extension'], "png") == 0) {
//message($path_info['basename']." is a png");
passthru(COPY_PNG." ".escapeshellarg($filepath)." ".escapeshellarg($output_filepath), $return_var);
if ($return_var != 0) die($return_var);
}
// classic copy
else {
//message("copy ".$path_info['basename']);
passthru("cp ".escapeshellarg($filepath)." ".escapeshellarg($output_filepath), $return_var);
if ($return_var != 0) die($return_var);
}
// important: set output file date with input file date
touch($output_filepath, $input_date, $input_date);
return 1;
}

Resources