Swift save files to project folder - swift

I'm writing a swift terminal program (let's say it's called "myProject") that requires me to save some files. I would like the files to be saved within the project folder programmatically.
I've seen in this post there are some environment variables like $(PROJECT_DIR) that give the path to the project folder. I've already checked that XCode does have the $(PROJECT_DIR) variable using:
$ xcodebuild -project myProject.xcodeproj -target "myProject" -showBuildSettings
But I'm not exactly sure how I can use this variable in my actual code.
Like if I tried
print($(PROJECT_DIR))
I would just get the errors:
use of unresolved identifier '$'
use of unresolved identifier 'PROJECT_DIR'
How do I get the path to my project folder programmatically?

Those aren't environment variables that are available to your program at runtime; they're used internally by Xcode for the build process.
Your program is located at Bundle.main.executableURL (NSBundle.mainBundle().executableURL in Swift 2) at runtime (executablePath is also available if you prefer).
If you have additional files that your program needs to access, they must be in a known location. You can use a standard directory in the filesystem such as /usr/local/bin, or you can create a bundle and copy those resources into the bundle as part of the build process.

Related

Xcode New "Run Script Phase" - How to handle output files?

I want to add a Run Script Phase to my Build Phases to call a swift executable that takes a plist file from my project and uses it to generate a swift file with some boilerplate code.
I figured out how to specify the input file for the Run Script Phase like this:
$(SRCROOT)/MyProject/MyData.plist
But for output files, Xcode gives me this $(DERIVED_FILE_DIR)/newOutputFile default value which, if I echo it via echo "$SCRIPT_OUTPUT_FILE_0", prints some strange path to the ....MyProject.build/DerivedSources folder. What is that? What do I do with this and how can I generate my output swift file and place it inside my project?
I don't really find much information about this $(DERIVED_FILE_DIR) (at least nothing that I understand, I've never worked with these things before).
Thanks!
Presumably the derived file directory is just a safe place to write output results to. It isn't in the project directory, but it is unique to the project.
However, you do want to write directly into the project directory (I presume), so just go ahead and do so, using the environmental variable PROJECT_DIR.

Added a command-line target to my Swift app, but it does not see the rest of the project

I originally created my project using Xcode's templates for iOS, and then arranged my files into "core" logic and "iOS" GUI. core contains a single file, Game.swift.
Now I've added a CLI target as well. However, the CLI target doesn't seem to "see" the files in core, and when I try to init the main object, I get:
Use of unresolved identifier 'Game'
I assume this is because adding a target after-the-fact failed to set up a Search Path or similar in the target, but I'm looking around and don't see anything obvious.

Include/exclude resources depending on scheme

