Merge branch 'add_target' of https://github.com/SidneyS/node-xcode into SidneyS-add_target
diff --git a/lib/pbxFile.js b/lib/pbxFile.js
index 887f6e6..4431f0d 100644
--- a/lib/pbxFile.js
+++ b/lib/pbxFile.js
@@ -1,100 +1,193 @@
 var path = require('path'),
-    util = require('util'),
-    M_EXTENSION = /[.]m$/, SOURCE_FILE = 'sourcecode.c.objc',
-    H_EXTENSION = /[.]h$/, HEADER_FILE = 'sourcecode.c.h',
-    BUNDLE_EXTENSION = /[.]bundle$/, BUNDLE = '"wrapper.plug-in"',
-    XIB_EXTENSION = /[.]xib$/, XIB_FILE = 'file.xib',
-    DYLIB_EXTENSION = /[.]dylib$/, DYLIB = '"compiled.mach-o.dylib"',
-    FRAMEWORK_EXTENSION = /[.]framework$/, FRAMEWORK = 'wrapper.framework',
-    ARCHIVE_EXTENSION = /[.]a$/, ARCHIVE = 'archive.ar',
-    DEFAULT_SOURCE_TREE = '"<group>"',
-    DEFAULT_FILE_ENCODING = 4;
+    util = require('util');
 
