You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by GitBox <gi...@apache.org> on 2018/12/10 06:40:27 UTC

[GitHub] erisu closed pull request #405: Cocoapods support improvement, using podspec tag in plugin.xml

erisu closed pull request #405: Cocoapods support improvement, using podspec tag in plugin.xml
URL: https://github.com/apache/cordova-ios/pull/405
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/bin/templates/scripts/cordova/Api.js b/bin/templates/scripts/cordova/Api.js
index 9a1da6aff..e193c3c83 100644
--- a/bin/templates/scripts/cordova/Api.js
+++ b/bin/templates/scripts/cordova/Api.js
@@ -30,6 +30,7 @@ var events = require('cordova-common').events;
 var PluginManager = require('cordova-common').PluginManager;
 var Q = require('q');
 var util = require('util');
+var xcode = require('xcode');
 var ConfigParser = require('cordova-common').ConfigParser;
 
 function setupEvents (externalEventEmitter) {
@@ -221,6 +222,7 @@ Api.prototype.prepare = function (cordovaProject) {
  *   CordovaError instance.
  */
 Api.prototype.addPlugin = function (plugin, installOptions) {
+
     var xcodeproj = projectFile.parse(this.locations);
     var self = this;
 
@@ -254,64 +256,13 @@ Api.prototype.addPlugin = function (plugin, installOptions) {
             }
         })
         .then(function () {
-            var frameworkTags = plugin.getFrameworks(self.platform);
-            var frameworkPods = frameworkTags.filter(function (obj) {
-                return (obj.type === 'podspec');
-            });
-
-            return Q.resolve(frameworkPods);
-        })
-        .then(function (frameworkPods) {
-            if (!(frameworkPods.length)) {
-                return Q.resolve();
-            }
-
-            var project_dir = self.locations.root;
-            var project_name = self.locations.xcodeCordovaProj.split('/').pop();
-            var minDeploymentTarget = self.getPlatformInfo().projectConfig.getPreference('deployment-target', 'ios');
-
-            var Podfile = require('./lib/Podfile').Podfile;
-            var PodsJson = require('./lib/PodsJson').PodsJson;
-
-            events.emit('verbose', 'Adding pods since the plugin contained <framework>(s) with type="podspec"');
-
-            var podsjsonFile = new PodsJson(path.join(project_dir, PodsJson.FILENAME));
-            var podfileFile = new Podfile(path.join(project_dir, Podfile.FILENAME), project_name, minDeploymentTarget);
-
-            frameworkPods.forEach(function (obj) {
-                var podJson = {
-                    name: obj.src,
-                    type: obj.type,
-                    spec: obj.spec
-                };
-
-                var val = podsjsonFile.get(podJson.name);
-                if (val) { // found
-                    if (podJson.spec !== val.spec) { // exists, different spec, print warning
-                        events.emit('warn', plugin.id + ' depends on ' + podJson.name + '@' + podJson.spec + ', which conflicts with another plugin. ' + podJson.name + '@' + val.spec + ' is already installed and was not overwritten.');
-                    }
-                    // increment count, but don't add in Podfile because it already exists
-                    podsjsonFile.increment(podJson.name);
-                } else { // not found, write new
-                    podJson.count = 1;
-                    podsjsonFile.setJson(podJson.name, podJson);
-                    // add to Podfile
-                    podfileFile.addSpec(podJson.name, podJson.spec);
-                }
-            });
-
-            // now that all the pods have been processed, write to pods.json
-            podsjsonFile.write();
-
-            // only write and pod install if the Podfile changed
-            if (podfileFile.isDirty()) {
-                podfileFile.write();
-                events.emit('verbose', 'Running `pod install` (to install plugins)');
-                projectFile.purgeProjectFileCache(self.locations.root);
-
-                return podfileFile.install(check_reqs.check_cocoapods);
-            } else {
-                events.emit('verbose', 'Podfile unchanged, skipping `pod install`');
+            if (plugin != null) {
+                var podSpecs = plugin.getPodSpecs(self.platform);
+                var frameworkTags = plugin.getFrameworks(self.platform);
+                var frameworkPods = frameworkTags.filter(function (obj) {
+                    return (obj.type === 'podspec');
+                });
+                return self.addPodSpecs(plugin, podSpecs, frameworkPods);
             }
         })
         // CB-11022 return non-falsy value to indicate
@@ -359,63 +310,303 @@ Api.prototype.removePlugin = function (plugin, uninstallOptions) {
             }
         })
         .then(function () {
-            var frameworkTags = plugin.getFrameworks(self.platform);
-            var frameworkPods = frameworkTags.filter(function (obj) {
-                return (obj.type === 'podspec');
+            if (plugin != null) {
+                var podSpecs = plugin.getPodSpecs(self.platform);
+                var frameworkTags = plugin.getFrameworks(self.platform);
+                var frameworkPods = frameworkTags.filter(function (obj) {
+                    return (obj.type === 'podspec');
+                });
+                return self.removePodSpecs(plugin, podSpecs, frameworkPods);
+            }
+        })
+        // CB-11022 return non-falsy value to indicate
+        // that there is no need to run prepare after
+        .thenResolve(true);
+};
+
+/**
+ * adding CocoaPods libraries
+ *
+ * @param  {PluginInfo}  plugin  A PluginInfo instance that represents plugin
+ *   that will be installed.
+ * @param  {Object}  podSpecs: the return value of plugin.getPodSpecs(self.platform)
+ * @param  {Object}  frameworkPods: framework tags object with type === 'podspec'
+ * @return  {Promise}  Return a promise
+ */
+
+Api.prototype.addPodSpecs = function (plugin, podSpecs, frameworkPods) {
+    var self = this;
+
+    var project_dir = self.locations.root;
+    var project_name = self.locations.xcodeCordovaProj.split('/').pop();
+    var minDeploymentTarget = self.getPlatformInfo().projectConfig.getPreference('deployment-target', 'ios');
+
+    var Podfile = require('./lib/Podfile').Podfile;
+    var PodsJson = require('./lib/PodsJson').PodsJson;
+    var podsjsonFile = new PodsJson(path.join(project_dir, PodsJson.FILENAME));
+    var podfileFile = new Podfile(path.join(project_dir, Podfile.FILENAME), project_name, minDeploymentTarget);
+
+    if (podSpecs.length) {
+        events.emit('verbose', 'Adding pods since the plugin contained <podspecs>');
+        podSpecs.forEach(function (obj) {
+            // declarations
+            Object.keys(obj.declarations).forEach(function (key) {
+                if (obj.declarations[key] === 'true') {
+                    var declaration = Podfile.proofDeclaration(key);
+                    var podJson = {
+                        declaration: declaration
+                    };
+                    var val = podsjsonFile.getDeclaration(declaration);
+                    if (val) {
+                        podsjsonFile.incrementDeclaration(declaration);
+                    } else {
+                        podJson.count = 1;
+                        podsjsonFile.setJsonDeclaration(declaration, podJson);
+                        podfileFile.addDeclaration(podJson.declaration);
+                    }
+                }
+            });
+            // sources
+            Object.keys(obj.sources).forEach(function (key) {
+                var podJson = {
+                    source: obj.sources[key].source
+                };
+                var val = podsjsonFile.getSource(key);
+                if (val) {
+                    podsjsonFile.incrementSource(key);
+                } else {
+                    podJson.count = 1;
+                    podsjsonFile.setJsonSource(key, podJson);
+                    podfileFile.addSource(podJson.source);
+                }
+            });
+            // libraries
+            Object.keys(obj.libraries).forEach(function (key) {
+                var podJson = Object.assign({}, obj.libraries[key]);
+                var val = podsjsonFile.getLibrary(key);
+                if (val) {
+                    events.emit('warn', plugin.id + ' depends on ' + podJson.name + ', which may conflict with another plugin. ' + podJson.name + '@' + val.spec + ' is already installed and was not overwritten.');
+                    podsjsonFile.incrementLibrary(key);
+                } else {
+                    podJson.count = 1;
+                    podsjsonFile.setJsonLibrary(key, podJson);
+                    podfileFile.addSpec(podJson.name, podJson);
+                }
             });
 
-            return Q.resolve(frameworkPods);
-        })
-        .then(function (frameworkPods) {
-            if (!(frameworkPods.length)) {
-                return Q.resolve();
+        });
+    }
+
+    if (frameworkPods.length) {
+        events.emit('warn', '"framework" tag with type "podspec" is deprecated and will be removed. Please use the "podspec" tag.');
+        events.emit('verbose', 'Adding pods since the plugin contained <framework>(s) with type="podspec"');
+        frameworkPods.forEach(function (obj) {
+            var podJson = {
+                name: obj.src,
+                type: obj.type,
+                spec: obj.spec
+            };
+
+            var val = podsjsonFile.getLibrary(podJson.name);
+            if (val) { // found
+                if (podJson.spec !== val.spec) { // exists, different spec, print warning
+                    events.emit('warn', plugin.id + ' depends on ' + podJson.name + '@' + podJson.spec + ', which conflicts with another plugin. ' + podJson.name + '@' + val.spec + ' is already installed and was not overwritten.');
+                }
+                // increment count, but don't add in Podfile because it already exists
+                podsjsonFile.incrementLibrary(podJson.name);
+            } else { // not found, write new
+                podJson.count = 1;
+                podsjsonFile.setJsonLibrary(podJson.name, podJson);
+                // add to Podfile
+                podfileFile.addSpec(podJson.name, podJson.spec);
             }
+        });
+    }
 
-            var project_dir = self.locations.root;
-            var project_name = self.locations.xcodeCordovaProj.split('/').pop();
+    if (podSpecs.length > 0 || frameworkPods.length > 0) {
+        // now that all the pods have been processed, write to pods.json
+        podsjsonFile.write();
 
-            var Podfile = require('./lib/Podfile').Podfile;
-            var PodsJson = require('./lib/PodsJson').PodsJson;
+        // only write and pod install if the Podfile changed
+        if (podfileFile.isDirty()) {
+            podfileFile.write();
+            events.emit('verbose', 'Running `pod install` (to install plugins)');
+            projectFile.purgeProjectFileCache(self.locations.root);
 
-            events.emit('verbose', 'Adding pods since the plugin contained <framework>(s) with type=\"podspec\"'); /* eslint no-useless-escape : 0 */
+            return podfileFile.install(check_reqs.check_cocoapods)
+                .then(function () {
+                    self.setSwiftVersionForCocoaPodsLibraries(podsjsonFile);
+                });
+        } else {
+            events.emit('verbose', 'Podfile unchanged, skipping `pod install`');
+        }
+    }
+    return Q.when();
+};
+
+/**
+ * removing CocoaPods libraries
+ *
+ * @param  {PluginInfo}  plugin  A PluginInfo instance that represents plugin
+ *   that will be installed.
+ * @param  {Object}  podSpecs: the return value of plugin.getPodSpecs(self.platform)
+ * @param  {Object}  frameworkPods: framework tags object with type === 'podspec'
+ * @return  {Promise}  Return a promise
+ */
 
-            var podsjsonFile = new PodsJson(path.join(project_dir, PodsJson.FILENAME));
-            var podfileFile = new Podfile(path.join(project_dir, Podfile.FILENAME), project_name);
+Api.prototype.removePodSpecs = function (plugin, podSpecs, frameworkPods) {
+    var self = this;
 
-            frameworkPods.forEach(function (obj) {
+    var project_dir = self.locations.root;
+    var project_name = self.locations.xcodeCordovaProj.split('/').pop();
+
+    var Podfile = require('./lib/Podfile').Podfile;
+    var PodsJson = require('./lib/PodsJson').PodsJson;
+    var podsjsonFile = new PodsJson(path.join(project_dir, PodsJson.FILENAME));
+    var podfileFile = new Podfile(path.join(project_dir, Podfile.FILENAME), project_name);
+
+    if (podSpecs.length) {
+        events.emit('verbose', 'Adding pods since the plugin contained <podspecs>');
+        podSpecs.forEach(function (obj) {
+            // declarations
+            Object.keys(obj.declarations).forEach(function (key) {
+                if (obj.declarations[key] === 'true') {
+                    var declaration = Podfile.proofDeclaration(key);
+                    var podJson = {
+                        declaration: declaration
+                    };
+                    var val = podsjsonFile.getDeclaration(declaration);
+                    if (val) {
+                        podsjsonFile.decrementDeclaration(declaration);
+                    } else {
+                        var message = util.format('plugin \"%s\" declaration \"%s\" does not seem to be in pods.json, nothing to remove. Will attempt to remove from Podfile.', plugin.id, podJson.declaration); /* eslint no-useless-escape : 0 */
+                        events.emit('verbose', message);
+                    }
+                    if (!val || val.count === 0) {
+                        podfileFile.removeDeclaration(podJson.declaration);
+                    }
+                }
+            });
+            // sources
+            Object.keys(obj.sources).forEach(function (key) {
                 var podJson = {
-                    name: obj.src,
-                    type: obj.type,
-                    spec: obj.spec
+                    source: obj.sources[key].source
                 };
-
-                var val = podsjsonFile.get(podJson.name);
-                if (val) { // found, decrement count
-                    podsjsonFile.decrement(podJson.name);
-                } else { // not found (perhaps a sync error)
+                var val = podsjsonFile.getSource(key);
+                if (val) {
+                    podsjsonFile.decrementSource(key);
+                } else {
+                    var message = util.format('plugin \"%s\" source \"%s\" does not seem to be in pods.json, nothing to remove. Will attempt to remove from Podfile.', plugin.id, podJson.source); /* eslint no-useless-escape : 0 */
+                    events.emit('verbose', message);
+                }
+                if (!val || val.count === 0) {
+                    podfileFile.removeSource(podJson.source);
+                }
+            });
+            // libraries
+            Object.keys(obj.libraries).forEach(function (key) {
+                var podJson = Object.assign({}, obj.libraries[key]);
+                var val = podsjsonFile.getLibrary(key);
+                if (val) {
+                    podsjsonFile.decrementLibrary(key);
+                } else {
                     var message = util.format('plugin \"%s\" podspec \"%s\" does not seem to be in pods.json, nothing to remove. Will attempt to remove from Podfile.', plugin.id, podJson.name); /* eslint no-useless-escape : 0 */
                     events.emit('verbose', message);
                 }
-
-                // always remove from the Podfile
-                podfileFile.removeSpec(podJson.name);
+                if (!val || val.count === 0) {
+                    podfileFile.removeSpec(podJson.name);
+                }
             });
 
-            // now that all the pods have been processed, write to pods.json
-            podsjsonFile.write();
-
-            if (podfileFile.isDirty()) {
-                podfileFile.write();
-                events.emit('verbose', 'Running `pod install` (to uninstall pods)');
+        });
+    }
 
-                return podfileFile.install(check_reqs.check_cocoapods);
-            } else {
-                events.emit('verbose', 'Podfile unchanged, skipping `pod install`');
+    if (frameworkPods.length) {
+        events.emit('warn', '"framework" tag with type "podspec" is deprecated and will be removed. Please use the "podspec" tag.');
+        events.emit('verbose', 'Adding pods since the plugin contained <framework>(s) with type=\"podspec\"'); /* eslint no-useless-escape : 0 */
+        frameworkPods.forEach(function (obj) {
+            var podJson = {
+                name: obj.src,
+                type: obj.type,
+                spec: obj.spec
+            };
+
+            var val = podsjsonFile.getLibrary(podJson.name);
+            if (val) { // found, decrement count
+                podsjsonFile.decrementLibrary(podJson.name);
+            } else { // not found (perhaps a sync error)
+                var message = util.format('plugin \"%s\" podspec \"%s\" does not seem to be in pods.json, nothing to remove. Will attempt to remove from Podfile.', plugin.id, podJson.name); /* eslint no-useless-escape : 0 */
+                events.emit('verbose', message);
             }
-        })
-        // CB-11022 return non-falsy value to indicate
-        // that there is no need to run prepare after
-        .thenResolve(true);
+
+            // always remove from the Podfile
+            podfileFile.removeSpec(podJson.name);
+        });
+    }
+
+    if (podSpecs.length > 0 || frameworkPods.length > 0) {
+        // now that all the pods have been processed, write to pods.json
+        podsjsonFile.write();
+
+        if (podfileFile.isDirty()) {
+            podfileFile.write();
+            events.emit('verbose', 'Running `pod install` (to uninstall pods)');
+
+            return podfileFile.install(check_reqs.check_cocoapods)
+                .then(function () {
+                    self.setSwiftVersionForCocoaPodsLibraries(podsjsonFile);
+                });
+        } else {
+            events.emit('verbose', 'Podfile unchanged, skipping `pod install`');
+        }
+    }
+    return Q.when();
+};
+
+/**
+ * set Swift Version for all CocoaPods libraries
+ *
+ * @param  {PodsJson}  podsjsonFile  A PodsJson instance that represents pods.json
+ */
+
+Api.prototype.setSwiftVersionForCocoaPodsLibraries = function (podsjsonFile) {
+    var self = this;
+    var __dirty = false;
+    var podPbxPath = path.join(self.root, 'Pods', 'Pods.xcodeproj', 'project.pbxproj');
+    var podXcodeproj = xcode.project(podPbxPath);
+    podXcodeproj.parseSync();
+    var podTargets = podXcodeproj.pbxNativeTargetSection();
+    var podConfigurationList = podXcodeproj.pbxXCConfigurationList();
+    var podConfigs = podXcodeproj.pbxXCBuildConfigurationSection();
+
+    var libraries = podsjsonFile.getLibraries();
+    Object.keys(libraries).forEach(function (key) {
+        var podJson = libraries[key];
+        var name = podJson.name;
+        var swiftVersion = podJson['swift-version'];
+        if (swiftVersion) {
+            __dirty = true;
+            Object.keys(podTargets).filter(function (targetKey) {
+                return podTargets[targetKey].productName === name;
+            }).map(function (targetKey) {
+                return podTargets[targetKey].buildConfigurationList;
+            }).map(function (buildConfigurationListId) {
+                return podConfigurationList[buildConfigurationListId];
+            }).map(function (buildConfigurationList) {
+                return buildConfigurationList.buildConfigurations;
+            }).reduce(function (acc, buildConfigurations) {
+                return acc.concat(buildConfigurations);
+            }, []).map(function (buildConfiguration) {
+                return buildConfiguration.value;
+            }).forEach(function (buildId) {
+                __dirty = true;
+                podConfigs[buildId].buildSettings['SWIFT_VERSION'] = swiftVersion;
+            });
+        }
+    });
+    if (__dirty) {
+        fs.writeFileSync(podPbxPath, podXcodeproj.writeSync(), 'utf-8');
+    }
 };
 
 /**
diff --git a/bin/templates/scripts/cordova/lib/Podfile.js b/bin/templates/scripts/cordova/lib/Podfile.js
index ffa5be226..2e946f7a5 100644
--- a/bin/templates/scripts/cordova/lib/Podfile.js
+++ b/bin/templates/scripts/cordova/lib/Podfile.js
@@ -27,14 +27,22 @@ var superspawn = require('cordova-common').superspawn;
 var CordovaError = require('cordova-common').CordovaError;
 
 Podfile.FILENAME = 'Podfile';
+Podfile.declarationRegexpMap = {
+    'use_frameworks!': 'use[-_]frameworks!?',
+    'inhibit_all_warnings!': 'inhibit[-_]all[-_]warnings!?'
+};
 
 function Podfile (podFilePath, projectName, minDeploymentTarget) {
+    this.declarationToken = '##INSERT_DECLARATION##';
+    this.sourceToken = '##INSERT_SOURCE##';
     this.podToken = '##INSERT_POD##';
 
     this.path = podFilePath;
     this.projectName = projectName;
     this.minDeploymentTarget = minDeploymentTarget || '9.0';
     this.contents = null;
+    this.sources = null;
+    this.declarations = null;
     this.pods = null;
     this.__dirty = false;
 
@@ -56,10 +64,72 @@ function Podfile (podFilePath, projectName, minDeploymentTarget) {
     } else {
         events.emit('verbose', 'Podfile found in platforms/ios');
         // parse for pods
-        this.pods = this.__parseForPods(fs.readFileSync(this.path, 'utf8'));
+        var fileText = fs.readFileSync(this.path, 'utf8');
+        this.declarations = this.__parseForDeclarations(fileText);
+        this.sources = this.__parseForSources(fileText);
+        this.pods = this.__parseForPods(fileText);
     }
 }
 
+Podfile.prototype.__parseForDeclarations = function (text) {
+    // split by \n
+    var arr = text.split('\n');
+
+    // getting lines between "platform :ios, '9.0'"" and "target 'HelloCordova'" do
+    var declarationsPreRE = new RegExp('platform :ios,\\s+\'[^\']+\'');
+    var declarationsPostRE = new RegExp('target\\s+\'[^\']+\'\\s+do');
+    var declarationRE = new RegExp('^\\s*[^#]');
+
+    return arr.reduce(function (acc, line) {
+        switch (acc.state) {
+        case 0:
+            if (declarationsPreRE.exec(line)) {
+                acc.state = 1; // Start to read
+            }
+            break;
+        case 1:
+            if (declarationsPostRE.exec(line)) {
+                acc.state = 2; // Finish to read
+            } else {
+                acc.lines.push(line);
+            }
+            break;
+        case 2:
+        default:
+            // do nothing;
+        }
+        return acc;
+    }, {state: 0, lines: []})
+        .lines
+        .filter(function (line) {
+            return declarationRE.exec(line);
+        })
+        .reduce(function (obj, line) {
+            obj[line] = line;
+            return obj;
+        }, {});
+};
+
+Podfile.prototype.__parseForSources = function (text) {
+    // split by \n
+    var arr = text.split('\n');
+
+    var sourceRE = new RegExp('source \'(.*)\'');
+    return arr.filter(function (line) {
+        var m = sourceRE.exec(line);
+
+        return (m !== null);
+    })
+        .reduce(function (obj, line) {
+            var m = sourceRE.exec(line);
+            if (m !== null) {
+                var source = m[1];
+                obj[source] = source;
+            }
+            return obj;
+        }, {});
+};
+
 Podfile.prototype.__parseForPods = function (text) {
     // split by \n
     var arr = text.split('\n');
@@ -68,7 +138,8 @@ Podfile.prototype.__parseForPods = function (text) {
     //     pod 'Foobar', '1.2'
     //     pod 'Foobar', 'abc 123 1.2'
     //     pod 'PonyDebugger', :configurations => ['Debug', 'Beta']
-    var podRE = new RegExp('pod \'([^\']*)\'\\s*,?\\s*(.*)');
+    // var podRE = new RegExp('pod \'([^\']*)\'\\s*,?\\s*(.*)');
+    var podRE = new RegExp('pod \'([^\']*)\'\\s*(?:,\\s*\'([^\']*)\'\\s*)?,?\\s*(.*)');
 
     // only grab lines that don't have the pod spec'
     return arr.filter(function (line) {
@@ -80,9 +151,16 @@ Podfile.prototype.__parseForPods = function (text) {
             var m = podRE.exec(line);
 
             if (m !== null) {
-                // strip out any single quotes around the value m[2]
-                var podSpec = m[2].replace(/^\'|\'$/g, ''); /* eslint no-useless-escape : 0 */
-                obj[m[1]] = podSpec; // i.e pod 'Foo', '1.2' ==> { 'Foo' : '1.2'}
+                var podspec = {
+                    name: m[1]
+                };
+                if (m[2]) {
+                    podspec.spec = m[2];
+                }
+                if (m[3]) {
+                    podspec.options = m[3];
+                }
+                obj[m[1]] = podspec; // i.e pod 'Foo', '1.2' ==> { 'Foo' : '1.2'}
             }
 
             return obj;
@@ -98,12 +176,14 @@ Podfile.prototype.getTemplate = function () {
     var projectName = this.escapeSingleQuotes(this.projectName);
     return util.format(
         '# DO NOT MODIFY -- auto-generated by Apache Cordova\n' +
+            '%s\n' +
             'platform :ios, \'%s\'\n' +
+            '%s\n' +
             'target \'%s\' do\n' +
             '\tproject \'%s.xcodeproj\'\n' +
             '%s\n' +
             'end\n',
-        this.minDeploymentTarget, projectName, projectName, this.podToken);
+        this.sourceToken, this.minDeploymentTarget, this.declarationToken, projectName, projectName, this.podToken);
 };
 
 Podfile.prototype.addSpec = function (name, spec) {
@@ -115,6 +195,14 @@ Podfile.prototype.addSpec = function (name, spec) {
         throw new CordovaError('Podfile addSpec: name is not specified.');
     }
 
+    if (typeof spec === 'string') {
+        if (spec.startsWith(':')) {
+            spec = {name: name, options: spec};
+        } else {
+            spec = {name: name, spec: spec};
+        }
+    }
+
     this.pods[name] = spec;
     this.__dirty = true;
 
@@ -138,7 +226,60 @@ Podfile.prototype.existsSpec = function (name) {
     return (name in this.pods);
 };
 
+Podfile.prototype.addSource = function (src) {
+    this.sources[src] = src;
+    this.__dirty = true;
+
+    events.emit('verbose', util.format('Added source line for `%s`', src));
+};
+
+Podfile.prototype.removeSource = function (src) {
+    if (this.existsSource(src)) {
+        delete this.sources[src];
+        this.__dirty = true;
+    }
+
+    events.emit('verbose', util.format('Removed source line for `%s`', src));
+};
+
+Podfile.prototype.existsSource = function (src) {
+    return (src in this.sources);
+};
+
+Podfile.prototype.addDeclaration = function (declaration) {
+    this.declarations[declaration] = declaration;
+    this.__dirty = true;
+
+    events.emit('verbose', util.format('Added declaration line for `%s`', declaration));
+};
+
+Podfile.prototype.removeDeclaration = function (declaration) {
+    if (this.existsDeclaration(declaration)) {
+        delete this.declarations[declaration];
+        this.__dirty = true;
+    }
+
+    events.emit('verbose', util.format('Removed source line for `%s`', declaration));
+};
+
+Podfile.proofDeclaration = function (declaration) {
+    var list = Object.keys(Podfile.declarationRegexpMap).filter(function (key) {
+        var regexp = new RegExp(Podfile.declarationRegexpMap[key]);
+        return regexp.test(declaration);
+    });
+    if (list.length > 0) {
+        return list[0];
+    }
+    return declaration;
+};
+
+Podfile.prototype.existsDeclaration = function (declaration) {
+    return (declaration in this.declarations);
+};
+
 Podfile.prototype.clear = function () {
+    this.sources = {};
+    this.declarations = {};
     this.pods = {};
     this.__dirty = true;
 };
@@ -155,22 +296,61 @@ Podfile.prototype.write = function () {
     var podsString =
     Object.keys(this.pods).map(function (key) {
         var name = key;
-        var spec = self.pods[key];
-
-        if (spec.length) {
-            if (spec.indexOf(':') === 0) {
-                // don't quote it, it's a specification (starts with ':')
-                return util.format('\tpod \'%s\', %s', name, spec);
+        var json = self.pods[key];
+
+        if (typeof json === 'string') { // compatibility for using framework tag.
+            var spec = json;
+            if (spec.length) {
+                if (spec.indexOf(':') === 0) {
+                    // don't quote it, it's a specification (starts with ':')
+                    return util.format('\tpod \'%s\', %s', name, spec);
+                } else {
+                    // quote it, it's a version
+                    return util.format('\tpod \'%s\', \'%s\'', name, spec);
+                }
             } else {
-                // quote it, it's a version
-                return util.format('\tpod \'%s\', \'%s\'', name, spec);
+                return util.format('\tpod \'%s\'', name);
             }
         } else {
-            return util.format('\tpod \'%s\'', name);
+            var list = ['\'' + name + '\''];
+            if ('spec' in json) {
+                list.push('\'' + json.spec + '\'');
+            }
+
+            var options = ['tag', 'branch', 'commit', 'git', 'podspec'].filter(function (tag) {
+                return tag in json;
+            }).map(function (tag) {
+                return ':' + tag + ' => \'' + json[tag] + '\'';
+            });
+            if ('configurations' in json) {
+                options.push(':configurations => [' + json['configurations'].split(',').map(function (conf) { return '\'' + conf.trim() + '\''; }).join(',') + ']');
+            }
+            if ('options' in json) {
+                options = [json.options];
+            }
+            if (options.length > 0) {
+                list.push(options.join(', '));
+            }
+            return util.format('\tpod %s', list.join(', '));
         }
     }).join('\n');
 
-    text = text.replace(this.podToken, podsString);
+    var sourcesString =
+    Object.keys(this.sources).map(function (key) {
+        var source = self.sources[key];
+        return util.format('source \'%s\'', source);
+    }).join('\n');
+
+    var declarationString =
+    Object.keys(this.declarations).map(function (key) {
+        var declaration = self.declarations[key];
+        return declaration;
+    }).join('\n');
+
+    text = text.replace(this.podToken, podsString)
+        .replace(this.sourceToken, sourcesString)
+        .replace(this.declarationToken, declarationString);
+
     fs.writeFileSync(this.path, text, 'utf8');
     this.__dirty = false;
 
diff --git a/bin/templates/scripts/cordova/lib/PodsJson.js b/bin/templates/scripts/cordova/lib/PodsJson.js
index 047052739..25afbac36 100644
--- a/bin/templates/scripts/cordova/lib/PodsJson.js
+++ b/bin/templates/scripts/cordova/lib/PodsJson.js
@@ -24,6 +24,9 @@ var events = require('cordova-common').events;
 var CordovaError = require('cordova-common').CordovaError;
 
 PodsJson.FILENAME = 'pods.json';
+PodsJson.LIBRARY = 'libraries';
+PodsJson.SOURCE = 'sources';
+PodsJson.DECLARATION = 'declarations';
 
 function PodsJson (podsJsonPath) {
     this.path = podsJsonPath;
@@ -43,25 +46,81 @@ function PodsJson (podsJsonPath) {
     } else {
         events.emit('verbose', 'pods.json found in platforms/ios');
         // load contents
-        this.contents = fs.readFileSync(this.path, 'utf8');
-        this.contents = JSON.parse(this.contents);
+        var contents = fs.readFileSync(this.path, 'utf8');
+        this.contents = JSON.parse(contents);
     }
+    this.__updateFormatIfNecessary();
 }
 
-PodsJson.prototype.get = function (name) {
-    return this.contents[name];
+PodsJson.prototype.__isOldFormat = function () {
+    if (this.contents !== null) {
+        if (this.contents.declarations === undefined ||
+            this.contents.sources === undefined ||
+            this.contents.libraries === undefined) {
+            return true;
+        }
+    }
+    return false;
 };
 
-PodsJson.prototype.remove = function (name) {
-    if (this.contents[name]) {
-        delete this.contents[name];
+PodsJson.prototype.__updateFormatIfNecessary = function () {
+    if (this.__isOldFormat()) {
+        this.contents = {
+            declarations: {},
+            sources: {},
+            libraries: this.contents
+        };
         this.__dirty = true;
-        events.emit('verbose', util.format('Remove from pods.json for `%s`', name));
+        events.emit('verbose', 'Update format of pods.json');
     }
 };
 
+PodsJson.prototype.getLibraries = function () {
+    return this.contents[PodsJson.LIBRARY];
+};
+
+PodsJson.prototype.__get = function (kind, name) {
+    return this.contents[kind][name];
+};
+
+PodsJson.prototype.getLibrary = function (name) {
+    return this.__get(PodsJson.LIBRARY, name);
+};
+
+PodsJson.prototype.getSource = function (name) {
+    return this.__get(PodsJson.SOURCE, name);
+};
+
+PodsJson.prototype.getDeclaration = function (name) {
+    return this.__get(PodsJson.DECLARATION, name);
+};
+
+PodsJson.prototype.__remove = function (kind, name) {
+    if (this.contents[kind][name]) {
+        delete this.contents[kind][name];
+        this.__dirty = true;
+        events.emit('verbose', util.format('Remove from pods.json for `%s` - `%s`', name));
+    }
+};
+
+PodsJson.prototype.removeLibrary = function (name) {
+    this.__remove(PodsJson.LIBRARY, name);
+};
+
+PodsJson.prototype.removeSource = function (name) {
+    this.__remove(PodsJson.SOURCE, name);
+};
+
+PodsJson.prototype.removeDeclaration = function (name) {
+    this.__remove(PodsJson.DECLARATION, name);
+};
+
 PodsJson.prototype.clear = function () {
-    this.contents = {};
+    this.contents = {
+        declarations: {},
+        sources: {},
+        libraries: {}
+    };
     this.__dirty = true;
 };
 
@@ -78,34 +137,69 @@ PodsJson.prototype.write = function () {
     }
 };
 
-PodsJson.prototype.set = function (name, type, spec, count) {
-    this.setJson(name, { name: name, type: type, spec: spec, count: count });
-};
-
-PodsJson.prototype.increment = function (name) {
-    var val = this.get(name);
+PodsJson.prototype.__increment = function (kind, name) {
+    var val = this.__get(kind, name);
     if (val) {
         val.count++;
-        this.setJson(val);
     }
 };
 
-PodsJson.prototype.decrement = function (name) {
-    var val = this.get(name);
+PodsJson.prototype.incrementLibrary = function (name) {
+    this.__increment(PodsJson.LIBRARY, name);
+};
+
+PodsJson.prototype.incrementSource = function (name) {
+    this.__increment(PodsJson.SOURCE, name);
+};
+
+PodsJson.prototype.incrementDeclaration = function (name) {
+    this.__increment(PodsJson.DECLARATION, name);
+};
+
+PodsJson.prototype.__decrement = function (kind, name) {
+    var val = this.__get(kind, name);
     if (val) {
         val.count--;
         if (val.count <= 0) {
-            this.remove(name);
-        } else {
-            this.setJson(val);
+            this.__remove(kind, name);
         }
     }
 };
 
-PodsJson.prototype.setJson = function (name, json) {
-    this.contents[name] = json;
+PodsJson.prototype.decrementLibrary = function (name) {
+    this.__decrement(PodsJson.LIBRARY, name);
+};
+
+PodsJson.prototype.decrementSource = function (name) {
+    this.__decrement(PodsJson.SOURCE, name);
+};
+
+PodsJson.prototype.decrementDeclaration = function (name) {
+    this.__decrement(PodsJson.DECLARATION, name);
+};
+
+PodsJson.prototype.__setJson = function (kind, name, json) {
+    this.contents[kind][name] = Object.assign({}, json);
     this.__dirty = true;
-    events.emit('verbose', util.format('Set pods.json for `%s`', name));
+    events.emit('verbose', util.format('Set pods.json for `%s` - `%s`', kind, name));
+};
+
+// sample json for library
+// { name: "Eureka", spec: "4.2.0", "swift-version": "4.1", count: 1 }
+PodsJson.prototype.setJsonLibrary = function (name, json) {
+    this.__setJson(PodsJson.LIBRARY, name, json);
+};
+
+// sample json for source
+// { source: "https://github.com/brightcove/BrightcoveSpecs.git", count: 1 }
+PodsJson.prototype.setJsonSource = function (name, json) {
+    this.__setJson(PodsJson.SOURCE, name, json);
+};
+
+// sample json for declaration
+// { declaration: ""}
+PodsJson.prototype.setJsonDeclaration = function (name, json) {
+    this.__setJson(PodsJson.DECLARATION, name, json);
 };
 
 PodsJson.prototype.isDirty = function () {
diff --git a/bin/templates/scripts/cordova/lib/plugman/pluginHandlers.js b/bin/templates/scripts/cordova/lib/plugman/pluginHandlers.js
index 1f6920fa9..7894f4625 100644
--- a/bin/templates/scripts/cordova/lib/plugman/pluginHandlers.js
+++ b/bin/templates/scripts/cordova/lib/plugman/pluginHandlers.js
@@ -129,16 +129,7 @@ var handlers = {
             if (!obj.custom) { // CB-9825 cocoapod integration for plugins
                 var keepFrameworks = keep_these_frameworks;
                 if (keepFrameworks.indexOf(src) < 0) {
-                    if (obj.type === 'podspec') {
-                        var podsJSON = require(path.join(project.projectDir, 'pods.json'));
-                        if (podsJSON[src]) {
-                            if (podsJSON[src].count > 1) {
-                                podsJSON[src].count = podsJSON[src].count - 1;
-                            } else {
-                                delete podsJSON[src];
-                            }
-                        }
-                    } else {
+                    if (obj.type !== 'podspec') {
                         // this should be refactored
                         project.frameworks[src] = project.frameworks[src] || 1;
                         project.frameworks[src]--;
diff --git a/tests/spec/unit/Api.spec.js b/tests/spec/unit/Api.spec.js
index 2e8db2da5..32fca88c1 100644
--- a/tests/spec/unit/Api.spec.js
+++ b/tests/spec/unit/Api.spec.js
@@ -42,9 +42,17 @@ var Q = require('q');
 var FIXTURES = path.join(__dirname, 'fixtures');
 var iosProjectFixture = path.join(FIXTURES, 'ios-config-xml');
 
+function compareListWithoutOrder (list1, list2) {
+    expect(list1.sort()).toEqual(list2.sort());
+}
+
 describe('Platform Api', function () {
 
     describe('constructor', function () {
+        beforeEach(function () {
+            events.removeAllListeners();
+        });
+
         it('Test 001 : should throw if provided directory does not contain an xcodeproj file', function () {
             expect(function () { new Api('ios', path.join(FIXTURES, '..')); }).toThrow(); /* eslint no-new : 0 */
         });
@@ -130,6 +138,7 @@ describe('Platform Api', function () {
         var api;
         var projectRoot = iosProjectFixture;
         beforeEach(function () {
+            events.removeAllListeners();
             api = new Api('ios', projectRoot);
             spyOn(fs, 'readdirSync').and.returnValue([api.locations.xcodeProjDir]);
             spyOn(projectFile, 'parse').and.returnValue({
@@ -160,7 +169,8 @@ describe('Platform Api', function () {
         describe('addPlugin', function () {
             var my_plugin = {
                 getHeaderFiles: function () { return []; },
-                getFrameworks: function () {}
+                getFrameworks: function () {},
+                getPodSpecs: function () { return []; }
             };
             beforeEach(function () {
                 spyOn(PluginManager, 'get').and.returnValue({
@@ -199,7 +209,175 @@ describe('Platform Api', function () {
                             console.error(err);
                         }).done(done);
                 });
-
+            });
+            describe('adding pods since the plugin contained <podspecs>', function () {
+                var podsjson_mock;
+                var podfile_mock;
+                var my_pod_json = {
+                    declarations: {
+                        'use-frameworks': 'true',
+                        'inhibit_all_warnings!': 'true'
+                    },
+                    sources: {
+                        'https://github.com/sample/SampleSpecs.git': { source: 'https://github.com/sample/SampleSpecs.git' },
+                        'https://github.com/CocoaPods/Specs.git': { source: 'https://github.com/CocoaPods/Specs.git' }
+                    },
+                    libraries: {
+                        'AFNetworking': {
+                            name: 'AFNetworking',
+                            spec: '~> 3.2'
+                        },
+                        'Eureka': {
+                            name: 'Eureka',
+                            spec: '4.0',
+                            'swift-version': '4.1'
+                        },
+                        'HogeLib': {
+                            name: 'HogeLib',
+                            git: 'https://github.com/hoge/HogewLib.git',
+                            branch: 'develop'
+                        }
+                    }
+                };
+                beforeEach(function () {
+                    podsjson_mock = jasmine.createSpyObj('podsjson mock', ['getLibrary', 'getSource', 'getDeclaration',
+                        'incrementLibrary', 'incrementSource', 'incrementDeclaration', 'write',
+                        'setJsonLibrary', 'setJsonSource', 'setJsonDeclaration']);
+                    podfile_mock = jasmine.createSpyObj('podfile mock', ['isDirty', 'addSpec', 'addSource', 'addDeclaration', 'write', 'install']);
+                    spyOn(my_plugin, 'getFrameworks').and.returnValue([]);
+                    spyOn(my_plugin, 'getPodSpecs').and.returnValue([my_pod_json]);
+                    PodsJson_mod.PodsJson.and.callFake(function () {
+                        return podsjson_mock;
+                    });
+                    Podfile_mod.Podfile.and.callFake(function () {
+                        return podfile_mock;
+                    });
+                });
+                it('on a new declaration, it should add a new json to declarations', function (done) {
+                    api.addPlugin(my_plugin)
+                        .then(function () {
+                            expect(podsjson_mock.getDeclaration.calls.count()).toEqual(2);
+                            compareListWithoutOrder(podsjson_mock.getDeclaration.calls.allArgs(), [['use_frameworks!'], ['inhibit_all_warnings!']]);
+                            expect(podsjson_mock.setJsonDeclaration.calls.count()).toEqual(2);
+                            compareListWithoutOrder(podsjson_mock.setJsonDeclaration.calls.allArgs(),
+                                [['use_frameworks!', {declaration: 'use_frameworks!', count: 1}], ['inhibit_all_warnings!', {declaration: 'inhibit_all_warnings!', count: 1}]]);
+                            expect(podfile_mock.addDeclaration.calls.count()).toEqual(2);
+                            compareListWithoutOrder(podfile_mock.addDeclaration.calls.allArgs(), [['use_frameworks!'], ['inhibit_all_warnings!']]);
+                        }).fail(function (err) {
+                            fail('unexpected addPlugin fail handler invoked :' + err);
+                        }).done(done);
+                });
+                it('should increment count in declarations if already exists', function (done) {
+                    podsjson_mock.getDeclaration.and.callFake(function (declaration) {
+                        if (declaration === 'use_frameworks!') {
+                            return {declaration: 'use_frameworks!', count: 1};
+                        }
+                        return null;
+                    });
+                    api.addPlugin(my_plugin)
+                        .then(function () {
+                            expect(podsjson_mock.getDeclaration.calls.count()).toEqual(2);
+                            compareListWithoutOrder(podsjson_mock.getDeclaration.calls.allArgs(), [['use_frameworks!'], ['inhibit_all_warnings!']]);
+                            expect(podsjson_mock.incrementDeclaration).toHaveBeenCalledWith('use_frameworks!');
+                            expect(podsjson_mock.setJsonDeclaration.calls.count()).toEqual(1);
+                            compareListWithoutOrder(podsjson_mock.setJsonDeclaration.calls.allArgs(), [['inhibit_all_warnings!', {declaration: 'inhibit_all_warnings!', count: 1}]]);
+                            expect(podfile_mock.addDeclaration.calls.count()).toEqual(1);
+                            compareListWithoutOrder(podfile_mock.addDeclaration.calls.allArgs(), [['inhibit_all_warnings!']]);
+                        }).fail(function (err) {
+                            fail('unexpected addPlugin fail handler invoked :' + err);
+                        }).done(done);
+                });
+                it('on a new source, it should add a new json to sources', function (done) {
+                    api.addPlugin(my_plugin)
+                        .then(function () {
+                            expect(podsjson_mock.getSource.calls.count()).toEqual(2);
+                            compareListWithoutOrder(podsjson_mock.getSource.calls.allArgs(), [['https://github.com/sample/SampleSpecs.git'], ['https://github.com/CocoaPods/Specs.git']]);
+                            expect(podsjson_mock.setJsonSource.calls.count()).toEqual(2);
+                            compareListWithoutOrder(podsjson_mock.setJsonSource.calls.allArgs(), [
+                                ['https://github.com/sample/SampleSpecs.git', {source: 'https://github.com/sample/SampleSpecs.git', count: 1}],
+                                ['https://github.com/CocoaPods/Specs.git', {source: 'https://github.com/CocoaPods/Specs.git', count: 1}]
+                            ]);
+                            expect(podfile_mock.addSource.calls.count()).toEqual(2);
+                            compareListWithoutOrder(podfile_mock.addSource.calls.allArgs(), [['https://github.com/sample/SampleSpecs.git'], ['https://github.com/CocoaPods/Specs.git']]);
+                        }).fail(function (err) {
+                            fail('unexpected addPlugin fail handler invoked :' + err);
+                        }).done(done);
+                });
+                it('should increment count in sources if already exists', function (done) {
+                    podsjson_mock.getSource.and.callFake(function (source) {
+                        if (source === 'https://github.com/CocoaPods/Specs.git') {
+                            return {source: 'https://github.com/CocoaPods/Specs.git', count: 1};
+                        }
+                        return null;
+                    });
+                    api.addPlugin(my_plugin)
+                        .then(function () {
+                            expect(podsjson_mock.getSource.calls.count()).toEqual(2);
+                            compareListWithoutOrder(podsjson_mock.getSource.calls.allArgs(), [['https://github.com/sample/SampleSpecs.git'], ['https://github.com/CocoaPods/Specs.git']]);
+                            expect(podsjson_mock.incrementSource).toHaveBeenCalledWith('https://github.com/CocoaPods/Specs.git');
+                            expect(podsjson_mock.setJsonSource.calls.count()).toEqual(1);
+                            compareListWithoutOrder(podsjson_mock.setJsonSource.calls.allArgs(), [['https://github.com/sample/SampleSpecs.git', {source: 'https://github.com/sample/SampleSpecs.git', count: 1}]]);
+                            expect(podfile_mock.addSource.calls.count()).toEqual(1);
+                            compareListWithoutOrder(podfile_mock.addSource.calls.allArgs(), [['https://github.com/sample/SampleSpecs.git']]);
+                        }).fail(function (err) {
+                            fail('unexpected addPlugin fail handler invoked :' + err);
+                        }).done(done);
+                });
+                it('on a new library, it should add a new json to library', function (done) {
+                    api.addPlugin(my_plugin)
+                        .then(function () {
+                            expect(podsjson_mock.getLibrary.calls.count()).toEqual(3);
+                            compareListWithoutOrder(podsjson_mock.getLibrary.calls.allArgs(), [
+                                ['AFNetworking'],
+                                ['Eureka'],
+                                ['HogeLib']
+                            ]);
+                            expect(podsjson_mock.setJsonLibrary.calls.count()).toEqual(3);
+                            compareListWithoutOrder(podsjson_mock.setJsonLibrary.calls.allArgs(), [
+                                ['AFNetworking', {name: 'AFNetworking', spec: '~> 3.2', count: 1}],
+                                ['Eureka', {name: 'Eureka', spec: '4.0', 'swift-version': '4.1', count: 1}],
+                                ['HogeLib', {name: 'HogeLib', git: 'https://github.com/hoge/HogewLib.git', branch: 'develop', count: 1}]
+                            ]);
+                            expect(podfile_mock.addSpec.calls.count()).toEqual(3);
+                            compareListWithoutOrder(podfile_mock.addSpec.calls.allArgs(), [
+                                ['AFNetworking', {name: 'AFNetworking', spec: '~> 3.2', count: 1}],
+                                ['Eureka', {name: 'Eureka', spec: '4.0', 'swift-version': '4.1', count: 1}],
+                                ['HogeLib', {name: 'HogeLib', git: 'https://github.com/hoge/HogewLib.git', branch: 'develop', count: 1}]
+                            ]);
+                        }).fail(function (err) {
+                            fail('unexpected addPlugin fail handler invoked :' + err);
+                        }).done(done);
+                });
+                it('should increment count in libraries if already exists', function (done) {
+                    podsjson_mock.getLibrary.and.callFake(function (library) {
+                        if (library === 'AFNetworking') {
+                            return {name: 'AFNetworking', spec: '~> 3.2', count: 1};
+                        }
+                        return null;
+                    });
+                    api.addPlugin(my_plugin)
+                        .then(function () {
+                            expect(podsjson_mock.getLibrary.calls.count()).toEqual(3);
+                            compareListWithoutOrder(podsjson_mock.getLibrary.calls.allArgs(), [
+                                ['AFNetworking'],
+                                ['Eureka'],
+                                ['HogeLib']
+                            ]);
+                            expect(podsjson_mock.incrementLibrary).toHaveBeenCalledWith('AFNetworking');
+                            expect(podsjson_mock.setJsonLibrary.calls.count()).toEqual(2);
+                            compareListWithoutOrder(podsjson_mock.setJsonLibrary.calls.allArgs(), [
+                                ['Eureka', {name: 'Eureka', spec: '4.0', 'swift-version': '4.1', count: 1}],
+                                ['HogeLib', {name: 'HogeLib', git: 'https://github.com/hoge/HogewLib.git', branch: 'develop', count: 1}]
+                            ]);
+                            expect(podfile_mock.addSpec.calls.count()).toEqual(2);
+                            compareListWithoutOrder(podfile_mock.addSpec.calls.allArgs(), [
+                                ['Eureka', {name: 'Eureka', spec: '4.0', 'swift-version': '4.1', count: 1}],
+                                ['HogeLib', {name: 'HogeLib', git: 'https://github.com/hoge/HogewLib.git', branch: 'develop', count: 1}]
+                            ]);
+                        }).fail(function (err) {
+                            fail('unexpected addPlugin fail handler invoked :' + err);
+                        }).done(done);
+                });
             });
             describe('with frameworks of `podspec` type', function () {
                 var podsjson_mock;
@@ -210,7 +388,7 @@ describe('Platform Api', function () {
                     spec: 'podspec!'
                 };
                 beforeEach(function () {
-                    podsjson_mock = jasmine.createSpyObj('podsjson mock', ['get', 'increment', 'write', 'setJson']);
+                    podsjson_mock = jasmine.createSpyObj('podsjson mock', ['getLibrary', 'incrementLibrary', 'write', 'setJsonLibrary']);
                     podfile_mock = jasmine.createSpyObj('podfile mock', ['isDirty', 'addSpec', 'write', 'install']);
                     spyOn(my_plugin, 'getFrameworks').and.returnValue([my_pod_json]);
                     PodsJson_mod.PodsJson.and.callFake(function () {
@@ -222,7 +400,7 @@ describe('Platform Api', function () {
                 });
                 // TODO: a little help with clearly labeling / describing the tests below? :(
                 it('should warn if Pods JSON contains name/src but differs in spec', function (done) {
-                    podsjson_mock.get.and.returnValue({
+                    podsjson_mock.getLibrary.and.returnValue({
                         spec: 'something different from ' + my_pod_json.spec
                     });
                     spyOn(events, 'emit');
@@ -235,12 +413,12 @@ describe('Platform Api', function () {
                         }).done(done);
                 });
                 it('should increment Pods JSON file if pod name/src already exists in file', function (done) {
-                    podsjson_mock.get.and.returnValue({
+                    podsjson_mock.getLibrary.and.returnValue({
                         spec: my_pod_json.spec
                     });
                     api.addPlugin(my_plugin)
                         .then(function () {
-                            expect(podsjson_mock.increment).toHaveBeenCalledWith('podsource!');
+                            expect(podsjson_mock.incrementLibrary).toHaveBeenCalledWith('podsource!');
                         }).fail(function (err) {
                             fail('unexpected addPlugin fail handler invoked');
                             console.error(err);
@@ -249,7 +427,7 @@ describe('Platform Api', function () {
                 it('on a new framework/pod name/src/key, it should add a new json to podsjson and add a new spec to podfile', function (done) {
                     api.addPlugin(my_plugin)
                         .then(function () {
-                            expect(podsjson_mock.setJson).toHaveBeenCalledWith(my_pod_json.src, jasmine.any(Object));
+                            expect(podsjson_mock.setJsonLibrary).toHaveBeenCalledWith(my_pod_json.src, jasmine.any(Object));
                             expect(podfile_mock.addSpec).toHaveBeenCalledWith(my_pod_json.src, my_pod_json.spec);
                         }).fail(function (err) {
                             fail('unexpected addPlugin fail handler invoked');
@@ -258,6 +436,7 @@ describe('Platform Api', function () {
                 });
                 it('should write out podfile and install if podfile was changed', function (done) {
                     podfile_mock.isDirty.and.returnValue(true);
+                    podfile_mock.install.and.returnValue({then: function () { }});
                     api.addPlugin(my_plugin)
                         .then(function () {
                             expect(podfile_mock.write).toHaveBeenCalled();
@@ -269,15 +448,15 @@ describe('Platform Api', function () {
                 });
                 it('if two frameworks with the same name are added, should honour the spec of the first-installed plugin', function (done) {
                     spyOn(events, 'emit');
-                    podsjson_mock.get.and.returnValue({
+                    podsjson_mock.getLibrary.and.returnValue({
                         spec: 'something different from ' + my_pod_json.spec
                     });
                     api.addPlugin(my_plugin)
                         .then(function () {
                             // Increment will non-destructively set the spec to keep it as it was...
-                            expect(podsjson_mock.increment).toHaveBeenCalledWith(my_pod_json.src);
+                            expect(podsjson_mock.incrementLibrary).toHaveBeenCalledWith(my_pod_json.src);
                             // ...whereas setJson would overwrite it completely.
-                            expect(podsjson_mock.setJson).not.toHaveBeenCalled();
+                            expect(podsjson_mock.setJsonLibrary).not.toHaveBeenCalled();
                         }).fail(function (err) {
                             fail('unexpected addPlugin fail handler invoked');
                             console.error(err);
@@ -285,5 +464,253 @@ describe('Platform Api', function () {
                 });
             });
         });
+        describe('removePlugin', function () {
+            var my_plugin = {
+                getHeaderFiles: function () { return []; },
+                getFrameworks: function () {},
+                getPodSpecs: function () { return []; }
+            };
+            beforeEach(function () {
+                spyOn(PluginManager, 'get').and.returnValue({
+                    removePlugin: function () { return Q(); }
+                });
+                spyOn(Podfile_mod, 'Podfile');
+                spyOn(PodsJson_mod, 'PodsJson');
+            });
+            describe('removing pods since the plugin contained <podspecs>', function () {
+                var podsjson_mock;
+                var podfile_mock;
+                var my_pod_json = {
+                    declarations: {
+                        'use-frameworks': 'true',
+                        'inhibit_all_warnings!': 'true'
+                    },
+                    sources: {
+                        'https://github.com/sample/SampleSpecs.git': { source: 'https://github.com/sample/SampleSpecs.git' },
+                        'https://github.com/CocoaPods/Specs.git': { source: 'https://github.com/CocoaPods/Specs.git' }
+                    },
+                    libraries: {
+                        'AFNetworking': {
+                            name: 'AFNetworking',
+                            spec: '~> 3.2'
+                        },
+                        'Eureka': {
+                            name: 'Eureka',
+                            spec: '4.0',
+                            'swift-version': '4.1'
+                        },
+                        'HogeLib': {
+                            name: 'HogeLib',
+                            git: 'https://github.com/hoge/HogewLib.git',
+                            branch: 'develop'
+                        }
+                    }
+                };
+                beforeEach(function () {
+                    podsjson_mock = jasmine.createSpyObj('podsjson mock', ['getLibrary', 'getSource', 'getDeclaration',
+                        'decrementLibrary', 'decrementSource', 'decrementDeclaration', 'write',
+                        'setJsonLibrary', 'setJsonSource', 'setJsonDeclaration']);
+                    podfile_mock = jasmine.createSpyObj('podfile mock', ['isDirty', 'removeSpec', 'removeSource', 'removeDeclaration', 'write', 'install']);
+                    spyOn(my_plugin, 'getFrameworks').and.returnValue([]);
+                    spyOn(my_plugin, 'getPodSpecs').and.returnValue([my_pod_json]);
+                    PodsJson_mod.PodsJson.and.callFake(function () {
+                        return podsjson_mock;
+                    });
+                    Podfile_mod.Podfile.and.callFake(function () {
+                        return podfile_mock;
+                    });
+                });
+                it('on a last declaration, it should remove a json from declarations', function (done) {
+                    var json1 = {declaration: 'use_frameworks!', count: 1};
+                    var json2 = {declaration: 'inhibit_all_warnings!', count: 1};
+                    podsjson_mock.getDeclaration.and.callFake(function (declaration) {
+                        if (declaration === 'use_frameworks!') {
+                            return json1;
+                        } else if (declaration === 'inhibit_all_warnings!') {
+                            return json2;
+                        }
+                        return null;
+                    });
+                    podsjson_mock.decrementDeclaration.and.callFake(function (declaration) {
+                        if (declaration === 'use_frameworks!') {
+                            json1.count--;
+                        } else if (declaration === 'inhibit_all_warnings!') {
+                            json2.count--;
+                        }
+                    });
+                    api.removePlugin(my_plugin)
+                        .then(function () {
+                            expect(podsjson_mock.getDeclaration.calls.count()).toEqual(2);
+                            compareListWithoutOrder(podsjson_mock.getDeclaration.calls.allArgs(), [['use_frameworks!'], ['inhibit_all_warnings!']]);
+                            expect(podsjson_mock.decrementDeclaration.calls.count()).toEqual(2);
+                            compareListWithoutOrder(podsjson_mock.decrementDeclaration.calls.allArgs(), [['use_frameworks!'], ['inhibit_all_warnings!']]);
+                            expect(podfile_mock.removeDeclaration.calls.count()).toEqual(2);
+                            compareListWithoutOrder(podfile_mock.removeDeclaration.calls.allArgs(), [['use_frameworks!'], ['inhibit_all_warnings!']]);
+                        }).fail(function (err) {
+                            fail('unexpected removePlugin fail handler invoked :' + err);
+                        }).done(done);
+                });
+                it('should decrement count in declarations and does not remove if count > 1', function (done) {
+                    var json1 = {declaration: 'use_frameworks!', count: 2};
+                    var json2 = {declaration: 'inhibit_all_warnings!', count: 1};
+                    podsjson_mock.getDeclaration.and.callFake(function (declaration) {
+                        if (declaration === 'use_frameworks!') {
+                            return json1;
+                        } else if (declaration === 'inhibit_all_warnings!') {
+                            return json2;
+                        }
+                        return null;
+                    });
+                    podsjson_mock.decrementDeclaration.and.callFake(function (declaration) {
+                        if (declaration === 'use_frameworks!') {
+                            json1.count--;
+                        } else if (declaration === 'inhibit_all_warnings!') {
+                            json2.count--;
+                        }
+                    });
+                    api.removePlugin(my_plugin)
+                        .then(function () {
+                            expect(podsjson_mock.getDeclaration.calls.count()).toEqual(2);
+                            compareListWithoutOrder(podsjson_mock.getDeclaration.calls.allArgs(), [['use_frameworks!'], ['inhibit_all_warnings!']]);
+                            expect(podsjson_mock.decrementDeclaration.calls.count()).toEqual(2);
+                            compareListWithoutOrder(podsjson_mock.decrementDeclaration.calls.allArgs(), [['use_frameworks!'], ['inhibit_all_warnings!']]);
+                            expect(podfile_mock.removeDeclaration.calls.count()).toEqual(1);
+                            compareListWithoutOrder(podfile_mock.removeDeclaration.calls.allArgs(), [['inhibit_all_warnings!']]);
+                        }).fail(function (err) {
+                            fail('unexpected removePlugin fail handler invoked :' + err);
+                        }).done(done);
+                });
+                it('on a last source, it should remove a json from sources', function (done) {
+                    var json1 = { source: 'https://github.com/sample/SampleSpecs.git', count: 1 };
+                    var json2 = { source: 'https://github.com/CocoaPods/Specs.git', count: 1 };
+                    podsjson_mock.getSource.and.callFake(function (source) {
+                        if (source === 'https://github.com/sample/SampleSpecs.git') {
+                            return json1;
+                        } else if (source === 'https://github.com/CocoaPods/Specs.git') {
+                            return json2;
+                        }
+                        return null;
+                    });
+                    podsjson_mock.decrementSource.and.callFake(function (source) {
+                        if (source === 'https://github.com/sample/SampleSpecs.git') {
+                            json1.count--;
+                        } else if (source === 'https://github.com/CocoaPods/Specs.git') {
+                            json2.count--;
+                        }
+                    });
+                    api.removePlugin(my_plugin)
+                        .then(function () {
+                            expect(podsjson_mock.getSource.calls.count()).toEqual(2);
+                            compareListWithoutOrder(podsjson_mock.getSource.calls.allArgs(), [['https://github.com/sample/SampleSpecs.git'], ['https://github.com/CocoaPods/Specs.git']]);
+                            expect(podsjson_mock.decrementSource.calls.count()).toEqual(2);
+                            compareListWithoutOrder(podsjson_mock.decrementSource.calls.allArgs(), [['https://github.com/sample/SampleSpecs.git'], ['https://github.com/CocoaPods/Specs.git']]);
+                            expect(podfile_mock.removeSource.calls.count()).toEqual(2);
+                            compareListWithoutOrder(podfile_mock.removeSource.calls.allArgs(), [['https://github.com/sample/SampleSpecs.git'], ['https://github.com/CocoaPods/Specs.git']]);
+                        }).fail(function (err) {
+                            fail('unexpected removePlugin fail handler invoked :' + err);
+                        }).done(done);
+                });
+                it('should decrement count in sources and does not remove if count > 1', function (done) {
+                    var json1 = { source: 'https://github.com/sample/SampleSpecs.git', count: 2 };
+                    var json2 = { source: 'https://github.com/CocoaPods/Specs.git', count: 1 };
+                    podsjson_mock.getSource.and.callFake(function (source) {
+                        if (source === 'https://github.com/sample/SampleSpecs.git') {
+                            return json1;
+                        } else if (source === 'https://github.com/CocoaPods/Specs.git') {
+                            return json2;
+                        }
+                        return null;
+                    });
+                    podsjson_mock.decrementSource.and.callFake(function (source) {
+                        if (source === 'https://github.com/sample/SampleSpecs.git') {
+                            json1.count--;
+                        } else if (source === 'https://github.com/CocoaPods/Specs.git') {
+                            json2.count--;
+                        }
+                    });
+                    api.removePlugin(my_plugin)
+                        .then(function () {
+                            expect(podsjson_mock.getSource.calls.count()).toEqual(2);
+                            compareListWithoutOrder(podsjson_mock.getSource.calls.allArgs(), [['https://github.com/sample/SampleSpecs.git'], ['https://github.com/CocoaPods/Specs.git']]);
+                            expect(podsjson_mock.decrementSource.calls.count()).toEqual(2);
+                            compareListWithoutOrder(podsjson_mock.decrementSource.calls.allArgs(), [['https://github.com/sample/SampleSpecs.git'], ['https://github.com/CocoaPods/Specs.git']]);
+                            expect(podfile_mock.removeSource.calls.count()).toEqual(1);
+                            compareListWithoutOrder(podfile_mock.removeSource.calls.allArgs(), [['https://github.com/CocoaPods/Specs.git']]);
+                        }).fail(function (err) {
+                            fail('unexpected removePlugin fail handler invoked :' + err);
+                        }).done(done);
+                });
+                it('on a last library, it should remove a json from libraries', function (done) {
+                    var json1 = Object.assign({}, my_pod_json.libraries['AFNetworking'], { count: 1 });
+                    var json2 = Object.assign({}, my_pod_json.libraries['Eureka'], { count: 1 });
+                    var json3 = Object.assign({}, my_pod_json.libraries['HogeLib'], { count: 1 });
+                    podsjson_mock.getLibrary.and.callFake(function (name) {
+                        if (name === json1.name) {
+                            return json1;
+                        } else if (name === json2.name) {
+                            return json2;
+                        } else if (name === json3.name) {
+                            return json3;
+                        }
+                        return null;
+                    });
+                    podsjson_mock.decrementLibrary.and.callFake(function (name) {
+                        if (name === json1.name) {
+                            json1.count--;
+                        } else if (name === json2.name) {
+                            json2.count--;
+                        } else if (name === json3.name) {
+                            json3.count--;
+                        }
+                    });
+                    api.removePlugin(my_plugin)
+                        .then(function () {
+                            expect(podsjson_mock.getLibrary.calls.count()).toEqual(3);
+                            compareListWithoutOrder(podsjson_mock.getLibrary.calls.allArgs(), [[json1.name], [json2.name], [json3.name]]);
+                            expect(podsjson_mock.decrementLibrary.calls.count()).toEqual(3);
+                            compareListWithoutOrder(podsjson_mock.decrementLibrary.calls.allArgs(), [[json1.name], [json2.name], [json3.name]]);
+                            expect(podfile_mock.removeSpec.calls.count()).toEqual(3);
+                            compareListWithoutOrder(podfile_mock.removeSpec.calls.allArgs(), [[json1.name], [json2.name], [json3.name]]);
+                        }).fail(function (err) {
+                            fail('unexpected removePlugin fail handler invoked :' + err);
+                        }).done(done);
+                });
+                it('should decrement count in libraries and does not remove if count > 1', function (done) {
+                    var json1 = Object.assign({}, my_pod_json.libraries['AFNetworking'], { count: 2 });
+                    var json2 = Object.assign({}, my_pod_json.libraries['Eureka'], { count: 1 });
+                    var json3 = Object.assign({}, my_pod_json.libraries['HogeLib'], { count: 1 });
+                    podsjson_mock.getLibrary.and.callFake(function (name) {
+                        if (name === json1.name) {
+                            return json1;
+                        } else if (name === json2.name) {
+                            return json2;
+                        } else if (name === json3.name) {
+                            return json3;
+                        }
+                        return null;
+                    });
+                    podsjson_mock.decrementLibrary.and.callFake(function (name) {
+                        if (name === json1.name) {
+                            json1.count--;
+                        } else if (name === json2.name) {
+                            json2.count--;
+                        } else if (name === json3.name) {
+                            json3.count--;
+                        }
+                    });
+                    api.removePlugin(my_plugin)
+                        .then(function () {
+                            expect(podsjson_mock.getLibrary.calls.count()).toEqual(3);
+                            compareListWithoutOrder(podsjson_mock.getLibrary.calls.allArgs(), [[json1.name], [json2.name], [json3.name]]);
+                            expect(podsjson_mock.decrementLibrary.calls.count()).toEqual(3);
+                            compareListWithoutOrder(podsjson_mock.decrementLibrary.calls.allArgs(), [[json1.name], [json2.name], [json3.name]]);
+                            expect(podfile_mock.removeSpec.calls.count()).toEqual(2);
+                            compareListWithoutOrder(podfile_mock.removeSpec.calls.allArgs(), [[json2.name], [json3.name]]);
+                        }).fail(function (err) {
+                            fail('unexpected removePlugin fail handler invoked :' + err);
+                        }).done(done);
+                });
+            });
+        });
     });
 });
diff --git a/tests/spec/unit/Podfile.spec.js b/tests/spec/unit/Podfile.spec.js
index 2d6e97c65..080d1110a 100644
--- a/tests/spec/unit/Podfile.spec.js
+++ b/tests/spec/unit/Podfile.spec.js
@@ -100,6 +100,8 @@ describe('unit tests for Podfile module', function () {
             podfile.addSpec('Foo-Baz', '4.0');
             podfile.addSpec('Foo~Baz@!%@!%!', '5.0');
             podfile.addSpec('Bla', ':configurations => [\'Debug\', \'Beta\']');
+            podfile.addSpec('Bla2', {'configurations': 'Debug,Release'});
+            podfile.addSpec('Bla3', {'configurations': 'Debug, Release'});
 
             podfile.write();
 
@@ -112,12 +114,14 @@ describe('unit tests for Podfile module', function () {
             expect(newPodfile.existsSpec('Foo~Baz@!%@!%!')).toBe(true);
             expect(newPodfile.existsSpec('Bla')).toBe(true);
 
-            expect(newPodfile.getSpec('Foo')).toBe(podfile.getSpec('Foo'));
-            expect(newPodfile.getSpec('Bar')).toBe(podfile.getSpec('Bar'));
-            expect(newPodfile.getSpec('Baz')).toBe(podfile.getSpec('Baz'));
-            expect(newPodfile.getSpec('Foo-Baz')).toBe(podfile.getSpec('Foo-Baz'));
-            expect(newPodfile.getSpec('Foo~Baz@!%@!%!')).toBe(podfile.getSpec('Foo~Baz@!%@!%!'));
-            expect(newPodfile.getSpec('Bla')).toBe(podfile.getSpec('Bla'));
+            expect(newPodfile.getSpec('Foo')).toEqual(podfile.getSpec('Foo'));
+            expect(newPodfile.getSpec('Bar')).toEqual(podfile.getSpec('Bar'));
+            expect(newPodfile.getSpec('Baz')).toEqual(podfile.getSpec('Baz'));
+            expect(newPodfile.getSpec('Foo-Baz')).toEqual(podfile.getSpec('Foo-Baz'));
+            expect(newPodfile.getSpec('Foo~Baz@!%@!%!')).toEqual(podfile.getSpec('Foo~Baz@!%@!%!'));
+            expect(newPodfile.getSpec('Bla')).toEqual(podfile.getSpec('Bla'));
+            expect(newPodfile.getSpec('Bla2').options).toEqual(':configurations => [\'Debug\',\'Release\']');
+            expect(newPodfile.getSpec('Bla3').options).toEqual(':configurations => [\'Debug\',\'Release\']');
         });
 
         it('Test 009 : runs before_install to install xcconfig paths', function () {
diff --git a/tests/spec/unit/PodsJson.spec.js b/tests/spec/unit/PodsJson.spec.js
index 254332327..5667a41b0 100644
--- a/tests/spec/unit/PodsJson.spec.js
+++ b/tests/spec/unit/PodsJson.spec.js
@@ -17,6 +17,7 @@
        under the License.
 */
 
+var fs = require('fs');
 var path = require('path');
 var util = require('util');
 var CordovaError = require('cordova-common').CordovaError;
@@ -26,7 +27,13 @@ var fixturePodsJson = path.resolve(__dirname, 'fixtures', 'testProj', 'platforms
 
 // tests are nested in a describe to ensure clean up happens after all unit tests are run
 describe('unit tests for Podfile module', function () {
-    var podsjson = new PodsJson(fixturePodsJson);
+    var podsjson = null;
+    beforeEach(function () {
+        podsjson = new PodsJson(fixturePodsJson);
+    });
+    afterEach(function () {
+        podsjson.destroy();
+    });
 
     describe('tests', function () {
 
@@ -37,15 +44,15 @@ describe('unit tests for Podfile module', function () {
             }).toThrow(new CordovaError(util.format('PodsJson: The file at %s is not `%s`.', dummyPath, PodsJson.FILENAME)));
         });
 
-        it('Test 002 : sets and gets pod test', function () {
+        it('Test 002 : setsJson and gets pod test', function () {
             var val0 = {
                 name: 'Foo',
                 type: 'podspec',
                 spec: '1.0',
                 count: 1
             };
-            podsjson.set(val0.name, val0.type, val0.spec, val0.count);
-            var val1 = podsjson.get(val0.name);
+            podsjson.setJsonLibrary(val0.name, val0);
+            var val1 = podsjson.getLibrary(val0.name);
 
             expect(val1).toBeTruthy();
             expect(val1.name).toEqual(val0.name);
@@ -61,8 +68,8 @@ describe('unit tests for Podfile module', function () {
                 spec: '2.0',
                 count: 2
             };
-            podsjson.setJson(val0.name, val0);
-            var val1 = podsjson.get(val0.name);
+            podsjson.setJsonLibrary(val0.name, val0);
+            var val1 = podsjson.getLibrary(val0.name);
 
             expect(val1).toBeTruthy();
             expect(val1.name).toEqual(val0.name);
@@ -70,8 +77,8 @@ describe('unit tests for Podfile module', function () {
             expect(val1.spec).toEqual(val0.spec);
             expect(val1.count).toEqual(val0.count);
 
-            podsjson.remove(val0.name);
-            val1 = podsjson.get(val0.name);
+            podsjson.removeLibrary(val0.name);
+            val1 = podsjson.getLibrary(val0.name);
             expect(val1).toBeFalsy();
         });
 
@@ -82,12 +89,12 @@ describe('unit tests for Podfile module', function () {
                 spec: '3.0',
                 count: 3
             };
-            podsjson.setJson(val0.name, val0);
+            podsjson.setJsonLibrary(val0.name, val0);
             podsjson.clear();
 
-            expect(podsjson.get(val0.name)).toBeFalsy();
-            expect(podsjson.get('Foo')).toBeFalsy();
-            expect(podsjson.get('Bar')).toBeFalsy();
+            expect(podsjson.getLibrary(val0.name)).toBeFalsy();
+            expect(podsjson.getLibrary('Foo')).toBeFalsy();
+            expect(podsjson.getLibrary('Bar')).toBeFalsy();
         });
 
         it('Test 005 : isDirty tests', function () {
@@ -98,13 +105,13 @@ describe('unit tests for Podfile module', function () {
                 count: 1
             };
 
-            podsjson.setJson(val0.name, val0);
+            podsjson.setJsonLibrary(val0.name, val0);
             expect(podsjson.isDirty()).toBe(true);
 
             podsjson.write();
             expect(podsjson.isDirty()).toBe(false);
 
-            podsjson.remove(val0.name);
+            podsjson.removeLibrary(val0.name);
             expect(podsjson.isDirty()).toBe(true);
 
             podsjson.clear();
@@ -122,24 +129,24 @@ describe('unit tests for Podfile module', function () {
                 count: 4
             };
 
-            podsjson.setJson(val0.name, val0);
-            expect(podsjson.get(val0.name).count).toBe(4);
+            podsjson.setJsonLibrary(val0.name, val0);
+            expect(podsjson.getLibrary(val0.name).count).toBe(4);
 
-            podsjson.increment(val0.name);
-            expect(podsjson.get(val0.name).count).toBe(5);
+            podsjson.incrementLibrary(val0.name);
+            expect(podsjson.getLibrary(val0.name).count).toBe(5);
 
-            podsjson.decrement(val0.name);
-            expect(podsjson.get(val0.name).count).toBe(4);
-            podsjson.decrement(val0.name);
-            expect(podsjson.get(val0.name).count).toBe(3);
-            podsjson.decrement(val0.name);
-            expect(podsjson.get(val0.name).count).toBe(2);
-            podsjson.decrement(val0.name);
-            expect(podsjson.get(val0.name).count).toBe(1);
+            podsjson.decrementLibrary(val0.name);
+            expect(podsjson.getLibrary(val0.name).count).toBe(4);
+            podsjson.decrementLibrary(val0.name);
+            expect(podsjson.getLibrary(val0.name).count).toBe(3);
+            podsjson.decrementLibrary(val0.name);
+            expect(podsjson.getLibrary(val0.name).count).toBe(2);
+            podsjson.decrementLibrary(val0.name);
+            expect(podsjson.getLibrary(val0.name).count).toBe(1);
 
             // this next decrement takes it down to zero, where the pod will just be removed
-            podsjson.decrement(val0.name);
-            expect(podsjson.get(val0.name)).toBeFalsy();
+            podsjson.decrementLibrary(val0.name);
+            expect(podsjson.getLibrary(val0.name)).toBeFalsy();
         });
 
         it('Test 007 : writes pods to the pods.json', function () {
@@ -151,17 +158,17 @@ describe('unit tests for Podfile module', function () {
                 'Baz': { name: 'Baz', type: 'podspec', spec: '3.0', count: 3 }
             };
 
-            podsjson.setJson('Foo', vals.Foo);
-            podsjson.setJson('Bar', vals.Bar);
-            podsjson.setJson('Baz', vals.Baz);
+            podsjson.setJsonLibrary('Foo', vals.Foo);
+            podsjson.setJsonLibrary('Bar', vals.Bar);
+            podsjson.setJsonLibrary('Baz', vals.Baz);
 
             podsjson.write();
 
             // verify by reading it back in a new PodsJson
             var newPodsJson = new PodsJson(fixturePodsJson);
-            expect(newPodsJson.get('Foo')).toBeTruthy();
-            expect(newPodsJson.get('Bar')).toBeTruthy();
-            expect(newPodsJson.get('Baz')).toBeTruthy();
+            expect(newPodsJson.getLibrary('Foo')).toBeTruthy();
+            expect(newPodsJson.getLibrary('Bar')).toBeTruthy();
+            expect(newPodsJson.getLibrary('Baz')).toBeTruthy();
 
             function podEqual (a, b) {
                 return (
@@ -172,14 +179,76 @@ describe('unit tests for Podfile module', function () {
                 );
             }
 
-            expect(podEqual(podsjson.get('Foo'), newPodsJson.get('Foo'))).toBe(true);
-            expect(podEqual(podsjson.get('Bar'), newPodsJson.get('Bar'))).toBe(true);
-            expect(podEqual(podsjson.get('Baz'), newPodsJson.get('Baz'))).toBe(true);
+            expect(podEqual(podsjson.getLibrary('Foo'), newPodsJson.getLibrary('Foo'))).toBe(true);
+            expect(podEqual(podsjson.getLibrary('Bar'), newPodsJson.getLibrary('Bar'))).toBe(true);
+            expect(podEqual(podsjson.getLibrary('Baz'), newPodsJson.getLibrary('Baz'))).toBe(true);
         });
 
-    });
+        it('Test 008 : setJson, get, increment, decrement, remove and write for Declaration', function () {
+            var result = null;
+            var writeFileSyncSpy = spyOn(fs, 'writeFileSync');
+            writeFileSyncSpy.and.callFake(function (filepath, data, encode) {
+                result = data;
+            });
+            var json = {
+                declaration: 'use_frameworks!',
+                count: 1
+            };
+            var json2 = {
+                declaration: 'inhibit_all_warnings!',
+                count: 2
+            };
+            podsjson.setJsonDeclaration(json.declaration, json);
+            expect(podsjson.getDeclaration(json.declaration)).not.toBe(json);
+            expect(podsjson.getDeclaration(json.declaration)).toEqual(json);
+            podsjson.incrementDeclaration(json.declaration);
+            expect(podsjson.getDeclaration(json.declaration).count).toEqual(2);
+            podsjson.decrementDeclaration(json.declaration);
+            expect(podsjson.getDeclaration(json.declaration).count).toEqual(1);
+            podsjson.setJsonDeclaration(json2.declaration, json2);
+            expect(podsjson.getDeclaration(json.declaration)).toEqual(json);
+            expect(podsjson.getDeclaration(json2.declaration)).toEqual(json2);
+            podsjson.removeDeclaration(json.declaration);
+            expect(podsjson.getDeclaration(json.declaration)).toBeUndefined();
+            podsjson.write();
+            expect(writeFileSyncSpy).toHaveBeenCalled();
+            expect(JSON.parse(result).declarations[json2.declaration]).toEqual(json2);
+        });
+
+        it('Test 009 : setJson, get, increment, decrement, remove and write for Source', function () {
+            var result = null;
+            var writeFileSyncSpy = spyOn(fs, 'writeFileSync');
+            writeFileSyncSpy.and.callFake(function (filepath, data, encode) {
+                result = data;
+            });
+            var json = {
+                source: 'https://github.com/brightcove/BrightcoveSpecs.git',
+                count: 1
+            };
+            var json2 = {
+                source: 'https://github.com/CocoaPods/Specs.git',
+                count: 2
+            };
+            podsjson.setJsonSource(json.source, json);
+            expect(podsjson.getSource(json.source)).not.toBe(json);
+            expect(podsjson.getSource(json.source)).toEqual(json);
+            podsjson.incrementSource(json.source);
+            expect(podsjson.getSource(json.source).count).toEqual(2);
+            podsjson.decrementSource(json.source);
+            expect(podsjson.getSource(json.source).count).toEqual(1);
+            podsjson.setJsonSource(json2.source, json2);
+            expect(podsjson.getSource(json.source)).toEqual(json);
+            expect(podsjson.getSource(json2.source)).toEqual(json2);
+            podsjson.removeSource(json.source);
+            expect(podsjson.getSource(json.source)).toBeUndefined();
+            podsjson.write();
+            expect(writeFileSyncSpy).toHaveBeenCalled();
+            expect(JSON.parse(result).sources[json2.source]).toEqual(json2);
+        });
 
-    it('Test 008 : tear down', function () {
-        podsjson.destroy();
     });
+
+    // it('Test 008 : tear down', function () {
+    //     podsjson.destroy();
+    // });
 });
diff --git a/tests/spec/unit/prepare.spec.js b/tests/spec/unit/prepare.spec.js
index 033f05034..2e7434c70 100644
--- a/tests/spec/unit/prepare.spec.js
+++ b/tests/spec/unit/prepare.spec.js
@@ -18,7 +18,9 @@
  */
 
 'use strict';
-var fs = require('fs-extra');
+var fs = require('fs');
+var fse = require('fs-extra');
+
 var os = require('os');
 var path = require('path');
 var shell = require('shelljs');
@@ -769,7 +771,7 @@ describe('prepare', function () {
 
         it('<access> - should handle wildcard, with NSAllowsArbitraryLoadsInWebContent', function (done) {
 
-            var readFile = spyOn(fs, 'readFileSync');
+            var readFile = spyOn(fse, 'readFileSync');
             var configXml = '<?xml version="1.0" encoding="UTF-8"?><widget id="io.cordova.hellocordova" ios-CFBundleIdentifier="io.cordova.hellocordova.ios" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0"><name>SampleApp</name>' +
             '<access origin="*" allows-arbitrary-loads-in-web-content="true" />' +
             '</widget>';
@@ -789,7 +791,7 @@ describe('prepare', function () {
 
         it('<access> - should handle wildcard, with NSAllowsArbitraryLoadsForMedia set (fixed allows-arbitrary-loads-for-media)', function (done) {
 
-            var readFile = spyOn(fs, 'readFileSync');
+            var readFile = spyOn(fse, 'readFileSync');
             var configXml = '<?xml version="1.0" encoding="UTF-8"?><widget id="io.cordova.hellocordova" ios-CFBundleIdentifier="io.cordova.hellocordova.ios" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0"><name>SampleApp</name>' +
             '<access origin="*" allows-arbitrary-loads-for-media="true" />' +
             '</widget>';
@@ -808,7 +810,7 @@ describe('prepare', function () {
 
         it('<access> - should handle wildcard, with NSAllowsArbitraryLoadsForMedia not set (fixed allows-arbitrary-loads-for-media)', function (done) {
 
-            var readFile = spyOn(fs, 'readFileSync');
+            var readFile = spyOn(fse, 'readFileSync');
             var configXml = '<?xml version="1.0" encoding="UTF-8"?><widget id="io.cordova.hellocordova" ios-CFBundleIdentifier="io.cordova.hellocordova.ios" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0"><name>SampleApp</name>' +
             '<access origin="*" allows-arbitrary-loads-for-media="false" />' +
             '</widget>';
@@ -827,7 +829,7 @@ describe('prepare', function () {
 
         it('<access> - should handle wildcard, with NSAllowsArbitraryLoadsForMedia set (deprecated allows-arbitrary-loads-in-media)', function (done) {
 
-            var readFile = spyOn(fs, 'readFileSync');
+            var readFile = spyOn(fse, 'readFileSync');
             var configXml = '<?xml version="1.0" encoding="UTF-8"?><widget id="io.cordova.hellocordova" ios-CFBundleIdentifier="io.cordova.hellocordova.ios" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0"><name>SampleApp</name>' +
             '<access origin="*" allows-arbitrary-loads-in-media="true" />' +
             '</widget>';
@@ -846,7 +848,7 @@ describe('prepare', function () {
 
         it('<access> - should handle wildcard, with NSAllowsArbitraryLoadsForMedia not set (deprecated allows-arbitrary-loads-in-media)', function (done) {
 
-            var readFile = spyOn(fs, 'readFileSync');
+            var readFile = spyOn(fse, 'readFileSync');
             var configXml = '<?xml version="1.0" encoding="UTF-8"?><widget id="io.cordova.hellocordova" ios-CFBundleIdentifier="io.cordova.hellocordova.ios" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0"><name>SampleApp</name>' +
             '<access origin="*" allows-arbitrary-loads-in-media="false" />' +
             '</widget>';
@@ -865,7 +867,7 @@ describe('prepare', function () {
 
         it('<access> - should handle wildcard, with NSAllowsLocalNetworking', function (done) {
 
-            var readFile = spyOn(fs, 'readFileSync');
+            var readFile = spyOn(fse, 'readFileSync');
             var configXml = '<?xml version="1.0" encoding="UTF-8"?><widget id="io.cordova.hellocordova" ios-CFBundleIdentifier="io.cordova.hellocordova.ios" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0"><name>SampleApp</name>' +
             '<access origin="*" allows-local-networking="true" />' +
             '</widget>';
@@ -885,7 +887,7 @@ describe('prepare', function () {
 
         it('<access> - should handle wildcard, with NSAllowsArbitraryLoadsInWebContent, NSAllowsArbitraryLoadsForMedia, NSAllowsLocalNetworking', function (done) {
 
-            var readFile = spyOn(fs, 'readFileSync');
+            var readFile = spyOn(fse, 'readFileSync');
             var configXml = '<?xml version="1.0" encoding="UTF-8"?><widget id="io.cordova.hellocordova" ios-CFBundleIdentifier="io.cordova.hellocordova.ios" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0"><name>SampleApp</name>' +
             '<access origin="*" allows-arbitrary-loads-in-web-content="true" allows-arbitrary-loads-in-media="true" allows-local-networking="true" />' +
             '</widget>';
@@ -904,7 +906,7 @@ describe('prepare', function () {
         });
         it('<access> - sanity check - no wildcard but has NSAllowsArbitraryLoadsInWebContent, NSAllowsArbitraryLoadsForMedia, NSAllowsLocalNetworking', function (done) {
 
-            var readFile = spyOn(fs, 'readFileSync');
+            var readFile = spyOn(fse, 'readFileSync');
             var configXml = '<?xml version="1.0" encoding="UTF-8"?><widget id="io.cordova.hellocordova" ios-CFBundleIdentifier="io.cordova.hellocordova.ios" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0"><name>SampleApp</name>' +
             '<access origin="http://cordova.apache.org" allows-arbitrary-loads-in-web-content="true" allows-arbitrary-loads-in-media="true" allows-local-networking="true" />' +
             '</widget>';
@@ -1174,7 +1176,7 @@ describe('prepare', function () {
 
         it('<allow-navigation> - should handle wildcard', function (done) {
 
-            var readFile = spyOn(fs, 'readFileSync');
+            var readFile = spyOn(fse, 'readFileSync');
             var configXml = '<?xml version="1.0" encoding="UTF-8"?><widget id="io.cordova.hellocordova" ios-CFBundleIdentifier="io.cordova.hellocordova.ios" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0"><name>SampleApp</name>' +
             '<allow-navigation href="*" />' +
             '</widget>';
@@ -1194,7 +1196,7 @@ describe('prepare', function () {
 
         it('<allow-navigation> - sanity check - no wildcard but has NSAllowsArbitraryLoadsInWebContent, NSAllowsArbitraryLoadsForMedia, NSAllowsLocalNetworking', function (done) {
 
-            var readFile = spyOn(fs, 'readFileSync');
+            var readFile = spyOn(fse, 'readFileSync');
             var configXml = '<?xml version="1.0" encoding="UTF-8"?><widget id="io.cordova.hellocordova" ios-CFBundleIdentifier="io.cordova.hellocordova.ios" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0"><name>SampleApp</name>' +
             '<allow-navigation href="http://cordova.apache.org" allows-arbitrary-loads-in-web-content="true" allows-arbitrary-loads-in-media="true" allows-local-networking="true" />' +
             '</widget>';


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org