Linked Source Files Across PhoneGap Projects on OSX

If you are manually building PhoneGap projects across multiple platforms, managing source files can sometimes become a little bit tricky. If you are building for Android, you need a project within Eclipse. If you are building for iOS, you need a project within Xcode. If you are building for both, you need to make sure that the code in the Eclipse project is in synch with the code in the Xcode project so that the platform-specific apps are in parity with each other.

Keeping the project source code in synch can be achieved using manual copy/paste between projects, but this is messy and error prone. The synchronization can also be scripted using ANT or other scripting language, but this requires an additional script and/or step in your build process. Although scripting is a reliable process, sometimes you just don’t need the script.

If you don’t want to manually synch things, and you don’t want a script, you can use a symlink to link directory paths. Basically, create a “src” folder outside of each project, and create a symlink reference to the src folder inside of the “www” folder for each project.   Symlinks allow a logical directory mapping, which actually points to another location on the hard disk.

From the command line, you just use the following command:

ln -s source target

To setup your project, first create your directory structure.  I created a parent folder for the project. Inside of that folder, I created a “project-ios” folder, “project-android” folder, and “src” folder.   The “src” folder will contain the shared HTML/JavaScript for the application.   The “project-ios” folder will contain the Xcode project, and the “project-android” folder will contain the Eclipse project.

Project Structure

Next, create the actual iOS and Android projects inside of these folders, following the normal setup instructions:

Once you have set up both projects, you’ll need to configure the symlinks.   Put a copy of the “index.html” file into your “src” directory.  Next, go to the “www” directory for each project and delete the “index.html” file to remove any ambiguity or chance for error.

However, DO NOT DELETE THE PHONEGAP.js files!

The phonegap.1.4.1.js files are platform specific.  The Android version will not work with iOS, and the iOS version will not work with Android.

Next, navigate to your root folder that contains the “src”, “project-iOS”, and “project-Android” folders. Here you will create the actual symlink references.   When doing so, be sure to use the full path to the source and target destinations.  You will need to create a symlink reference from the “src” directory to “project-ios/www/src”, and a symlink reference from the “src” directory to “project-android/assets/www/src”.

If you try to use a relative path from your current location, it will give you errors and a massive headache.     You can use “pwd” to get a reference to the full path of your current directory.

Here are the commands that I used on my system, where the root directory is “/Users/triceam/Documents/dev/phonegap-sharedsource”:

ln -s /Users/triceam/Documents/dev/phonegap-sharedsource/src/ /Users/triceam/Documents/dev/phonegap-sharedsource/project-ios/www/src
ln -s /Users/triceam/Documents/dev/phonegap-sharedsource/src/ /Users/triceam/Documents/dev/phonegap-sharedsource/project-android/assets/www/src

You can see this in my console in the image below:

Create Symlinks

This will create logical links to the root “src” folder, which can be treated like any other directory structure.

Symlink References

In both Eclipse and Xcode, this will show up as a normal folder.  Any edits in one IDE will show up in the other IDE since they are pointing to the same physical directory on the hard disk.

Content in Eclipse & Xcode

Next, you’ll need to update the PhoneGap projects to point to the shared index.html file.

In the Eclipse project, open your main Android activity and change the call to super.loadUrl to refer to the index.html file inside of the linked “src” directory in the onCreate method.

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    super.loadUrl("file:///android_asset/www/src/index.html");
}

In the iOS project, open AppDelegate.m. You’ll also need to update it to reference the index.html file inside of “src”. You’ll just need to edit the start page to “src/index.html” inside of the function: (BOOL) application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions

self.viewController.wwwFolderName = @"www";
self.viewController.startPage = @"src/index.html";

Also, be sure to update the link inside of “src/index.html” to point to the project-specific PhoneGap JavaScript files in the parent directory “../phonegap-1.4.1.js”
(they are not inside of the linked folder):

