Merge branch 'upstream/master'

# Conflicts:
#	lib/pbxProject.js
diff --git a/lib/parseJob.js b/lib/parseJob.js
index 804be10..9d6bffa 100644
--- a/lib/parseJob.js
+++ b/lib/parseJob.js
@@ -6,11 +6,10 @@
     fileContents, obj;
 
 try {
-    fileContents = fs.readFileSync(path, 'utf-8'),
-    obj = parser.parse(fileContents)
-    process.send(obj)
-    process.exit()
+    fileContents = fs.readFileSync(path, 'utf-8');
+    obj = parser.parse(fileContents);
+    process.send(obj);
 } catch (e) {
-    process.send(e)
-    process.exit(1)
+    process.send(e);
+    process.exitCode = 1;
 }
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 4d9ecb4..92c6886 100644
--- a/lib/pbxProject.js
+++ b/lib/pbxProject.js
@@ -19,10 +19,10 @@
 
 util.inherits(pbxProject, EventEmitter)
 
-pbxProject.prototype.parse = function (cb) {
+pbxProject.prototype.parse = function(cb) {
     var worker = fork(__dirname + '/parseJob.js', [this.filepath])
 
-    worker.on('message', function (msg) {
+    worker.on('message', function(msg) {
         if (msg.name == 'SyntaxError' || msg.code) {
             this.emit('error', msg);
         } else {
@@ -39,19 +39,19 @@
     return this;
 }
 
-pbxProject.prototype.parseSync = function () {
+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 () {
+pbxProject.prototype.writeSync = function() {
     this.writer = new pbxWriter(this.hash);
     return this.writer.writeSync();
 }
 
-pbxProject.prototype.allUuids = function () {
+pbxProject.prototype.allUuids = function() {
     var sections = this.hash.project.objects,
         uuids = [],
         section;
@@ -61,18 +61,18 @@
         uuids = uuids.concat(Object.keys(section))
     }
 
-    uuids = uuids.filter(function (str) {
+    uuids = uuids.filter(function(str) {
         return !COMMENT_KEY.test(str) && str.length == 24;
     });
 
     return uuids;
 }
 
-pbxProject.prototype.generateUuid = function () {
+pbxProject.prototype.generateUuid = function() {
     var id = uuid.v4()
-                .replace(/-/g,'')
-                .substr(0,24)
-                .toUpperCase()
+        .replace(/-/g, '')
+        .substr(0, 24)
+        .toUpperCase()
 
     if (this.allUuids().indexOf(id) >= 0) {
         return this.generateUuid();
@@ -81,7 +81,7 @@
     }
 }
 
-pbxProject.prototype.addPluginFile = function (path, opt) {
+pbxProject.prototype.addPluginFile = function(path, opt) {
     var file = new pbxFile(path, opt);
 
     file.plugin = true; // durr
@@ -98,7 +98,7 @@
     return file;
 }
 
-pbxProject.prototype.removePluginFile = function (path, opt) {
+pbxProject.prototype.removePluginFile = function(path, opt) {
     var file = new pbxFile(path, opt);
     correctForPluginsPath(file, this);
 
@@ -108,10 +108,46 @@
     return file;
 }
 
+pbxProject.prototype.addProductFile = function(targetPath, opt) {
+    var file = new pbxFile(targetPath, opt);
 
-pbxProject.prototype.addSourceFile = function (path, opt) {
-  
-    var file = this.addPluginFile(path, 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;
+}
+
+/**
+ *
+ * @param path {String}
+ * @param opt {Object} see pbxFile for avail options
+ * @param group {String} group key
+ * @returns {Object} file; see pbxFile
+ */
+pbxProject.prototype.addSourceFile = function (path, opt, group) {
+    var file;
+    if (group) {
+        file = this.addFile(path, group, opt);
+    }
+    else {
+        file = this.addPluginFile(path, opt);
+    }
+
     if (!file) return false;
 
     file.target = opt ? opt.target : undefined;
@@ -123,9 +159,21 @@
     return file;
 }
 
-
-pbxProject.prototype.removeSourceFile = function (path, opt) {
-    var file = this.removePluginFile(path, opt)
+/**
+ *
+ * @param path {String}
+ * @param opt {Object} see pbxFile for avail options
+ * @param group {String} group key
+ * @returns {Object} file; see pbxFile
+ */
+pbxProject.prototype.removeSourceFile = function (path, opt, group) {
+    var file;
+    if (group) {
+        file = this.removeFile(path, group, opt);
+    }
+    else {
+        file = this.removePluginFile(path, opt);
+    }
     file.target = opt ? opt.target : undefined;
     this.removeFromPbxBuildFileSection(file);        // PBXBuildFile
     this.removeFromPbxSourcesBuildPhase(file);       // PBXSourcesBuildPhase
@@ -133,15 +181,39 @@
     return file;
 }
 
-pbxProject.prototype.addHeaderFile = function (path, opt) {
-    return this.addPluginFile(path, opt)
+/**
+ *
+ * @param path {String}
+ * @param opt {Object} see pbxFile for avail options
+ * @param group {String} group key
+ * @returns {Object} file; see pbxFile
+ */
+pbxProject.prototype.addHeaderFile = function (path, opt, group) {
+    if (group) {
+        return this.addFile(path, group, opt);
+    }
+    else {
+        return this.addPluginFile(path, opt);
+    }
 }
 
-pbxProject.prototype.removeHeaderFile = function (path, opt) {
-    return this.removePluginFile(path, opt)
+/**
+ *
+ * @param path {String}
+ * @param opt {Object} see pbxFile for avail options
+ * @param group {String} group key
+ * @returns {Object} file; see pbxFile
+ */
+pbxProject.prototype.removeHeaderFile = function (path, opt, group) {
+    if (group) {
+        return this.removeFile(path, group, opt);
+    }
+    else {
+        return this.removePluginFile(path, opt);
+    }
 }
 
-pbxProject.prototype.addResourceFile = function (path, opt) {
+pbxProject.prototype.addResourceFile = function(path, opt) {
     opt = opt || {};
 
     var file;
@@ -173,43 +245,43 @@
     return file;
 }
 
-pbxProject.prototype.removeResourceFile = function (path, opt) {
+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) {
+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.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);
+
+    if (opt && opt.customFramework == true) {
+        this.addToFrameworkSearchPaths(file);
     }
 
     return file;
 }
 
-pbxProject.prototype.removeFramework = function (fpath, opt) {
+pbxProject.prototype.removeFramework = function(fpath, opt) {
     var file = new pbxFile(fpath, opt);
     file.target = opt ? opt.target : undefined;
 
@@ -217,15 +289,64 @@
     this.removeFromPbxFileReferenceSection(file);    // PBXFileReference
     this.removeFromFrameworksPbxGroup(file);         // PBXGroup
     this.removeFromPbxFrameworksBuildPhase(file);    // PBXFrameworksBuildPhase
-    
-    if(opt && opt.customFramework) {
-      this.removeFromFrameworkSearchPaths(path.dirname(fpath));
+
+    if (opt && opt.customFramework) {
+        this.removeFromFrameworkSearchPaths(path.dirname(fpath));
     }
 
     return file;
 }
 
-pbxProject.prototype.addStaticLibrary = function (path, opt) {
+
+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;
@@ -254,18 +375,18 @@
 }
 
 // helper addition functions
-pbxProject.prototype.addToPbxBuildFileSection = function (file) {
+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) {
+pbxProject.prototype.removeFromPbxBuildFileSection = function(file) {
     var uuid;
 
-    for(uuid in this.pbxBuildFileSection()) {
-        if(this.pbxBuildFileSection()[uuid].fileRef_comment == file.basename) {
+    for (uuid in this.pbxBuildFileSection()) {
+        if (this.pbxBuildFileSection()[uuid].fileRef_comment == file.basename) {
             file.uuid = uuid;
             delete this.pbxBuildFileSection()[uuid];
         }
@@ -274,7 +395,7 @@
     delete this.pbxBuildFileSection()[commentKey];
 }
 
-pbxProject.prototype.addPbxGroup = function (filePathsArray, name, path, sourceTree) {
+pbxProject.prototype.addPbxGroup = function(filePathsArray, name, path, sourceTree) {
     var groups = this.hash.project.objects['PBXGroup'],
         pbxGroupUuid = this.generateUuid(),
         commentKey = f("%s_comment", pbxGroupUuid),
@@ -287,15 +408,15 @@
         },
         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]};
+
+        filePathToReference[fileReference.path] = { fileRef: fileReferenceKey, basename: fileReferenceSection[key] };
     }
 
     for (var index = 0; index < filePathsArray.length; index++) {
@@ -308,7 +429,7 @@
             pbxGroup.children.push(pbxGroupChild(filePathToReference[filePathQuoted]));
             continue;
         }
-        
+
         var file = new pbxFile(filePath);
         file.uuid = this.generateUuid();
         file.fileRef = this.generateUuid();
@@ -316,13 +437,13 @@
         this.addToPbxBuildFileSection(file);        // PBXBuildFile
         pbxGroup.children.push(pbxGroupChild(file));
     }
-    
+
     if (groups) {
         groups[pbxGroupUuid] = pbxGroup;
         groups[commentKey] = name;
     }
-    
-    return {uuid: pbxGroupUuid, pbxGroup: pbxGroup};
+
+    return { uuid: pbxGroupUuid, pbxGroup: pbxGroup };
 }
 
 pbxProject.prototype.removePbxGroup = function (groupName) {
@@ -340,146 +461,175 @@
     }
 }
 
-pbxProject.prototype.addToPbxFileReferenceSection = function (file) {
+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) {
+pbxProject.prototype.removeFromPbxFileReferenceSection = function(file) {
+
+    var i;
     var refObj = pbxFileReferenceObj(file);
-    var pbxFileReferenceSection = this.pbxFileReferenceSection();
-    for (var i in pbxFileReferenceSection) {
-        var pbxFileReferenceSectionName = quote(pbxFileReferenceSection[i].name);
-        var pbxFileReferenceSectionPath = quote(pbxFileReferenceSection[i].path);
-        var refObjName = quote(refObj.name);
-        var refObjPath = quote(refObj.path);
-        if (pbxFileReferenceSectionName == refObjName ||
-            pbxFileReferenceSectionName == refObjPath ||
-            pbxFileReferenceSectionPath == refObjPath ||
-            pbxFileReferenceSectionPath == refObjName) {
+    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 pbxFileReferenceSection[i];
+            delete this.pbxFileReferenceSection()[i];
             break;
         }
     }
-    
-    if (typeof file.fileRef !== 'undefined') {
-        var commentKey = f("%s_comment", file.fileRef);
-        delete pbxFileReferenceSection[commentKey];
+    var commentKey = f("%s_comment", file.fileRef);
+    if (this.pbxFileReferenceSection()[commentKey] != undefined) {
+        delete this.pbxFileReferenceSection()[commentKey];
     }
 
     return file;
 }
 
-pbxProject.prototype.addToPluginsPbxGroup = function (file) {
+pbxProject.prototype.addToPluginsPbxGroup = function(file) {
     var pluginsGroup = this.pbxGroupByName('Plugins');
     pluginsGroup.children.push(pbxGroupChild(file));
 }
 
-pbxProject.prototype.removeFromPluginsPbxGroup = function (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) {
+    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) {
+pbxProject.prototype.addToResourcesPbxGroup = function(file) {
     var pluginsGroup = this.pbxGroupByName('Resources');
     pluginsGroup.children.push(pbxGroupChild(file));
 }
 
-pbxProject.prototype.removeFromResourcesPbxGroup = function (file) {
-    var groupChild = pbxGroupChild(file);
-    var resourcesGroupChildren = this.pbxGroupByName('Resources').children;
-    for (var i in resourcesGroupChildren) {
-        var resourcesGroupChild = resourcesGroupChildren[i];
-        if ((typeof groupChild.value === 'undefined' || groupChild.value == resourcesGroupChild.value) &&
-            groupChild.comment == resourcesGroupChild.comment) {
-            resourcesGroupChildren.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) {
+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.addToPbxSourcesBuildPhase = function (file) {
+
+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) {
+pbxProject.prototype.removeFromPbxSourcesBuildPhase = function(file) {
 
     var sources = this.pbxSourcesBuildPhaseObj(file.target), i;
-    for(i in sources.files) {
-        if(sources.files[i].comment == longComment(file)) {
+    for (i in sources.files) {
+        if (sources.files[i].comment == longComment(file)) {
             sources.files.splice(i, 1);
-            break; 
+            break;
         }
     }
 }
 
-pbxProject.prototype.addToPbxResourcesBuildPhase = function (file) {
+pbxProject.prototype.addToPbxResourcesBuildPhase = function(file) {
     var sources = this.pbxResourcesBuildPhaseObj(file.target);
     sources.files.push(pbxBuildPhaseObj(file));
 }
 
-pbxProject.prototype.removeFromPbxResourcesBuildPhase = function (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)) {
+    for (i in sources.files) {
+        if (sources.files[i].comment == longComment(file)) {
             sources.files.splice(i, 1);
             break;
         }
     }
 }
 
-pbxProject.prototype.addToPbxFrameworksBuildPhase = function (file) {
+pbxProject.prototype.addToPbxFrameworksBuildPhase = function(file) {
     var sources = this.pbxFrameworksBuildPhaseObj(file.target);
     sources.files.push(pbxBuildPhaseObj(file));
 }
 
-pbxProject.prototype.removeFromPbxFrameworksBuildPhase = function (file) {
+pbxProject.prototype.removeFromPbxFrameworksBuildPhase = function(file) {
     var sources = this.pbxFrameworksBuildPhaseObj(file.target);
-    for(i in sources.files) {
-        if(sources.files[i].comment == longComment(file)) {
+    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) {
+pbxProject.prototype.addXCConfigurationList = function(configurationObjectsArray, defaultConfigurationName, comment) {
     var pbxBuildConfigurationSection = this.pbxXCBuildConfigurationSection(),
-        pbxXCConfigurationListSection = this.pbxXCConfigurationList(), 
+        pbxXCConfigurationListSection = this.pbxXCConfigurationList(),
         xcConfigurationListUuid = this.generateUuid(),
         commentKey = f("%s_comment", xcConfigurationListUuid),
-        xcConfigurationList = { 
+        xcConfigurationList = {
             isa: 'XCConfigurationList',
             buildConfigurations: [],
             defaultConfigurationIsVisible: 0,
-            defaultConfigurationName: defaultConfigurationName 
+            defaultConfigurationName: defaultConfigurationName
         };
 
     for (var index = 0; index < configurationObjectsArray.length; index++) {
@@ -489,32 +639,32 @@
 
         pbxBuildConfigurationSection[configurationUuid] = configuration;
         pbxBuildConfigurationSection[configurationCommentKey] = configuration.name;
-        xcConfigurationList.buildConfigurations.push({value: configurationUuid, comment: configuration.name});
+        xcConfigurationList.buildConfigurations.push({ value: configurationUuid, comment: configuration.name });
     }
-    
+
     if (pbxXCConfigurationListSection) {
         pbxXCConfigurationListSection[xcConfigurationListUuid] = xcConfigurationList;
         pbxXCConfigurationListSection[commentKey] = comment;
     }
-    
-    return {uuid: xcConfigurationListUuid, xcConfigurationList: xcConfigurationList};
+
+    return { uuid: xcConfigurationListUuid, xcConfigurationList: xcConfigurationList };
 }
 
-pbxProject.prototype.addTargetDependency = function (target, dependencyTargets) {
+pbxProject.prototype.addTargetDependency = function(target, dependencyTargets) {
     if (!target)
         return undefined;
 
-    var nativeTargets = this.pbxNativeTarget();
+    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],
@@ -527,61 +677,84 @@
             targetDependencyCommentKey = f("%s_comment", targetDependencyUuid),
             itemProxyUuid = this.generateUuid(),
             itemProxyCommentKey = f("%s_comment", itemProxyUuid),
-            itemProxy =  {
+            itemProxy = {
                 isa: pbxContainerItemProxy,
                 containerPortal: this.hash.project['rootObject'],
                 containerPortal_comment: this.hash.project['rootObject_comment'],
                 proxyType: 1,
                 remoteGlobalIDString: dependencyTargetUuid,
-                remoteInfo: nativeTargets[dependencyTargetUuid].name 
+                remoteInfo: nativeTargets[dependencyTargetUuid].name
             },
             targetDependency = {
                 isa: pbxTargetDependency,
-                target: dependencyTargetUuid, 
+                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})
+            nativeTargets[target].dependencies.push({ value: targetDependencyUuid, comment: pbxTargetDependency })
         }
     }
-    
-    return {uuid: target, target: nativeTargets[target]};
+
+    return { uuid: target, target: nativeTargets[target] };
 }
 
-pbxProject.prototype.addBuildPhase = function (filePathsArray, isa, comment) {
-    var section = this.hash.project.objects[isa],
+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: isa,
+            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];
+        fileReference = fileReferenceSection[buildFile.fileRef];
 
         if (!fileReference) continue;
 
         var pbxFileObj = new pbxFile(fileReference.path);
-        
-        filePathToBuildFile[fileReference.path] = {uuid: buildFileKey, basename: pbxFileObj.basename, group: pbxFileObj.group};
+
+        filePathToBuildFile[fileReference.path] = { uuid: buildFileKey, basename: pbxFileObj.basename, group: pbxFileObj.group };
     }
 
     for (var index = 0; index < filePathsArray.length; index++) {
@@ -596,55 +769,68 @@
             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;
+
+    if (buildPhaseSection) {
+        buildPhaseSection[buildPhaseUuid] = buildPhase;
+        buildPhaseSection[commentKey] = comment;
     }
-    
-    return {uuid: buildPhaseUuid, buildPhase: buildPhase};
+
+    return { uuid: buildPhaseUuid, buildPhase: buildPhase };
 }
 
 // helper access functions
-pbxProject.prototype.pbxProjectSection = function () {
+pbxProject.prototype.pbxProjectSection = function() {
     return this.hash.project.objects['PBXProject'];
 }
-pbxProject.prototype.pbxBuildFileSection = function () {
+pbxProject.prototype.pbxBuildFileSection = function() {
     return this.hash.project.objects['PBXBuildFile'];
 }
 
-pbxProject.prototype.pbxXCBuildConfigurationSection = function () {
+pbxProject.prototype.pbxXCBuildConfigurationSection = function() {
     return this.hash.project.objects['XCBuildConfiguration'];
 }
 
-pbxProject.prototype.pbxFileReferenceSection = function () {
+pbxProject.prototype.pbxFileReferenceSection = function() {
     return this.hash.project.objects['PBXFileReference'];
 }
 
-pbxProject.prototype.pbxNativeTarget = function () {
+pbxProject.prototype.pbxNativeTargetSection = function() {
     return this.hash.project.objects['PBXNativeTarget'];
 }
 
-pbxProject.prototype.pbxXCConfigurationList = function () {
+pbxProject.prototype.pbxXCConfigurationList = function() {
     return this.hash.project.objects['XCConfigurationList'];
 }
 
-pbxProject.prototype.pbxGroupByName = function (name) {
-    return this.pbxItemByComment(name, 'PBXGroup');    
+pbxProject.prototype.pbxGroupByName = function(name) {
+    var groups = this.hash.project.objects['PBXGroup'],
+        key, groupKey;
+
+    for (key in groups) {
+        // only look for comments
+        if (!COMMENT_KEY.test(key)) continue;
+
+        if (groups[key] == name) {
+            groupKey = key.split(COMMENT_KEY)[0];
+            return groups[groupKey];
+        }
+    }
+
+    return null;
 }
 
-pbxProject.prototype.pbxTargetByName = function (name) {
+pbxProject.prototype.pbxTargetByName = function(name) {
     return this.pbxItemByComment(name, 'PBXNativeTarget');
 }
 
-pbxProject.prototype.pbxItemByComment = function (name, pbxSectionName) {
+pbxProject.prototype.pbxItemByComment = function(name, pbxSectionName) {
     var section = this.hash.project.objects[pbxSectionName],
         key, itemKey;
 
@@ -661,70 +847,107 @@
     return null;
 }
 
-pbxProject.prototype.pbxSourcesBuildPhaseObj = function (target) {
+pbxProject.prototype.pbxSourcesBuildPhaseObj = function(target) {
     return this.buildPhaseObject('PBXSourcesBuildPhase', 'Sources', target);
 }
 
-pbxProject.prototype.pbxResourcesBuildPhaseObj = function (target) {
-    return this.buildPhaseObject('PBXResourcesBuildPhase', 'Resources',target);
+pbxProject.prototype.pbxResourcesBuildPhaseObj = function(target) {
+    return this.buildPhaseObject('PBXResourcesBuildPhase', 'Resources', target);
 }
 
-pbxProject.prototype.pbxFrameworksBuildPhaseObj = function (target) {
-    return this.buildPhaseObject('PBXFrameworksBuildPhase', 'Frameworks',target);
+pbxProject.prototype.pbxFrameworksBuildPhaseObj = function(target) {
+    return this.buildPhaseObject('PBXFrameworksBuildPhase', 'Frameworks', target);
 }
 
 // Find Build Phase from group/target
-pbxProject.prototype.buildPhase = function (group,target) {
+pbxProject.prototype.buildPhase = function(group, target) {
 
     if (!target)
         return undefined;
 
-     var nativeTargets = this.pbxNativeTarget();
+    var nativeTargets = this.pbxNativeTargetSection();
      if (typeof nativeTargets[target] == "undefined")
-        throw new Error("Invalid target: "+target);
+        throw new Error("Invalid target: " + target);
 
-     var nativeTarget= nativeTargets[target];
-     var buildPhases = nativeTarget.buildPhases;
+    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";
-     } 
-}
+            return buildPhase.value + "_comment";
+        }
+    }
 
-pbxProject.prototype.buildPhaseObject = function (name, group,target) {
+pbxProject.prototype.buildPhaseObject = function(name, group, target) {
     var section = this.hash.project.objects[name],
         obj, sectionKey, key;
-    var buildPhase = this.buildPhase(group,target);
+    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];  
+            sectionKey = key.split(COMMENT_KEY)[0];
             return section[sectionKey];
         }
-     }
+    }
     return null;
 }
 
+pbxProject.prototype.addBuildProperty = function(prop, value, build_name) {
+    var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
+        key, configuration;
 
-pbxProject.prototype.updateBuildProperty = function(prop, value) {
-    var config = this.pbxXCBuildConfigurationSection();
-    propReplace(config, prop, value);
+    for (key in configurations){
+        configuration = configurations[key];
+        if (!build_name || configuration.name === build_name){
+            configuration.buildSettings[prop] = value;
+        }
+    }
+}
+
+pbxProject.prototype.removeBuildProperty = function(prop, build_name) {
+    var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
+        key, configuration;
+
+    for (key in configurations){
+        configuration = configurations[key];
+        if (configuration.buildSettings[prop] &&
+            !build_name || configuration.name === build_name){
+            delete configuration.buildSettings[prop];
+        }
+    }
+}
+
+/**
+ *
+ * @param prop {String}
+ * @param value {String|Array|Object|Number|Boolean}
+ * @param build {String} Release or Debug
+ */
+pbxProject.prototype.updateBuildProperty = function(prop, value, build) {
+    var configs = this.pbxXCBuildConfigurationSection();
+    for (var configName in configs) {
+        if (!COMMENT_KEY.test(configName)) {
+            var config = configs[configName];
+            if ( (build && config.name === build) || (!build) ) {
+                config.buildSettings[prop] = value;
+            }
+        }
+    }
 }
 
 pbxProject.prototype.updateProductName = function(name) {
     this.updateBuildProperty('PRODUCT_NAME', '"' + name + '"');
 }
 
-pbxProject.prototype.removeFromFrameworkSearchPaths = function (file) {
+pbxProject.prototype.removeFromFrameworkSearchPaths = function(file) {
     var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
         INHERITED = '"$(inherited)"',
         SEARCH_PATHS = 'FRAMEWORK_SEARCH_PATHS',
@@ -752,7 +975,7 @@
     }
 }
 
-pbxProject.prototype.addToFrameworkSearchPaths = function (file) {
+pbxProject.prototype.addToFrameworkSearchPaths = function(file) {
     var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
         INHERITED = '"$(inherited)"',
         config, buildSettings, searchPaths;
@@ -764,7 +987,7 @@
             continue;
 
         if (!buildSettings['FRAMEWORK_SEARCH_PATHS']
-                || buildSettings['FRAMEWORK_SEARCH_PATHS'] === INHERITED) {
+            || buildSettings['FRAMEWORK_SEARCH_PATHS'] === INHERITED) {
             buildSettings['FRAMEWORK_SEARCH_PATHS'] = [INHERITED];
         }
 
@@ -772,7 +995,7 @@
     }
 }
 
-pbxProject.prototype.removeFromLibrarySearchPaths = function (file) {
+pbxProject.prototype.removeFromLibrarySearchPaths = function(file) {
     var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
         INHERITED = '"$(inherited)"',
         SEARCH_PATHS = 'LIBRARY_SEARCH_PATHS',
@@ -800,7 +1023,7 @@
     }
 }
 
-pbxProject.prototype.addToLibrarySearchPaths = function (file) {
+pbxProject.prototype.addToLibrarySearchPaths = function(file) {
     var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
         INHERITED = '"$(inherited)"',
         config, buildSettings, searchPaths;
@@ -812,7 +1035,7 @@
             continue;
 
         if (!buildSettings['LIBRARY_SEARCH_PATHS']
-                || buildSettings['LIBRARY_SEARCH_PATHS'] === INHERITED) {
+            || buildSettings['LIBRARY_SEARCH_PATHS'] === INHERITED) {
             buildSettings['LIBRARY_SEARCH_PATHS'] = [INHERITED];
         }
 
@@ -824,7 +1047,7 @@
     }
 }
 
-pbxProject.prototype.removeFromHeaderSearchPaths = function (file) {
+pbxProject.prototype.removeFromHeaderSearchPaths = function(file) {
     var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
         INHERITED = '"$(inherited)"',
         SEARCH_PATHS = 'HEADER_SEARCH_PATHS',
@@ -849,7 +1072,7 @@
 
     }
 }
-pbxProject.prototype.addToHeaderSearchPaths = function (file) {
+pbxProject.prototype.addToHeaderSearchPaths = function(file) {
     var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
         INHERITED = '"$(inherited)"',
         config, buildSettings, searchPaths;
@@ -942,7 +1165,7 @@
 }
 
 // a JS getter. hmmm
-pbxProject.prototype.__defineGetter__("productName", function () {
+pbxProject.prototype.__defineGetter__("productName", function() {
     var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
         config, productName;
 
@@ -956,19 +1179,125 @@
 });
 
 // check if file is present
-pbxProject.prototype.hasFile = function (filePath) {
+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 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 = {};
@@ -996,26 +1325,18 @@
 }
 
 function pbxFileReferenceObj(file) {
-    var obj = Object.create(null);
+    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
+    };
 
-    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;
+    return fileObject;
 }
 
 function pbxGroupChild(file) {
@@ -1036,12 +1357,52 @@
     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;
+    return file.basename || path.basename(file.path);
+}
+
+function pbxNativeTargetComment(target) {
+    return target.name;
 }
 
 function longComment(file) {
@@ -1107,8 +1468,241 @@
     if (str) return str.replace(/^"(.*)"$/, "$1");
 }
 
-function quote(str) {
-    if (str) return '"' + unquote(str) + '"';
+
+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
+    }
+}
+
+/*** NEW ***/
+
+pbxProject.prototype.addToPbxGroup = function (file, groupKey) {
+    var group = this.getPBXGroupByKey(groupKey);
+    if (group && group.children !== undefined) {
+        if (typeof file === 'string') {
+            //Group Key
+            var childGroup = {
+                value:file,
+                comment: this.getPBXGroupByKey(file).name
+            };
+
+            group.children.push(childGroup);
+        }
+        else {
+            //File Object
+            group.children.push(pbxGroupChild(file));
+        }
+    }
+}
+
+pbxProject.prototype.removeFromPbxGroup = function (file, groupKey) {
+    var group = this.getPBXGroupByKey(groupKey);
+    if (group) {
+        var groupChildren = group.children, i;
+        for(i in groupChildren) {
+            if(pbxGroupChild(file).value == groupChildren[i].value &&
+                pbxGroupChild(file).comment == groupChildren[i].comment) {
+                groupChildren.splice(i, 1);
+                break;
+            }
+        }
+    }
+}
+
+pbxProject.prototype.getPBXGroupByKey = function(key) {
+    return this.hash.project.objects['PBXGroup'][key];
+};
+
+pbxProject.prototype.findPBXGroupKey = function(criteria) {
+    var groups = this.hash.project.objects['PBXGroup'];
+    var target;
+
+    for (var key in groups) {
+        // only look for comments
+        if (COMMENT_KEY.test(key)) continue;
+
+        var group = groups[key];
+        if (criteria && criteria.path && criteria.name) {
+            if (criteria.path === group.path && criteria.name === group.name) {
+                target = key;
+                break
+            }
+        }
+        else if (criteria && criteria.path) {
+            if (criteria.path === group.path) {
+                target = key;
+                break
+            }
+        }
+        else if (criteria && criteria.name) {
+            if (criteria.name === group.name) {
+                target = key;
+                break
+            }
+        }
+    }
+
+    return target;
+}
+
+pbxProject.prototype.pbxCreateGroup = function(name, pathName) {
+
+    //Create object
+    var model = {
+        isa:"PBXGroup",
+        children: [],
+        name: name,
+        path: pathName,
+        sourceTree: '"<group>"'
+    };
+    var key = this.generateUuid();
+
+    //Create comment
+    var commendId = key + '_comment';
+
+    //add obj and commentObj to groups;
+    var groups = this.hash.project.objects['PBXGroup'];
+    groups[commendId] = name;
+    groups[key] = model;
+
+    return key;
+}
+
+
+pbxProject.prototype.getPBXObject = function(name) {
+    return this.hash.project.objects[name];
+}
+
+
+
+pbxProject.prototype.addFile = function (path, group, opt) {
+    var file = new pbxFile(path, opt);
+
+    // null is better for early errors
+    if (this.hasFile(file.path)) return null;
+
+    file.fileRef = this.generateUuid();
+
+    this.addToPbxFileReferenceSection(file);    // PBXFileReference
+    this.addToPbxGroup(file, group);            // PBXGroup
+
+    return file;
+}
+
+pbxProject.prototype.removeFile = function (path, group, opt) {
+    var file = new pbxFile(path, opt);
+
+    this.removeFromPbxFileReferenceSection(file);    // PBXFileReference
+    this.removeFromPbxGroup(file, group);            // PBXGroup
+
+    return file;
+}
+
+
+
+pbxProject.prototype.getBuildProperty = function(prop, build) {
+    var target;
+    var configs = this.pbxXCBuildConfigurationSection();
+    for (var configName in configs) {
+        if (!COMMENT_KEY.test(configName)) {
+            var config = configs[configName];
+            if ( (build && config.name === build) || (build === undefined) ) {
+                if (config.buildSettings[prop] !== undefined) {
+                    target = config.buildSettings[prop];
+                }
+            }
+        }
+    }
+    return target;
+}
+
+pbxProject.prototype.getBuildConfigByName = function(name) {
+    var target = {};
+    var configs = this.pbxXCBuildConfigurationSection();
+    for (var configName in configs) {
+        if (!COMMENT_KEY.test(configName)) {
+            var config = configs[configName];
+            if (config.name === name)  {
+                target[configName] = config;
+            }
+        }
+    }
+    return target;
+}
+
+
 module.exports = pbxProject;
diff --git a/package.json b/package.json
index 2b27495..fe18e2b 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
   "author": "Andrew Lunny <alunny@gmail.com>",
   "name": "xcode",
   "description": "parser for xcodeproj/project.pbxproj files",
-  "version": "0.8.0",
+  "version": "0.8.2",
   "main":"index.js",
   "repository": {
     "url": "https://github.com/alunny/node-xcode.git"
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/group.js b/test/group.js
new file mode 100644
index 0000000..8523b83
--- /dev/null
+++ b/test/group.js
@@ -0,0 +1,320 @@
+var pbx = require('../lib/pbxProject'),
+    project,
+    projectHash;
+
+var findChildInGroup = function(obj, target) {
+    var found = false;
+
+    for (var i = 0, j = obj.children.length; i < j; i++) {
+        if (obj.children[i].value === target) {
+            found = true;
+            break;
+        }
+    }
+
+    return found;
+}
+
+var findFileByUUID = function(obj, target) {
+    var found = false;
+
+    for (var k = 0, l = obj.files.length; k < l; k++) {
+        if (obj.files[k].value === target) {
+            found = true;
+            break;
+        }
+    }
+
+    return found;
+}
+
+var findByFileRef = function(obj, target) {
+    var found = false;
+
+    for (var property in obj) {
+        if (!/comment/.test(property)) {
+            if (obj[property].fileRef === target) {
+                found = true;
+                break;
+            }
+        }
+    }
+    return found;
+}
+
+var findByName = function(obj, target) {
+    var found = false;
+    for (var property in obj) {
+        if (!/comment/.test(property)) {
+            var value = obj[property];
+            if (value.name === target) {
+                found = true;
+            }
+        }
+    }
+    return found;
+}
+
+exports.setUp = function(callback) {
+    project = new pbx('test/parser/projects/group.pbxproj');
+    projectHash = project.parseSync();
+    callback();
+}
+
+exports.getGroupByKey = {
+    'should return PBXGroup for Classes': function(test) {
+        var groupKey = project.findPBXGroupKey({name: 'Classes'});
+        var group = project.getPBXGroupByKey(groupKey);
+        test.ok(group.name === 'Classes');
+        test.done();
+    },
+    'should return PBXGroup for Plugins': function(test) {
+        var groupKey = project.findPBXGroupKey({name: 'Plugins'});
+        var group = project.getPBXGroupByKey(groupKey);
+        test.ok(group.name === 'Plugins');
+        test.done();
+    }
+}
+
+exports.createGroup = {
+    'should create a new Test Group': function(test) {
+        var found = false;
+        var groups = project.getPBXObject('PBXGroup');
+
+        var found = findByName(groups, 'Test');
+        test.ok(found === false);
+
+
+        var group = project.findPBXGroupKey({name:'Test'});
+        test.ok(group === undefined);
+
+        project.pbxCreateGroup('Test', 'Test');
+
+        groups = project.getPBXObject('PBXGroup');
+        found = findByName(groups, 'Test');
+        test.ok(found === true);
+
+        group = project.findPBXGroupKey({name:'Test'});
+        test.ok(typeof group === 'string');
+        test.done();
+    }
+}
+
+exports.findGroupKey = {
+    'should return a valid group key':function(test) {
+        var keyByName = project.findPBXGroupKey({ name: 'Classes'});
+        var keyByPath = project.findPBXGroupKey({ path: 'icons'});
+        var keyByPathName = project.findPBXGroupKey({ path: '"HelloCordova/Plugins"', name: 'Plugins'});
+        var nonExistingKey = project.findPBXGroupKey({ name: 'Foo'});
+
+        test.ok(keyByName === '080E96DDFE201D6D7F000001');
+        test.ok(keyByPath === '308D052D1370CCF300D202BF');
+        test.ok(keyByPathName === '307C750510C5A3420062BCA9');
+        test.ok(nonExistingKey === undefined);
+
+        test.done();
+    }
+}
+
+exports.addGroupToGroup = {
+    'should create a new test group then add group to Classes group': function(test) {
+        var testKey = project.pbxCreateGroup('Test', 'Test');
+        var classesKey = project.findPBXGroupKey({name: 'Classes'});
+        project.addToPbxGroup(testKey, classesKey);
+
+        var classesGroup = project.getPBXGroupByKey(classesKey);
+        var foundTestGroup = false;
+        for (var i = 0, j = classesGroup.children.length; i < j; i++) {
+            var child = classesGroup.children[i];
+            if (child.value === testKey && child.comment === 'Test') {
+                foundTestGroup = true;
+            }
+        }
+
+        test.ok(foundTestGroup);
+
+        test.done();
+    }
+}
+
+exports.addSourceFileToGroup = {
+    'should create group + add source file' : function(test) {
+        var testKey = project.pbxCreateGroup('Test', 'Test');
+        var file = project.addSourceFile('Notifications.m', {}, testKey);
+
+        var foundInGroup = findChildInGroup(project.getPBXGroupByKey(testKey),file.fileRef );
+        test.ok(foundInGroup);
+
+        var foundInBuildFileSection = findByFileRef(project.pbxBuildFileSection(), file.fileRef);
+        test.ok(foundInBuildFileSection);
+
+        var foundInBuildPhase = findFileByUUID(project.pbxSourcesBuildPhaseObj(), file.uuid);
+        test.ok(foundInBuildPhase);
+
+        test.done();
+    }
+}
+
+exports.removeSourceFileFromGroup = {
+    'should create group + add source file then remove source file' : function(test) {
+        var testKey = project.pbxCreateGroup('Test', 'Test');
+        var file = project.addSourceFile('Notifications.m', {}, testKey);
+
+        var foundInGroup = findChildInGroup(project.getPBXGroupByKey(testKey),file.fileRef );
+        test.ok(foundInGroup);
+
+        var foundInBuildFileSection = findByFileRef(project.pbxBuildFileSection(), file.fileRef);
+        test.ok(foundInBuildFileSection);
+
+        var foundInBuildPhase = findFileByUUID(project.pbxSourcesBuildPhaseObj(), file.uuid);
+        test.ok(foundInBuildPhase);
+
+        project.removeSourceFile('Notifications.m', {}, testKey);
+
+        var foundInGroup = findChildInGroup(project.getPBXGroupByKey(testKey),file.fileRef );
+        test.ok(!foundInGroup);
+
+        var foundInBuildFileSection = findByFileRef(project.pbxBuildFileSection(), file.fileRef);
+        test.ok(!foundInBuildFileSection);
+
+        var foundInBuildPhase = findFileByUUID(project.pbxSourcesBuildPhaseObj(), file.uuid);
+        test.ok(!foundInBuildPhase);
+
+        test.done();
+    }
+}
+
+exports.addHeaderFileToGroup = {
+    'should create group + add source file' : function(test) {
+        var testKey = project.pbxCreateGroup('Test', 'Test');
+        var file = project.addHeaderFile('Notifications.h', {}, testKey);
+
+        var foundInGroup = findChildInGroup(project.getPBXGroupByKey(testKey),file.fileRef );
+        test.ok(foundInGroup);
+
+        test.done();
+    }
+}
+
+exports.removeHeaderFileFromGroup = {
+    'should create group + add source file then remove source file' : function(test) {
+        var testKey = project.pbxCreateGroup('Test', 'Test');
+        var file = project.addHeaderFile('Notifications.h', {}, testKey);
+
+        var foundInGroup = findChildInGroup(project.getPBXGroupByKey(testKey),file.fileRef );
+        test.ok(foundInGroup);
+
+        project.removeHeaderFile('Notifications.h', {}, testKey);
+
+        var foundInGroup = findChildInGroup(project.getPBXGroupByKey(testKey),file.fileRef );
+        test.ok(!foundInGroup);
+
+        test.done();
+    }
+}
+
+exports.retrieveBuildPropertyForBuild = {
+    'should retrieve valid build property ':function(test) {
+        var releaseTargetedDeviceFamily = project.getBuildProperty('TARGETED_DEVICE_FAMILY', 'Release');
+        var debugTargetedDeviceFamily = project.getBuildProperty('TARGETED_DEVICE_FAMILY', 'Debug');
+        var nonExistingProperty = project.getBuildProperty('FOO', 'Debug');
+        var nonExistingBuild = project.getBuildProperty('TARGETED_DEVICE_FAMILY', 'Foo');
+
+        test.equal(releaseTargetedDeviceFamily, '"1,2"');
+        test.equal(debugTargetedDeviceFamily,'"1"');
+        test.equal(nonExistingProperty, undefined);
+        test.equal(nonExistingBuild, undefined);
+
+        test.done();
+    }
+}
+
+exports.retrieveBuildConfigByName = {
+    'should retrieve valid build config':function(test) {
+        var releaseBuildConfig = project.getBuildConfigByName('Release');
+        for (var property in releaseBuildConfig) {
+            var value = releaseBuildConfig[property];
+            test.ok(value.name === 'Release');
+        }
+
+        var debugBuildConfig = project.getBuildConfigByName('Debug');
+        for (var property in debugBuildConfig) {
+            var value = debugBuildConfig[property];
+            test.ok(value.name === 'Debug');
+        }
+
+        var nonExistingBuildConfig = project.getBuildConfigByName('Foo');
+        test.deepEqual(nonExistingBuildConfig, {});
+
+        test.done();
+    }
+}
+
+/* This proves the issue in 0.6.7
+exports.validatePropReplaceException = {
+    'should throw TypeError for updateBuildProperty VALID_ARCHS when none existed' : function(test) {
+        test.throws(
+            function() {
+                project.updateBuildProperty('VALID_ARCHS', '"armv7 armv7s');
+            },
+            TypeError,
+            "Object object has no method 'hasOwnProperty'"
+        );
+        test.done();
+    }
+}
+*/
+
+exports.validatePropReplaceFix = {
+    'should create build configuration for VALID_ARCHS when none existed' : function(test) {
+        project.updateBuildProperty('VALID_ARCHS', '"armv7 armv7s"', 'Debug');
+        test.done();
+    }
+}
+
+exports.validateHasFile = {
+    'should return true for has file MainViewController.m': function(test) {
+        var result = project.hasFile('MainViewController.m');
+        test.ok(result.path == "MainViewController.m");
+        test.done();
+    }
+}
+
+exports.testWritingPBXProject = {
+
+    'should successfully write to PBXProject TargetAttributes': function(test) {
+        var pbxProjectObj = project.getPBXObject('PBXProject');
+        var pbxProject;
+        for (var property in pbxProjectObj) {
+            if (!/comment/.test(property)) {
+                pbxProject = pbxProjectObj[property];
+            }
+        }
+
+        var target;
+        var projectTargets = pbxProject.targets;
+        for (var i = 0, j = pbxProject.targets.length; i < j; i++ ) {
+            target = pbxProject.targets[i].value;
+        }
+
+        pbxProject.attributes.TargetAttributes = {};
+        pbxProject.attributes.TargetAttributes[target] = {
+            DevelopmentTeam: 'N6X4RJZZ5D',
+            SystemCapabilities: {
+                "com.apple.BackgroundModes": {
+                    enabled : 0
+                },
+                "com.apple.DataProtection" : {
+                    enabled : 0
+                },
+                "com.apple.Keychain" : {
+                    enabled: 1
+                }
+            }
+        };
+
+        var output = project.writeSync();
+
+        test.done();
+    }
+}
\ No newline at end of file
diff --git a/test/parser/projects/group.pbxproj b/test/parser/projects/group.pbxproj
new file mode 100644
index 0000000..700242c
--- /dev/null
+++ b/test/parser/projects/group.pbxproj
@@ -0,0 +1,575 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 46;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		1D3623260D0F684500981E51 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D3623250D0F684500981E51 /* AppDelegate.m */; };
+		1D60589B0D05DD56006BFB54 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; };
+		288765FD0DF74451002DB57D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 288765FC0DF74451002DB57D /* CoreGraphics.framework */; };
+		301BF552109A68D80062928A /* libCordova.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 301BF535109A57CC0062928A /* libCordova.a */; };
+		302D95F114D2391D003F00A1 /* MainViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 302D95EF14D2391D003F00A1 /* MainViewController.m */; };
+		302D95F214D2391D003F00A1 /* MainViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 302D95F014D2391D003F00A1 /* MainViewController.xib */; };
+		305D5FD1115AB8F900A74A75 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 305D5FD0115AB8F900A74A75 /* MobileCoreServices.framework */; };
+		3088BBBD154F3926009F9C59 /* Default-Landscape@2x~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = 3088BBB7154F3926009F9C59 /* Default-Landscape@2x~ipad.png */; };
+		3088BBBE154F3926009F9C59 /* Default-Landscape~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = 3088BBB8154F3926009F9C59 /* Default-Landscape~ipad.png */; };
+		3088BBBF154F3926009F9C59 /* Default-Portrait@2x~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = 3088BBB9154F3926009F9C59 /* Default-Portrait@2x~ipad.png */; };
+		3088BBC0154F3926009F9C59 /* Default-Portrait~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = 3088BBBA154F3926009F9C59 /* Default-Portrait~ipad.png */; };
+		3088BBC1154F3926009F9C59 /* Default@2x~iphone.png in Resources */ = {isa = PBXBuildFile; fileRef = 3088BBBB154F3926009F9C59 /* Default@2x~iphone.png */; };
+		3088BBC2154F3926009F9C59 /* Default~iphone.png in Resources */ = {isa = PBXBuildFile; fileRef = 3088BBBC154F3926009F9C59 /* Default~iphone.png */; };
+		308D05371370CCF300D202BF /* icon-72.png in Resources */ = {isa = PBXBuildFile; fileRef = 308D052E1370CCF300D202BF /* icon-72.png */; };
+		308D05381370CCF300D202BF /* icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 308D052F1370CCF300D202BF /* icon.png */; };
+		308D05391370CCF300D202BF /* icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 308D05301370CCF300D202BF /* icon@2x.png */; };
+		30B4F30019D5E07200D9F7D8 /* Default-667h.png in Resources */ = {isa = PBXBuildFile; fileRef = 30B4F2FD19D5E07200D9F7D8 /* Default-667h.png */; };
+		30B4F30119D5E07200D9F7D8 /* Default-736h.png in Resources */ = {isa = PBXBuildFile; fileRef = 30B4F2FE19D5E07200D9F7D8 /* Default-736h.png */; };
+		30B4F30219D5E07200D9F7D8 /* Default-Landscape-736h.png in Resources */ = {isa = PBXBuildFile; fileRef = 30B4F2FF19D5E07200D9F7D8 /* Default-Landscape-736h.png */; };
+		30C1856619D5FC0A00212699 /* icon-60@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 30C1856519D5FC0A00212699 /* icon-60@3x.png */; };
+		30FC414916E50CA1004E6F35 /* icon-72@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 30FC414816E50CA1004E6F35 /* icon-72@2x.png */; };
+		5B1594DD16A7569C00FEF299 /* AssetsLibrary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B1594DC16A7569C00FEF299 /* AssetsLibrary.framework */; };
+		7E7966DE1810823500FA85AD /* icon-40.png in Resources */ = {isa = PBXBuildFile; fileRef = 7E7966D41810823500FA85AD /* icon-40.png */; };
+		7E7966DF1810823500FA85AD /* icon-40@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7E7966D51810823500FA85AD /* icon-40@2x.png */; };
+		7E7966E01810823500FA85AD /* icon-50.png in Resources */ = {isa = PBXBuildFile; fileRef = 7E7966D61810823500FA85AD /* icon-50.png */; };
+		7E7966E11810823500FA85AD /* icon-50@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7E7966D71810823500FA85AD /* icon-50@2x.png */; };
+		7E7966E21810823500FA85AD /* icon-60.png in Resources */ = {isa = PBXBuildFile; fileRef = 7E7966D81810823500FA85AD /* icon-60.png */; };
+		7E7966E31810823500FA85AD /* icon-60@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7E7966D91810823500FA85AD /* icon-60@2x.png */; };
+		7E7966E41810823500FA85AD /* icon-76.png in Resources */ = {isa = PBXBuildFile; fileRef = 7E7966DA1810823500FA85AD /* icon-76.png */; };
+		7E7966E51810823500FA85AD /* icon-76@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7E7966DB1810823500FA85AD /* icon-76@2x.png */; };
+		7E7966E61810823500FA85AD /* icon-small.png in Resources */ = {isa = PBXBuildFile; fileRef = 7E7966DC1810823500FA85AD /* icon-small.png */; };
+		7E7966E71810823500FA85AD /* icon-small@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7E7966DD1810823500FA85AD /* icon-small@2x.png */; };
+		D4A0D8761607E02300AEF8BB /* Default-568h@2x~iphone.png in Resources */ = {isa = PBXBuildFile; fileRef = D4A0D8751607E02300AEF8BB /* Default-568h@2x~iphone.png */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+		301BF534109A57CC0062928A /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 301BF52D109A57CC0062928A /* CordovaLib.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = D2AAC07E0554694100DB518D;
+			remoteInfo = CordovaLib;
+		};
+		301BF550109A68C00062928A /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 301BF52D109A57CC0062928A /* CordovaLib.xcodeproj */;
+			proxyType = 1;
+			remoteGlobalIDString = D2AAC07D0554694100DB518D;
+			remoteInfo = CordovaLib;
+		};
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+		1D3623240D0F684500981E51 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
+		1D3623250D0F684500981E51 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
+		1D6058910D05DD3D006BFB54 /* HelloCordova.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "HelloCordova.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+		1F766FDD13BBADB100FB74C0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = Localizable.strings; sourceTree = "<group>"; };
+		1F766FE013BBADB100FB74C0 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = Localizable.strings; sourceTree = "<group>"; };
+		288765FC0DF74451002DB57D /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
+		29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+		301BF52D109A57CC0062928A /* CordovaLib.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = CordovaLib.xcodeproj; path = "CordovaLib/CordovaLib.xcodeproj"; sourceTree = "<group>"; };
+		301BF56E109A69640062928A /* www */ = {isa = PBXFileReference; lastKnownFileType = folder; path = www; sourceTree = SOURCE_ROOT; };
+		302D95EE14D2391D003F00A1 /* MainViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MainViewController.h; sourceTree = "<group>"; };
+		302D95EF14D2391D003F00A1 /* MainViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MainViewController.m; sourceTree = "<group>"; };
+		302D95F014D2391D003F00A1 /* MainViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MainViewController.xib; sourceTree = "<group>"; };
+		305D5FD0115AB8F900A74A75 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; };
+		3088BBB7154F3926009F9C59 /* Default-Landscape@2x~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Landscape@2x~ipad.png"; sourceTree = "<group>"; };
+		3088BBB8154F3926009F9C59 /* Default-Landscape~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Landscape~ipad.png"; sourceTree = "<group>"; };
+		3088BBB9154F3926009F9C59 /* Default-Portrait@2x~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Portrait@2x~ipad.png"; sourceTree = "<group>"; };
+		3088BBBA154F3926009F9C59 /* Default-Portrait~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Portrait~ipad.png"; sourceTree = "<group>"; };
+		3088BBBB154F3926009F9C59 /* Default@2x~iphone.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x~iphone.png"; sourceTree = "<group>"; };
+		3088BBBC154F3926009F9C59 /* Default~iphone.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default~iphone.png"; sourceTree = "<group>"; };
+		308D052E1370CCF300D202BF /* icon-72.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-72.png"; sourceTree = "<group>"; };
+		308D052F1370CCF300D202BF /* icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon.png; sourceTree = "<group>"; };
+		308D05301370CCF300D202BF /* icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon@2x.png"; sourceTree = "<group>"; };
+		30B4F2FD19D5E07200D9F7D8 /* Default-667h.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-667h.png"; sourceTree = "<group>"; };
+		30B4F2FE19D5E07200D9F7D8 /* Default-736h.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-736h.png"; sourceTree = "<group>"; };
+		30B4F2FF19D5E07200D9F7D8 /* Default-Landscape-736h.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Landscape-736h.png"; sourceTree = "<group>"; };
+		30C1856519D5FC0A00212699 /* icon-60@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-60@3x.png"; sourceTree = "<group>"; };
+		30FC414816E50CA1004E6F35 /* icon-72@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-72@2x.png"; sourceTree = "<group>"; };
+		32CA4F630368D1EE00C91783 /* HelloCordova-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "HelloCordova-Prefix.pch"; sourceTree = "<group>"; };
+		5B1594DC16A7569C00FEF299 /* AssetsLibrary.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AssetsLibrary.framework; path = System/Library/Frameworks/AssetsLibrary.framework; sourceTree = SDKROOT; };
+		7E7966D41810823500FA85AD /* icon-40.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-40.png"; sourceTree = "<group>"; };
+		7E7966D51810823500FA85AD /* icon-40@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-40@2x.png"; sourceTree = "<group>"; };
+		7E7966D61810823500FA85AD /* icon-50.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-50.png"; sourceTree = "<group>"; };
+		7E7966D71810823500FA85AD /* icon-50@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-50@2x.png"; sourceTree = "<group>"; };
+		7E7966D81810823500FA85AD /* icon-60.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-60.png"; sourceTree = "<group>"; };
+		7E7966D91810823500FA85AD /* icon-60@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-60@2x.png"; sourceTree = "<group>"; };
+		7E7966DA1810823500FA85AD /* icon-76.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-76.png"; sourceTree = "<group>"; };
+		7E7966DB1810823500FA85AD /* icon-76@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-76@2x.png"; sourceTree = "<group>"; };
+		7E7966DC1810823500FA85AD /* icon-small.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-small.png"; sourceTree = "<group>"; };
+		7E7966DD1810823500FA85AD /* icon-small@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-small@2x.png"; sourceTree = "<group>"; };
+		8D1107310486CEB800E47090 /* HelloCordova-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "HelloCordova-Info.plist"; path = "../HelloCordova-Info.plist"; plistStructureDefinitionIdentifier = "com.apple.xcode.plist.structure-definition.iphone.info-plist"; sourceTree = "<group>"; };
+		D4A0D8751607E02300AEF8BB /* Default-568h@2x~iphone.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x~iphone.png"; sourceTree = "<group>"; };
+		EB87FDF21871DA7A0020F90C /* merges */ = {isa = PBXFileReference; lastKnownFileType = folder; name = merges; path = ../../merges; sourceTree = "<group>"; };
+		EB87FDF31871DA8E0020F90C /* www */ = {isa = PBXFileReference; lastKnownFileType = folder; name = www; path = ../../www; sourceTree = "<group>"; };
+		EB87FDF41871DAF40020F90C /* config.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = config.xml; path = ../../config.xml; sourceTree = "<group>"; };
+		F840E1F0165FE0F500CFE078 /* config.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = config.xml; path = "HelloCordova/config.xml"; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		1D60588F0D05DD3D006BFB54 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				5B1594DD16A7569C00FEF299 /* AssetsLibrary.framework in Frameworks */,
+				301BF552109A68D80062928A /* libCordova.a in Frameworks */,
+				288765FD0DF74451002DB57D /* CoreGraphics.framework in Frameworks */,
+				305D5FD1115AB8F900A74A75 /* MobileCoreServices.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		080E96DDFE201D6D7F000001 /* Classes */ = {
+			isa = PBXGroup;
+			children = (
+				302D95EE14D2391D003F00A1 /* MainViewController.h */,
+				302D95EF14D2391D003F00A1 /* MainViewController.m */,
+				302D95F014D2391D003F00A1 /* MainViewController.xib */,
+				1D3623240D0F684500981E51 /* AppDelegate.h */,
+				1D3623250D0F684500981E51 /* AppDelegate.m */,
+			);
+			name = Classes;
+			path = "HelloCordova/Classes";
+			sourceTree = SOURCE_ROOT;
+		};
+		19C28FACFE9D520D11CA2CBB /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				1D6058910D05DD3D006BFB54 /* HelloCordova.app */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		29B97314FDCFA39411CA2CEA /* CustomTemplate */ = {
+			isa = PBXGroup;
+			children = (
+				EB87FDF41871DAF40020F90C /* config.xml */,
+				EB87FDF31871DA8E0020F90C /* www */,
+				EB87FDF21871DA7A0020F90C /* merges */,
+				EB87FDF11871DA420020F90C /* Staging */,
+				301BF52D109A57CC0062928A /* CordovaLib.xcodeproj */,
+				080E96DDFE201D6D7F000001 /* Classes */,
+				307C750510C5A3420062BCA9 /* Plugins */,
+				29B97315FDCFA39411CA2CEA /* Other Sources */,
+				29B97317FDCFA39411CA2CEA /* Resources */,
+				29B97323FDCFA39411CA2CEA /* Frameworks */,
+				19C28FACFE9D520D11CA2CBB /* Products */,
+			);
+			name = CustomTemplate;
+			sourceTree = "<group>";
+		};
+		29B97315FDCFA39411CA2CEA /* Other Sources */ = {
+			isa = PBXGroup;
+			children = (
+				32CA4F630368D1EE00C91783 /* HelloCordova-Prefix.pch */,
+				29B97316FDCFA39411CA2CEA /* main.m */,
+			);
+			name = "Other Sources";
+			path = "HelloCordova";
+			sourceTree = "<group>";
+		};
+		29B97317FDCFA39411CA2CEA /* Resources */ = {
+			isa = PBXGroup;
+			children = (
+				308D052D1370CCF300D202BF /* icons */,
+				308D05311370CCF300D202BF /* splash */,
+				8D1107310486CEB800E47090 /* HelloCordova-Info.plist */,
+			);
+			name = Resources;
+			path = "HelloCordova/Resources";
+			sourceTree = "<group>";
+		};
+		29B97323FDCFA39411CA2CEA /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				5B1594DC16A7569C00FEF299 /* AssetsLibrary.framework */,
+				288765FC0DF74451002DB57D /* CoreGraphics.framework */,
+				305D5FD0115AB8F900A74A75 /* MobileCoreServices.framework */,
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
+		301BF52E109A57CC0062928A /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				301BF535109A57CC0062928A /* libCordova.a */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		307C750510C5A3420062BCA9 /* Plugins */ = {
+			isa = PBXGroup;
+			children = (
+			);
+			name = Plugins;
+			path = "HelloCordova/Plugins";
+			sourceTree = SOURCE_ROOT;
+		};
+		308D052D1370CCF300D202BF /* icons */ = {
+			isa = PBXGroup;
+			children = (
+				30C1856519D5FC0A00212699 /* icon-60@3x.png */,
+				7E7966D41810823500FA85AD /* icon-40.png */,
+				7E7966D51810823500FA85AD /* icon-40@2x.png */,
+				7E7966D61810823500FA85AD /* icon-50.png */,
+				7E7966D71810823500FA85AD /* icon-50@2x.png */,
+				7E7966D81810823500FA85AD /* icon-60.png */,
+				7E7966D91810823500FA85AD /* icon-60@2x.png */,
+				7E7966DA1810823500FA85AD /* icon-76.png */,
+				7E7966DB1810823500FA85AD /* icon-76@2x.png */,
+				7E7966DC1810823500FA85AD /* icon-small.png */,
+				7E7966DD1810823500FA85AD /* icon-small@2x.png */,
+				30FC414816E50CA1004E6F35 /* icon-72@2x.png */,
+				308D052E1370CCF300D202BF /* icon-72.png */,
+				308D052F1370CCF300D202BF /* icon.png */,
+				308D05301370CCF300D202BF /* icon@2x.png */,
+			);
+			path = icons;
+			sourceTree = "<group>";
+		};
+		308D05311370CCF300D202BF /* splash */ = {
+			isa = PBXGroup;
+			children = (
+				30B4F2FD19D5E07200D9F7D8 /* Default-667h.png */,
+				30B4F2FE19D5E07200D9F7D8 /* Default-736h.png */,
+				30B4F2FF19D5E07200D9F7D8 /* Default-Landscape-736h.png */,
+				D4A0D8751607E02300AEF8BB /* Default-568h@2x~iphone.png */,
+				3088BBB7154F3926009F9C59 /* Default-Landscape@2x~ipad.png */,
+				3088BBB8154F3926009F9C59 /* Default-Landscape~ipad.png */,
+				3088BBB9154F3926009F9C59 /* Default-Portrait@2x~ipad.png */,
+				3088BBBA154F3926009F9C59 /* Default-Portrait~ipad.png */,
+				3088BBBB154F3926009F9C59 /* Default@2x~iphone.png */,
+				3088BBBC154F3926009F9C59 /* Default~iphone.png */,
+			);
+			path = splash;
+			sourceTree = "<group>";
+		};
+		EB87FDF11871DA420020F90C /* Staging */ = {
+			isa = PBXGroup;
+			children = (
+				F840E1F0165FE0F500CFE078 /* config.xml */,
+				301BF56E109A69640062928A /* www */,
+			);
+			name = Staging;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		1D6058900D05DD3D006BFB54 /* HelloCordova */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "HelloCordova" */;
+			buildPhases = (
+				304B58A110DAC018002A0835 /* Copy www directory */,
+				1D60588D0D05DD3D006BFB54 /* Resources */,
+				1D60588E0D05DD3D006BFB54 /* Sources */,
+				1D60588F0D05DD3D006BFB54 /* Frameworks */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				301BF551109A68C00062928A /* PBXTargetDependency */,
+			);
+			name = "HelloCordova";
+			productName = "HelloCordova";
+			productReference = 1D6058910D05DD3D006BFB54 /* HelloCordova.app */;
+			productType = "com.apple.product-type.application";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		29B97313FDCFA39411CA2CEA /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastUpgradeCheck = 0510;
+			};
+			buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "__CLI__" */;
+			compatibilityVersion = "Xcode 3.2";
+			developmentRegion = English;
+			hasScannedForEncodings = 1;
+			knownRegions = (
+				English,
+				Japanese,
+				French,
+				German,
+				en,
+				es,
+				de,
+				se,
+			);
+			mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */;
+			projectDirPath = "";
+			projectReferences = (
+				{
+					ProductGroup = 301BF52E109A57CC0062928A /* Products */;
+					ProjectRef = 301BF52D109A57CC0062928A /* CordovaLib.xcodeproj */;
+				},
+			);
+			projectRoot = "";
+			targets = (
+				1D6058900D05DD3D006BFB54 /* HelloCordova */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXReferenceProxy section */
+		301BF535109A57CC0062928A /* libCordova.a */ = {
+			isa = PBXReferenceProxy;
+			fileType = archive.ar;
+			path = libCordova.a;
+			remoteRef = 301BF534109A57CC0062928A /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+/* End PBXReferenceProxy section */
+
+/* Begin PBXResourcesBuildPhase section */
+		1D60588D0D05DD3D006BFB54 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				7E7966E41810823500FA85AD /* icon-76.png in Resources */,
+				7E7966DF1810823500FA85AD /* icon-40@2x.png in Resources */,
+				308D05371370CCF300D202BF /* icon-72.png in Resources */,
+				30B4F30119D5E07200D9F7D8 /* Default-736h.png in Resources */,
+				308D05381370CCF300D202BF /* icon.png in Resources */,
+				308D05391370CCF300D202BF /* icon@2x.png in Resources */,
+				302D95F214D2391D003F00A1 /* MainViewController.xib in Resources */,
+				7E7966E01810823500FA85AD /* icon-50.png in Resources */,
+				7E7966E31810823500FA85AD /* icon-60@2x.png in Resources */,
+				7E7966E61810823500FA85AD /* icon-small.png in Resources */,
+				3088BBBD154F3926009F9C59 /* Default-Landscape@2x~ipad.png in Resources */,
+				3088BBBE154F3926009F9C59 /* Default-Landscape~ipad.png in Resources */,
+				3088BBBF154F3926009F9C59 /* Default-Portrait@2x~ipad.png in Resources */,
+				7E7966E71810823500FA85AD /* icon-small@2x.png in Resources */,
+				3088BBC0154F3926009F9C59 /* Default-Portrait~ipad.png in Resources */,
+				30B4F30019D5E07200D9F7D8 /* Default-667h.png in Resources */,
+				7E7966DE1810823500FA85AD /* icon-40.png in Resources */,
+				3088BBC1154F3926009F9C59 /* Default@2x~iphone.png in Resources */,
+				7E7966E21810823500FA85AD /* icon-60.png in Resources */,
+				3088BBC2154F3926009F9C59 /* Default~iphone.png in Resources */,
+				D4A0D8761607E02300AEF8BB /* Default-568h@2x~iphone.png in Resources */,
+				30B4F30219D5E07200D9F7D8 /* Default-Landscape-736h.png in Resources */,
+				30C1856619D5FC0A00212699 /* icon-60@3x.png in Resources */,
+				7E7966E11810823500FA85AD /* icon-50@2x.png in Resources */,
+				7E7966E51810823500FA85AD /* icon-76@2x.png in Resources */,
+				30FC414916E50CA1004E6F35 /* icon-72@2x.png in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+		304B58A110DAC018002A0835 /* Copy www directory */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Copy www directory";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "cordova/lib/copy-www-build-step.sh";
+			showEnvVarsInLog = 0;
+		};
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		1D60588E0D05DD3D006BFB54 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				1D60589B0D05DD56006BFB54 /* main.m in Sources */,
+				1D3623260D0F684500981E51 /* AppDelegate.m in Sources */,
+				302D95F114D2391D003F00A1 /* MainViewController.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+		301BF551109A68C00062928A /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			name = CordovaLib;
+			targetProxy = 301BF550109A68C00062928A /* PBXContainerItemProxy */;
+		};
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+		1D6058940D05DD3E006BFB54 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				COPY_PHASE_STRIP = NO;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PRECOMPILE_PREFIX_HEADER = YES;
+				GCC_PREFIX_HEADER = "HelloCordova/HelloCordova-Prefix.pch";
+				GCC_THUMB_SUPPORT = NO;
+				GCC_VERSION = "";
+				INFOPLIST_FILE = "HelloCordova/HelloCordova-Info.plist";
+				IPHONEOS_DEPLOYMENT_TARGET = 6.0;
+				OTHER_LDFLAGS = (
+					"-weak_framework",
+					CoreFoundation,
+					"-weak_framework",
+					UIKit,
+					"-weak_framework",
+					AVFoundation,
+					"-weak_framework",
+					CoreMedia,
+					"-weak-lSystem",
+					"-ObjC",
+				);
+				PRODUCT_NAME = "HelloCordova";
+				TARGETED_DEVICE_FAMILY = "1";
+			};
+			name = Debug;
+		};
+		1D6058950D05DD3E006BFB54 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				COPY_PHASE_STRIP = YES;
+				GCC_PRECOMPILE_PREFIX_HEADER = YES;
+				GCC_PREFIX_HEADER = "HelloCordova/HelloCordova-Prefix.pch";
+				GCC_THUMB_SUPPORT = NO;
+				GCC_VERSION = "";
+				INFOPLIST_FILE = "HelloCordova/HelloCordova-Info.plist";
+				IPHONEOS_DEPLOYMENT_TARGET = 6.0;
+				OTHER_LDFLAGS = (
+					"-weak_framework",
+					CoreFoundation,
+					"-weak_framework",
+					UIKit,
+					"-weak_framework",
+					AVFoundation,
+					"-weak_framework",
+					CoreMedia,
+					"-weak-lSystem",
+					"-ObjC",
+				);
+				PRODUCT_NAME = "HelloCordova";
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Release;
+		};
+		C01FCF4F08A954540054247B /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				GCC_C_LANGUAGE_STANDARD = c99;
+				GCC_THUMB_SUPPORT = NO;
+				GCC_VERSION = "";
+				GCC_WARN_ABOUT_RETURN_TYPE = YES;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				HEADER_SEARCH_PATHS = (
+					"\"$(TARGET_BUILD_DIR)/usr/local/lib/include\"",
+					"\"$(OBJROOT)/UninstalledProducts/include\"",
+					"\"$(BUILT_PRODUCTS_DIR)\"",
+				);
+				IPHONEOS_DEPLOYMENT_TARGET = 6.0;
+				ONLY_ACTIVE_ARCH = YES;
+				OTHER_LDFLAGS = (
+					"-weak_framework",
+					CoreFoundation,
+					"-weak_framework",
+					UIKit,
+					"-weak_framework",
+					AVFoundation,
+					"-weak_framework",
+					CoreMedia,
+					"-weak-lSystem",
+					"-ObjC",
+				);
+				SDKROOT = iphoneos;
+				SKIP_INSTALL = NO;
+				USER_HEADER_SEARCH_PATHS = "";
+			};
+			name = Debug;
+		};
+		C01FCF5008A954540054247B /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				GCC_C_LANGUAGE_STANDARD = c99;
+				GCC_THUMB_SUPPORT = NO;
+				GCC_VERSION = "";
+				GCC_WARN_ABOUT_RETURN_TYPE = YES;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				HEADER_SEARCH_PATHS = (
+					"\"$(TARGET_BUILD_DIR)/usr/local/lib/include\"",
+					"\"$(OBJROOT)/UninstalledProducts/include\"",
+					"\"$(BUILT_PRODUCTS_DIR)\"",
+				);
+				IPHONEOS_DEPLOYMENT_TARGET = 6.0;
+				OTHER_LDFLAGS = (
+					"-weak_framework",
+					CoreFoundation,
+					"-weak_framework",
+					UIKit,
+					"-weak_framework",
+					AVFoundation,
+					"-weak_framework",
+					CoreMedia,
+					"-weak-lSystem",
+					"-ObjC",
+				);
+				SDKROOT = iphoneos;
+				SKIP_INSTALL = NO;
+				USER_HEADER_SEARCH_PATHS = "";
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "HelloCordova" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				1D6058940D05DD3E006BFB54 /* Debug */,
+				1D6058950D05DD3E006BFB54 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		C01FCF4E08A954540054247B /* Build configuration list for PBXProject "__CLI__" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				C01FCF4F08A954540054247B /* Debug */,
+				C01FCF5008A954540054247B /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 29B97313FDCFA39411CA2CEA /* Project object */;
+}
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/pbxProject.js b/test/pbxProject.js
index 77786b7..eacb61b 100644
--- a/test/pbxProject.js
+++ b/test/pbxProject.js
@@ -177,6 +177,90 @@
     }
 }
 
+exports['addBuildProperty function'] = {
+    setUp:function(callback) {
+        callback();
+    },
+    tearDown:function(callback) {
+        fs.writeFileSync(bcpbx, original_pbx, 'utf-8');
+        callback();
+    },
+    'should add 4 build properties in the .pbxproj file': function (test) {
+        var myProj = new pbx('test/parser/projects/build-config.pbxproj');
+        myProj.parse(function(err, hash) {
+            myProj.addBuildProperty('ENABLE_BITCODE', 'NO');
+            var newContents = myProj.writeSync();
+            test.equal(newContents.match(/ENABLE_BITCODE\s*=\s*NO/g).length, 4);
+            test.done();
+        });
+    },
+    'should add 2 build properties in the .pbxproj file for specific build': function (test) {
+        var myProj = new pbx('test/parser/projects/build-config.pbxproj');
+        myProj.parse(function(err, hash) {
+            myProj.addBuildProperty('ENABLE_BITCODE', 'NO', 'Release');
+            var newContents = myProj.writeSync();
+            test.equal(newContents.match(/ENABLE_BITCODE\s*=\s*NO/g).length, 2);
+            test.done();
+        });
+    },
+    'should not add build properties in the .pbxproj file for nonexist build': function (test) {
+        var myProj = new pbx('test/parser/projects/build-config.pbxproj');
+        myProj.parse(function(err, hash) {
+            myProj.addBuildProperty('ENABLE_BITCODE', 'NO', 'nonexist');
+            var newContents = myProj.writeSync();
+            test.ok(!newContents.match(/ENABLE_BITCODE\s*=\s*NO/g));
+            test.done();
+        });
+    }
+}
+
+exports['removeBuildProperty function'] = {
+    setUp:function(callback) {
+        callback();
+    },
+    tearDown:function(callback) {
+        fs.writeFileSync(bcpbx, original_pbx, 'utf-8');
+        callback();
+    },
+    'should remove all build properties in the .pbxproj file': function (test) {
+        var myProj = new pbx('test/parser/projects/build-config.pbxproj');
+        myProj.parse(function(err, hash) {
+            myProj.removeBuildProperty('IPHONEOS_DEPLOYMENT_TARGET');
+            var newContents = myProj.writeSync();
+            test.ok(!newContents.match(/IPHONEOS_DEPLOYMENT_TARGET/));
+            test.done();
+        });
+    },
+    'should remove specific build properties in the .pbxproj file': function (test) {
+        var myProj = new pbx('test/parser/projects/build-config.pbxproj');
+        myProj.parse(function(err, hash) {
+            myProj.removeBuildProperty('IPHONEOS_DEPLOYMENT_TARGET', 'Debug');
+            var newContents = myProj.writeSync();
+            test.equal(newContents.match(/IPHONEOS_DEPLOYMENT_TARGET/g).length, 2);
+            test.done();
+        });
+    },
+    'should not remove any build properties in the .pbxproj file': function (test) {
+        var myProj = new pbx('test/parser/projects/build-config.pbxproj');
+        myProj.parse(function(err, hash) {
+            myProj.removeBuildProperty('IPHONEOS_DEPLOYMENT_TARGET', 'notexist');
+            var newContents = myProj.writeSync();
+            test.equal(newContents.match(/IPHONEOS_DEPLOYMENT_TARGET/g).length, 4);
+            test.done();
+        });
+    },
+    'should fine with remove inexist build properties in the .pbxproj file': function (test) {
+        var myProj = new pbx('test/parser/projects/build-config.pbxproj');
+        myProj.parse(function(err, hash) {
+            myProj.removeBuildProperty('ENABLE_BITCODE');
+            var newContents = myProj.writeSync();
+            test.ok(!newContents.match(/ENABLE_BITCODE/));
+            test.done();
+        });
+    }
+
+}
+
 exports['productName field'] = {
     'should return the product name': function (test) {
         var newProj = new pbx('.');
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>"');