Skip to content

Commit 1e8d3f9

Browse files
committed
Added simple-plist package to parse plists
Added findTargetKey utility method Renamed pbxVersionGroupSection to xcVersionGroupSection Implemented addToXcVersionGroupSection Implemented addDataModelDocument Added dataModelDocument test Added 2 data model documents for tests (single and multiple models)
1 parent 718d6a6 commit 1e8d3f9

File tree

7 files changed

+277
-3
lines changed

7 files changed

+277
-3
lines changed

lib/pbxProject.js

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ var util = require('util'),
88
pbxFile = require('./pbxFile'),
99
fs = require('fs'),
1010
parser = require('./parser/pbxproj'),
11+
plist = require('simple-plist'),
1112
COMMENT_KEY = /_comment$/
1213

1314
function pbxProject(filename) {
@@ -507,6 +508,26 @@ pbxProject.prototype.removeFromPbxFileReferenceSection = function(file) {
507508
return file;
508509
}
509510

511+
pbxProject.prototype.addToXcVersionGroupSection = function(file) {
512+
if (!file.models || !file.currentModel) {
513+
throw new Error("Cannot create a XCVersionGroup section from not a data model document file");
514+
}
515+
516+
var commentKey = f("%s_comment", file.fileRef);
517+
518+
if (!this.xcVersionGroupSection()[file.fileRef]) {
519+
this.xcVersionGroupSection()[file.fileRef] = {
520+
isa: 'XCVersionGroup',
521+
children: file.models.map(function (el) { return el.fileRef; }),
522+
currentVersion: file.currentModel.fileRef,
523+
path: file.path,
524+
sourceTree: '"<group>"',
525+
versionGroupType: 'wrapper.xcdatamodel'
526+
};
527+
this.xcVersionGroupSection()[commentKey] = path.basename(file.path);
528+
}
529+
}
530+
510531
pbxProject.prototype.addToPluginsPbxGroup = function(file) {
511532
var pluginsGroup = this.pbxGroupByName('Plugins');
512533
pluginsGroup.children.push(pbxGroupChild(file));
@@ -805,7 +826,7 @@ pbxProject.prototype.pbxNativeTargetSection = function() {
805826
return this.hash.project.objects['PBXNativeTarget'];
806827
}
807828

808-
pbxProject.prototype.pbxVersionGroupSection = function () {
829+
pbxProject.prototype.xcVersionGroupSection = function () {
809830
if (typeof this.hash.project.objects['XCVersionGroup'] !== 'object') {
810831
this.hash.project.objects['XCVersionGroup'] = {};
811832
}
@@ -838,6 +859,22 @@ pbxProject.prototype.pbxTargetByName = function(name) {
838859
return this.pbxItemByComment(name, 'PBXNativeTarget');
839860
}
840861

862+
pbxProject.prototype.findTargetKey = function(name) {
863+
var targets = this.hash.project.objects['PBXNativeTarget'];
864+
865+
for (var key in targets) {
866+
// only look for comments
867+
if (COMMENT_KEY.test(key)) continue;
868+
869+
var target = targets[key];
870+
if (target.name === name) {
871+
return key;
872+
}
873+
}
874+
875+
return null;
876+
}
877+
841878
pbxProject.prototype.pbxItemByComment = function(name, pbxSectionName) {
842879
var section = this.hash.project.objects[pbxSectionName],
843880
key, itemKey;
@@ -1712,5 +1749,61 @@ pbxProject.prototype.getBuildConfigByName = function(name) {
17121749
return target;
17131750
}
17141751

1752+
pbxProject.prototype.addDataModelDocument = function(filePath, group, opt) {
1753+
if (!group) {
1754+
group = 'Resources';
1755+
}
1756+
if (!this.getPBXGroupByKey(group)) {
1757+
group = this.findPBXGroupKey({ name: group });
1758+
}
1759+
1760+
var file = new pbxFile(filePath, opt);
1761+
1762+
if (!file || this.hasFile(file.path)) return null;
1763+
1764+
file.fileRef = this.generateUuid();
1765+
this.addToPbxGroup(file, group);
1766+
1767+
if (!file) return false;
1768+
1769+
file.target = opt ? opt.target : undefined;
1770+
file.uuid = this.generateUuid();
1771+
1772+
this.addToPbxBuildFileSection(file);
1773+
this.addToPbxSourcesBuildPhase(file);
1774+
1775+
file.models = [];
1776+
var currentVersionName;
1777+
var modelFiles = fs.readdirSync(file.path);
1778+
for (var index in modelFiles) {
1779+
var modelFileName = modelFiles[index];
1780+
var modelFilePath = path.join(filePath, modelFileName);
1781+
1782+
if (modelFileName == '.xccurrentversion') {
1783+
currentVersionName = plist.readFileSync(modelFilePath)._XCCurrentVersionName;
1784+
continue;
1785+
}
1786+
1787+
var modelFile = new pbxFile(modelFilePath);
1788+
modelFile.fileRef = this.generateUuid();
1789+
1790+
this.addToPbxFileReferenceSection(modelFile);
1791+
1792+
file.models.push(modelFile);
1793+
1794+
if (currentVersionName && currentVersionName === modelFileName) {
1795+
file.currentModel = modelFile;
1796+
}
1797+
}
1798+
1799+
if (!file.currentModel) {
1800+
file.currentModel = file.models[0];
1801+
}
1802+
1803+
this.addToXcVersionGroupSection(file);
1804+
1805+
return file;
1806+
}
1807+
17151808

17161809
module.exports = pbxProject;

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,17 @@
33
"name": "xcode",
44
"description": "parser for xcodeproj/project.pbxproj files",
55
"version": "0.8.2",
6-
"main":"index.js",
6+
"main": "index.js",
77
"repository": {
88
"url": "https://github.com/alunny/node-xcode.git"
99
},
1010
"engines": {
1111
"node": ">=0.6.7"
1212
},
1313
"dependencies": {
14+
"node-uuid":"1.3.3",
1415
"pegjs":"0.6.2",
15-
"node-uuid":"1.3.3"
16+
"simple-plist": "0.0.4"
1617
},
1718
"devDependencies": {
1819
"nodeunit":"0.9.0"

test/dataModelDocument.js

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
var jsonProject = require('./fixtures/full-project')
2+
fullProjectStr = JSON.stringify(jsonProject),
3+
path = require('path'),
4+
pbx = require('../lib/pbxProject'),
5+
pbxFile = require('../lib/pbxFile'),
6+
proj = new pbx('.'),
7+
singleDataModelFilePath = __dirname + '/fixtures/single-data-model.xcdatamodeld',
8+
multipleDataModelFilePath = __dirname + '/fixtures/multiple-data-model.xcdatamodeld';
9+
10+
function cleanHash() {
11+
return JSON.parse(fullProjectStr);
12+
}
13+
14+
exports.setUp = function (callback) {
15+
proj.hash = cleanHash();
16+
callback();
17+
}
18+
19+
exports.dataModelDocument = {
20+
'should return a pbxFile': function (test) {
21+
var newFile = proj.addDataModelDocument(singleDataModelFilePath);
22+
23+
test.equal(newFile.constructor, pbxFile);
24+
test.done()
25+
},
26+
'should set a uuid on the pbxFile': function (test) {
27+
var newFile = proj.addDataModelDocument(singleDataModelFilePath);
28+
29+
test.ok(newFile.uuid);
30+
test.done()
31+
},
32+
'should set a fileRef on the pbxFile': function (test) {
33+
var newFile = proj.addDataModelDocument(singleDataModelFilePath);
34+
35+
test.ok(newFile.fileRef);
36+
test.done()
37+
},
38+
'should set an optional target on the pbxFile': function (test) {
39+
var newFile = proj.addDataModelDocument(singleDataModelFilePath, undefined, { target: target }),
40+
target = proj.findTargetKey('TestApp');
41+
42+
test.equal(newFile.target, target);
43+
test.done()
44+
},
45+
'should populate the PBXBuildFile section with 2 fields': function (test) {
46+
var newFile = proj.addDataModelDocument(singleDataModelFilePath),
47+
buildFileSection = proj.pbxBuildFileSection(),
48+
bfsLength = Object.keys(buildFileSection).length;
49+
50+
test.equal(59 + 1, bfsLength);
51+
test.ok(buildFileSection[newFile.uuid]);
52+
test.ok(buildFileSection[newFile.uuid + '_comment']);
53+
54+
test.done();
55+
},
56+
'should populate the PBXFileReference section with 2 fields for single model document': function (test) {
57+
var newFile = proj.addDataModelDocument(singleDataModelFilePath),
58+
fileRefSection = proj.pbxFileReferenceSection(),
59+
frsLength = Object.keys(fileRefSection).length;
60+
61+
test.equal(66 + 2, frsLength);
62+
test.ok(fileRefSection[newFile.models[0].fileRef]);
63+
test.ok(fileRefSection[newFile.models[0].fileRef + '_comment']);
64+
65+
test.done();
66+
},
67+
'should populate the PBXFileReference section with 2 fields for each model of a model document': function (test) {
68+
var newFile = proj.addDataModelDocument(multipleDataModelFilePath),
69+
fileRefSection = proj.pbxFileReferenceSection(),
70+
frsLength = Object.keys(fileRefSection).length;
71+
72+
test.equal(66 + 2 * 2, frsLength);
73+
test.ok(fileRefSection[newFile.models[0].fileRef]);
74+
test.ok(fileRefSection[newFile.models[0].fileRef + '_comment']);
75+
test.ok(fileRefSection[newFile.models[1].fileRef]);
76+
test.ok(fileRefSection[newFile.models[1].fileRef + '_comment']);
77+
78+
test.done();
79+
},
80+
'should add to resources group by default': function (test) {
81+
var newFile = proj.addDataModelDocument(singleDataModelFilePath);
82+
groupChildren = proj.pbxGroupByName('Resources').children,
83+
found = false;
84+
85+
for (var index in groupChildren) {
86+
if (groupChildren[index].comment === 'single-data-model.xcdatamodeld') {
87+
found = true;
88+
break;
89+
}
90+
}
91+
test.ok(found);
92+
test.done();
93+
},
94+
'should add to group specified by key': function (test) {
95+
var group = 'Frameworks',
96+
newFile = proj.addDataModelDocument(singleDataModelFilePath, proj.findPBXGroupKey({ name: group }));
97+
groupChildren = proj.pbxGroupByName(group).children;
98+
99+
var found = false;
100+
for (var index in groupChildren) {
101+
if (groupChildren[index].comment === path.basename(singleDataModelFilePath)) {
102+
found = true;
103+
break;
104+
}
105+
}
106+
test.ok(found);
107+
test.done();
108+
},
109+
'should add to group specified by name': function (test) {
110+
var group = 'Frameworks',
111+
newFile = proj.addDataModelDocument(singleDataModelFilePath, group);
112+
groupChildren = proj.pbxGroupByName(group).children;
113+
114+
var found = false;
115+
for (var index in groupChildren) {
116+
if (groupChildren[index].comment === path.basename(singleDataModelFilePath)) {
117+
found = true;
118+
break;
119+
}
120+
}
121+
test.ok(found);
122+
test.done();
123+
},
124+
'should add to the PBXSourcesBuildPhase': function (test) {
125+
var newFile = proj.addDataModelDocument(singleDataModelFilePath),
126+
sources = proj.pbxSourcesBuildPhaseObj();
127+
128+
test.equal(sources.files.length, 2 + 1);
129+
test.done();
130+
},
131+
'should create a XCVersionGroup section': function (test) {
132+
var newFile = proj.addDataModelDocument(singleDataModelFilePath),
133+
xcVersionGroupSection = proj.xcVersionGroupSection();
134+
135+
test.ok(xcVersionGroupSection[newFile.fileRef]);
136+
test.done();
137+
},
138+
'should populate the XCVersionGroup comment correctly': function (test) {
139+
var newFile = proj.addDataModelDocument(singleDataModelFilePath),
140+
xcVersionGroupSection = proj.xcVersionGroupSection(),
141+
commentKey = newFile.fileRef + '_comment';
142+
143+
test.equal(xcVersionGroupSection[commentKey], path.basename(singleDataModelFilePath));
144+
test.done();
145+
},
146+
'should add the XCVersionGroup object correctly': function (test) {
147+
var newFile = proj.addDataModelDocument(singleDataModelFilePath),
148+
xcVersionGroupSection = proj.xcVersionGroupSection(),
149+
xcVersionGroupEntry = xcVersionGroupSection[newFile.fileRef];
150+
151+
test.equal(xcVersionGroupEntry.isa, 'XCVersionGroup');
152+
test.equal(xcVersionGroupEntry.children[0], newFile.models[0].fileRef);
153+
test.equal(xcVersionGroupEntry.currentVersion, newFile.currentModel.fileRef);
154+
test.equal(xcVersionGroupEntry.path, singleDataModelFilePath);
155+
test.equal(xcVersionGroupEntry.sourceTree, '"<group>"');
156+
test.equal(xcVersionGroupEntry.versionGroupType, 'wrapper.xcdatamodel');
157+
158+
test.done();
159+
}
160+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>_XCCurrentVersionName</key>
6+
<string>2.xcdatamodel</string>
7+
</dict>
8+
</plist>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="9057" systemVersion="15B42" minimumToolsVersion="Xcode 7.0">
3+
<elements/>
4+
</model>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="9057" systemVersion="15B42" minimumToolsVersion="Xcode 7.0">
3+
<elements/>
4+
</model>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="9057" systemVersion="15B42" minimumToolsVersion="Xcode 7.0">
3+
<elements/>
4+
</model>

0 commit comments

Comments
 (0)