I have some large resources that I only need for my simulator runs—I'd like to have them automatically excluded from all device builds. Is there any way to do this with Xcode 4 short of custom build scripts that copy the resources?
I went with a Run Script phase with the following:
if [ ${PLATFORM_NAME} != "iphonesimulator" ]; then
echo "device build -- removing resources..."
rm "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/test_a.mp3"
rm "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/test_b.mp3"
# reveal the binary in the Finder
/usr/bin/open --reveal "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
else
echo "simulator build..."
fi
Targets dictate what's included in a product. Duplicate your target and create a scheme for it. Modify that target's membership.
Art's answer works well.
I'm adding this alternative answer because I have certain requirements and I found a solution that addresses those.
In my case, I need some large resources to be only in some test builds, but I don't want them to be included in the project or checked in with project. I also want to avoid Xcode pointlessly copying a large file from one folder to another during the build.
My solution is as follows:
Create a new folder on disk under your Resources/ folder, titled FolderLinkedResources.
Add a folder reference to this folder in the Xcode project.
This is a link to an actual folder on hard disk, rather than a project folder group. You do it via an option in the Add Files dialog:
Then at build time I have a custom build phase script (placed earlier than compilation) that hard-links the required resource file into the referenced folder on disk:
# COPY_SPECIAL_RESOURCES is only defined in schemas where I require the special resource(s).
# SpecialResources folder is a sibling folder alongside my entire Xcode project folder
if [ ${COPY_SPECIAL_RESOURCES} == "1" ]; then
ln ../SpecialResources/mySpecialResourceFile.bin Resources/FolderLinkedResources/
fi
Now the build will include your special resource.
Note that since the resource file is inside a folder reference in the project, the built app will actually contain the resource file in a folder, rather than at the top level. This means that the usual call to retrieve your resource will not work:
NSString *resourcePath = [[NSBundle mainBundle]
pathForResource:#"mySpecialResourceFile"
ofType:#"bin"];
To fix this, you need to also provide the folder name containing the resource:
NSString *resourcePath = [[NSBundle mainBundle]
pathForResource:#"mySpecialResourceFile"
ofType:#"bin"
inDirectory:#"FolderLinkedResources"];
Note: this technique also works well for when you don't actually know what the resources are until build time! As long as your script step hard-links in the files, they will make it into the build.
In the interests of tidyness and sanity, I include a run script build phase that happens after the compilation, which clears out the hard link:
if [ ${COPY_SPECIAL_RESOURCES} == "1" ]; then
rm -rf Resources/FolderLinkedResources/*.*
fi
Finally, in case anyone is interested in my actual use case for this: I have pre-recorded HTTP communications data for certain offline test builds of my app. This data is in no way a part of the core app, so I don't want it checked in with app itself, or part of the project by default.
Custom build scripts is your best friend in Xcode. Have the script delete the resource after the build and before the code sign.

Settings bundle with non-standard name

May I use a settings bundle in xcode which is not called Settings.bundle?
If I have multiple settings bundles (for multiple build targets), must I define these additional names anywhere? My application seems to always look for one called Settings.bundle.
For those of you who aren't good at shell scripts, here's a template version of what I used. In addition to a Debug and Release build, we also had a Debug-Special build with its own Info.plist in the build settings, and a different Settings.bundle/Root.plist file. I created copies of the Setting.bundle/ folder to Settings(original).bundle and Settings(special).bundle and added this script to the pre-build:
subdir="original" ; [[ $CONFIGURATION = "Debug-Special" ]] && subdir="special" ; cp "$SRCROOT/Settings($subdir).bundle/Root.plist" "$SRCROOT/Settings.bundle/"
Be sure to select your project to add the environment variables to the shell that runs the script.
I realize the thread is pretty old in code years but it is still relevant, and hopefully this will save the next person who finds it some time.
Solved this by adding a script in pre-build where the alternative bundle is renamed to Settings.bundle

Can Xcode include application resource files that are generated during the build process?

I have a bunch of content files for my iPhone app that I generate via shell script. It takes way too long to be a part of the Xcode build process, so I run it periodically.
I don't want to have to continually add these files to my Xcode project in order to get them included my app resources folder. Is there a way to get Xcode to copy the contents of a folder into the app resources at build time? (It'd be a bonus if I could specify other locations, such as the approot documents folder.)
I tried adding a new 'Copy Files Build Phase' to my target, but that didn't seem to work. This seems like a common problem, but I couldn't find anything about it here.
-- Edit (What I did)
Per Jasarien and cdespinosa's suggestions, I did the following in a build script. I decided not to copy to the destination, because that would only work when using the simulator, I don't think that'll work when deploying to a device.
cp -rf "$PROJECT_DIR/mystuff" "$CONFIGURATION_BUILD_DIR/$CONTENTS_FOLDER_PATH/"
-- Edit 2
This doesn't appear to get files onto my iPhone. Any ideas?
You can setup a Run Script build phase in your app's target. Write the script so that it copies the resources into the app bundle.
This is the path i use for output:
${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}
Just copy your files in that directory, it's the bundle (simulator or device).
You can also take a look at my answer here : how to set up a Xcode build rule with a variable output file list?