58

Right now I'm using a few scripts to generate files that I'm including as resources in Xcode. The thing is I'm running the script, then deleting from the project, then adding back into the project. There must be a way to automate this last step, so that the script can generate the files and automatically add them into the xcode project for me.

I'm using bash but any language examples would help.

Thanks, Andrew

Pascal
  • 16,846
  • 4
  • 60
  • 69
Andrew
  • 1,497
  • 1
  • 14
  • 22
  • 4
    @Andrew: did you manage to get this working? I'm curious about the final result. – e.James Feb 04 '10 at 00:24
  • I know it's a bit late, but I just came across [this article](http://www.benzado.com/blog/post/352/file-references-relative-to-derived_file_dir-in-xcode) explaining how to do something that sounds like what you're looking for. – Richard Apr 03 '11 at 13:17

7 Answers7

43

I had a similar need as Andrew. I needed to be able to include resources from a script without knowing those resources ahead of time. Here's the solutions I came up with:

Add a new Run Script build phase after “Copy Bundle Resource” that contains the following command:

find -L ${SRCROOT}/SomeDerivedResources \
    -type f -not -name ".*" \
    -not -name "`basename ${INFOPLIST_FILE}`" \
    | xargs -t -I {} \
    cp {} ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/

Looks scary, but let’s break it down:

find -L ${SRCROOT}/SomeDerivedResources

This crawls the directory SomeDerivedResources in our source root (-L tells it to follow symbolic links)

-type f

Only include regular files

-not -name ".*"

Ignore files starting with a dot

-not -name "`basename ${INFOPLIST_FILE}`"

In my case, my Info plists live in my SomeDerivedResources directory so we need to exclude that file from being copied to our product

| xargs -t -I {}

Pipe the results of find into xargs with -t (echo resulting commands to stderr so they show up in our build log), -I (run the command once for each input file) and use {} as our argument placeholder

cp {} ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/

Lastly, copy each found file (denoted by {}) to our product’s resource directory.

I realized when typing this that using an rsync setup instead of cp could prevent us from copying resources each time you build. If your resources are very large it might be worth looking in to.

(Also, a folder reference wouldn’t work for my need for a few reasons. One, my icons are in my DerivedResources directory and having them in a subdirectory in the bundle seems not to work. Also, I ideally wanted to be able to use [UIImage imageNamed:@"MyAwesomeHappyImage.png"] and -pathForResource:ofType: (and some of my files are nested further inside my DerivedResources directory). If your needs don’t contain those restraints, I highly suggest you go the folder reference route.)