-function detectLastType(path) {
-    if (M_EXTENSION.test(path))
-        return SOURCE_FILE;
+var DEFAULT_SOURCETREE = '"<group>"',
+    DEFAULT_PRODUCT_SOURCETREE = 'BUILT_PRODUCTS_DIR',
+    DEFAULT_FILEENCODING = 4,
+    DEFAULT_GROUP = 'Resources',
+    DEFAULT_FILETYPE = 'unknown';
 
-    if (H_EXTENSION.test(path))
-        return HEADER_FILE;
+var FILETYPE_BY_EXTENSION = {
+        a: 'archive.ar',
+        app: 'wrapper.application',
+        appex: 'wrapper.app-extension',
+        bundle: 'wrapper.plug-in',
+        dylib: 'compiled.mach-o.dylib',
+        framework: 'wrapper.framework',
+        h: 'sourcecode.c.h',
+        m: 'sourcecode.c.objc',
+        markdown: 'text',
+        mdimporter: 'wrapper.cfbundle',
+        octest: 'wrapper.cfbundle',
+        pch: 'sourcecode.c.h',
+        plist: 'text.plist.xml',
+        sh: 'text.script.sh',
+        swift: 'sourcecode.swift',
+        xcassets: 'folder.assetcatalog',
+        xcconfig: 'text.xcconfig',
+        xcdatamodel: 'wrapper.xcdatamodel',
+        xcodeproj: 'wrapper.pb-project',
+        xctest: 'wrapper.cfbundle',
+        xib: 'file.xib'
+    },
+    GROUP_BY_FILETYPE = {
+        'archive.ar': 'Frameworks',
+        'compiled.mach-o.dylib': 'Frameworks',
+        'wrapper.framework': 'Frameworks',
+        'sourcecode.c.h': 'Resources',
+        'sourcecode.c.objc': 'Sources',
+        'sourcecode.swift': 'Sources'
+    },
+    PATH_BY_FILETYPE = {
+        'compiled.mach-o.dylib': 'usr/lib/',
+        'wrapper.framework': 'System/Library/Frameworks/'
+    },
+    SOURCETREE_BY_FILETYPE = {
+        'compiled.mach-o.dylib': 'SDKROOT',
+        'wrapper.framework': 'SDKROOT'
+    },
+    ENCODING_BY_FILETYPE = {
+        'sourcecode.c.h': 4,
+        'sourcecode.c.h': 4,
+        'sourcecode.c.objc': 4,
+        'sourcecode.swift': 4,
+        'text': 4,
+        'text.plist.xml': 4,
+        'text.script.sh': 4,
+        'text.xcconfig': 4
+    };
 
-    if (BUNDLE_EXTENSION.test(path))
-        return BUNDLE;
 
-    if (XIB_EXTENSION.test(path))
-        return XIB_FILE;
-
-    if (FRAMEWORK_EXTENSION.test(path))
-        return FRAMEWORK;
-
-    if (DYLIB_EXTENSION.test(path))
-        return DYLIB;
-
-    if (ARCHIVE_EXTENSION.test(path))
-        return ARCHIVE;
-
-    // dunno
-    return 'unknown';
+function unquoted(text){
+    return text.replace (/(^")|("$)/g, '')
 }
 
-function fileEncoding(file) {
-    if (file.lastType != BUNDLE && !file.customFramework) {
-        return DEFAULT_FILE_ENCODING;
+function detectType(filePath) {
+    var extension = path.extname(filePath).substring(1),
+        filetype = FILETYPE_BY_EXTENSION[unquoted(extension)];
+
+    if (!filetype) {
+        return DEFAULT_FILETYPE;
+    }
+
+    return filetype;
+}
+
+function defaultExtension(fileRef) {
+    var filetype = fileRef.lastKnownFileType || fileRef.explicitFileType;
+
+    for(var extension in FILETYPE_BY_EXTENSION) {
+        if(FILETYPE_BY_EXTENSION.hasOwnProperty(unquoted(extension)) ) {
+             if(FILETYPE_BY_EXTENSION[unquoted(extension)] === filetype )
+                 return extension;
+        }
     }
 }
 
-function defaultSourceTree(file) {
-    if (( file.lastType == DYLIB || file.lastType == FRAMEWORK ) && !file.customFramework) {
-        return 'SDKROOT';
-    } else {
-        return DEFAULT_SOURCE_TREE;
+function defaultEncoding(fileRef) {
+    var filetype = fileRef.lastKnownFileType || fileRef.explicitFileType,
+        encoding = ENCODING_BY_FILETYPE[unquoted(filetype)];
+
+    if (encoding) {
+        return encoding;
     }
 }
 
-function correctPath(file, filepath) {
-    if (file.lastType == FRAMEWORK && !file.customFramework) {
-        return 'System/Library/Frameworks/' + filepath;
-    } else if (file.lastType == DYLIB) {
-        return 'usr/lib/' + filepath;
-    } else {
-        return filepath;
+function detectGroup(fileRef) {
+    var filetype = fileRef.lastKnownFileType || fileRef.explicitFileType,
+        groupName = GROUP_BY_FILETYPE[unquoted(filetype)];
+
+    if (!groupName) {
+        return DEFAULT_GROUP;
     }
+
+    return groupName;
 }
 
-function correctGroup(file) {
-    if (file.lastType == SOURCE_FILE) {
-        return 'Sources';
-    } else if (file.lastType == DYLIB || file.lastType == ARCHIVE || file.lastType == FRAMEWORK) {
-        return 'Frameworks';
-    } else {
-        return 'Resources';
+function detectSourcetree(fileRef) {
+    
+    var filetype = fileRef.lastKnownFileType || fileRef.explicitFileType,
+        sourcetree = SOURCETREE_BY_FILETYPE[unquoted(filetype)];
+
+    if (fileRef.explicitFileType) {
+        return DEFAULT_PRODUCT_SOURCETREE;
     }
+    
+    if (fileRef.customFramework) {
+        return DEFAULT_SOURCETREE;
+    }
+    
+    if (!sourcetree) {
+        return DEFAULT_SOURCETREE;
+    }
+
+    return sourcetree;
+}
+
+function defaultPath(fileRef, filePath) {
+    var filetype = fileRef.lastKnownFileType || fileRef.explicitFileType,
+        defaultPath = PATH_BY_FILETYPE[unquoted(filetype)];
+
+    if (fileRef.customFramework) {
+        return filePath;
+    }
+    
+    if (defaultPath) {
+        return path.join(defaultPath, path.basename(filePath));
+    }
+
+    return filePath;
+}
+
+function defaultGroup(fileRef) {
+    var groupName = GROUP_BY_FILETYPE[fileRef.lastKnownFileType];
+
+    if (!groupName) {
+        return DEFAULT_GROUP;
+    }
+
+    return defaultGroup;
 }
 
 function pbxFile(filepath, opt) {
     var opt = opt || {};
+    
+    self = this;
 
-    this.lastType = opt.lastType || detectLastType(filepath);
+    this.lastKnownFileType = opt.lastKnownFileType || detectType(filepath);
+    this.group = detectGroup(self);
 
     // for custom frameworks
-    if(opt.customFramework == true) {
-      this.customFramework = true;
-      this.dirname = path.dirname(filepath);
+    if (opt.customFramework == true) {
+        this.customFramework = true;
+        this.dirname = path.dirname(filepath);
     }
 
     this.basename = path.basename(filepath);
-    this.path = correctPath(this, filepath);
-    this.group = correctGroup(this);
+    this.path = defaultPath(this, filepath);
+    this.fileEncoding = this.defaultEncoding = opt.defaultEncoding || defaultEncoding(self);
 
-    this.sourceTree = opt.sourceTree || defaultSourceTree(this);
-    this.fileEncoding = opt.fileEncoding || fileEncoding(this);
 
-    if (opt.weak && opt.weak === true) 
-      this.settings = { ATTRIBUTES: ['Weak'] };
+    // When referencing products / build output files
+    if (opt.explicitFileType) {
+        this.explicitFileType = opt.explicitFileType;
+        this.basename = this.basename + '.' + defaultExtension(this);
+        delete this.path;
+        delete this.lastKnownFileType;
+        delete this.group;
+        delete this.defaultEncoding;
+    }
+
+    this.sourceTree = opt.sourceTree || detectSourcetree(self);
+    this.includeInIndex = 0;
+
+    if (opt.weak && opt.weak === true)
+        this.settings = { ATTRIBUTES: ['Weak'] };
 
     if (opt.compilerFlags) {
         if (!this.settings)
-          this.settings = {};
-          this.settings.COMPILER_FLAGS = util.format('"%s"', opt.compilerFlags);
+            this.settings = {};
+        this.settings.COMPILER_FLAGS = util.format('"%s"', opt.compilerFlags);
     }
 }
 
diff --git a/lib/pbxProject.js b/lib/pbxProject.js
index 3628401..435bcd9 100644
--- a/lib/pbxProject.js
+++ b/lib/pbxProject.js
@@ -1,1019 +1,1367 @@
-var util = require('util'),
-    f = util.format,
-    EventEmitter = require('events').EventEmitter,
-    path = require('path'),
-    uuid = require('node-uuid'),
-    fork = require('child_process').fork,
-    pbxWriter = require('./pbxWriter'),
-    pbxFile = require('./pbxFile'),
-    fs = require('fs'),
-    parser = require('./parser/pbxproj'),
-    COMMENT_KEY = /_comment$/
-
-function pbxProject(filename) {
-    if (!(this instanceof pbxProject))
-        return new pbxProject(filename);
-
-    this.filepath = path.resolve(filename)
-}
-
-util.inherits(pbxProject, EventEmitter)
-
-pbxProject.prototype.parse = function (cb) {
-    var worker = fork(__dirname + '/parseJob.js', [this.filepath])
-
-    worker.on('message', function (msg) {
-        if (msg.name == 'SyntaxError' || msg.code) {
-            this.emit('error', msg);
-        } else {
-            this.hash = msg;
-            this.emit('end', null, msg)
-        }
-    }.bind(this));
-
-    if (cb) {
-        this.on('error', cb);
-        this.on('end', cb);
-    }
-
-    return this;
-}
-
-pbxProject.prototype.parseSync = function () {
-    var file_contents = fs.readFileSync(this.filepath, 'utf-8');
-
-    this.hash = parser.parse(file_contents);
-    return this;
-}
-
-pbxProject.prototype.writeSync = function () {
-    this.writer = new pbxWriter(this.hash);
-    return this.writer.writeSync();
-}
-
-pbxProject.prototype.allUuids = function () {
-    var sections = this.hash.project.objects,
-        uuids = [],
-        section;
-
-    for (key in sections) {
-        section = sections[key]
-        uuids = uuids.concat(Object.keys(section))
-    }
-
-    uuids = uuids.filter(function (str) {
-        return !COMMENT_KEY.test(str) && str.length == 24;
-    });
-
-    return uuids;
-}
-
-pbxProject.prototype.generateUuid = function () {
-    var id = uuid.v4()
-                .replace(/-/g,'')
-                .substr(0,24)
-                .toUpperCase()
-
-    if (this.allUuids().indexOf(id) >= 0) {
-        return this.generateUuid();
-    } else {
-        return id;
-    }
-}
-
-pbxProject.prototype.addPluginFile = function (path, opt) {
-    var file = new pbxFile(path, opt);
-
-    file.plugin = true; // durr
-    correctForPluginsPath(file, this);
-
-    // null is better for early errors
-    if (this.hasFile(file.path)) return null;
-
-    file.fileRef = this.generateUuid();
-
-    this.addToPbxFileReferenceSection(file);    // PBXFileReference
-    this.addToPluginsPbxGroup(file);            // PBXGroup
-
-    return file;
-}
-
-pbxProject.prototype.removePluginFile = function (path, opt) {
-    var file = new pbxFile(path, opt);
-    correctForPluginsPath(file, this);
-
-    this.removeFromPbxFileReferenceSection(file);    // PBXFileReference
-    this.removeFromPluginsPbxGroup(file);            // PBXGroup
-
-    return file;
-}
-
-
-pbxProject.prototype.addSourceFile = function (path, opt) {
-  
-    var file = this.addPluginFile(path, opt);
-    if (!file) return false;
-
-    file.target = opt ? opt.target : undefined;
-    file.uuid = this.generateUuid();
-
-    this.addToPbxBuildFileSection(file);        // PBXBuildFile
-    this.addToPbxSourcesBuildPhase(file);       // PBXSourcesBuildPhase
-
-    return file;
-}
-
-
-pbxProject.prototype.removeSourceFile = function (path, opt) {
-    var file = this.removePluginFile(path, opt)
-    file.target = opt ? opt.target : undefined;
-    this.removeFromPbxBuildFileSection(file);        // PBXBuildFile
-    this.removeFromPbxSourcesBuildPhase(file);       // PBXSourcesBuildPhase
-
-    return file;
-}
-
-pbxProject.prototype.addHeaderFile = function (path, opt) {
-    return this.addPluginFile(path, opt)
-}
-
-pbxProject.prototype.removeHeaderFile = function (path, opt) {
-    return this.removePluginFile(path, opt)
-}
-
-pbxProject.prototype.addResourceFile = function (path, opt) {
-    opt = opt || {};
-
-    var file;
-
-    if (opt.plugin) {
-        file = this.addPluginFile(path, opt);
-        if (!file) return false;
-    } else {
-        file = new pbxFile(path, opt);
-        if (this.hasFile(file.path)) return false;
-    }
-
-    file.uuid = this.generateUuid();
-    file.target = opt ? opt.target : undefined;
-
-    if (!opt.plugin) {
-        correctForResourcesPath(file, this);
-        file.fileRef = this.generateUuid();
-    }
-
-    this.addToPbxBuildFileSection(file);        // PBXBuildFile
-    this.addToPbxResourcesBuildPhase(file);     // PBXResourcesBuildPhase
-
-    if (!opt.plugin) {
-        this.addToPbxFileReferenceSection(file);    // PBXFileReference
-        this.addToResourcesPbxGroup(file);          // PBXGroup
-    }
-
-    return file;
-}
-
-pbxProject.prototype.removeResourceFile = function (path, opt) {
-    var file = new pbxFile(path, opt);
-    file.target = opt ? opt.target : undefined;
-    
-    correctForResourcesPath(file, this);
-
-    this.removeFromPbxBuildFileSection(file);        // PBXBuildFile
-    this.removeFromPbxFileReferenceSection(file);    // PBXFileReference
-    this.removeFromResourcesPbxGroup(file);          // PBXGroup
-    this.removeFromPbxResourcesBuildPhase(file);     // PBXResourcesBuildPhase
-    
-    return file;
-}
-
-pbxProject.prototype.addFramework = function (fpath, opt) {
-    var file = new pbxFile(fpath, opt);
-    // catch duplicates
-    if (this.hasFile(file.path)) return false;
-
-    file.uuid = this.generateUuid();
-    file.fileRef = this.generateUuid();    
-    file.target = opt ? opt.target : undefined;
-
-
-    this.addToPbxBuildFileSection(file);        // PBXBuildFile
-    this.addToPbxFileReferenceSection(file);    // PBXFileReference
-    this.addToFrameworksPbxGroup(file);         // PBXGroup
-    this.addToPbxFrameworksBuildPhase(file);    // PBXFrameworksBuildPhase
-    
-    if(opt && opt.customFramework == true) {
-      this.addToFrameworkSearchPaths(file);
-    }
-
-    return file;
-}
-
-pbxProject.prototype.removeFramework = function (fpath, opt) {
-    var file = new pbxFile(fpath, opt);
-    file.target = opt ? opt.target : undefined;
-
-    this.removeFromPbxBuildFileSection(file);        // PBXBuildFile
-    this.removeFromPbxFileReferenceSection(file);    // PBXFileReference
-    this.removeFromFrameworksPbxGroup(file);         // PBXGroup
-    this.removeFromPbxFrameworksBuildPhase(file);    // PBXFrameworksBuildPhase
-    
-    if(opt && opt.customFramework) {
-      this.removeFromFrameworkSearchPaths(path.dirname(fpath));
-    }
-
-    return file;
-}
-
-pbxProject.prototype.addStaticLibrary = function (path, opt) {
-    opt = opt || {};
-
-    var file;
-
-    if (opt.plugin) {
-        file = this.addPluginFile(path, opt);
-        if (!file) return false;
-    } else {
-        file = new pbxFile(path, opt);
-        if (this.hasFile(file.path)) return false;
-    }
-
-    file.uuid = this.generateUuid();
-    file.target = opt ? opt.target : undefined;
-
-    if (!opt.plugin) {
-        file.fileRef = this.generateUuid();
-        this.addToPbxFileReferenceSection(file);    // PBXFileReference
-    }
-
-    this.addToPbxBuildFileSection(file);        // PBXBuildFile
-    this.addToPbxFrameworksBuildPhase(file);    // PBXFrameworksBuildPhase
-    this.addToLibrarySearchPaths(file);        // make sure it gets built!
-
-    return file;
-}
-
-// helper addition functions
-pbxProject.prototype.addToPbxBuildFileSection = function (file) {
-    var commentKey = f("%s_comment", file.uuid);
-
-    this.pbxBuildFileSection()[file.uuid] = pbxBuildFileObj(file);
-    this.pbxBuildFileSection()[commentKey] = pbxBuildFileComment(file);
-}
-
-pbxProject.prototype.removeFromPbxBuildFileSection = function (file) {
-    var uuid;
-
-    for(uuid in this.pbxBuildFileSection()) {
-        if(this.pbxBuildFileSection()[uuid].fileRef_comment == file.basename) {
-            file.uuid = uuid;
-            delete this.pbxBuildFileSection()[uuid];
-        }
-    }
-    var commentKey = f("%s_comment", file.uuid);
-    delete this.pbxBuildFileSection()[commentKey];
-}
-
-pbxProject.prototype.addPbxGroup = function (filePathsArray, name, path, sourceTree) {
-    var groups = this.hash.project.objects['PBXGroup'],
-        pbxGroupUuid = this.generateUuid(),
-        commentKey = f("%s_comment", pbxGroupUuid),
-        pbxGroup = {
-            isa: 'PBXGroup',
-            children: [],
-            name: name,
-            path: path,
-            sourceTree: sourceTree ? sourceTree : '"<group>"'
-        },
-        fileReferenceSection = this.pbxFileReferenceSection(),
-        filePathToReference = {};
-        
-    for (var key in fileReferenceSection) {
-        // only look for comments
-        if (!COMMENT_KEY.test(key)) continue;
-        
-        var fileReferenceKey = key.split(COMMENT_KEY)[0],
-            fileReference = fileReferenceSection[fileReferenceKey];
-        
-        filePathToReference[fileReference.path] = {fileRef: fileReferenceKey, basename: fileReferenceSection[key]};
-    }
-
-    for (var index = 0; index < filePathsArray.length; index++) {
-        var filePath = filePathsArray[index],
-            filePathQuoted = "\"" + filePath + "\"";
-        if (filePathToReference[filePath]) {
-            pbxGroup.children.push(pbxGroupChild(filePathToReference[filePath]));
-            continue;
-        } else if (filePathToReference[filePathQuoted]) {
-            pbxGroup.children.push(pbxGroupChild(filePathToReference[filePathQuoted]));
-            continue;
-        }
-        
-        var file = new pbxFile(filePath);
-        file.uuid = this.generateUuid();
-        file.fileRef = this.generateUuid();
-        this.addToPbxFileReferenceSection(file);    // PBXFileReference
-        this.addToPbxBuildFileSection(file);        // PBXBuildFile
-        pbxGroup.children.push(pbxGroupChild(file));
-    }
-    
-    if (groups) {
-        groups[pbxGroupUuid] = pbxGroup;
-        groups[commentKey] = name;
-    }
-    
-    return {uuid: pbxGroupUuid, pbxGroup: pbxGroup};
-}
-
-pbxProject.prototype.addToPbxFileReferenceSection = function (file) {
-    var commentKey = f("%s_comment", file.fileRef);
-
-    this.pbxFileReferenceSection()[file.fileRef] = pbxFileReferenceObj(file);
-    this.pbxFileReferenceSection()[commentKey] = pbxFileReferenceComment(file);
-}
-
-pbxProject.prototype.removeFromPbxFileReferenceSection = function (file) {
-
-    var i;
-    var refObj = pbxFileReferenceObj(file);
-    for(i in this.pbxFileReferenceSection()) {
-        if(this.pbxFileReferenceSection()[i].name == refObj.name ||
-           ('"' + this.pbxFileReferenceSection()[i].name + '"') == refObj.name ||
-           this.pbxFileReferenceSection()[i].path == refObj.path ||
-           ('"' + this.pbxFileReferenceSection()[i].path + '"') == refObj.path) {
-            file.fileRef = file.uuid = i;
-            delete this.pbxFileReferenceSection()[i];
-            break;
-        }
-    }
-    var commentKey = f("%s_comment", file.fileRef);
-    if(this.pbxFileReferenceSection()[commentKey] != undefined) {
-        delete this.pbxFileReferenceSection()[commentKey];
-    }
-
-    return file;
-}
-
-pbxProject.prototype.addToPluginsPbxGroup = function (file) {
-    var pluginsGroup = this.pbxGroupByName('Plugins');
-    pluginsGroup.children.push(pbxGroupChild(file));
-}
-
-pbxProject.prototype.removeFromPluginsPbxGroup = function (file) {
-    var pluginsGroupChildren = this.pbxGroupByName('Plugins').children, i;
-    for(i in pluginsGroupChildren) {
-        if(pbxGroupChild(file).value == pluginsGroupChildren[i].value &&
-           pbxGroupChild(file).comment == pluginsGroupChildren[i].comment) {
-            pluginsGroupChildren.splice(i, 1);
-            break;
-        }
-    }
-}
-
-pbxProject.prototype.addToResourcesPbxGroup = function (file) {
-    var pluginsGroup = this.pbxGroupByName('Resources');
-    pluginsGroup.children.push(pbxGroupChild(file));
-}
-
-pbxProject.prototype.removeFromResourcesPbxGroup = function (file) {
-    var pluginsGroupChildren = this.pbxGroupByName('Resources').children, i;
-    for(i in pluginsGroupChildren) {
-        if(pbxGroupChild(file).value == pluginsGroupChildren[i].value &&
-           pbxGroupChild(file).comment == pluginsGroupChildren[i].comment) {
-            pluginsGroupChildren.splice(i, 1);
-            break;
-        }
-    }
-}
-
-pbxProject.prototype.addToFrameworksPbxGroup = function (file) {
-    var pluginsGroup = this.pbxGroupByName('Frameworks');
-    pluginsGroup.children.push(pbxGroupChild(file));
-}
-
-pbxProject.prototype.removeFromFrameworksPbxGroup = function (file) {
-    var pluginsGroupChildren = this.pbxGroupByName('Frameworks').children;
-    
-    for(i in pluginsGroupChildren) {
-        if(pbxGroupChild(file).value == pluginsGroupChildren[i].value &&
-           pbxGroupChild(file).comment == pluginsGroupChildren[i].comment) {
-            pluginsGroupChildren.splice(i, 1);
-            break;
-        }
-    }
-}
-pbxProject.prototype.addToPbxSourcesBuildPhase = function (file) {
-    var sources = this.pbxSourcesBuildPhaseObj(file.target);
-    sources.files.push(pbxBuildPhaseObj(file));
-}
-
-pbxProject.prototype.removeFromPbxSourcesBuildPhase = function (file) {
-
-    var sources = this.pbxSourcesBuildPhaseObj(file.target), i;
-    for(i in sources.files) {
-        if(sources.files[i].comment == longComment(file)) {
-            sources.files.splice(i, 1);
-            break; 
-        }
-    }
-}
-
-pbxProject.prototype.addToPbxResourcesBuildPhase = function (file) {
-    var sources = this.pbxResourcesBuildPhaseObj(file.target);
-    sources.files.push(pbxBuildPhaseObj(file));
-}
-
-pbxProject.prototype.removeFromPbxResourcesBuildPhase = function (file) {
-    var sources = this.pbxResourcesBuildPhaseObj(file.target), i;
-
-    for(i in sources.files) {
-        if(sources.files[i].comment == longComment(file)) {
-            sources.files.splice(i, 1);
-            break;
-        }
-    }
-}
-
-pbxProject.prototype.addToPbxFrameworksBuildPhase = function (file) {
-    var sources = this.pbxFrameworksBuildPhaseObj(file.target);
-    sources.files.push(pbxBuildPhaseObj(file));
-}
-
-pbxProject.prototype.removeFromPbxFrameworksBuildPhase = function (file) {
-    var sources = this.pbxFrameworksBuildPhaseObj(file.target);
-    for(i in sources.files) {
-        if(sources.files[i].comment == longComment(file)) {
-            sources.files.splice(i, 1);
-            break;
-        }
-    }
-}
-
-pbxProject.prototype.addXCConfigurationList = function (configurationObjectsArray, defaultConfigurationName, comment) {
-    var pbxBuildConfigurationSection = this.pbxXCBuildConfigurationSection(),
-        pbxXCConfigurationListSection = this.pbxXCConfigurationList(), 
-        xcConfigurationListUuid = this.generateUuid(),
-        commentKey = f("%s_comment", xcConfigurationListUuid),
-        xcConfigurationList = { 
-            isa: 'XCConfigurationList',
-            buildConfigurations: [],
-            defaultConfigurationIsVisible: 0,
-            defaultConfigurationName: defaultConfigurationName 
-        };
-
-    for (var index = 0; index < configurationObjectsArray.length; index++) {
-        var configuration = configurationObjectsArray[index],
-            configurationUuid = this.generateUuid(),
-            configurationCommentKey = f("%s_comment", configurationUuid);
-
-        pbxBuildConfigurationSection[configurationUuid] = configuration;
-        pbxBuildConfigurationSection[configurationCommentKey] = configuration.name;
-        xcConfigurationList.buildConfigurations.push({value: configurationUuid, comment: configuration.name});
-    }
-    
-    if (pbxXCConfigurationListSection) {
-        pbxXCConfigurationListSection[xcConfigurationListUuid] = xcConfigurationList;
-        pbxXCConfigurationListSection[commentKey] = comment;
-    }
-    
-    return {uuid: xcConfigurationListUuid, xcConfigurationList: xcConfigurationList};
-}
-
-pbxProject.prototype.addTargetDependency = function (target, dependencyTargets) {
-    if (!target)
-        return undefined;
-
-    var nativeTargets = this.pbxNativeTarget();
-
-    if (typeof nativeTargets[target] == "undefined")
-        throw new Error("Invalid target: " + target);
-        
-    for (var index = 0; index < dependencyTargets.length; index++) {
-        var dependencyTarget = dependencyTargets[index];
-        if (typeof nativeTargets[dependencyTarget] == "undefined")
-            throw new Error("Invalid target: " + dependencyTarget);
-    }
-    
-    var pbxTargetDependency = 'PBXTargetDependency',
-        pbxContainerItemProxy = 'PBXContainerItemProxy',
-        pbxTargetDependencySection = this.hash.project.objects[pbxTargetDependency],
-        pbxContainerItemProxySection = this.hash.project.objects[pbxContainerItemProxy];
-
-    for (var index = 0; index < dependencyTargets.length; index++) {
-        var dependencyTargetUuid = dependencyTargets[index],
-            dependencyTargetCommentKey = f("%s_comment", dependencyTargetUuid),
-            targetDependencyUuid = this.generateUuid(),
-            targetDependencyCommentKey = f("%s_comment", targetDependencyUuid),
-            itemProxyUuid = this.generateUuid(),
-            itemProxyCommentKey = f("%s_comment", itemProxyUuid),
-            itemProxy =  {
-                isa: pbxContainerItemProxy,
-                containerPortal: this.hash.project['rootObject'],
-                containerPortal_comment: this.hash.project['rootObject_comment'],
-                proxyType: 1,
-                remoteGlobalIDString: dependencyTargetUuid,
-                remoteInfo: nativeTargets[dependencyTargetUuid].name 
-            },
-            targetDependency = {
-                isa: pbxTargetDependency,
-                target: dependencyTargetUuid, 
-                target_comment: nativeTargets[dependencyTargetCommentKey],
-                targetProxy: itemProxyUuid,
-                targetProxy_comment: pbxContainerItemProxy
-            };
-            
-        if (pbxContainerItemProxySection && pbxTargetDependencySection) {
-            pbxContainerItemProxySection[itemProxyUuid] = itemProxy;
-            pbxContainerItemProxySection[itemProxyCommentKey] = pbxContainerItemProxy;
-            pbxTargetDependencySection[targetDependencyUuid] = targetDependency;
-            pbxTargetDependencySection[targetDependencyCommentKey] = pbxTargetDependency;
-            nativeTargets[target].dependencies.push({value: targetDependencyUuid, comment: pbxTargetDependency})
-        }
-    }
-    
-    return {uuid: target, target: nativeTargets[target]};
-}
-
-pbxProject.prototype.addBuildPhase = function (filePathsArray, isa, comment) {
-    var section = this.hash.project.objects[isa],
-        fileReferenceSection = this.pbxFileReferenceSection(),
-        buildFileSection = this.pbxBuildFileSection(),
-        buildPhaseUuid = this.generateUuid(),
-        commentKey = f("%s_comment", buildPhaseUuid),
-        buildPhase = {
-            isa: isa,
-            buildActionMask: 2147483647,
-            files: [],
-            runOnlyForDeploymentPostprocessing: 0
-        },
-        filePathToBuildFile = {};
-    
-    for (var key in buildFileSection) {
-        // only look for comments
-        if (!COMMENT_KEY.test(key)) continue;
-        
-        var buildFileKey = key.split(COMMENT_KEY)[0],
-            buildFile = buildFileSection[buildFileKey];
-            fileReference = fileReferenceSection[buildFile.fileRef];
-
-        if (!fileReference) continue;
-
-        var pbxFileObj = new pbxFile(fileReference.path);
-        
-        filePathToBuildFile[fileReference.path] = {uuid: buildFileKey, basename: pbxFileObj.basename, group: pbxFileObj.group};
-    }
-
-    for (var index = 0; index < filePathsArray.length; index++) {
-        var filePath = filePathsArray[index],
-            filePathQuoted = "\"" + filePath + "\"",
-            file = new pbxFile(filePath);
-
-        if (filePathToBuildFile[filePath]) {
-            buildPhase.files.push(pbxBuildPhaseObj(filePathToBuildFile[filePath]));
-            continue;
-        } else if (filePathToBuildFile[filePathQuoted]) {
-            buildPhase.files.push(pbxBuildPhaseObj(filePathToBuildFile[filePathQuoted]));
-            continue;
-        }
-        
-        file.uuid = this.generateUuid();
-        file.fileRef = this.generateUuid();
-        this.addToPbxFileReferenceSection(file);    // PBXFileReference
-        this.addToPbxBuildFileSection(file);        // PBXBuildFile
-        buildPhase.files.push(pbxBuildPhaseObj(file));
-    }
-    
-    if (section) {
-        section[buildPhaseUuid] = buildPhase;
-        section[commentKey] = comment;
-    }
-    
-    return {uuid: buildPhaseUuid, buildPhase: buildPhase};
-}
-
-// helper access functions
-pbxProject.prototype.pbxProjectSection = function () {
-    return this.hash.project.objects['PBXProject'];
-}
-pbxProject.prototype.pbxBuildFileSection = function () {
-    return this.hash.project.objects['PBXBuildFile'];
-}
-
-pbxProject.prototype.pbxXCBuildConfigurationSection = function () {
-    return this.hash.project.objects['XCBuildConfiguration'];
-}
-
-pbxProject.prototype.pbxFileReferenceSection = function () {
-    return this.hash.project.objects['PBXFileReference'];
-}
-
-pbxProject.prototype.pbxNativeTarget = function () {
-    return this.hash.project.objects['PBXNativeTarget'];
-}
-
-pbxProject.prototype.pbxXCConfigurationList = function () {
-    return this.hash.project.objects['XCConfigurationList'];
-}
-
-pbxProject.prototype.pbxGroupByName = function (name) {
-    return this.pbxItemByComment(name, 'PBXGroup');    
-}
-
-pbxProject.prototype.pbxTargetByName = function (name) {
-    return this.pbxItemByComment(name, 'PBXNativeTarget');
-}
-
-pbxProject.prototype.pbxItemByComment = function (name, pbxSectionName) {
-    var section = this.hash.project.objects[pbxSectionName],
-        key, itemKey;
-
-    for (key in section) {
-        // only look for comments
-        if (!COMMENT_KEY.test(key)) continue;
-
-        if (section[key] == name) {
-            itemKey = key.split(COMMENT_KEY)[0];
-            return section[itemKey];
-        }
-    }
-
-    return null;
-}
-
-pbxProject.prototype.pbxSourcesBuildPhaseObj = function (target) {
-    return this.buildPhaseObject('PBXSourcesBuildPhase', 'Sources', target);
-}
-
-pbxProject.prototype.pbxResourcesBuildPhaseObj = function (target) {
-    return this.buildPhaseObject('PBXResourcesBuildPhase', 'Resources',target);
-}
-
-pbxProject.prototype.pbxFrameworksBuildPhaseObj = function (target) {
-    return this.buildPhaseObject('PBXFrameworksBuildPhase', 'Frameworks',target);
-}
-
-// Find Build Phase from group/target
-pbxProject.prototype.buildPhase = function (group,target) {
-
-    if (!target)
-        return undefined;
-
-     var nativeTargets = this.pbxNativeTarget();
-     if (typeof nativeTargets[target] == "undefined")
-        throw new Error("Invalid target: "+target);
-
-     var nativeTarget= nativeTargets[target];
-     var buildPhases = nativeTarget.buildPhases;
-     for(var i in buildPhases)
-     {
-        var buildPhase = buildPhases[i];
-        if (buildPhase.comment==group)
-            return buildPhase.value+"_comment";
-     } 
-}
-
-pbxProject.prototype.buildPhaseObject = function (name, group,target) {
-    var section = this.hash.project.objects[name],
-        obj, sectionKey, key;
-    var buildPhase = this.buildPhase(group,target);
-
-    for (key in section) {
-
-        // only look for comments
-        if (!COMMENT_KEY.test(key)) continue;
-  
-        // select the proper buildPhase
-        if (buildPhase && buildPhase!=key)
-            continue;
-        if (section[key] == group) {
-            sectionKey = key.split(COMMENT_KEY)[0];  
-            return section[sectionKey];
-        }
-     }
-    return null;
-}
-
-
-pbxProject.prototype.updateBuildProperty = function(prop, value) {
-    var config = this.pbxXCBuildConfigurationSection();
-    propReplace(config, prop, value);
-}
-
-pbxProject.prototype.updateProductName = function(name) {
-    this.updateBuildProperty('PRODUCT_NAME', '"' + name + '"');
-}
-
-pbxProject.prototype.removeFromFrameworkSearchPaths = function (file) {
-    var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
-        INHERITED = '"$(inherited)"',
-        SEARCH_PATHS = 'FRAMEWORK_SEARCH_PATHS',
-        config, buildSettings, searchPaths;
-    var new_path = searchPathForFile(file, this);
-
-    for (config in configurations) {
-        buildSettings = configurations[config].buildSettings;
-
-        if (unquote(buildSettings['PRODUCT_NAME']) != this.productName)
-            continue;
-
-        searchPaths = buildSettings[SEARCH_PATHS];
-
-        if (searchPaths) {
-            var matches = searchPaths.filter(function(p) {
-                return p.indexOf(new_path) > -1;
-            });
-            matches.forEach(function(m) {
-                var idx = searchPaths.indexOf(m);
-                searchPaths.splice(idx, 1);
-            });
-        }
-
-    }
-}
-
-pbxProject.prototype.addToFrameworkSearchPaths = function (file) {
-    var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
-        INHERITED = '"$(inherited)"',
-        config, buildSettings, searchPaths;
-
-    for (config in configurations) {
-        buildSettings = configurations[config].buildSettings;
-
-        if (unquote(buildSettings['PRODUCT_NAME']) != this.productName)
-            continue;
-
-        if (!buildSettings['FRAMEWORK_SEARCH_PATHS']
-                || buildSettings['FRAMEWORK_SEARCH_PATHS'] === INHERITED) {
-            buildSettings['FRAMEWORK_SEARCH_PATHS'] = [INHERITED];
-        }
-
-        buildSettings['FRAMEWORK_SEARCH_PATHS'].push(searchPathForFile(file, this));
-    }
-}
-
-pbxProject.prototype.removeFromLibrarySearchPaths = function (file) {
-    var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
-        INHERITED = '"$(inherited)"',
-        SEARCH_PATHS = 'LIBRARY_SEARCH_PATHS',
-        config, buildSettings, searchPaths;
-    var new_path = searchPathForFile(file, this);
-
-    for (config in configurations) {
-        buildSettings = configurations[config].buildSettings;
-
-        if (unquote(buildSettings['PRODUCT_NAME']) != this.productName)
-            continue;
-
-        searchPaths = buildSettings[SEARCH_PATHS];
-
-        if (searchPaths) {
-            var matches = searchPaths.filter(function(p) {
-                return p.indexOf(new_path) > -1;
-            });
-            matches.forEach(function(m) {
-                var idx = searchPaths.indexOf(m);
-                searchPaths.splice(idx, 1);
-            });
-        }
-
-    }
-}
-
-pbxProject.prototype.addToLibrarySearchPaths = function (file) {
-    var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
-        INHERITED = '"$(inherited)"',
-        config, buildSettings, searchPaths;
-
-    for (config in configurations) {
-        buildSettings = configurations[config].buildSettings;
-
-        if (unquote(buildSettings['PRODUCT_NAME']) != this.productName)
-            continue;
-
-        if (!buildSettings['LIBRARY_SEARCH_PATHS']
-                || buildSettings['LIBRARY_SEARCH_PATHS'] === INHERITED) {
-            buildSettings['LIBRARY_SEARCH_PATHS'] = [INHERITED];
-        }
-
-        if (typeof file === 'string') {
-            buildSettings['LIBRARY_SEARCH_PATHS'].push(file);
-        } else {
-            buildSettings['LIBRARY_SEARCH_PATHS'].push(searchPathForFile(file, this));
-        }
-    }
-}
-
-pbxProject.prototype.removeFromHeaderSearchPaths = function (file) {
-    var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
-        INHERITED = '"$(inherited)"',
-        SEARCH_PATHS = 'HEADER_SEARCH_PATHS',
-        config, buildSettings, searchPaths;
-    var new_path = searchPathForFile(file, this);
-
-    for (config in configurations) {
-        buildSettings = configurations[config].buildSettings;
-
-        if (unquote(buildSettings['PRODUCT_NAME']) != this.productName)
-            continue;
-
-        if (buildSettings[SEARCH_PATHS]) {
-            var matches = buildSettings[SEARCH_PATHS].filter(function(p) {
-                return p.indexOf(new_path) > -1;
-            });
-            matches.forEach(function(m) {
-                var idx = buildSettings[SEARCH_PATHS].indexOf(m);
-                buildSettings[SEARCH_PATHS].splice(idx, 1);
-            });
-        }
-
-    }
-}
-pbxProject.prototype.addToHeaderSearchPaths = function (file) {
-    var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
-        INHERITED = '"$(inherited)"',
-        config, buildSettings, searchPaths;
-
-    for (config in configurations) {
-        buildSettings = configurations[config].buildSettings;
-
-        if (unquote(buildSettings['PRODUCT_NAME']) != this.productName)
-            continue;
-
-        if (!buildSettings['HEADER_SEARCH_PATHS']) {
-            buildSettings['HEADER_SEARCH_PATHS'] = [INHERITED];
-        }
-
-        if (typeof file === 'string') {
-            buildSettings['HEADER_SEARCH_PATHS'].push(file);
-        } else {
-            buildSettings['HEADER_SEARCH_PATHS'].push(searchPathForFile(file, this));
-        }
-    }
-}
-// a JS getter. hmmm
-pbxProject.prototype.__defineGetter__("productName", function () {
-    var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
-        config, productName;
-
-    for (config in configurations) {
-        productName = configurations[config].buildSettings['PRODUCT_NAME'];
-
-        if (productName) {
-            return unquote(productName);
-        }
-    }
-});
-
-// check if file is present
-pbxProject.prototype.hasFile = function (filePath) {
-    var files = nonComments(this.pbxFileReferenceSection()),
-        file, id;
-    for (id in files) {
-        file = files[id];
-        if (file.path == filePath || file.path == ('"' + filePath + '"')) {
-            return true;
-        }
-    }
-
-    return false;
-}
-
-// helper recursive prop search+replace
-function propReplace(obj, prop, value) {
-    var o = {};
-    for (var p in obj) {
-        if (o.hasOwnProperty.call(obj, p)) {
-            if (typeof obj[p] == 'object' && !Array.isArray(obj[p])) {
-                propReplace(obj[p], prop, value);
-            } else if (p == prop) {
-                obj[p] = value;
-            }
-        }
-    }
-}
-
-// helper object creation functions
-function pbxBuildFileObj(file) {
-    var obj = Object.create(null);
-
-    obj.isa = 'PBXBuildFile';
-    obj.fileRef = file.fileRef;
-    obj.fileRef_comment = file.basename;
-    if (file.settings) obj.settings = file.settings;
-
-    return obj;
-}
-
-function pbxFileReferenceObj(file) {
-    var obj = Object.create(null);
-
-    obj.isa = 'PBXFileReference';
-    obj.lastKnownFileType = file.lastType;
-    
-    obj.name = "\"" + file.basename + "\"";
-    obj.path = "\"" + file.path.replace(/\\/g, '/') + "\"";
-    
-    obj.sourceTree = file.sourceTree;
-
-    if (file.fileEncoding)
-        obj.fileEncoding = file.fileEncoding;
-        
-    if (file.explicitFileType)
-        obj.explicitFileType = file.explicitFileType;
-        
-    if ('includeInIndex' in file)
-        obj.includeInIndex = file.includeInIndex;
-
-    return obj;
-}
-
-function pbxGroupChild(file) {
-    var obj = Object.create(null);
-
-    obj.value = file.fileRef;
-    obj.comment = file.basename;
-
-    return obj;
-}
-
-function pbxBuildPhaseObj(file) {
-    var obj = Object.create(null);
-
-    obj.value = file.uuid;
-    obj.comment = longComment(file);
-
-    return obj;
-}
-
-function pbxBuildFileComment(file) {
-    return longComment(file);
-}
-
-function pbxFileReferenceComment(file) {
-    return file.basename;
-}
-
-function longComment(file) {
-    return f("%s in %s", file.basename, file.group);
-}
-
-// respect <group> path
-function correctForPluginsPath(file, project) {
-    return correctForPath(file, project, 'Plugins');
-}
-
-function correctForResourcesPath(file, project) {
-    return correctForPath(file, project, 'Resources');
-}
-
-function correctForFrameworksPath(file, project) {
-    return correctForPath(file, project, 'Frameworks');
-}
-
-function correctForPath(file, project, group) {
-    var r_group_dir = new RegExp('^' + group + '[\\\\/]');
-
-    if (project.pbxGroupByName(group).path)
-        file.path = file.path.replace(r_group_dir, '');
-
-    return file;
-}
-
-function searchPathForFile(file, proj) {
-    var plugins = proj.pbxGroupByName('Plugins'),
-        pluginsPath = plugins ? plugins.path : null,
-        fileDir = path.dirname(file.path);
-
-    if (fileDir == '.') {
-        fileDir = '';
-    } else {
-        fileDir = '/' + fileDir;
-    }
-
-    if (file.plugin && pluginsPath) {
-        return '"\\"$(SRCROOT)/' + unquote(pluginsPath) + '\\""';
-    } else if (file.customFramework && file.dirname) {
-        return '"\\"' + file.dirname + '\\""';
-    } else {
-        return '"\\"$(SRCROOT)/' + proj.productName + fileDir + '\\""';
-    }
-}
-
-function nonComments(obj) {
-    var keys = Object.keys(obj),
-        newObj = {}, i = 0;
-
-    for (i; i < keys.length; i++) {
-        if (!COMMENT_KEY.test(keys[i])) {
-            newObj[keys[i]] = obj[keys[i]];
-        }
-    }
-
-    return newObj;
-}
-
-function unquote(str) {
-    if (str) return str.replace(/^"(.*)"$/, "$1");
-}
-
-module.exports = pbxProject;
+var util = require('util'),

+    f = util.format,

+    EventEmitter = require('events').EventEmitter,

+    path = require('path'),

+    uuid = require('node-uuid'),

+    fork = require('child_process').fork,

+    pbxWriter = require('./pbxWriter'),

+    pbxFile = require('./pbxFile'),

+    fs = require('fs'),

+    parser = require('./parser/pbxproj'),

+    COMMENT_KEY = /_comment$/

+

+function pbxProject(filename) {

+    if (!(this instanceof pbxProject))

+        return new pbxProject(filename);

+

+    this.filepath = path.resolve(filename)

+}

+

+util.inherits(pbxProject, EventEmitter)

+

+pbxProject.prototype.parse = function(cb) {

+    var worker = fork(__dirname + '/parseJob.js', [this.filepath])

+

+    worker.on('message', function(msg) {

+        if (msg.name == 'SyntaxError' || msg.code) {

+            this.emit('error', msg);

+        } else {

+            this.hash = msg;

+            this.emit('end', null, msg)

+        }

+    }.bind(this));

+

+    if (cb) {

+        this.on('error', cb);

+        this.on('end', cb);

+    }

+

+    return this;

+}

+

+pbxProject.prototype.parseSync = function() {

+    var file_contents = fs.readFileSync(this.filepath, 'utf-8');

+

+    this.hash = parser.parse(file_contents);

+    return this;

+}

+

+pbxProject.prototype.writeSync = function() {

+    this.writer = new pbxWriter(this.hash);

+    return this.writer.writeSync();

+}

+

+pbxProject.prototype.allUuids = function() {

+    var sections = this.hash.project.objects,

+        uuids = [],

+        section;

+

+    for (key in sections) {

+        section = sections[key]

+        uuids = uuids.concat(Object.keys(section))

+    }

+

+    uuids = uuids.filter(function(str) {

+        return !COMMENT_KEY.test(str) && str.length == 24;

+    });

+

+    return uuids;

+}

+

+pbxProject.prototype.generateUuid = function() {

+    var id = uuid.v4()

+        .replace(/-/g, '')

+        .substr(0, 24)

+        .toUpperCase()

+

+    if (this.allUuids().indexOf(id) >= 0) {

+        return this.generateUuid();

+    } else {

+        return id;

+    }

+}

+

+pbxProject.prototype.addPluginFile = function(path, opt) {

+    var file = new pbxFile(path, opt);

+

+    file.plugin = true; // durr

+    correctForPluginsPath(file, this);

+

+    // null is better for early errors

+    if (this.hasFile(file.path)) return null;

+

+    file.fileRef = this.generateUuid();

+

+    this.addToPbxFileReferenceSection(file);    // PBXFileReference

+    this.addToPluginsPbxGroup(file);            // PBXGroup

+

+    return file;

+}

+

+pbxProject.prototype.removePluginFile = function(path, opt) {

+    var file = new pbxFile(path, opt);

+    correctForPluginsPath(file, this);

+

+    this.removeFromPbxFileReferenceSection(file);    // PBXFileReference

+    this.removeFromPluginsPbxGroup(file);            // PBXGroup

+

+    return file;

+}

+

+pbxProject.prototype.addProductFile = function(targetPath, opt) {

+    var file = new pbxFile(targetPath, opt);

+

+    file.includeInIndex = 0;

+    file.fileRef = this.generateUuid();

+    file.target = opt ? opt.target : undefined;

+    file.group = opt ? opt.group : undefined;

+    file.uuid = this.generateUuid();

+    file.path = file.basename;

+

+    this.addToPbxFileReferenceSection(file);

+    this.addToProductsPbxGroup(file);                // PBXGroup

+

+    return file;

+}

+

+pbxProject.prototype.removeProductFile = function(path, opt) {

+    var file = new pbxFile(path, opt);

+

+    this.removeFromProductsPbxGroup(file);           // PBXGroup

+

+    return file;

+}

+

+pbxProject.prototype.addSourceFile = function(path, opt) {

+

+    var file = this.addPluginFile(path, opt);

+    if (!file) return false;

+

+    file.target = opt ? opt.target : undefined;

+    file.uuid = this.generateUuid();

+

+    this.addToPbxBuildFileSection(file);        // PBXBuildFile

+    this.addToPbxSourcesBuildPhase(file);       // PBXSourcesBuildPhase

+

+    return file;

+}

+

+

+pbxProject.prototype.removeSourceFile = function(path, opt) {

+    var file = this.removePluginFile(path, opt)

+    file.target = opt ? opt.target : undefined;

+    this.removeFromPbxBuildFileSection(file);        // PBXBuildFile

+    this.removeFromPbxSourcesBuildPhase(file);       // PBXSourcesBuildPhase

+

+    return file;

+}

+

+pbxProject.prototype.addHeaderFile = function(path, opt) {

+    return this.addPluginFile(path, opt)

+}

+

+pbxProject.prototype.removeHeaderFile = function(path, opt) {

+    return this.removePluginFile(path, opt)

+}

+

+pbxProject.prototype.addResourceFile = function(path, opt) {

+    opt = opt || {};

+

+    var file;

+

+    if (opt.plugin) {

+        file = this.addPluginFile(path, opt);

+        if (!file) return false;

+    } else {

+        file = new pbxFile(path, opt);

+        if (this.hasFile(file.path)) return false;

+    }

+

+    file.uuid = this.generateUuid();

+    file.target = opt ? opt.target : undefined;

+

+    if (!opt.plugin) {

+        correctForResourcesPath(file, this);

+        file.fileRef = this.generateUuid();

+    }

+

+    this.addToPbxBuildFileSection(file);        // PBXBuildFile

+    this.addToPbxResourcesBuildPhase(file);     // PBXResourcesBuildPhase

+

+    if (!opt.plugin) {

+        this.addToPbxFileReferenceSection(file);    // PBXFileReference

+        this.addToResourcesPbxGroup(file);          // PBXGroup

+    }

+

+    return file;

+}

+

+pbxProject.prototype.removeResourceFile = function(path, opt) {

+    var file = new pbxFile(path, opt);

+    file.target = opt ? opt.target : undefined;

+

+    correctForResourcesPath(file, this);

+

+    this.removeFromPbxBuildFileSection(file);        // PBXBuildFile

+    this.removeFromPbxFileReferenceSection(file);    // PBXFileReference

+    this.removeFromResourcesPbxGroup(file);          // PBXGroup

+    this.removeFromPbxResourcesBuildPhase(file);     // PBXResourcesBuildPhase

+

+    return file;

+}

+

+pbxProject.prototype.addFramework = function(fpath, opt) {

+

+    var file = new pbxFile(fpath, opt);

+

+    file.uuid = this.generateUuid();

+    file.fileRef = this.generateUuid();

+    file.target = opt ? opt.target : undefined;

+

+    if (this.hasFile(file.path)) return false;

+

+    this.addToPbxBuildFileSection(file);        // PBXBuildFile

+    this.addToPbxFileReferenceSection(file);    // PBXFileReference

+    this.addToFrameworksPbxGroup(file);         // PBXGroup

+    this.addToPbxFrameworksBuildPhase(file);    // PBXFrameworksBuildPhase

+

+    if (opt && opt.customFramework == true) {

+        this.addToFrameworkSearchPaths(file);

+    }

+

+    return file;

+}

+

+pbxProject.prototype.removeFramework = function(fpath, opt) {

+    var file = new pbxFile(fpath, opt);

+    file.target = opt ? opt.target : undefined;

+

+    this.removeFromPbxBuildFileSection(file);        // PBXBuildFile

+    this.removeFromPbxFileReferenceSection(file);    // PBXFileReference

+    this.removeFromFrameworksPbxGroup(file);         // PBXGroup

+    this.removeFromPbxFrameworksBuildPhase(file);    // PBXFrameworksBuildPhase

+

+    if (opt && opt.customFramework) {

+        this.removeFromFrameworkSearchPaths(path.dirname(fpath));

+    }

+

+    return file;

+}

+

+

+pbxProject.prototype.addCopyfile = function(fpath, opt) {

+    var file = new pbxFile(fpath, opt);

+

+    // catch duplicates

+    if (this.hasFile(file.path)) {

+        file = this.hasFile(file.path);

+    }

+

+    file.fileRef = file.uuid = this.generateUuid();

+    file.target = opt ? opt.target : undefined;

+

+    this.addToPbxBuildFileSection(file);        // PBXBuildFile

+    this.addToPbxFileReferenceSection(file);    // PBXFileReference

+    this.addToPbxCopyfilesBuildPhase(file);     // PBXCopyFilesBuildPhase

+

+    return file;

+}

+

+pbxProject.prototype.pbxCopyfilesBuildPhaseObj = function(target) {

+    return this.buildPhaseObject('PBXCopyFilesBuildPhase', 'Copy Files', target);

+}

+

+pbxProject.prototype.addToPbxCopyfilesBuildPhase = function(file) {

+    var sources = this.buildPhaseObject('PBXCopyFilesBuildPhase', 'Copy Files', file.target);

+    sources.files.push(pbxBuildPhaseObj(file));

+}

+

+pbxProject.prototype.removeCopyfile = function(fpath, opt) {

+    var file = new pbxFile(fpath, opt);

+    file.target = opt ? opt.target : undefined;

+

+    this.removeFromPbxBuildFileSection(file);        // PBXBuildFile

+    this.removeFromPbxFileReferenceSection(file);    // PBXFileReference

+    this.removeFromPbxCopyfilesBuildPhase(file);    // PBXFrameworksBuildPhase

+

+    return file;

+}

+

+pbxProject.prototype.removeFromPbxCopyfilesBuildPhase = function(file) {

+    var sources = this.pbxCopyfilesBuildPhaseObj(file.target);

+    for (i in sources.files) {

+        if (sources.files[i].comment == longComment(file)) {

+            sources.files.splice(i, 1);

+            break;

+        }

+    }

+}

+

+pbxProject.prototype.addStaticLibrary = function(path, opt) {

+    opt = opt || {};

+

+    var file;

+

+    if (opt.plugin) {

+        file = this.addPluginFile(path, opt);

+        if (!file) return false;

+    } else {

+        file = new pbxFile(path, opt);

+        if (this.hasFile(file.path)) return false;

+    }

+

+    file.uuid = this.generateUuid();

+    file.target = opt ? opt.target : undefined;

+

+    if (!opt.plugin) {

+        file.fileRef = this.generateUuid();

+        this.addToPbxFileReferenceSection(file);    // PBXFileReference

+    }

+

+    this.addToPbxBuildFileSection(file);        // PBXBuildFile

+    this.addToPbxFrameworksBuildPhase(file);    // PBXFrameworksBuildPhase

+    this.addToLibrarySearchPaths(file);        // make sure it gets built!

+

+    return file;

+}

+

+// helper addition functions

+pbxProject.prototype.addToPbxBuildFileSection = function(file) {

+    var commentKey = f("%s_comment", file.uuid);

+

+    this.pbxBuildFileSection()[file.uuid] = pbxBuildFileObj(file);

+    this.pbxBuildFileSection()[commentKey] = pbxBuildFileComment(file);

+}

+

+pbxProject.prototype.removeFromPbxBuildFileSection = function(file) {

+    var uuid;

+

+    for (uuid in this.pbxBuildFileSection()) {

+        if (this.pbxBuildFileSection()[uuid].fileRef_comment == file.basename) {

+            file.uuid = uuid;

+            delete this.pbxBuildFileSection()[uuid];

+        }

+    }

+    var commentKey = f("%s_comment", file.uuid);

+    delete this.pbxBuildFileSection()[commentKey];

+}

+

+pbxProject.prototype.addPbxGroup = function(filePathsArray, name, path, sourceTree) {

+    var groups = this.hash.project.objects['PBXGroup'],

+        pbxGroupUuid = this.generateUuid(),

+        commentKey = f("%s_comment", pbxGroupUuid),

+        pbxGroup = {

+            isa: 'PBXGroup',

+            children: [],

+            name: name,

+            path: path,

+            sourceTree: sourceTree ? sourceTree : '"<group>"'

+        },

+        fileReferenceSection = this.pbxFileReferenceSection(),

+        filePathToReference = {};

+

+    for (var key in fileReferenceSection) {

+        // only look for comments

+        if (!COMMENT_KEY.test(key)) continue;

+

+        var fileReferenceKey = key.split(COMMENT_KEY)[0],

+            fileReference = fileReferenceSection[fileReferenceKey];

+

+        filePathToReference[fileReference.path] = { fileRef: fileReferenceKey, basename: fileReferenceSection[key] };

+    }

+

+    for (var index = 0; index < filePathsArray.length; index++) {

+        var filePath = filePathsArray[index],

+            filePathQuoted = "\"" + filePath + "\"";

+        if (filePathToReference[filePath]) {

+            pbxGroup.children.push(pbxGroupChild(filePathToReference[filePath]));

+            continue;

+        } else if (filePathToReference[filePathQuoted]) {

+            pbxGroup.children.push(pbxGroupChild(filePathToReference[filePathQuoted]));

+            continue;

+        }

+

+        var file = new pbxFile(filePath);

+        file.uuid = this.generateUuid();

+        file.fileRef = this.generateUuid();

+        this.addToPbxFileReferenceSection(file);    // PBXFileReference

+        this.addToPbxBuildFileSection(file);        // PBXBuildFile

+        pbxGroup.children.push(pbxGroupChild(file));

+    }

+

+    if (groups) {

+        groups[pbxGroupUuid] = pbxGroup;

+        groups[commentKey] = name;

+    }

+

+    return { uuid: pbxGroupUuid, pbxGroup: pbxGroup };

+}

+

+pbxProject.prototype.addToPbxProjectSection = function(target) {

+

+    var newTarget = {

+            value: target.uuid,

+            comment: pbxNativeTargetComment(target.pbxNativeTarget)

+        };

+

+    this.pbxProjectSection()[this.getFirstProject()['uuid']]['targets'].push(newTarget);

+}

+

+pbxProject.prototype.addToPbxNativeTargetSection = function(target) {

+    var commentKey = f("%s_comment", target.uuid);

+

+    this.pbxNativeTargetSection()[target.uuid] = target.pbxNativeTarget;

+    this.pbxNativeTargetSection()[commentKey] = target.pbxNativeTarget.name;

+}

+

+pbxProject.prototype.addToPbxFileReferenceSection = function(file) {

+    var commentKey = f("%s_comment", file.fileRef);

+

+    this.pbxFileReferenceSection()[file.fileRef] = pbxFileReferenceObj(file);

+    this.pbxFileReferenceSection()[commentKey] = pbxFileReferenceComment(file);

+}

+

+pbxProject.prototype.removeFromPbxFileReferenceSection = function(file) {

+

+    var i;

+    var refObj = pbxFileReferenceObj(file);

+    for (i in this.pbxFileReferenceSection()) {

+        if (this.pbxFileReferenceSection()[i].name == refObj.name ||

+            ('"' + this.pbxFileReferenceSection()[i].name + '"') == refObj.name ||

+            this.pbxFileReferenceSection()[i].path == refObj.path ||

+            ('"' + this.pbxFileReferenceSection()[i].path + '"') == refObj.path) {

+            file.fileRef = file.uuid = i;

+            delete this.pbxFileReferenceSection()[i];

+            break;

+        }

+    }

+    var commentKey = f("%s_comment", file.fileRef);

+    if (this.pbxFileReferenceSection()[commentKey] != undefined) {

+        delete this.pbxFileReferenceSection()[commentKey];

+    }

+

+    return file;

+}

+

+pbxProject.prototype.addToPluginsPbxGroup = function(file) {

+    var pluginsGroup = this.pbxGroupByName('Plugins');

+    pluginsGroup.children.push(pbxGroupChild(file));

+}

+

+pbxProject.prototype.removeFromPluginsPbxGroup = function(file) {

+    var pluginsGroupChildren = this.pbxGroupByName('Plugins').children, i;

+    for (i in pluginsGroupChildren) {

+        if (pbxGroupChild(file).value == pluginsGroupChildren[i].value &&

+            pbxGroupChild(file).comment == pluginsGroupChildren[i].comment) {

+            pluginsGroupChildren.splice(i, 1);

+            break;

+        }

+    }

+}

+

+pbxProject.prototype.addToResourcesPbxGroup = function(file) {

+    var pluginsGroup = this.pbxGroupByName('Resources');

+    pluginsGroup.children.push(pbxGroupChild(file));

+}

+

+pbxProject.prototype.removeFromResourcesPbxGroup = function(file) {

+    var pluginsGroupChildren = this.pbxGroupByName('Resources').children, i;

+    for (i in pluginsGroupChildren) {

+        if (pbxGroupChild(file).value == pluginsGroupChildren[i].value &&

+            pbxGroupChild(file).comment == pluginsGroupChildren[i].comment) {

+            pluginsGroupChildren.splice(i, 1);

+            break;

+        }

+    }

+}

+

+pbxProject.prototype.addToFrameworksPbxGroup = function(file) {

+    var pluginsGroup = this.pbxGroupByName('Frameworks');

+    pluginsGroup.children.push(pbxGroupChild(file));

+}

+

+pbxProject.prototype.removeFromFrameworksPbxGroup = function(file) {

+    var pluginsGroupChildren = this.pbxGroupByName('Frameworks').children;

+

+    for (i in pluginsGroupChildren) {

+        if (pbxGroupChild(file).value == pluginsGroupChildren[i].value &&

+            pbxGroupChild(file).comment == pluginsGroupChildren[i].comment) {

+            pluginsGroupChildren.splice(i, 1);

+            break;

+        }

+    }

+}

+

+

+pbxProject.prototype.addToProductsPbxGroup = function(file) {

+    var productsGroup = this.pbxGroupByName('Products');

+    productsGroup.children.push(pbxGroupChild(file));

+}

+

+pbxProject.prototype.removeFromProductsPbxGroup = function(file) {

+    var productsGroupChildren = this.pbxGroupByName('Products').children, i;

+    for (i in productsGroupChildren) {

+        if (pbxGroupChild(file).value == productsGroupChildren[i].value &&

+            pbxGroupChild(file).comment == productsGroupChildren[i].comment) {

+            productsGroupChildren.splice(i, 1);

+            break;

+        }

+    }

+}

+

+pbxProject.prototype.addToPbxSourcesBuildPhase = function(file) {

+    var sources = this.pbxSourcesBuildPhaseObj(file.target);

+    sources.files.push(pbxBuildPhaseObj(file));

+}

+

+pbxProject.prototype.removeFromPbxSourcesBuildPhase = function(file) {

+

+    var sources = this.pbxSourcesBuildPhaseObj(file.target), i;

+    for (i in sources.files) {

+        if (sources.files[i].comment == longComment(file)) {

+            sources.files.splice(i, 1);

+            break;

+        }

+    }

+}

+

+pbxProject.prototype.addToPbxResourcesBuildPhase = function(file) {

+    var sources = this.pbxResourcesBuildPhaseObj(file.target);

+    sources.files.push(pbxBuildPhaseObj(file));

+}

+

+pbxProject.prototype.removeFromPbxResourcesBuildPhase = function(file) {

+    var sources = this.pbxResourcesBuildPhaseObj(file.target), i;

+

+    for (i in sources.files) {

+        if (sources.files[i].comment == longComment(file)) {

+            sources.files.splice(i, 1);

+            break;

+        }

+    }

+}

+

+pbxProject.prototype.addToPbxFrameworksBuildPhase = function(file) {

+    var sources = this.pbxFrameworksBuildPhaseObj(file.target);

+    sources.files.push(pbxBuildPhaseObj(file));

+}

+

+pbxProject.prototype.removeFromPbxFrameworksBuildPhase = function(file) {

+    var sources = this.pbxFrameworksBuildPhaseObj(file.target);

+    for (i in sources.files) {

+        if (sources.files[i].comment == longComment(file)) {

+            sources.files.splice(i, 1);

+            break;

+        }

+    }

+}

+

+pbxProject.prototype.addXCConfigurationList = function(configurationObjectsArray, defaultConfigurationName, comment) {

+    var pbxBuildConfigurationSection = this.pbxXCBuildConfigurationSection(),

+        pbxXCConfigurationListSection = this.pbxXCConfigurationList(),

+        xcConfigurationListUuid = this.generateUuid(),

+        commentKey = f("%s_comment", xcConfigurationListUuid),

+        xcConfigurationList = {

+            isa: 'XCConfigurationList',

+            buildConfigurations: [],

+            defaultConfigurationIsVisible: 0,

+            defaultConfigurationName: defaultConfigurationName

+        };

+

+    for (var index = 0; index < configurationObjectsArray.length; index++) {

+        var configuration = configurationObjectsArray[index],

+            configurationUuid = this.generateUuid(),

+            configurationCommentKey = f("%s_comment", configurationUuid);

+

+        pbxBuildConfigurationSection[configurationUuid] = configuration;

+        pbxBuildConfigurationSection[configurationCommentKey] = configuration.name;

+        xcConfigurationList.buildConfigurations.push({ value: configurationUuid, comment: configuration.name });

+    }

+

+    if (pbxXCConfigurationListSection) {

+        pbxXCConfigurationListSection[xcConfigurationListUuid] = xcConfigurationList;

+        pbxXCConfigurationListSection[commentKey] = comment;

+    }

+

+    return { uuid: xcConfigurationListUuid, xcConfigurationList: xcConfigurationList };

+}

+

+pbxProject.prototype.addTargetDependency = function(target, dependencyTargets) {

+    if (!target)

+        return undefined;

+

+    var nativeTargets = this.pbxNativeTargetSection();

+

+    if (typeof nativeTargets[target] == "undefined")

+        throw new Error("Invalid target: " + target);

+

+    for (var index = 0; index < dependencyTargets.length; index++) {

+        var dependencyTarget = dependencyTargets[index];

+        if (typeof nativeTargets[dependencyTarget] == "undefined")

+            throw new Error("Invalid target: " + dependencyTarget);

+        }

+

+    var pbxTargetDependency = 'PBXTargetDependency',

+        pbxContainerItemProxy = 'PBXContainerItemProxy',

+        pbxTargetDependencySection = this.hash.project.objects[pbxTargetDependency],

+        pbxContainerItemProxySection = this.hash.project.objects[pbxContainerItemProxy];

+

+    for (var index = 0; index < dependencyTargets.length; index++) {

+        var dependencyTargetUuid = dependencyTargets[index],

+            dependencyTargetCommentKey = f("%s_comment", dependencyTargetUuid),

+            targetDependencyUuid = this.generateUuid(),

+            targetDependencyCommentKey = f("%s_comment", targetDependencyUuid),

+            itemProxyUuid = this.generateUuid(),

+            itemProxyCommentKey = f("%s_comment", itemProxyUuid),

+            itemProxy = {

+                isa: pbxContainerItemProxy,

+                containerPortal: this.hash.project['rootObject'],

+                containerPortal_comment: this.hash.project['rootObject_comment'],

+                proxyType: 1,

+                remoteGlobalIDString: dependencyTargetUuid,

+                remoteInfo: nativeTargets[dependencyTargetUuid].name

+            },

+            targetDependency = {

+                isa: pbxTargetDependency,

+                target: dependencyTargetUuid,

+                target_comment: nativeTargets[dependencyTargetCommentKey],

+                targetProxy: itemProxyUuid,

+                targetProxy_comment: pbxContainerItemProxy

+            };

+

+        if (pbxContainerItemProxySection && pbxTargetDependencySection) {

+            pbxContainerItemProxySection[itemProxyUuid] = itemProxy;

+            pbxContainerItemProxySection[itemProxyCommentKey] = pbxContainerItemProxy;

+            pbxTargetDependencySection[targetDependencyUuid] = targetDependency;

+            pbxTargetDependencySection[targetDependencyCommentKey] = pbxTargetDependency;

+            nativeTargets[target].dependencies.push({ value: targetDependencyUuid, comment: pbxTargetDependency })

+        }

+    }

+

+    return { uuid: target, target: nativeTargets[target] };

+}

+

+pbxProject.prototype.addBuildPhase = function(filePathsArray, buildPhaseType, comment, target, folderType, subfolderPath) {

+    var buildPhaseSection,

+        fileReferenceSection = this.pbxFileReferenceSection(),

+        buildFileSection = this.pbxBuildFileSection(),

+        buildPhaseUuid = this.generateUuid(),

+        buildPhaseTargetUuid = target || this.getFirstTarget().uuid,

+        commentKey = f("%s_comment", buildPhaseUuid),

+        buildPhase = {

+            isa: buildPhaseType,

+            buildActionMask: 2147483647,

+            files: [],

+            runOnlyForDeploymentPostprocessing: 0

+        },

+        filePathToBuildFile = {};

+

+    if (buildPhaseType === 'PBXCopyFilesBuildPhase') {

+        buildPhase = pbxCopyFilesBuildPhaseObj(buildPhase, folderType, subfolderPath, comment);

+    }

+

+    if (!this.hash.project.objects[buildPhaseType]) {

+        this.hash.project.objects[buildPhaseType] = new Object();

+    }

+

+    if (!this.hash.project.objects[buildPhaseType][buildPhaseUuid]) {

+        this.hash.project.objects[buildPhaseType][buildPhaseUuid] = buildPhase;

+        this.hash.project.objects[buildPhaseType][commentKey] = comment;

+    }

+

+    if (this.hash.project.objects['PBXNativeTarget'][buildPhaseTargetUuid]['buildPhases']) {

+        this.hash.project.objects['PBXNativeTarget'][buildPhaseTargetUuid]['buildPhases'].push({

+            value: buildPhaseUuid,

+            comment: comment

+        })

+

+    }

+

+

+    for (var key in buildFileSection) {

+        // only look for comments

+        if (!COMMENT_KEY.test(key)) continue;

+

+        var buildFileKey = key.split(COMMENT_KEY)[0],

+            buildFile = buildFileSection[buildFileKey];

+        fileReference = fileReferenceSection[buildFile.fileRef];

+

+        if (!fileReference) continue;

+

+        var pbxFileObj = new pbxFile(fileReference.path);

+

+        filePathToBuildFile[fileReference.path] = { uuid: buildFileKey, basename: pbxFileObj.basename, group: pbxFileObj.group };

+    }

+

+    for (var index = 0; index < filePathsArray.length; index++) {

+        var filePath = filePathsArray[index],

+            filePathQuoted = "\"" + filePath + "\"",

+            file = new pbxFile(filePath);

+

+        if (filePathToBuildFile[filePath]) {

+            buildPhase.files.push(pbxBuildPhaseObj(filePathToBuildFile[filePath]));

+            continue;

+        } else if (filePathToBuildFile[filePathQuoted]) {

+            buildPhase.files.push(pbxBuildPhaseObj(filePathToBuildFile[filePathQuoted]));

+            continue;

+        }

+

+        file.uuid = this.generateUuid();

+        file.fileRef = this.generateUuid();

+        this.addToPbxFileReferenceSection(file);    // PBXFileReference

+        this.addToPbxBuildFileSection(file);        // PBXBuildFile

+        buildPhase.files.push(pbxBuildPhaseObj(file));

+    }

+

+    if (buildPhaseSection) {

+        buildPhaseSection[buildPhaseUuid] = buildPhase;

+        buildPhaseSection[commentKey] = comment;

+    }

+

+    return { uuid: buildPhaseUuid, buildPhase: buildPhase };

+}

+

+// helper access functions

+pbxProject.prototype.pbxProjectSection = function() {

+    return this.hash.project.objects['PBXProject'];

+}

+pbxProject.prototype.pbxBuildFileSection = function() {

+    return this.hash.project.objects['PBXBuildFile'];

+}

+

+pbxProject.prototype.pbxXCBuildConfigurationSection = function() {

+    return this.hash.project.objects['XCBuildConfiguration'];

+}

+

+pbxProject.prototype.pbxFileReferenceSection = function() {

+    return this.hash.project.objects['PBXFileReference'];

+}

+

+pbxProject.prototype.pbxNativeTargetSection = function() {

+    return this.hash.project.objects['PBXNativeTarget'];

+}

+

+pbxProject.prototype.pbxXCConfigurationList = function() {

+    return this.hash.project.objects['XCConfigurationList'];

+}

+

+pbxProject.prototype.pbxGroupByName = function(name) {

+    return this.pbxItemByComment(name, 'PBXGroup');

+}

+

+pbxProject.prototype.pbxTargetByName = function(name) {

+    return this.pbxItemByComment(name, 'PBXNativeTarget');

+}

+

+pbxProject.prototype.pbxItemByComment = function(name, pbxSectionName) {

+    var section = this.hash.project.objects[pbxSectionName],

+        key, itemKey;

+

+    for (key in section) {

+        // only look for comments

+        if (!COMMENT_KEY.test(key)) continue;

+

+        if (section[key] == name) {

+            itemKey = key.split(COMMENT_KEY)[0];

+            return section[itemKey];

+        }

+    }

+

+    return null;

+}

+

+pbxProject.prototype.pbxSourcesBuildPhaseObj = function(target) {

+    return this.buildPhaseObject('PBXSourcesBuildPhase', 'Sources', target);

+}

+

+pbxProject.prototype.pbxResourcesBuildPhaseObj = function(target) {

+    return this.buildPhaseObject('PBXResourcesBuildPhase', 'Resources', target);

+}

+

+pbxProject.prototype.pbxFrameworksBuildPhaseObj = function(target) {

+    return this.buildPhaseObject('PBXFrameworksBuildPhase', 'Frameworks', target);

+}

+

+// Find Build Phase from group/target

+pbxProject.prototype.buildPhase = function(group, target) {

+

+    if (!target)

+        return undefined;

+

+    var nativeTargets = this.pbxNativeTargetSection();

+     if (typeof nativeTargets[target] == "undefined")

+        throw new Error("Invalid target: " + target);

+

+    var nativeTarget = nativeTargets[target];

+    var buildPhases = nativeTarget.buildPhases;

+     for(var i in buildPhases)

+     {

+        var buildPhase = buildPhases[i];

+        if (buildPhase.comment==group)

+            return buildPhase.value + "_comment";

+        }

+    }

+

+pbxProject.prototype.buildPhaseObject = function(name, group, target) {

+    var section = this.hash.project.objects[name],

+        obj, sectionKey, key;

+    var buildPhase = this.buildPhase(group, target);

+

+    for (key in section) {

+

+        // only look for comments

+        if (!COMMENT_KEY.test(key)) continue;

+

+        // select the proper buildPhase

+        if (buildPhase && buildPhase!=key)

+            continue;

+        if (section[key] == group) {

+            sectionKey = key.split(COMMENT_KEY)[0];

+            return section[sectionKey];

+        }

+    }

+    return null;

+}

+

+

+pbxProject.prototype.updateBuildProperty = function(prop, value) {

+    var config = this.pbxXCBuildConfigurationSection();

+    propReplace(config, prop, value);

+}

+

+pbxProject.prototype.updateProductName = function(name) {

+    this.updateBuildProperty('PRODUCT_NAME', '"' + name + '"');

+}

+

+pbxProject.prototype.removeFromFrameworkSearchPaths = function(file) {

+    var configurations = nonComments(this.pbxXCBuildConfigurationSection()),

+        INHERITED = '"$(inherited)"',

+        SEARCH_PATHS = 'FRAMEWORK_SEARCH_PATHS',

+        config, buildSettings, searchPaths;

+    var new_path = searchPathForFile(file, this);

+

+    for (config in configurations) {

+        buildSettings = configurations[config].buildSettings;

+

+        if (unquote(buildSettings['PRODUCT_NAME']) != this.productName)

+            continue;

+

+        searchPaths = buildSettings[SEARCH_PATHS];

+

+        if (searchPaths) {

+            var matches = searchPaths.filter(function(p) {

+                return p.indexOf(new_path) > -1;

+            });

+            matches.forEach(function(m) {

+                var idx = searchPaths.indexOf(m);

+                searchPaths.splice(idx, 1);

+            });

+        }

+

+    }

+}

+

+pbxProject.prototype.addToFrameworkSearchPaths = function(file) {

+    var configurations = nonComments(this.pbxXCBuildConfigurationSection()),

+        INHERITED = '"$(inherited)"',

+        config, buildSettings, searchPaths;

+

+    for (config in configurations) {

+        buildSettings = configurations[config].buildSettings;

+

+        if (unquote(buildSettings['PRODUCT_NAME']) != this.productName)

+            continue;

+

+        if (!buildSettings['FRAMEWORK_SEARCH_PATHS']

+            || buildSettings['FRAMEWORK_SEARCH_PATHS'] === INHERITED) {

+            buildSettings['FRAMEWORK_SEARCH_PATHS'] = [INHERITED];

+        }

+

+        buildSettings['FRAMEWORK_SEARCH_PATHS'].push(searchPathForFile(file, this));

+    }

+}

+

+pbxProject.prototype.removeFromLibrarySearchPaths = function(file) {

+    var configurations = nonComments(this.pbxXCBuildConfigurationSection()),

+        INHERITED = '"$(inherited)"',

+        SEARCH_PATHS = 'LIBRARY_SEARCH_PATHS',

+        config, buildSettings, searchPaths;

+    var new_path = searchPathForFile(file, this);

+

+    for (config in configurations) {

+        buildSettings = configurations[config].buildSettings;

+

+        if (unquote(buildSettings['PRODUCT_NAME']) != this.productName)

+            continue;

+

+        searchPaths = buildSettings[SEARCH_PATHS];

+

+        if (searchPaths) {

+            var matches = searchPaths.filter(function(p) {

+                return p.indexOf(new_path) > -1;

+            });

+            matches.forEach(function(m) {

+                var idx = searchPaths.indexOf(m);

+                searchPaths.splice(idx, 1);

+            });

+        }

+

+    }

+}

+

+pbxProject.prototype.addToLibrarySearchPaths = function(file) {

+    var configurations = nonComments(this.pbxXCBuildConfigurationSection()),

+        INHERITED = '"$(inherited)"',

+        config, buildSettings, searchPaths;

+

+    for (config in configurations) {

+        buildSettings = configurations[config].buildSettings;

+

+        if (unquote(buildSettings['PRODUCT_NAME']) != this.productName)

+            continue;

+

+        if (!buildSettings['LIBRARY_SEARCH_PATHS']

+            || buildSettings['LIBRARY_SEARCH_PATHS'] === INHERITED) {

+            buildSettings['LIBRARY_SEARCH_PATHS'] = [INHERITED];

+        }

+

+        if (typeof file === 'string') {

+            buildSettings['LIBRARY_SEARCH_PATHS'].push(file);

+        } else {

+            buildSettings['LIBRARY_SEARCH_PATHS'].push(searchPathForFile(file, this));

+        }

+    }

+}

+

+pbxProject.prototype.removeFromHeaderSearchPaths = function(file) {

+    var configurations = nonComments(this.pbxXCBuildConfigurationSection()),

+        INHERITED = '"$(inherited)"',

+        SEARCH_PATHS = 'HEADER_SEARCH_PATHS',

+        config, buildSettings, searchPaths;

+    var new_path = searchPathForFile(file, this);

+

+    for (config in configurations) {

+        buildSettings = configurations[config].buildSettings;

+

+        if (unquote(buildSettings['PRODUCT_NAME']) != this.productName)

+            continue;

+

+        if (buildSettings[SEARCH_PATHS]) {

+            var matches = buildSettings[SEARCH_PATHS].filter(function(p) {

+                return p.indexOf(new_path) > -1;

+            });

+            matches.forEach(function(m) {

+                var idx = buildSettings[SEARCH_PATHS].indexOf(m);

+                buildSettings[SEARCH_PATHS].splice(idx, 1);

+            });

+        }

+

+    }

+}

+pbxProject.prototype.addToHeaderSearchPaths = function(file) {

+    var configurations = nonComments(this.pbxXCBuildConfigurationSection()),

+        INHERITED = '"$(inherited)"',

+        config, buildSettings, searchPaths;

+

+    for (config in configurations) {

+        buildSettings = configurations[config].buildSettings;

+

+        if (unquote(buildSettings['PRODUCT_NAME']) != this.productName)

+            continue;

+

+        if (!buildSettings['HEADER_SEARCH_PATHS']) {

+            buildSettings['HEADER_SEARCH_PATHS'] = [INHERITED];

+        }

+

+        if (typeof file === 'string') {

+            buildSettings['HEADER_SEARCH_PATHS'].push(file);

+        } else {

+            buildSettings['HEADER_SEARCH_PATHS'].push(searchPathForFile(file, this));

+        }

+    }

+}

+// a JS getter. hmmm

+pbxProject.prototype.__defineGetter__("productName", function() {

+    var configurations = nonComments(this.pbxXCBuildConfigurationSection()),

+        config, productName;

+

+    for (config in configurations) {

+        productName = configurations[config].buildSettings['PRODUCT_NAME'];

+

+        if (productName) {

+            return unquote(productName);

+        }

+    }

+});

+

+// check if file is present

+pbxProject.prototype.hasFile = function(filePath) {

+    var files = nonComments(this.pbxFileReferenceSection()),

+        file, id;

+    for (id in files) {

+        file = files[id];

+        if (file.path == filePath || file.path == ('"' + filePath + '"')) {

+            return file;

+        }

+    }

+

+    return false;

+}

+

+pbxProject.prototype.addTarget = function(name, type, subfolder) {

+

+    // Setup uuid and name of new target

+    var targetUuid = this.generateUuid(),

+        targetType = type,

+        targetSubfolder = subfolder || name,

+        targetName = name.trim();

+        

+    // Check type against list of allowed target types

+    if (!targetName) {

+        throw new Error("Target name missing.");

+    }    

+

+    // Check type against list of allowed target types

+    if (!targetType) {

+        throw new Error("Target type missing.");

+    } 

+

+    // Check type against list of allowed target types

+    if (!producttypeForTargettype(targetType)) {

+        throw new Error("Target type invalid: " + targetType);

+    }

+    

+    // Build Configuration: Create

+    var buildConfigurationsList = [

+        {

+            name: 'Debug',

+            isa: 'XCBuildConfiguration',

+            buildSettings: {

+                GCC_PREPROCESSOR_DEFINITIONS: ['"DEBUG=1"', '"$(inherited)"'],

+                INFOPLIST_FILE: '"' + path.join(targetSubfolder, targetSubfolder + '-Info.plist' + '"'),

+                LD_RUNPATH_SEARCH_PATHS: '"$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"',

+                PRODUCT_NAME: '"' + targetName + '"',

+                SKIP_INSTALL: 'YES'

+            }

+        },

+        {

+            name: 'Release',

+            isa: 'XCBuildConfiguration',

+            buildSettings: {

+                INFOPLIST_FILE: '"' + path.join(targetSubfolder, targetSubfolder + '-Info.plist' + '"'),

+                LD_RUNPATH_SEARCH_PATHS: '"$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"',

+                PRODUCT_NAME: '"' + targetName + '"',

+                SKIP_INSTALL: 'YES'

+            }

+        }

+    ];

+

+    // Build Configuration: Add

+    var buildConfigurations = this.addXCConfigurationList(buildConfigurationsList, 'Release', 'Build configuration list for PBXNativeTarget "' + targetName +'"');

+

+    // Product: Create

+    var productName = targetName,

+        productType = producttypeForTargettype(targetType),

+        productFileType = filetypeForProducttype(productType),

+        productFile = this.addProductFile(productName, { group: 'Copy Files', 'target': targetUuid, 'explicitFileType': productFileType}),

+        productFileName = productFile.basename;

+

+

+    // Product: Add to build file list

+    this.addToPbxBuildFileSection(productFile);

+

+    // Target: Create

+    var target = {

+            uuid: targetUuid,

+            pbxNativeTarget: {

+                isa: 'PBXNativeTarget',

+                name: '"' + targetName + '"',

+                productName: '"' + targetName + '"',

+                productReference: productFile.fileRef,

+                productType: '"' + producttypeForTargettype(targetType) + '"',

+                buildConfigurationList: buildConfigurations.uuid,

+                buildPhases: [],

+                buildRules: [],

+                dependencies: []

+            }

+    };

+

+    // Target: Add to PBXNativeTarget section

+    this.addToPbxNativeTargetSection(target)

+

+    // Product: Embed (only for "extension"-type targets)

+    if (targetType === 'app_extension') {

+

+        // Create CopyFiles phase in first target

+        this.addBuildPhase([], 'PBXCopyFilesBuildPhase', 'Copy Files', this.getFirstTarget().uuid,  targetType)

+

+        // Add product to CopyFiles phase

+        this.addToPbxCopyfilesBuildPhase(productFile)

+

+       // this.addBuildPhaseToTarget(newPhase.buildPhase, this.getFirstTarget().uuid)

+

+    };

+

+    // Target: Add uuid to root project

+    this.addToPbxProjectSection(target);

+

+    // Target: Add dependency for this target to first (main) target

+    this.addTargetDependency(this.getFirstTarget().uuid, [target.uuid]);

+

+

+    // Return target on success

+    return target;

+

+};

+

+// helper recursive prop search+replace

+function propReplace(obj, prop, value) {

+    var o = {};

+    for (var p in obj) {

+        if (o.hasOwnProperty.call(obj, p)) {

+            if (typeof obj[p] == 'object' && !Array.isArray(obj[p])) {

+                propReplace(obj[p], prop, value);

+            } else if (p == prop) {

+                obj[p] = value;

+            }

+        }

+    }

+}

+

+// helper object creation functions

+function pbxBuildFileObj(file) {

+    var obj = Object.create(null);

+

+    obj.isa = 'PBXBuildFile';

+    obj.fileRef = file.fileRef;

+    obj.fileRef_comment = file.basename;

+    if (file.settings) obj.settings = file.settings;

+

+    return obj;

+}

+

+function pbxFileReferenceObj(file) {

+    var fileObject = {

+        isa: "PBXFileReference",

+        name: "\"" + file.basename + "\"",

+        path: "\"" + file.path.replace(/\\/g, '/') + "\"",

+        sourceTree: file.sourceTree,

+        fileEncoding: file.fileEncoding,

+        lastKnownFileType: file.lastKnownFileType,

+        explicitFileType: file.explicitFileType,

+        includeInIndex: file.includeInIndex

+    };

+

+    return fileObject;

+}

+

+function pbxGroupChild(file) {

+    var obj = Object.create(null);

+

+    obj.value = file.fileRef;

+    obj.comment = file.basename;

+

+    return obj;

+}

+

+function pbxBuildPhaseObj(file) {

+    var obj = Object.create(null);

+

+    obj.value = file.uuid;

+    obj.comment = longComment(file);

+

+    return obj;

+}

+

+function pbxCopyFilesBuildPhaseObj(obj, folderType, subfolderPath, phaseName){

+

+     // Add additional properties for 'CopyFiles' build phase

+    var DESTINATION_BY_TARGETTYPE = {

+        application: 'wrapper',

+        app_extension: 'plugins',

+        bundle: 'wrapper',

+        command_line_tool: 'wrapper',

+        dynamic_library: 'products_directory',

+        framework: 'shared_frameworks',

+        static_library: 'products_directory',

+        unit_test_bundle: 'wrapper',

+        watch_app: 'wrapper',

+        watch_extension: 'plugins'

+    }

+    var SUBFOLDERSPEC_BY_DESTINATION = {

+        absolute_path: 0,

+        executables: 6,

+        frameworks: 10,

+        java_resources: 15,

+        plugins: 13,

+        products_directory: 16,

+        resources: 7,

+        shared_frameworks: 11,

+        shared_support: 12,

+        wrapper: 1,

+        xpc_services: 0

+    }

+

+    obj.name = '"' + phaseName + '"';

+    obj.dstPath = subfolderPath || '""';

+    obj.dstSubfolderSpec = SUBFOLDERSPEC_BY_DESTINATION[DESTINATION_BY_TARGETTYPE[folderType]];

+

+    return obj;

+}

+

+function pbxBuildFileComment(file) {

+    return longComment(file);

+}

+

+function pbxFileReferenceComment(file) {

+    return file.basename || path.basename(file.path);

+}

+

+function pbxNativeTargetComment(target) {

+    return target.name;

+}

+

+function longComment(file) {

+    return f("%s in %s", file.basename, file.group);

+}

+

+// respect <group> path

+function correctForPluginsPath(file, project) {

+    return correctForPath(file, project, 'Plugins');

+}

+

+function correctForResourcesPath(file, project) {

+    return correctForPath(file, project, 'Resources');

+}

+

+function correctForFrameworksPath(file, project) {

+    return correctForPath(file, project, 'Frameworks');

+}

+

+function correctForPath(file, project, group) {

+    var r_group_dir = new RegExp('^' + group + '[\\\\/]');

+

+    if (project.pbxGroupByName(group).path)

+        file.path = file.path.replace(r_group_dir, '');

+

+    return file;

+}

+

+function searchPathForFile(file, proj) {

+    var plugins = proj.pbxGroupByName('Plugins'),

+        pluginsPath = plugins ? plugins.path : null,

+        fileDir = path.dirname(file.path);

+

+    if (fileDir == '.') {

+        fileDir = '';

+    } else {

+        fileDir = '/' + fileDir;

+    }

+

+    if (file.plugin && pluginsPath) {

+        return '"\\"$(SRCROOT)/' + unquote(pluginsPath) + '\\""';

+    } else if (file.customFramework && file.dirname) {

+        return '"\\"' + file.dirname + '\\""';

+    } else {

+        return '"\\"$(SRCROOT)/' + proj.productName + fileDir + '\\""';

+    }

+}

+

+function nonComments(obj) {

+    var keys = Object.keys(obj),

+        newObj = {}, i = 0;

+

+    for (i; i < keys.length; i++) {

+        if (!COMMENT_KEY.test(keys[i])) {

+            newObj[keys[i]] = obj[keys[i]];

+        }

+    }

+

+    return newObj;

+}

+

+function unquote(str) {

+    if (str) return str.replace(/^"(.*)"$/, "$1");

+}

+

+

+function buildPhaseNameForIsa (isa) {

+

+    BUILDPHASENAME_BY_ISA = {

+        PBXCopyFilesBuildPhase: 'Copy Files',

+        PBXResourcesBuildPhase: 'Resources',

+        PBXSourcesBuildPhase: 'Sources',

+        PBXFrameworksBuildPhase: 'Frameworks'

+    }

+

+    return BUILDPHASENAME_BY_ISA[isa]

+}

+

+function producttypeForTargettype (targetType) {

+

+    PRODUCTTYPE_BY_TARGETTYPE = {

+            application: 'com.apple.product-type.application',

+            app_extension: 'com.apple.product-type.app-extension',

+            bundle: 'com.apple.product-type.bundle',

+            command_line_tool: 'com.apple.product-type.tool',

+            dynamic_library: 'com.apple.product-type.library.dynamic',

+            framework: 'com.apple.product-type.framework',

+            static_library: 'com.apple.product-type.library.static',

+            unit_test_bundle: 'com.apple.product-type.bundle.unit-test',

+            watch_app: 'com.apple.product-type.application.watchapp',

+            watch_extension: 'com.apple.product-type.watchkit-extension'

+        };

+

+    return PRODUCTTYPE_BY_TARGETTYPE[targetType]

+}

+

+function filetypeForProducttype (productType) {

+

+    FILETYPE_BY_PRODUCTTYPE = {

+            'com.apple.product-type.application': '"wrapper.application"',

+            'com.apple.product-type.app-extension': '"wrapper.app-extension"',

+            'com.apple.product-type.bundle': '"wrapper.plug-in"',

+            'com.apple.product-type.tool': '"compiled.mach-o.dylib"',

+            'com.apple.product-type.library.dynamic': '"compiled.mach-o.dylib"',

+            'com.apple.product-type.framework': '"wrapper.framework"',

+            'com.apple.product-type.library.static': '"archive.ar"',

+            'com.apple.product-type.bundle.unit-test': '"wrapper.cfbundle"',

+            'com.apple.product-type.application.watchapp': '"wrapper.application"',

+            'com.apple.product-type.watchkit-extension': '"wrapper.app-extension"'

+        };

+

+    return FILETYPE_BY_PRODUCTTYPE[productType]

+}

+

+pbxProject.prototype.getFirstProject = function() {

+

+    // Get pbxProject container

+    var pbxProjectContainer = this.pbxProjectSection();

+

+    // Get first pbxProject UUID

+    var firstProjectUuid = Object.keys(pbxProjectContainer)[0];

+

+    // Get first pbxProject

+    var firstProject = pbxProjectContainer[firstProjectUuid];

+

+     return {

+        uuid: firstProjectUuid,

+        firstProject: firstProject

+    }

+}

+

+pbxProject.prototype.getFirstTarget = function() {

+

+    // Get first targets UUID

+    var firstTargetUuid = this.getFirstProject()['firstProject']['targets'][0].value;

+

+    // Get first pbxNativeTarget

+    var firstTarget = this.pbxNativeTargetSection()[firstTargetUuid];

+

+    return {

+        uuid: firstTargetUuid,

+        firstTarget: firstTarget

+    }

+}

+

+module.exports = pbxProject;

diff --git a/test/addFramework.js b/test/addFramework.js
index d4a4bf4..6b36f4b 100644
--- a/test/addFramework.js
+++ b/test/addFramework.js
@@ -81,7 +81,7 @@
             fileRefEntry = fileRefSection[newFile.fileRef];
 
         test.equal(fileRefEntry.isa, 'PBXFileReference');
-        test.equal(fileRefEntry.lastKnownFileType, '"compiled.mach-o.dylib"');
+        test.equal(fileRefEntry.lastKnownFileType, 'compiled.mach-o.dylib');
         test.equal(fileRefEntry.name, '"libsqlite3.dylib"');
         test.equal(fileRefEntry.path, '"usr/lib/libsqlite3.dylib"');
         test.equal(fileRefEntry.sourceTree, 'SDKROOT');
diff --git a/test/addResourceFile.js b/test/addResourceFile.js
index 4caa22b..e464ecd 100644
--- a/test/addResourceFile.js
+++ b/test/addResourceFile.js
@@ -90,7 +90,7 @@
 
         test.equal(fileRefEntry.isa, 'PBXFileReference');
         test.equal(fileRefEntry.fileEncoding, undefined);
-        test.equal(fileRefEntry.lastKnownFileType, '"wrapper.plug-in"');
+        test.equal(fileRefEntry.lastKnownFileType, 'wrapper.plug-in');
         test.equal(fileRefEntry.name, '"assets.bundle"');
         test.equal(fileRefEntry.path, '"Resources/assets.bundle"');
         test.equal(fileRefEntry.sourceTree, '"<group>"');
@@ -151,7 +151,7 @@
                             
             test.equal(fileRefEntry.isa, 'PBXFileReference');
             test.equal(fileRefEntry.fileEncoding, undefined);
-            test.equal(fileRefEntry.lastKnownFileType, '"wrapper.plug-in"');
+            test.equal(fileRefEntry.lastKnownFileType, 'wrapper.plug-in');
             test.equal(fileRefEntry.name, '"assets.bundle"');
             test.equal(fileRefEntry.path, '"Plugins/assets.bundle"');
             test.equal(fileRefEntry.sourceTree, '"<group>"');
diff --git a/test/addTarget.js b/test/addTarget.js
new file mode 100644
index 0000000..265bb0d
--- /dev/null
+++ b/test/addTarget.js
@@ -0,0 +1,89 @@
+var fullProject = require('./fixtures/full-project')
+    fullProjectStr = JSON.stringify(fullProject),
+    pbx = require('../lib/pbxProject'),
+    proj = new pbx('.');
+
+function cleanHash() {
+    return JSON.parse(fullProjectStr);
+}
+
+var TARGET_NAME = 'TestExtension',
+    TARGET_TYPE = 'app_extension',
+    TARGET_SUBFOLDER_NAME = 'TestExtensionFiles';
+
+exports.setUp = function (callback) {
+    proj.hash = cleanHash();
+    callback();
+}
+
+exports.addTarget = {
+    'should throw when target name is missing': function (test) {
+        test.throws(function() {
+            proj.addTarget(null, TARGET_TYPE);
+        });
+        
+        test.done();
+    },
+    'should throw when target type missing': function (test) {
+        test.throws(function() {
+            proj.addTarget(TARGET_NAME, null);
+        });
+        
+        test.done();
+    },
+    'should create a new target': function (test) {
+        var target = proj.addTarget(TARGET_NAME, TARGET_TYPE, TARGET_SUBFOLDER_NAME);
+        
+        test.ok(typeof target == 'object');
+        test.ok(target.uuid);
+        test.ok(target.pbxNativeTarget);
+        test.ok(target.pbxNativeTarget.isa);
+        test.ok(target.pbxNativeTarget.name);
+        test.ok(target.pbxNativeTarget.productName);
+        test.ok(target.pbxNativeTarget.productReference);
+        test.ok(target.pbxNativeTarget.productType);
+        test.ok(target.pbxNativeTarget.buildConfigurationList);
+        test.ok(target.pbxNativeTarget.buildPhases);
+        test.ok(target.pbxNativeTarget.buildRules);
+        test.ok(target.pbxNativeTarget.dependencies);
+                
+        test.done();
+    },
+    'should create a new target and add source, framework, resource and header files and the corresponding build phases': function (test) {
+        var target = proj.addTarget(TARGET_NAME, TARGET_TYPE, TARGET_SUBFOLDER_NAME),
+            options = { 'target' : target.uuid };
+   
+        var sourceFile = proj.addSourceFile('Plugins/file.m', options),
+            sourcePhase = proj.addBuildPhase([], 'PBXSourcesBuildPhase', 'Sources', target.uuid),
+            resourceFile = proj.addResourceFile('assets.bundle', options),
+            resourcePhase = proj.addBuildPhase([], 'PBXResourcesBuildPhase', 'Resources', target.uuid),
+            frameworkFile = proj.addFramework('libsqlite3.dylib', options);
+            frameworkPhase = proj.addBuildPhase([], 'PBXFrameworkBuildPhase', 'Frameworks', target.uuid),
+            headerFile = proj.addHeaderFile('file.h', options);
+        
+        test.ok(sourcePhase);
+        test.ok(resourcePhase);
+        test.ok(frameworkPhase);
+          
+        test.equal(sourceFile.constructor, pbxFile);
+        test.equal(resourceFile.constructor, pbxFile);
+        test.equal(frameworkFile.constructor, pbxFile);
+        test.equal(headerFile.constructor, pbxFile);
+        
+        test.ok(typeof target == 'object');
+        test.ok(target.uuid);
+        test.ok(target.pbxNativeTarget);
+        test.ok(target.pbxNativeTarget.isa);
+        test.ok(target.pbxNativeTarget.name);
+        test.ok(target.pbxNativeTarget.productName);
+        test.ok(target.pbxNativeTarget.productReference);
+        test.ok(target.pbxNativeTarget.productType);
+        test.ok(target.pbxNativeTarget.buildConfigurationList);
+        test.ok(target.pbxNativeTarget.buildPhases);
+        test.ok(target.pbxNativeTarget.buildRules);
+        test.ok(target.pbxNativeTarget.dependencies);
+    
+        test.done();
+        
+    }   
+}
diff --git a/test/addTargetDependency.js b/test/addTargetDependency.js
index d20c790..2ed6983 100644
--- a/test/addTargetDependency.js
+++ b/test/addTargetDependency.js
@@ -40,7 +40,7 @@
         test.done();
     },
     'should add targetDependencies to target': function (test) {
-        var targetInPbxProj = proj.pbxNativeTarget()['1D6058900D05DD3D006BFB55'];
+        var targetInPbxProj = proj.pbxNativeTargetSection()['1D6058900D05DD3D006BFB55'];
         test.deepEqual(targetInPbxProj.dependencies, []);
         
         var target = proj.addTargetDependency('1D6058900D05DD3D006BFB55', ['1D6058900D05DD3D006BFB54', '1D6058900D05DD3D006BFB55']).target;
diff --git a/test/pbxFile.js b/test/pbxFile.js
index 0e3c4ee..9a55b0a 100644
--- a/test/pbxFile.js
+++ b/test/pbxFile.js
@@ -1,74 +1,67 @@
 var pbxFile = require('../lib/pbxFile');
 
-exports['lastType'] = {
+exports['lastKnownFileType'] = {
     'should detect that a .m path means sourcecode.c.objc': function (test) {
         var sourceFile = new pbxFile('Plugins/ChildBrowser.m');
 
-        test.equal('sourcecode.c.objc', sourceFile.lastType);
+        test.equal('sourcecode.c.objc', sourceFile.lastKnownFileType);
         test.done();
     },
 
     'should detect that a .h path means sourceFile.c.h': function (test) {
         var sourceFile = new pbxFile('Plugins/ChildBrowser.h');
 
-        test.equal('sourcecode.c.h', sourceFile.lastType);
+        test.equal('sourcecode.c.h', sourceFile.lastKnownFileType);
         test.done();
     },
 
     'should detect that a .bundle path means "wrapper.plug-in"': function (test) {
         var sourceFile = new pbxFile('Plugins/ChildBrowser.bundle');
 
-        test.equal('"wrapper.plug-in"', sourceFile.lastType);
+        test.equal('wrapper.plug-in', sourceFile.lastKnownFileType);
         test.done();
     },
 
     'should detect that a .xib path means file.xib': function (test) {
         var sourceFile = new pbxFile('Plugins/ChildBrowser.xib');
 
-        test.equal('file.xib', sourceFile.lastType);
+        test.equal('file.xib', sourceFile.lastKnownFileType);
         test.done();
     },
 
     'should detect that a .dylib path means "compiled.mach-o.dylib"': function (test) {
         var sourceFile = new pbxFile('libsqlite3.dylib');
 
-        test.equal('"compiled.mach-o.dylib"', sourceFile.lastType);
+        test.equal('compiled.mach-o.dylib', sourceFile.lastKnownFileType);
         test.done();
     },
 
     'should detect that a .framework path means wrapper.framework': function (test) {
         var sourceFile = new pbxFile('MessageUI.framework');
 
-        test.equal('wrapper.framework', sourceFile.lastType);
+        test.equal('wrapper.framework', sourceFile.lastKnownFileType);
         test.done();
     },
 
-    'should detect that a .framework/.a path means archive.ar': function (test) {
-        var sourceFile = new pbxFile('MessageUI.framework/libGoogleAnalytics.a');
-
-        test.equal('archive.ar', sourceFile.lastType);
-        test.done();
-    },
-    
     'should detect that a .a path means archive.ar': function (test) {
         var sourceFile = new pbxFile('libGoogleAnalytics.a');
 
-        test.equal('archive.ar', sourceFile.lastType);
+        test.equal('archive.ar', sourceFile.lastKnownFileType);
         test.done();
     },
 
-    'should allow lastType to be overridden': function (test) {
+    'should allow lastKnownFileType to be overridden': function (test) {
         var sourceFile = new pbxFile('Plugins/ChildBrowser.m',
-                { lastType: 'somestupidtype' });
+                { lastKnownFileType: 'somestupidtype' });
 
-        test.equal('somestupidtype', sourceFile.lastType);
+        test.equal('somestupidtype', sourceFile.lastKnownFileType);
         test.done();
     },
 
-    'should set lastType to unknown if undetectable': function (test) {
+    'should set lastKnownFileType to unknown if undetectable': function (test) {
         var sourceFile = new pbxFile('Plugins/ChildBrowser.guh');
 
-        test.equal('unknown', sourceFile.lastType);
+        test.equal('unknown', sourceFile.lastKnownFileType);
         test.done();
     }
 }
diff --git a/test/removeResourceFile.js b/test/removeResourceFile.js
index 7260aa5..25b1347 100644
--- a/test/removeResourceFile.js
+++ b/test/removeResourceFile.js
@@ -140,7 +140,7 @@
 
         test.equal(fileRefEntry.isa, 'PBXFileReference');
         test.equal(fileRefEntry.fileEncoding, undefined);
-        test.equal(fileRefEntry.lastKnownFileType, '"wrapper.plug-in"');
+        test.equal(fileRefEntry.lastKnownFileType, 'wrapper.plug-in');
         test.equal(fileRefEntry.name, '"assets.bundle"');
         test.equal(fileRefEntry.path, '"Resources/assets.bundle"');
         test.equal(fileRefEntry.sourceTree, '"<group>"');