<script charset="utf-8" type="text/javascript" src="../phonegap-1.4.1.js"></script>
  • Zlatka

    Thanks! Really useful and well explained :)

  • halsafar

    XCode does not appear to follow the sym links. If I put a real index.html in the folder it works. If I setup the symlinks it fails to resolve the symlinks when bundling the project.

    • http://www.tricedesigns.com Andrew

      How do you have the links setup? It works for me. In mine, I have the simlinks inside of the www folder.

  • jayseeg

    I’m late to this party, but…

    I get a ‘The argument is invalid.’ alert when I try to run to a device in xcode 4.4.1.

    Anyone else running into this problem, and/or have a solution?

  • Rachel

    I thought symlinks weren’t working for me, because Xcode would follow them, but after build, I would get the “ERROR: Start Page at ‘www/index.html’ was not found.” message.

    Only now I realized that the link target must be absolute. I’ve always used relative targets (ln -s ../../www/index.html index.html) and it was working fine on my Ubuntu box, compiling this phonegap project to Android.

    Apparently the iOS compiler isn’t compatible with relative symlinks, so I had to use absolute ones, which is actually the example in this post.

    This is a shame because absolute paths will usually only work for one dev on one machine. My current symlink is ln -s /Users/rachel/projects/projectname/www/index.html index.html.

    I thought I’d post this here seeing I spent a lot of time until getting to this conclusion.

    • http://www.tricedesigns.com Andrew

      Thanks! Yeah, they have to be absolute, or else you run into a bunch of problems.

  • http://skeater.co.uk Pete

    @jayseeg I’m getting that same error “The argument is invalid.”.

    I’ve used absolute paths in my symlinks, and I’ve tried using a bash run script to copy the files but it’s not working.

    Did anyone else get or solve this problem?

    • http://www.tricedesigns.com Andrew

      I’m using Xcode 4.5.1, and not seeing this issue – the SymLinks still work for me. Sorry, I’m no help.

  • Pete

    @Andrew “the argument is invalid” only gets thrown when I try to run the app on a device.

    It works fine on the simulator

  • http://www.ten24web.com Sumit Verma

    Adding a run script for build phase fixes the error. Here is the script:

    rsync -pvtrlL –cvs-exclude \
    $PROJECT_DIR/www* \
    $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH

  • Pingback: My Workflow for Developing PhoneGap Applications | ANDREW TRICE()

  • http://appvanced.net Maurice

    Hi Andrew,

    Great article, helped a me a lot!

    One thing though:
    in more recent versions of Phonegap (e.g. 2.5.0) there’s no longer the need to modify the AppDelegate.m and the Android Main activity to change the “index.html” location.
    This can now be configured in “config.xml” (both iOS & Android):

    Best regards,
    Maurice

  • http://appvanced.net Maurice

    Seems some HTML fell out…
    The config.xml should be updated to:

    <content src="src/index.html" />

    • http://www.tricedesigns.com Andrew

      Thanks! YEs, it did change, so it’s now much easier.

  • http://findmybeer.com Christopher Johnson

    Andrew, your blog posts are really helpful. I consider them to be the missing manual for phonegap!

    Regarding linking of source files, can this process tie into phonegap build and how? Can you make your ‘src’ directory into a git repo and push it to phonegap build, or are there other considerations?

    Thank you!

    • http://www.tricedesigns.com Andrew

      Hi Christopher,
      I don’t think this exact process can tie into a PhoneGap build compilation. There are ways to create shared Git modules/submodules, but I honestly do not know the details about them, and have not personally tested them with PhoneGap Build.

  • Adrian

    Great post – been looking for a way to share www folder for a while! (worked fine for me in Xcode 4.6(iOS), Eclipse (Android) with Cordova 2.7.0, JQuery Mobile 1.3.1 – both in sims and on live devices)

  • way2tour

    Great post – If we have to get the content from web service to mange the html pages how can we do ?

  • Tony Baldarelli

    I followed these directions and the build portion ( when I tried to install it to a device ) did not work – got two “The argument is invalid” errors. I am using XCode 4.5.1. Here is the file in question. Note the full absolute path.

    mac-mini:www work$ pwd
    /Users/work/Documents/wmsapp_iphone/www
    mac-mini:www work$ ls -lt wmsapp
    lrwxr-xr-x 1 work staff 72 Aug 8 13:04 wmsapp -> /Users/work/Documents/workspace/wmsapp/target/wmsapp-1.0-SNAPSHOT/wmsapp
    mac-mini:www work$

    What am I doing wrong?

    Some notes:

    1. It does work in the simulator, I just cannot install it onto a device.

    2. If I copy my “wmsapp” folder instead of creating a sym link, it works fine.

    3. Symlink command I ran to get above: ln -s /Users/work/Documents/workspace/wmsapp/target/wmsapp-1.0-SNAPSHOT/wmsapp /Users/work/Documents/wmsapp_iphone/www/wmsapp

  • Marco Chiodetti

    Great post! Thank you very much! Anyone know something about the splashpage to be crossplatform (Android and iOS)?

  • the_new_mr

    I tried using relative links and it seemed to work. Perhaps the new Xcode can work with relative links okay? I’m on Xcode 5.0.1