Ben Cochran
  • 1,290
  • 11
  • 13
  • 1
    Awesomeness for: "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/" --- exactly what I needed, and couldn't find in Apple's docs. I have no idea why Xcode4 is too lazy to automatically do this when you add the Output Files section :( :( – Adam Jun 25 '12 at 14:28
  • Related to not being able to access a subdirectory in a bundle - it's likely to be because the bundle and the subdirectory have the same name. If you do not use exactly the same name, it should work fine. – Agathe Aug 12 '12 at 00:48
  • I'm using this script and it works perfectly! One thing though... when we modify the .app file using this method, it seems like apple won't let you install the project on a device because it was tampered with. Any suggestions? – kevinl Aug 30 '13 at 16:10
  • 2
    @kevinl Are you running the script as a “Run Script” build phase in Xcode? Code signing happens after these steps, so it should run just fine. – Ben Cochran Sep 06 '13 at 05:22
  • @BenCochran ah yes. That was part of the problem. Everything works now thanks. – kevinl Sep 06 '13 at 19:31
  • Thanks @BenCochran! For the ones that are having problems with the spaces in the path (if the project/target has a space within the name, e.g. "Example Project.app"), you can try: ROOT="${SRCROOT}/SomeDerivedResources" OUTPUT="${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/" find -L "${ROOT}" -type f -not -name ".*" | xargs -t -I {} cp {} "${OUTPUT}" – Radu Dan Feb 18 '14 at 11:00
  • Very clever use of find/xargs/cp with great explanation! But to save time and avoid copying identical files every time you build, you can replace everything with this one command: `rsync -ua "${SRCROOT}/SomeDerivedResources/" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/" --exclude="\`basename ${INFOPLIST_FILE}\`"` The -u option prevents overwriting files unless the source file is newer. – Winter Dragoness Aug 06 '14 at 17:43
  • It won't let me edit my comment anymore but to exclude files starting with dot, I believe you would add `--exclude="/.*"` – Winter Dragoness Aug 06 '14 at 17:51
  • This seems to only work for resources such as images and data. Can you provide an example script that would work for copying source files for compile? – Mike Onorato Dec 12 '17 at 04:44
37

This can be done by adding a new build phase to your application.

  1. In your Xcode project browser, find the target for your application, and expand it to show all of the build phases.

  2. Add a new "run script" build phase to your target. The easiest way is to right-click on the target and choose "Add/New Build Phase/New Run Script Build Phase"

  3. Adding the new build phase should bring up an inspector window. In this window, you can enter the entire shell script, or simply a command line to run the script.

  4. Here's the gold: At the bottom of the inspector window you can specify input files and output files. Specifying input files sets up dependencies automatically (the shell script will only be executed if some of the input files have been modified). Specifying output files automatically propagates the dependencies to those files. When your shell script is run, Xcode knows that it needs to deal with those files that the shell script has modified.

  5. Be sure to drag your new build phase up to the top of the list of phases as shown in the screenshot below. The order will be important if you need those resource files to be included in the bundle.

  6. Save, build, commit to the repository, ask for a raise, get some fresh air and have a nice day! :)

Iulian Onofrei
  • 9,188
  • 10
  • 67
  • 113
e.James
  • 116,942
  • 41
  • 177
  • 214
  • 7
    Thanks! In my case, I don't know the output files ahead of time. I'm looking to dynamically add files to the project, in some kind of sub-group. Is that possible? Or am I missing something about your example? – Andrew Sep 03 '09 at 19:21
  • 1
    Hmm. That's tricky. For what purpose are the files being added to the project? Do they just need to be copied into the Application bundle, or do they need to be compiled with the rest of the code? I can think of a few solutions, but I'll need to know a little bit more about how you need to use the files after they have been generated. – e.James Sep 04 '09 at 00:49
  • Thanks for the response! Basically, the files just need to be copied into the main bundle. They are only assets. But since the actual asset names are often changing or new ones are being added, I don't know all the names ahead of time, except their common extensions and their directory path. The way they are "generated" is they are checked into SVN :) I'm just looking for a way to save me time by having to click on "add existing file..." and then adding them manually... would much prefer an "svn update" followed by a compile :) Thanks! – Andrew Sep 08 '09 at 02:14
  • 2
    Andrew, did you find a solution for this? – Alastair Stuart Sep 21 '09 at 23:21
  • 2
    Not yet, the add script stuff was very useful and I've done that now, but I still can't find a way to automatically add files to a project. Any ideas? – Andrew Sep 25 '09 at 00:52
  • 1
    Is there a way to include .h and .m files dynamically to xcode project?? Or Is there any other way apart from Xcode where I can add .h or .m file in xcode project file?? – DShah Nov 06 '13 at 12:42
  • Note that in XCode7, I had to drag and drop other items below the script. It wouldn't let me drag the script before the Compile Sources. – Volomike Dec 14 '15 at 09:46
  • 6
    those links are dead – Pup Feb 16 '17 at 19:37
12

For those with large number of files, to avoid having to recopy (or recheck) each file, as suggested by @Ben Cochran (thanks a lot for the great script), this is how to do it with rsync:

enter image description here

lucasart
  • 1,643
  • 1
  • 20
  • 29
  • I had an issue with needing my app bundle to include to a large number of resource files that were provided by a separate service BUT changed with every release, this solved my problem perfectly. – Kevin James Hunt Feb 02 '15 at 23:32
  • @KevinJamesHunt I also have a similar requirement, I have a set of js files which is added as folders in the Resources directory. Every time there is a change I am manually copying and replacing. Instead, I want to be automated lets say the files are somewhere in the server, then is there a way to run a script which will download the content from server and copy it to the resources folder. – anoop4real Aug 09 '17 at 07:27
8

Basically, the files just need to be copied into the main bundle

In that case just add a folder reference to the project (Create a folder in your project folder and then drag it into your projects "Resources" group (in the "Files & Groups" list; then in the sheet that appears select the "Create Folder References for any added Folder" radio button) and Xcode will copy the folder and all of its contents into the target bundle at build time.

Just an additional note: If you use this method to add image subfolders you'll have to prefix the image name with the subfolder name to use '[UIImage imageNamed:]'. For example if you have an image named "Rendezvous.png" in a subfolder named "MyImages":

`

// this won't work
UIImage * image = [UIImage imageNamed:@"Rendezvous"];
if (image) {
    NSLog(@"Found Rendezvous!");
} else {
    NSLog(@"Didn't find Rendezvous.");
}

// but this will!
image = [UIImage imageNamed:@"MyImages/Rendezvous"];
if (image) {
    NSLog(@"Found MyImages/Rendezvous!");
} else {
    NSLog(@"Didn't find MyImages/Rendezvous.");
}

`

geowar
  • 4,397
  • 1
  • 28
  • 24
  • This would have totally worked too, and is much easier :) The only thing it changes is you can't use functions like [UIImage imageNamed:] because the files stay in the folder within the bundle. But a great solution none-the-less. – Andrew Sep 22 '10 at 20:54
  • 1
    Actually you can still use "[UIImage imageNamed:]"; you just need to include the sub-folder name with the image name. For example: '[UIImage imageNamed:@"MyImagesFolder/MyImageName"];'. – geowar Sep 04 '13 at 15:15
  • the folder solution works perfectly if u want add some code classes (.swift, for example). PS. now you need press option, when "add new files" screen appears, to select if u gonna use folders or groups – Zaporozhchenko Oleksandr Feb 21 '18 at 07:56
5

If you already have the files somewhere on your system, relative to your source root, you can always add a "Copy Files" phase. This will allow you to specify a directory where your resources should be copied from.

You can combine this with the Build Script phase answer provided to you already. For instance, run a script to check out your assets from Subversion into a subdirectory of your project, and then follow that up with a Copy Files phase that copies from "$(SRCROOT)/Assets".

  • Excellent idea. This coupled with the above example does exactly what I needed at the time, I'll definitely be doing this for the next update of my app. – Andrew May 18 '10 at 00:13
  • @Andrew can you please explain how exactly you can do this on XCode 4? Thank you very much! – Mihai Fratu Oct 06 '11 at 18:32
  • You can only specify the destination folder, not the source folder. – akc May 14 '13 at 14:06
0

I found myself with a similar situation using Ionic Capacitor. What I was expecting was to include files on the "Copy Bundle Resources" bundle phase. What I found is that Ionic already packs you some inclusions and if you slip your files along this folders you get it included as well.

enter image description here

Do you see the App folder inclusion? It our entry point.

To include on it I add a script that do something like this:

cp -Rf ./includes/yourfolder/ ./ios/App/App/
Igor
  • 804
  • 10
  • 23
0

I managed to solve the issue

"Code object is not signed at all"

that can be encountered during build upload to iTunes Connect in this way:

I didnot include the script to Bundle resources.

So the script (in this case Python file) is executed during build, (it does what it has to do) but it is not included in the bundle of the app.

How to do?

Open Build Phases, go to Copy Bundle Resources section, select the file and remove it with (-).