react-native-notify-kit 10.8.0 → 10.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/README.md +34 -17
  2. package/android/build.gradle +50 -4
  3. package/android/src/main/AndroidManifest.xml +4 -0
  4. package/android/src/main/java/app/notifee/core/ForegroundService.java +1002 -58
  5. package/android/src/main/java/app/notifee/core/ForegroundServiceRunLimitReceiver.java +15 -0
  6. package/android/src/main/java/app/notifee/core/ForegroundServiceRunLimitScheduler.java +461 -0
  7. package/android/src/main/java/app/notifee/core/NotificationManager.java +6 -0
  8. package/android/src/main/java/app/notifee/core/event/NotificationEvent.java +6 -0
  9. package/android/src/main/java/app/notifee/core/model/NotificationAndroidModel.java +29 -0
  10. package/android/src/main/kotlin/io/invertase/notifee/NotifeeEventSubscriber.kt +3 -0
  11. package/android/src/test/java/app/notifee/core/ForegroundServiceRunLimitTest.java +2357 -0
  12. package/android/src/test/java/app/notifee/core/ForegroundServiceTest.java +3093 -21
  13. package/android/src/test/java/app/notifee/core/ShadowServiceCompatibilityTest.java +170 -0
  14. package/android/src/test/resources/robolectric.properties +4 -0
  15. package/cli/dist/cli.js +0 -1
  16. package/cli/dist/commands/initNse.js +1 -2
  17. package/cli/dist/lib/detectProject.js +1 -2
  18. package/cli/dist/lib/initNseCore.js +0 -1
  19. package/cli/dist/lib/logger.js +0 -1
  20. package/cli/dist/lib/patchPodfile.js +0 -1
  21. package/cli/dist/lib/patchXcodeProject.js +22 -3
  22. package/cli/dist/lib/writeTemplates.js +0 -1
  23. package/cli/dist/lib/xcode.js +6 -0
  24. package/cli/dist/templates/NotificationService.swift.tmpl +74 -6
  25. package/cli/package.json +1 -0
  26. package/cli/vendor/xcode/AUTHORS +6 -0
  27. package/cli/vendor/xcode/LICENSE +202 -0
  28. package/cli/vendor/xcode/NOTICE +5 -0
  29. package/cli/vendor/xcode/README.md +32 -0
  30. package/cli/vendor/xcode/UPSTREAM-SHA256.json +12 -0
  31. package/cli/vendor/xcode/index.js +18 -0
  32. package/cli/vendor/xcode/lib/parseJob.js +32 -0
  33. package/cli/vendor/xcode/lib/parser/pbxproj.js +1904 -0
  34. package/cli/vendor/xcode/lib/parser/pbxproj.pegjs +280 -0
  35. package/cli/vendor/xcode/lib/pbxFile.js +233 -0
  36. package/cli/vendor/xcode/lib/pbxProject.js +2205 -0
  37. package/cli/vendor/xcode/lib/pbxWriter.js +309 -0
  38. package/dist/NotifeeNativeModule.web.d.ts +2 -2
  39. package/dist/types/Notification.d.ts +36 -1
  40. package/dist/types/Notification.js +13 -0
  41. package/dist/types/Notification.js.map +1 -1
  42. package/dist/types/NotificationAndroid.d.ts +9 -0
  43. package/dist/types/NotificationAndroid.js.map +1 -1
  44. package/dist/validators/validateAndroidNotification.js +26 -1
  45. package/dist/validators/validateAndroidNotification.js.map +1 -1
  46. package/dist/validators/validateAndroidStyle.js +6 -0
  47. package/dist/validators/validateAndroidStyle.js.map +1 -1
  48. package/dist/validators/validateIOSAttachment.js +3 -1
  49. package/dist/validators/validateIOSAttachment.js.map +1 -1
  50. package/dist/version.d.ts +1 -1
  51. package/dist/version.js +1 -1
  52. package/package.json +8 -5
  53. package/src/NotifeeNativeModule.web.ts +2 -2
  54. package/src/types/Notification.ts +40 -0
  55. package/src/types/NotificationAndroid.ts +10 -0
  56. package/src/validators/validateAndroidNotification.ts +31 -1
  57. package/src/validators/validateAndroidStyle.ts +10 -2
  58. package/src/validators/validateIOSAttachment.ts +5 -1
  59. package/src/version.ts +1 -1
  60. package/cli/dist/cli.bundle.js +0 -20834
  61. package/cli/dist/cli.d.ts +0 -2
  62. package/cli/dist/cli.js.map +0 -1
  63. package/cli/dist/commands/initNse.d.ts +0 -12
  64. package/cli/dist/commands/initNse.js.map +0 -1
  65. package/cli/dist/lib/detectProject.d.ts +0 -12
  66. package/cli/dist/lib/detectProject.js.map +0 -1
  67. package/cli/dist/lib/initNseCore.d.ts +0 -8
  68. package/cli/dist/lib/initNseCore.js.map +0 -1
  69. package/cli/dist/lib/logger.d.ts +0 -4
  70. package/cli/dist/lib/logger.js.map +0 -1
  71. package/cli/dist/lib/patchPodfile.d.ts +0 -23
  72. package/cli/dist/lib/patchPodfile.js.map +0 -1
  73. package/cli/dist/lib/patchXcodeProject.d.ts +0 -41
  74. package/cli/dist/lib/patchXcodeProject.js.map +0 -1
  75. package/cli/dist/lib/writeTemplates.d.ts +0 -11
  76. package/cli/dist/lib/writeTemplates.js.map +0 -1
  77. package/cli/dist/templates/templates/Info.plist.tmpl +0 -29
  78. package/cli/dist/templates/templates/NotificationService.swift.tmpl +0 -100
  79. package/cli/dist/templates/templates/NotifyKitNSE.entitlements.tmpl +0 -6
@@ -0,0 +1,2205 @@
1
+ /**
2
+ Licensed to the Apache Software Foundation (ASF) under one
3
+ or more contributor license agreements. See the NOTICE file
4
+ distributed with this work for additional information
5
+ regarding copyright ownership. The ASF licenses this file
6
+ to you under the Apache License, Version 2.0 (the
7
+ 'License'); you may not use this file except in compliance
8
+ with the License. You may obtain a copy of the License at
9
+ http://www.apache.org/licenses/LICENSE-2.0
10
+ Unless required by applicable law or agreed to in writing,
11
+ software distributed under the License is distributed on an
12
+ 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
13
+ KIND, either express or implied. See the License for the
14
+ specific language governing permissions and limitations
15
+ under the License.
16
+ */
17
+
18
+ // Modified by react-native-notify-kit (2026-10-17): UUID generation uses
19
+ // node:crypto.randomUUID() instead of uuid.v4(), following Apache upstream
20
+ // commit 1edf7059d347beb0e339d1cb4741581352b43138 (#153).
21
+
22
+ var util = require('util'),
23
+ f = util.format,
24
+ EventEmitter = require('events').EventEmitter,
25
+ path = require('path'),
26
+ crypto = require('node:crypto'),
27
+ fork = require('child_process').fork,
28
+ pbxWriter = require('./pbxWriter'),
29
+ pbxFile = require('./pbxFile'),
30
+ fs = require('fs'),
31
+ parser = require('./parser/pbxproj'),
32
+ plist = require('simple-plist'),
33
+ COMMENT_KEY = /_comment$/
34
+
35
+ function pbxProject(filename) {
36
+ if (!(this instanceof pbxProject))
37
+ return new pbxProject(filename);
38
+
39
+ this.filepath = path.resolve(filename)
40
+ }
41
+
42
+ util.inherits(pbxProject, EventEmitter)
43
+
44
+ pbxProject.prototype.parse = function(cb) {
45
+ var worker = fork(__dirname + '/parseJob.js', [this.filepath])
46
+
47
+ worker.on('message', function(msg) {
48
+ if (msg.name == 'SyntaxError' || msg.code) {
49
+ this.emit('error', msg);
50
+ } else {
51
+ this.hash = msg;
52
+ this.emit('end', null, msg)
53
+ }
54
+ }.bind(this));
55
+
56
+ if (cb) {
57
+ this.on('error', cb);
58
+ this.on('end', cb);
59
+ }
60
+
61
+ return this;
62
+ }
63
+
64
+ pbxProject.prototype.parseSync = function() {
65
+ var file_contents = fs.readFileSync(this.filepath, 'utf-8');
66
+
67
+ this.hash = parser.parse(file_contents);
68
+ return this;
69
+ }
70
+
71
+ pbxProject.prototype.writeSync = function(options) {
72
+ this.writer = new pbxWriter(this.hash, options);
73
+ return this.writer.writeSync();
74
+ }
75
+
76
+ pbxProject.prototype.allUuids = function() {
77
+ var sections = this.hash.project.objects,
78
+ uuids = [],
79
+ section;
80
+
81
+ for (key in sections) {
82
+ section = sections[key]
83
+ uuids = uuids.concat(Object.keys(section))
84
+ }
85
+
86
+ uuids = uuids.filter(function(str) {
87
+ return !COMMENT_KEY.test(str) && str.length == 24;
88
+ });
89
+
90
+ return uuids;
91
+ }
92
+
93
+ pbxProject.prototype.generateUuid = function() {
94
+ var id = crypto.randomUUID()
95
+ .replace(/-/g, '')
96
+ .substr(0, 24)
97
+ .toUpperCase()
98
+
99
+ if (this.allUuids().indexOf(id) >= 0) {
100
+ return this.generateUuid();
101
+ } else {
102
+ return id;
103
+ }
104
+ }
105
+
106
+ pbxProject.prototype.addPluginFile = function(path, opt) {
107
+ var file = new pbxFile(path, opt);
108
+
109
+ file.plugin = true; // durr
110
+ correctForPluginsPath(file, this);
111
+
112
+ // null is better for early errors
113
+ if (this.hasFile(file.path)) return null;
114
+
115
+ file.fileRef = this.generateUuid();
116
+
117
+ this.addToPbxFileReferenceSection(file); // PBXFileReference
118
+ this.addToPluginsPbxGroup(file); // PBXGroup
119
+
120
+ return file;
121
+ }
122
+
123
+ pbxProject.prototype.removePluginFile = function(path, opt) {
124
+ var file = new pbxFile(path, opt);
125
+ correctForPluginsPath(file, this);
126
+
127
+ this.removeFromPbxFileReferenceSection(file); // PBXFileReference
128
+ this.removeFromPluginsPbxGroup(file); // PBXGroup
129
+
130
+ return file;
131
+ }
132
+
133
+ pbxProject.prototype.addProductFile = function(targetPath, opt) {
134
+ var file = new pbxFile(targetPath, opt);
135
+
136
+ file.includeInIndex = 0;
137
+ file.fileRef = this.generateUuid();
138
+ file.target = opt ? opt.target : undefined;
139
+ file.group = opt ? opt.group : undefined;
140
+ file.uuid = this.generateUuid();
141
+ file.path = file.basename;
142
+
143
+ this.addToPbxFileReferenceSection(file);
144
+ this.addToProductsPbxGroup(file); // PBXGroup
145
+
146
+ return file;
147
+ }
148
+
149
+ pbxProject.prototype.removeProductFile = function(path, opt) {
150
+ var file = new pbxFile(path, opt);
151
+
152
+ this.removeFromProductsPbxGroup(file); // PBXGroup
153
+
154
+ return file;
155
+ }
156
+
157
+ /**
158
+ *
159
+ * @param path {String}
160
+ * @param opt {Object} see pbxFile for avail options
161
+ * @param group {String} group key
162
+ * @returns {Object} file; see pbxFile
163
+ */
164
+ pbxProject.prototype.addSourceFile = function (path, opt, group) {
165
+ var file;
166
+ if (group) {
167
+ file = this.addFile(path, group, opt);
168
+ }
169
+ else {
170
+ file = this.addPluginFile(path, opt);
171
+ }
172
+
173
+ if (!file) return false;
174
+
175
+ file.target = opt ? opt.target : undefined;
176
+ file.uuid = this.generateUuid();
177
+
178
+ this.addToPbxBuildFileSection(file); // PBXBuildFile
179
+ this.addToPbxSourcesBuildPhase(file); // PBXSourcesBuildPhase
180
+
181
+ return file;
182
+ }
183
+
184
+ /**
185
+ *
186
+ * @param path {String}
187
+ * @param opt {Object} see pbxFile for avail options
188
+ * @param group {String} group key
189
+ * @returns {Object} file; see pbxFile
190
+ */
191
+ pbxProject.prototype.removeSourceFile = function (path, opt, group) {
192
+ var file;
193
+ if (group) {
194
+ file = this.removeFile(path, group, opt);
195
+ }
196
+ else {
197
+ file = this.removePluginFile(path, opt);
198
+ }
199
+ file.target = opt ? opt.target : undefined;
200
+ this.removeFromPbxBuildFileSection(file); // PBXBuildFile
201
+ this.removeFromPbxSourcesBuildPhase(file); // PBXSourcesBuildPhase
202
+
203
+ return file;
204
+ }
205
+
206
+ /**
207
+ *
208
+ * @param path {String}
209
+ * @param opt {Object} see pbxFile for avail options
210
+ * @param group {String} group key
211
+ * @returns {Object} file; see pbxFile
212
+ */
213
+ pbxProject.prototype.addHeaderFile = function (path, opt, group) {
214
+ if (group) {
215
+ return this.addFile(path, group, opt);
216
+ }
217
+ else {
218
+ return this.addPluginFile(path, opt);
219
+ }
220
+ }
221
+
222
+ /**
223
+ *
224
+ * @param path {String}
225
+ * @param opt {Object} see pbxFile for avail options
226
+ * @param group {String} group key
227
+ * @returns {Object} file; see pbxFile
228
+ */
229
+ pbxProject.prototype.removeHeaderFile = function (path, opt, group) {
230
+ if (group) {
231
+ return this.removeFile(path, group, opt);
232
+ }
233
+ else {
234
+ return this.removePluginFile(path, opt);
235
+ }
236
+ }
237
+
238
+ /**
239
+ *
240
+ * @param path {String}
241
+ * @param opt {Object} see pbxFile for avail options
242
+ * @param group {String} group key
243
+ * @returns {Object} file; see pbxFile
244
+ */
245
+ pbxProject.prototype.addResourceFile = function(path, opt, group) {
246
+ opt = opt || {};
247
+
248
+ var file;
249
+
250
+ if (opt.plugin) {
251
+ file = this.addPluginFile(path, opt);
252
+ if (!file) return false;
253
+ } else {
254
+ file = new pbxFile(path, opt);
255
+ if (this.hasFile(file.path)) return false;
256
+ }
257
+
258
+ file.uuid = this.generateUuid();
259
+ file.target = opt ? opt.target : undefined;
260
+
261
+ if (!opt.plugin) {
262
+ correctForResourcesPath(file, this);
263
+ file.fileRef = this.generateUuid();
264
+ }
265
+
266
+ if (!opt.variantGroup) {
267
+ this.addToPbxBuildFileSection(file); // PBXBuildFile
268
+ this.addToPbxResourcesBuildPhase(file); // PBXResourcesBuildPhase
269
+ }
270
+
271
+ if (!opt.plugin) {
272
+ this.addToPbxFileReferenceSection(file); // PBXFileReference
273
+ if (group) {
274
+ if (this.getPBXGroupByKey(group)) {
275
+ this.addToPbxGroup(file, group); //Group other than Resources (i.e. 'splash')
276
+ }
277
+ else if (this.getPBXVariantGroupByKey(group)) {
278
+ this.addToPbxVariantGroup(file, group); // PBXVariantGroup
279
+ }
280
+ }
281
+ else {
282
+ this.addToResourcesPbxGroup(file); // PBXGroup
283
+ }
284
+
285
+ }
286
+
287
+ return file;
288
+ }
289
+
290
+ /**
291
+ *
292
+ * @param path {String}
293
+ * @param opt {Object} see pbxFile for avail options
294
+ * @param group {String} group key
295
+ * @returns {Object} file; see pbxFile
296
+ */
297
+ pbxProject.prototype.removeResourceFile = function(path, opt, group) {
298
+ var file = new pbxFile(path, opt);
299
+ file.target = opt ? opt.target : undefined;
300
+
301
+ correctForResourcesPath(file, this);
302
+
303
+ this.removeFromPbxBuildFileSection(file); // PBXBuildFile
304
+ this.removeFromPbxFileReferenceSection(file); // PBXFileReference
305
+ if (group) {
306
+ if (this.getPBXGroupByKey(group)) {
307
+ this.removeFromPbxGroup(file, group); //Group other than Resources (i.e. 'splash')
308
+ }
309
+ else if (this.getPBXVariantGroupByKey(group)) {
310
+ this.removeFromPbxVariantGroup(file, group); // PBXVariantGroup
311
+ }
312
+ }
313
+ else {
314
+ this.removeFromResourcesPbxGroup(file); // PBXGroup
315
+ }
316
+ this.removeFromPbxResourcesBuildPhase(file); // PBXResourcesBuildPhase
317
+
318
+ return file;
319
+ }
320
+
321
+ pbxProject.prototype.addFramework = function(fpath, opt) {
322
+ var customFramework = opt && opt.customFramework == true;
323
+ var link = !opt || (opt.link == undefined || opt.link); //defaults to true if not specified
324
+ var embed = opt && opt.embed; //defaults to false if not specified
325
+
326
+ if (opt) {
327
+ delete opt.embed;
328
+ }
329
+
330
+ var file = new pbxFile(fpath, opt);
331
+
332
+ file.uuid = this.generateUuid();
333
+ file.fileRef = this.generateUuid();
334
+ file.target = opt ? opt.target : undefined;
335
+
336
+ if (this.hasFile(file.path)) return false;
337
+
338
+ this.addToPbxBuildFileSection(file); // PBXBuildFile
339
+ this.addToPbxFileReferenceSection(file); // PBXFileReference
340
+ this.addToFrameworksPbxGroup(file); // PBXGroup
341
+
342
+ if (link) {
343
+ this.addToPbxFrameworksBuildPhase(file); // PBXFrameworksBuildPhase
344
+ }
345
+
346
+ if (customFramework) {
347
+ this.addToFrameworkSearchPaths(file);
348
+
349
+ if (embed) {
350
+ opt.embed = embed;
351
+ var embeddedFile = new pbxFile(fpath, opt);
352
+
353
+ embeddedFile.uuid = this.generateUuid();
354
+ embeddedFile.fileRef = file.fileRef;
355
+
356
+ //keeping a separate PBXBuildFile entry for Embed Frameworks
357
+ this.addToPbxBuildFileSection(embeddedFile); // PBXBuildFile
358
+
359
+ this.addToPbxEmbedFrameworksBuildPhase(embeddedFile); // PBXCopyFilesBuildPhase
360
+
361
+ return embeddedFile;
362
+ }
363
+ }
364
+
365
+ return file;
366
+ }
367
+
368
+ pbxProject.prototype.removeFramework = function(fpath, opt) {
369
+ var embed = opt && opt.embed;
370
+
371
+ if (opt) {
372
+ delete opt.embed;
373
+ }
374
+
375
+ var file = new pbxFile(fpath, opt);
376
+ file.target = opt ? opt.target : undefined;
377
+
378
+ this.removeFromPbxBuildFileSection(file); // PBXBuildFile
379
+ this.removeFromPbxFileReferenceSection(file); // PBXFileReference
380
+ this.removeFromFrameworksPbxGroup(file); // PBXGroup
381
+ this.removeFromPbxFrameworksBuildPhase(file); // PBXFrameworksBuildPhase
382
+
383
+ if (opt && opt.customFramework) {
384
+ this.removeFromFrameworkSearchPaths(file);
385
+ }
386
+
387
+ opt = opt || {};
388
+ opt.embed = true;
389
+ var embeddedFile = new pbxFile(fpath, opt);
390
+
391
+ embeddedFile.fileRef = file.fileRef;
392
+
393
+ this.removeFromPbxBuildFileSection(embeddedFile); // PBXBuildFile
394
+ this.removeFromPbxEmbedFrameworksBuildPhase(embeddedFile); // PBXCopyFilesBuildPhase
395
+
396
+ return file;
397
+ }
398
+
399
+
400
+ pbxProject.prototype.addCopyfile = function(fpath, opt) {
401
+ var file = new pbxFile(fpath, opt);
402
+
403
+ // catch duplicates
404
+ if (this.hasFile(file.path)) {
405
+ file = this.hasFile(file.path);
406
+ }
407
+
408
+ file.fileRef = file.uuid = this.generateUuid();
409
+ file.target = opt ? opt.target : undefined;
410
+
411
+ this.addToPbxBuildFileSection(file); // PBXBuildFile
412
+ this.addToPbxFileReferenceSection(file); // PBXFileReference
413
+ this.addToPbxCopyfilesBuildPhase(file); // PBXCopyFilesBuildPhase
414
+
415
+ return file;
416
+ }
417
+
418
+ pbxProject.prototype.pbxCopyfilesBuildPhaseObj = function(target) {
419
+ return this.buildPhaseObject('PBXCopyFilesBuildPhase', 'Copy Files', target);
420
+ }
421
+
422
+ pbxProject.prototype.addToPbxCopyfilesBuildPhase = function(file) {
423
+ var sources = this.buildPhaseObject('PBXCopyFilesBuildPhase', 'Copy Files', file.target);
424
+ sources.files.push(pbxBuildPhaseObj(file));
425
+ }
426
+
427
+ pbxProject.prototype.removeCopyfile = function(fpath, opt) {
428
+ var file = new pbxFile(fpath, opt);
429
+ file.target = opt ? opt.target : undefined;
430
+
431
+ this.removeFromPbxBuildFileSection(file); // PBXBuildFile
432
+ this.removeFromPbxFileReferenceSection(file); // PBXFileReference
433
+ this.removeFromPbxCopyfilesBuildPhase(file); // PBXFrameworksBuildPhase
434
+
435
+ return file;
436
+ }
437
+
438
+ pbxProject.prototype.removeFromPbxCopyfilesBuildPhase = function(file) {
439
+ var sources = this.pbxCopyfilesBuildPhaseObj(file.target);
440
+ for (i in sources.files) {
441
+ if (sources.files[i].comment == longComment(file)) {
442
+ sources.files.splice(i, 1);
443
+ break;
444
+ }
445
+ }
446
+ }
447
+
448
+ pbxProject.prototype.addStaticLibrary = function(path, opt) {
449
+ opt = opt || {};
450
+
451
+ var file;
452
+
453
+ if (opt.plugin) {
454
+ file = this.addPluginFile(path, opt);
455
+ if (!file) return false;
456
+ } else {
457
+ file = new pbxFile(path, opt);
458
+ if (this.hasFile(file.path)) return false;
459
+ }
460
+
461
+ file.uuid = this.generateUuid();
462
+ file.target = opt ? opt.target : undefined;
463
+
464
+ if (!opt.plugin) {
465
+ file.fileRef = this.generateUuid();
466
+ this.addToPbxFileReferenceSection(file); // PBXFileReference
467
+ }
468
+
469
+ this.addToPbxBuildFileSection(file); // PBXBuildFile
470
+ this.addToPbxFrameworksBuildPhase(file); // PBXFrameworksBuildPhase
471
+ this.addToLibrarySearchPaths(file); // make sure it gets built!
472
+
473
+ return file;
474
+ }
475
+
476
+ // helper addition functions
477
+ pbxProject.prototype.addToPbxBuildFileSection = function(file) {
478
+ var commentKey = f("%s_comment", file.uuid);
479
+
480
+ this.pbxBuildFileSection()[file.uuid] = pbxBuildFileObj(file);
481
+ this.pbxBuildFileSection()[commentKey] = pbxBuildFileComment(file);
482
+ }
483
+
484
+ pbxProject.prototype.removeFromPbxBuildFileSection = function(file) {
485
+ var uuid;
486
+
487
+ for (uuid in this.pbxBuildFileSection()) {
488
+ if (this.pbxBuildFileSection()[uuid].fileRef_comment == file.basename) {
489
+ file.uuid = uuid;
490
+ delete this.pbxBuildFileSection()[uuid];
491
+
492
+ var commentKey = f("%s_comment", uuid);
493
+ delete this.pbxBuildFileSection()[commentKey];
494
+ }
495
+ }
496
+ }
497
+
498
+ pbxProject.prototype.addPbxGroup = function(filePathsArray, name, path, sourceTree) {
499
+ var groups = this.hash.project.objects['PBXGroup'],
500
+ pbxGroupUuid = this.generateUuid(),
501
+ commentKey = f("%s_comment", pbxGroupUuid),
502
+ pbxGroup = {
503
+ isa: 'PBXGroup',
504
+ children: [],
505
+ name: name,
506
+ path: path,
507
+ sourceTree: sourceTree ? sourceTree : '"<group>"'
508
+ },
509
+ fileReferenceSection = this.pbxFileReferenceSection(),
510
+ filePathToReference = {};
511
+
512
+ for (var key in fileReferenceSection) {
513
+ // only look for comments
514
+ if (!COMMENT_KEY.test(key)) continue;
515
+
516
+ var fileReferenceKey = key.split(COMMENT_KEY)[0],
517
+ fileReference = fileReferenceSection[fileReferenceKey];
518
+
519
+ filePathToReference[fileReference.path] = { fileRef: fileReferenceKey, basename: fileReferenceSection[key] };
520
+ }
521
+
522
+ for (var index = 0; index < filePathsArray.length; index++) {
523
+ var filePath = filePathsArray[index],
524
+ filePathQuoted = "\"" + filePath + "\"";
525
+ if (filePathToReference[filePath]) {
526
+ pbxGroup.children.push(pbxGroupChild(filePathToReference[filePath]));
527
+ continue;
528
+ } else if (filePathToReference[filePathQuoted]) {
529
+ pbxGroup.children.push(pbxGroupChild(filePathToReference[filePathQuoted]));
530
+ continue;
531
+ }
532
+
533
+ var file = new pbxFile(filePath);
534
+ file.uuid = this.generateUuid();
535
+ file.fileRef = this.generateUuid();
536
+ this.addToPbxFileReferenceSection(file); // PBXFileReference
537
+ this.addToPbxBuildFileSection(file); // PBXBuildFile
538
+ pbxGroup.children.push(pbxGroupChild(file));
539
+ }
540
+
541
+ if (groups) {
542
+ groups[pbxGroupUuid] = pbxGroup;
543
+ groups[commentKey] = name;
544
+ }
545
+
546
+ return { uuid: pbxGroupUuid, pbxGroup: pbxGroup };
547
+ }
548
+
549
+ pbxProject.prototype.removePbxGroup = function (groupName) {
550
+ var section = this.hash.project.objects['PBXGroup'],
551
+ key, itemKey;
552
+
553
+ for (key in section) {
554
+ // only look for comments
555
+ if (!COMMENT_KEY.test(key)) continue;
556
+
557
+ if (section[key] == groupName) {
558
+ itemKey = key.split(COMMENT_KEY)[0];
559
+ delete section[itemKey];
560
+ }
561
+ }
562
+ }
563
+
564
+ pbxProject.prototype.addToPbxProjectSection = function(target) {
565
+
566
+ var newTarget = {
567
+ value: target.uuid,
568
+ comment: pbxNativeTargetComment(target.pbxNativeTarget)
569
+ };
570
+
571
+ this.pbxProjectSection()[this.getFirstProject()['uuid']]['targets'].push(newTarget);
572
+ }
573
+
574
+ pbxProject.prototype.addToPbxNativeTargetSection = function(target) {
575
+ var commentKey = f("%s_comment", target.uuid);
576
+
577
+ this.pbxNativeTargetSection()[target.uuid] = target.pbxNativeTarget;
578
+ this.pbxNativeTargetSection()[commentKey] = target.pbxNativeTarget.name;
579
+ }
580
+
581
+ pbxProject.prototype.addToPbxFileReferenceSection = function(file) {
582
+ var commentKey = f("%s_comment", file.fileRef);
583
+
584
+ this.pbxFileReferenceSection()[file.fileRef] = pbxFileReferenceObj(file);
585
+ this.pbxFileReferenceSection()[commentKey] = pbxFileReferenceComment(file);
586
+ }
587
+
588
+ pbxProject.prototype.removeFromPbxFileReferenceSection = function(file) {
589
+
590
+ var i;
591
+ var refObj = pbxFileReferenceObj(file);
592
+ for (i in this.pbxFileReferenceSection()) {
593
+ if (this.pbxFileReferenceSection()[i].name == refObj.name ||
594
+ ('"' + this.pbxFileReferenceSection()[i].name + '"') == refObj.name ||
595
+ this.pbxFileReferenceSection()[i].path == refObj.path ||
596
+ ('"' + this.pbxFileReferenceSection()[i].path + '"') == refObj.path) {
597
+ file.fileRef = file.uuid = i;
598
+ delete this.pbxFileReferenceSection()[i];
599
+ break;
600
+ }
601
+ }
602
+ var commentKey = f("%s_comment", file.fileRef);
603
+ if (this.pbxFileReferenceSection()[commentKey] != undefined) {
604
+ delete this.pbxFileReferenceSection()[commentKey];
605
+ }
606
+
607
+ return file;
608
+ }
609
+
610
+ pbxProject.prototype.addToXcVersionGroupSection = function(file) {
611
+ if (!file.models || !file.currentModel) {
612
+ throw new Error("Cannot create a XCVersionGroup section from not a data model document file");
613
+ }
614
+
615
+ var commentKey = f("%s_comment", file.fileRef);
616
+
617
+ if (!this.xcVersionGroupSection()[file.fileRef]) {
618
+ this.xcVersionGroupSection()[file.fileRef] = {
619
+ isa: 'XCVersionGroup',
620
+ children: file.models.map(function (el) { return el.fileRef; }),
621
+ currentVersion: file.currentModel.fileRef,
622
+ name: path.basename(file.path),
623
+ path: file.path,
624
+ sourceTree: '"<group>"',
625
+ versionGroupType: 'wrapper.xcdatamodel'
626
+ };
627
+ this.xcVersionGroupSection()[commentKey] = path.basename(file.path);
628
+ }
629
+ }
630
+
631
+ pbxProject.prototype.addToPluginsPbxGroup = function(file) {
632
+ var pluginsGroup = this.pbxGroupByName('Plugins');
633
+ if (!pluginsGroup) {
634
+ this.addPbxGroup([file.path], 'Plugins');
635
+ } else {
636
+ pluginsGroup.children.push(pbxGroupChild(file));
637
+ }
638
+ }
639
+
640
+ pbxProject.prototype.removeFromPluginsPbxGroup = function(file) {
641
+ if (!this.pbxGroupByName('Plugins')) {
642
+ return null;
643
+ }
644
+ var pluginsGroupChildren = this.pbxGroupByName('Plugins').children, i;
645
+ for (i in pluginsGroupChildren) {
646
+ if (pbxGroupChild(file).value == pluginsGroupChildren[i].value &&
647
+ pbxGroupChild(file).comment == pluginsGroupChildren[i].comment) {
648
+ pluginsGroupChildren.splice(i, 1);
649
+ break;
650
+ }
651
+ }
652
+ }
653
+
654
+ pbxProject.prototype.addToResourcesPbxGroup = function(file) {
655
+ var pluginsGroup = this.pbxGroupByName('Resources');
656
+ if (!pluginsGroup) {
657
+ this.addPbxGroup([file.path], 'Resources');
658
+ } else {
659
+ pluginsGroup.children.push(pbxGroupChild(file));
660
+ }
661
+ }
662
+
663
+ pbxProject.prototype.removeFromResourcesPbxGroup = function(file) {
664
+ if (!this.pbxGroupByName('Resources')) {
665
+ return null;
666
+ }
667
+ var pluginsGroupChildren = this.pbxGroupByName('Resources').children, i;
668
+ for (i in pluginsGroupChildren) {
669
+ if (pbxGroupChild(file).value == pluginsGroupChildren[i].value &&
670
+ pbxGroupChild(file).comment == pluginsGroupChildren[i].comment) {
671
+ pluginsGroupChildren.splice(i, 1);
672
+ break;
673
+ }
674
+ }
675
+ }
676
+
677
+ pbxProject.prototype.addToFrameworksPbxGroup = function(file) {
678
+ var pluginsGroup = this.pbxGroupByName('Frameworks');
679
+ if (!pluginsGroup) {
680
+ this.addPbxGroup([file.path], 'Frameworks');
681
+ } else {
682
+ pluginsGroup.children.push(pbxGroupChild(file));
683
+ }
684
+ }
685
+
686
+ pbxProject.prototype.removeFromFrameworksPbxGroup = function(file) {
687
+ if (!this.pbxGroupByName('Frameworks')) {
688
+ return null;
689
+ }
690
+ var pluginsGroupChildren = this.pbxGroupByName('Frameworks').children;
691
+
692
+ for (i in pluginsGroupChildren) {
693
+ if (pbxGroupChild(file).value == pluginsGroupChildren[i].value &&
694
+ pbxGroupChild(file).comment == pluginsGroupChildren[i].comment) {
695
+ pluginsGroupChildren.splice(i, 1);
696
+ break;
697
+ }
698
+ }
699
+ }
700
+
701
+ pbxProject.prototype.addToPbxEmbedFrameworksBuildPhase = function (file) {
702
+ var sources = this.pbxEmbedFrameworksBuildPhaseObj(file.target);
703
+ if (sources) {
704
+ sources.files.push(pbxBuildPhaseObj(file));
705
+ }
706
+ }
707
+
708
+ pbxProject.prototype.removeFromPbxEmbedFrameworksBuildPhase = function (file) {
709
+ var sources = this.pbxEmbedFrameworksBuildPhaseObj(file.target);
710
+ if (sources) {
711
+ var files = [];
712
+ for (i in sources.files) {
713
+ if (sources.files[i].comment != longComment(file)) {
714
+ files.push(sources.files[i]);
715
+ }
716
+ }
717
+ sources.files = files;
718
+ }
719
+ }
720
+
721
+ pbxProject.prototype.addToProductsPbxGroup = function(file) {
722
+ var productsGroup = this.pbxGroupByName('Products');
723
+ if (!productsGroup) {
724
+ this.addPbxGroup([file.path], 'Products');
725
+ } else {
726
+ productsGroup.children.push(pbxGroupChild(file));
727
+ }
728
+ }
729
+
730
+ pbxProject.prototype.removeFromProductsPbxGroup = function(file) {
731
+ if (!this.pbxGroupByName('Products')) {
732
+ return null;
733
+ }
734
+ var productsGroupChildren = this.pbxGroupByName('Products').children, i;
735
+ for (i in productsGroupChildren) {
736
+ if (pbxGroupChild(file).value == productsGroupChildren[i].value &&
737
+ pbxGroupChild(file).comment == productsGroupChildren[i].comment) {
738
+ productsGroupChildren.splice(i, 1);
739
+ break;
740
+ }
741
+ }
742
+ }
743
+
744
+ pbxProject.prototype.addToPbxSourcesBuildPhase = function(file) {
745
+ var sources = this.pbxSourcesBuildPhaseObj(file.target);
746
+ sources.files.push(pbxBuildPhaseObj(file));
747
+ }
748
+
749
+ pbxProject.prototype.removeFromPbxSourcesBuildPhase = function(file) {
750
+
751
+ var sources = this.pbxSourcesBuildPhaseObj(file.target), i;
752
+ for (i in sources.files) {
753
+ if (sources.files[i].comment == longComment(file)) {
754
+ sources.files.splice(i, 1);
755
+ break;
756
+ }
757
+ }
758
+ }
759
+
760
+ pbxProject.prototype.addToPbxResourcesBuildPhase = function(file) {
761
+ var sources = this.pbxResourcesBuildPhaseObj(file.target);
762
+ sources.files.push(pbxBuildPhaseObj(file));
763
+ }
764
+
765
+ pbxProject.prototype.removeFromPbxResourcesBuildPhase = function(file) {
766
+ var sources = this.pbxResourcesBuildPhaseObj(file.target), i;
767
+
768
+ for (i in sources.files) {
769
+ if (sources.files[i].comment == longComment(file)) {
770
+ sources.files.splice(i, 1);
771
+ break;
772
+ }
773
+ }
774
+ }
775
+
776
+ pbxProject.prototype.addToPbxFrameworksBuildPhase = function(file) {
777
+ var sources = this.pbxFrameworksBuildPhaseObj(file.target);
778
+ sources.files.push(pbxBuildPhaseObj(file));
779
+ }
780
+
781
+ pbxProject.prototype.removeFromPbxFrameworksBuildPhase = function(file) {
782
+ var sources = this.pbxFrameworksBuildPhaseObj(file.target);
783
+ for (i in sources.files) {
784
+ if (sources.files[i].comment == longComment(file)) {
785
+ sources.files.splice(i, 1);
786
+ break;
787
+ }
788
+ }
789
+ }
790
+
791
+ pbxProject.prototype.addXCConfigurationList = function(configurationObjectsArray, defaultConfigurationName, comment) {
792
+ var pbxBuildConfigurationSection = this.pbxXCBuildConfigurationSection(),
793
+ pbxXCConfigurationListSection = this.pbxXCConfigurationList(),
794
+ xcConfigurationListUuid = this.generateUuid(),
795
+ commentKey = f("%s_comment", xcConfigurationListUuid),
796
+ xcConfigurationList = {
797
+ isa: 'XCConfigurationList',
798
+ buildConfigurations: [],
799
+ defaultConfigurationIsVisible: 0,
800
+ defaultConfigurationName: defaultConfigurationName
801
+ };
802
+
803
+ for (var index = 0; index < configurationObjectsArray.length; index++) {
804
+ var configuration = configurationObjectsArray[index],
805
+ configurationUuid = this.generateUuid(),
806
+ configurationCommentKey = f("%s_comment", configurationUuid);
807
+
808
+ pbxBuildConfigurationSection[configurationUuid] = configuration;
809
+ pbxBuildConfigurationSection[configurationCommentKey] = configuration.name;
810
+ xcConfigurationList.buildConfigurations.push({ value: configurationUuid, comment: configuration.name });
811
+ }
812
+
813
+ if (pbxXCConfigurationListSection) {
814
+ pbxXCConfigurationListSection[xcConfigurationListUuid] = xcConfigurationList;
815
+ pbxXCConfigurationListSection[commentKey] = comment;
816
+ }
817
+
818
+ return { uuid: xcConfigurationListUuid, xcConfigurationList: xcConfigurationList };
819
+ }
820
+
821
+ pbxProject.prototype.addTargetDependency = function(target, dependencyTargets) {
822
+ if (!target)
823
+ return undefined;
824
+
825
+ var nativeTargets = this.pbxNativeTargetSection();
826
+
827
+ if (typeof nativeTargets[target] == "undefined")
828
+ throw new Error("Invalid target: " + target);
829
+
830
+ for (var index = 0; index < dependencyTargets.length; index++) {
831
+ var dependencyTarget = dependencyTargets[index];
832
+ if (typeof nativeTargets[dependencyTarget] == "undefined")
833
+ throw new Error("Invalid target: " + dependencyTarget);
834
+ }
835
+
836
+ var pbxTargetDependency = 'PBXTargetDependency',
837
+ pbxContainerItemProxy = 'PBXContainerItemProxy',
838
+ pbxTargetDependencySection = this.hash.project.objects[pbxTargetDependency],
839
+ pbxContainerItemProxySection = this.hash.project.objects[pbxContainerItemProxy];
840
+
841
+ for (var index = 0; index < dependencyTargets.length; index++) {
842
+ var dependencyTargetUuid = dependencyTargets[index],
843
+ dependencyTargetCommentKey = f("%s_comment", dependencyTargetUuid),
844
+ targetDependencyUuid = this.generateUuid(),
845
+ targetDependencyCommentKey = f("%s_comment", targetDependencyUuid),
846
+ itemProxyUuid = this.generateUuid(),
847
+ itemProxyCommentKey = f("%s_comment", itemProxyUuid),
848
+ itemProxy = {
849
+ isa: pbxContainerItemProxy,
850
+ containerPortal: this.hash.project['rootObject'],
851
+ containerPortal_comment: this.hash.project['rootObject_comment'],
852
+ proxyType: 1,
853
+ remoteGlobalIDString: dependencyTargetUuid,
854
+ remoteInfo: nativeTargets[dependencyTargetUuid].name
855
+ },
856
+ targetDependency = {
857
+ isa: pbxTargetDependency,
858
+ target: dependencyTargetUuid,
859
+ target_comment: nativeTargets[dependencyTargetCommentKey],
860
+ targetProxy: itemProxyUuid,
861
+ targetProxy_comment: pbxContainerItemProxy
862
+ };
863
+
864
+ if (pbxContainerItemProxySection && pbxTargetDependencySection) {
865
+ pbxContainerItemProxySection[itemProxyUuid] = itemProxy;
866
+ pbxContainerItemProxySection[itemProxyCommentKey] = pbxContainerItemProxy;
867
+ pbxTargetDependencySection[targetDependencyUuid] = targetDependency;
868
+ pbxTargetDependencySection[targetDependencyCommentKey] = pbxTargetDependency;
869
+ nativeTargets[target].dependencies.push({ value: targetDependencyUuid, comment: pbxTargetDependency })
870
+ }
871
+ }
872
+
873
+ return { uuid: target, target: nativeTargets[target] };
874
+ }
875
+
876
+ pbxProject.prototype.addBuildPhase = function(filePathsArray, buildPhaseType, comment, target, optionsOrFolderType, subfolderPath) {
877
+ var buildPhaseSection,
878
+ fileReferenceSection = this.pbxFileReferenceSection(),
879
+ buildFileSection = this.pbxBuildFileSection(),
880
+ buildPhaseUuid = this.generateUuid(),
881
+ buildPhaseTargetUuid = target || this.getFirstTarget().uuid,
882
+ commentKey = f("%s_comment", buildPhaseUuid),
883
+ buildPhase = {
884
+ isa: buildPhaseType,
885
+ buildActionMask: 2147483647,
886
+ files: [],
887
+ runOnlyForDeploymentPostprocessing: 0
888
+ },
889
+ filePathToBuildFile = {};
890
+
891
+ if (buildPhaseType === 'PBXCopyFilesBuildPhase') {
892
+ buildPhase = pbxCopyFilesBuildPhaseObj(buildPhase, optionsOrFolderType, subfolderPath, comment);
893
+ } else if (buildPhaseType === 'PBXShellScriptBuildPhase') {
894
+ buildPhase = pbxShellScriptBuildPhaseObj(buildPhase, optionsOrFolderType, comment)
895
+ }
896
+
897
+ if (!this.hash.project.objects[buildPhaseType]) {
898
+ this.hash.project.objects[buildPhaseType] = new Object();
899
+ }
900
+
901
+ if (!this.hash.project.objects[buildPhaseType][buildPhaseUuid]) {
902
+ this.hash.project.objects[buildPhaseType][buildPhaseUuid] = buildPhase;
903
+ this.hash.project.objects[buildPhaseType][commentKey] = comment;
904
+ }
905
+
906
+ if (this.hash.project.objects['PBXNativeTarget'][buildPhaseTargetUuid]['buildPhases']) {
907
+ this.hash.project.objects['PBXNativeTarget'][buildPhaseTargetUuid]['buildPhases'].push({
908
+ value: buildPhaseUuid,
909
+ comment: comment
910
+ })
911
+
912
+ }
913
+
914
+
915
+ for (var key in buildFileSection) {
916
+ // only look for comments
917
+ if (!COMMENT_KEY.test(key)) continue;
918
+
919
+ var buildFileKey = key.split(COMMENT_KEY)[0],
920
+ buildFile = buildFileSection[buildFileKey];
921
+ fileReference = fileReferenceSection[buildFile.fileRef];
922
+
923
+ if (!fileReference) continue;
924
+
925
+ var pbxFileObj = new pbxFile(fileReference.path);
926
+
927
+ filePathToBuildFile[fileReference.path] = { uuid: buildFileKey, basename: pbxFileObj.basename, group: pbxFileObj.group };
928
+ }
929
+
930
+ for (var index = 0; index < filePathsArray.length; index++) {
931
+ var filePath = filePathsArray[index],
932
+ filePathQuoted = "\"" + filePath + "\"",
933
+ file = new pbxFile(filePath);
934
+
935
+ if (filePathToBuildFile[filePath]) {
936
+ buildPhase.files.push(pbxBuildPhaseObj(filePathToBuildFile[filePath]));
937
+ continue;
938
+ } else if (filePathToBuildFile[filePathQuoted]) {
939
+ buildPhase.files.push(pbxBuildPhaseObj(filePathToBuildFile[filePathQuoted]));
940
+ continue;
941
+ }
942
+
943
+ file.uuid = this.generateUuid();
944
+ file.fileRef = this.generateUuid();
945
+ this.addToPbxFileReferenceSection(file); // PBXFileReference
946
+ this.addToPbxBuildFileSection(file); // PBXBuildFile
947
+ buildPhase.files.push(pbxBuildPhaseObj(file));
948
+ }
949
+
950
+ if (buildPhaseSection) {
951
+ buildPhaseSection[buildPhaseUuid] = buildPhase;
952
+ buildPhaseSection[commentKey] = comment;
953
+ }
954
+
955
+ return { uuid: buildPhaseUuid, buildPhase: buildPhase };
956
+ }
957
+
958
+ // helper access functions
959
+ pbxProject.prototype.pbxProjectSection = function() {
960
+ return this.hash.project.objects['PBXProject'];
961
+ }
962
+ pbxProject.prototype.pbxBuildFileSection = function() {
963
+ return this.hash.project.objects['PBXBuildFile'];
964
+ }
965
+
966
+ pbxProject.prototype.pbxXCBuildConfigurationSection = function() {
967
+ return this.hash.project.objects['XCBuildConfiguration'];
968
+ }
969
+
970
+ pbxProject.prototype.pbxFileReferenceSection = function() {
971
+ return this.hash.project.objects['PBXFileReference'];
972
+ }
973
+
974
+ pbxProject.prototype.pbxNativeTargetSection = function() {
975
+ return this.hash.project.objects['PBXNativeTarget'];
976
+ }
977
+
978
+ pbxProject.prototype.xcVersionGroupSection = function () {
979
+ if (typeof this.hash.project.objects['XCVersionGroup'] !== 'object') {
980
+ this.hash.project.objects['XCVersionGroup'] = {};
981
+ }
982
+
983
+ return this.hash.project.objects['XCVersionGroup'];
984
+ }
985
+
986
+ pbxProject.prototype.pbxXCConfigurationList = function() {
987
+ return this.hash.project.objects['XCConfigurationList'];
988
+ }
989
+
990
+ pbxProject.prototype.pbxGroupByName = function(name) {
991
+ var groups = this.hash.project.objects['PBXGroup'],
992
+ key, groupKey;
993
+
994
+ for (key in groups) {
995
+ // only look for comments
996
+ if (!COMMENT_KEY.test(key)) continue;
997
+
998
+ if (groups[key] == name) {
999
+ groupKey = key.split(COMMENT_KEY)[0];
1000
+ return groups[groupKey];
1001
+ }
1002
+ }
1003
+
1004
+ return null;
1005
+ }
1006
+
1007
+ pbxProject.prototype.pbxTargetByName = function(name) {
1008
+ return this.pbxItemByComment(name, 'PBXNativeTarget');
1009
+ }
1010
+
1011
+ pbxProject.prototype.findTargetKey = function(name) {
1012
+ var targets = this.hash.project.objects['PBXNativeTarget'];
1013
+
1014
+ for (var key in targets) {
1015
+ // only look for comments
1016
+ if (COMMENT_KEY.test(key)) continue;
1017
+
1018
+ var target = targets[key];
1019
+ if (target.name === name) {
1020
+ return key;
1021
+ }
1022
+ }
1023
+
1024
+ return null;
1025
+ }
1026
+
1027
+ pbxProject.prototype.pbxItemByComment = function(name, pbxSectionName) {
1028
+ var section = this.hash.project.objects[pbxSectionName],
1029
+ key, itemKey;
1030
+
1031
+ for (key in section) {
1032
+ // only look for comments
1033
+ if (!COMMENT_KEY.test(key)) continue;
1034
+
1035
+ if (section[key] == name) {
1036
+ itemKey = key.split(COMMENT_KEY)[0];
1037
+ return section[itemKey];
1038
+ }
1039
+ }
1040
+
1041
+ return null;
1042
+ }
1043
+
1044
+ pbxProject.prototype.pbxSourcesBuildPhaseObj = function(target) {
1045
+ return this.buildPhaseObject('PBXSourcesBuildPhase', 'Sources', target);
1046
+ }
1047
+
1048
+ pbxProject.prototype.pbxResourcesBuildPhaseObj = function(target) {
1049
+ return this.buildPhaseObject('PBXResourcesBuildPhase', 'Resources', target);
1050
+ }
1051
+
1052
+ pbxProject.prototype.pbxFrameworksBuildPhaseObj = function(target) {
1053
+ return this.buildPhaseObject('PBXFrameworksBuildPhase', 'Frameworks', target);
1054
+ }
1055
+
1056
+ pbxProject.prototype.pbxEmbedFrameworksBuildPhaseObj = function (target) {
1057
+ return this.buildPhaseObject('PBXCopyFilesBuildPhase', 'Embed Frameworks', target);
1058
+ };
1059
+
1060
+ // Find Build Phase from group/target
1061
+ pbxProject.prototype.buildPhase = function(group, target) {
1062
+
1063
+ if (!target)
1064
+ return undefined;
1065
+
1066
+ var nativeTargets = this.pbxNativeTargetSection();
1067
+ if (typeof nativeTargets[target] == "undefined")
1068
+ throw new Error("Invalid target: " + target);
1069
+
1070
+ var nativeTarget = nativeTargets[target];
1071
+ var buildPhases = nativeTarget.buildPhases;
1072
+ for(var i in buildPhases)
1073
+ {
1074
+ var buildPhase = buildPhases[i];
1075
+ if (buildPhase.comment==group)
1076
+ return buildPhase.value + "_comment";
1077
+ }
1078
+ }
1079
+
1080
+ pbxProject.prototype.buildPhaseObject = function(name, group, target) {
1081
+ var section = this.hash.project.objects[name],
1082
+ obj, sectionKey, key;
1083
+ var buildPhase = this.buildPhase(group, target);
1084
+
1085
+ for (key in section) {
1086
+
1087
+ // only look for comments
1088
+ if (!COMMENT_KEY.test(key)) continue;
1089
+
1090
+ // select the proper buildPhase
1091
+ if (buildPhase && buildPhase!=key)
1092
+ continue;
1093
+ if (section[key] == group) {
1094
+ sectionKey = key.split(COMMENT_KEY)[0];
1095
+ return section[sectionKey];
1096
+ }
1097
+ }
1098
+ return null;
1099
+ }
1100
+
1101
+ pbxProject.prototype.addBuildProperty = function(prop, value, build_name) {
1102
+ var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
1103
+ key, configuration;
1104
+
1105
+ for (key in configurations){
1106
+ configuration = configurations[key];
1107
+ if (!build_name || configuration.name === build_name){
1108
+ configuration.buildSettings[prop] = value;
1109
+ }
1110
+ }
1111
+ }
1112
+
1113
+ pbxProject.prototype.removeBuildProperty = function(prop, build_name) {
1114
+ var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
1115
+ key, configuration;
1116
+
1117
+ for (key in configurations){
1118
+ configuration = configurations[key];
1119
+ if (configuration.buildSettings[prop] &&
1120
+ !build_name || configuration.name === build_name){
1121
+ delete configuration.buildSettings[prop];
1122
+ }
1123
+ }
1124
+ }
1125
+
1126
+ /**
1127
+ *
1128
+ * @param prop {String}
1129
+ * @param value {String|Array|Object|Number|Boolean}
1130
+ * @param build {String} Release or Debug
1131
+ * @param targetName {String} the target which will be updated
1132
+ */
1133
+ pbxProject.prototype.updateBuildProperty = function(prop, value, build, targetName) {
1134
+ let validConfigs = [];
1135
+
1136
+ if(targetName) {
1137
+ const target = this.pbxTargetByName(targetName);
1138
+ const targetBuildConfigs = target && target.buildConfigurationList;
1139
+
1140
+ const xcConfigList = this.pbxXCConfigurationList();
1141
+
1142
+ // Collect the UUID's from the configuration of our target
1143
+ for (const configName in xcConfigList) {
1144
+ if (!COMMENT_KEY.test(configName) && targetBuildConfigs === configName) {
1145
+ const buildVariants = xcConfigList[configName].buildConfigurations;
1146
+
1147
+ for (const item of buildVariants) {
1148
+ validConfigs.push(item.value);
1149
+ }
1150
+
1151
+ break;
1152
+ }
1153
+ }
1154
+ }
1155
+
1156
+ var configs = this.pbxXCBuildConfigurationSection();
1157
+ for (var configName in configs) {
1158
+ if (!COMMENT_KEY.test(configName)) {
1159
+ if (targetName && !validConfigs.includes(configName)) continue;
1160
+
1161
+ var config = configs[configName];
1162
+ if ( (build && config.name === build) || (!build) ) {
1163
+ config.buildSettings[prop] = value;
1164
+ }
1165
+ }
1166
+ }
1167
+ }
1168
+
1169
+ pbxProject.prototype.updateProductName = function(name) {
1170
+ this.updateBuildProperty('PRODUCT_NAME', '"' + name + '"');
1171
+ }
1172
+
1173
+ pbxProject.prototype.removeFromFrameworkSearchPaths = function(file) {
1174
+ var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
1175
+ INHERITED = '"$(inherited)"',
1176
+ SEARCH_PATHS = 'FRAMEWORK_SEARCH_PATHS',
1177
+ config, buildSettings, searchPaths;
1178
+ var new_path = searchPathForFile(file, this);
1179
+
1180
+ for (config in configurations) {
1181
+ buildSettings = configurations[config].buildSettings;
1182
+
1183
+ if (unquote(buildSettings['PRODUCT_NAME']) != this.productName)
1184
+ continue;
1185
+
1186
+ searchPaths = buildSettings[SEARCH_PATHS];
1187
+
1188
+ if (searchPaths && Array.isArray(searchPaths)) {
1189
+ var matches = searchPaths.filter(function(p) {
1190
+ return p.indexOf(new_path) > -1;
1191
+ });
1192
+ matches.forEach(function(m) {
1193
+ var idx = searchPaths.indexOf(m);
1194
+ searchPaths.splice(idx, 1);
1195
+ });
1196
+ }
1197
+ }
1198
+ }
1199
+
1200
+ pbxProject.prototype.addToFrameworkSearchPaths = function(file) {
1201
+ var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
1202
+ INHERITED = '"$(inherited)"',
1203
+ config, buildSettings, searchPaths;
1204
+
1205
+ for (config in configurations) {
1206
+ buildSettings = configurations[config].buildSettings;
1207
+
1208
+ if (unquote(buildSettings['PRODUCT_NAME']) != this.productName)
1209
+ continue;
1210
+
1211
+ if (!buildSettings['FRAMEWORK_SEARCH_PATHS']
1212
+ || buildSettings['FRAMEWORK_SEARCH_PATHS'] === INHERITED) {
1213
+ buildSettings['FRAMEWORK_SEARCH_PATHS'] = [INHERITED];
1214
+ }
1215
+
1216
+ buildSettings['FRAMEWORK_SEARCH_PATHS'].push(searchPathForFile(file, this));
1217
+ }
1218
+ }
1219
+
1220
+ pbxProject.prototype.removeFromLibrarySearchPaths = function(file) {
1221
+ var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
1222
+ INHERITED = '"$(inherited)"',
1223
+ SEARCH_PATHS = 'LIBRARY_SEARCH_PATHS',
1224
+ config, buildSettings, searchPaths;
1225
+ var new_path = searchPathForFile(file, this);
1226
+
1227
+ for (config in configurations) {
1228
+ buildSettings = configurations[config].buildSettings;
1229
+
1230
+ if (unquote(buildSettings['PRODUCT_NAME']) != this.productName)
1231
+ continue;
1232
+
1233
+ searchPaths = buildSettings[SEARCH_PATHS];
1234
+
1235
+ if (searchPaths && Array.isArray(searchPaths)) {
1236
+ var matches = searchPaths.filter(function(p) {
1237
+ return p.indexOf(new_path) > -1;
1238
+ });
1239
+ matches.forEach(function(m) {
1240
+ var idx = searchPaths.indexOf(m);
1241
+ searchPaths.splice(idx, 1);
1242
+ });
1243
+ }
1244
+
1245
+ }
1246
+ }
1247
+
1248
+ pbxProject.prototype.addToLibrarySearchPaths = function(file) {
1249
+ var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
1250
+ INHERITED = '"$(inherited)"',
1251
+ config, buildSettings, searchPaths;
1252
+
1253
+ for (config in configurations) {
1254
+ buildSettings = configurations[config].buildSettings;
1255
+
1256
+ if (unquote(buildSettings['PRODUCT_NAME']) != this.productName)
1257
+ continue;
1258
+
1259
+ if (!buildSettings['LIBRARY_SEARCH_PATHS']
1260
+ || buildSettings['LIBRARY_SEARCH_PATHS'] === INHERITED) {
1261
+ buildSettings['LIBRARY_SEARCH_PATHS'] = [INHERITED];
1262
+ }
1263
+
1264
+ if (typeof file === 'string') {
1265
+ buildSettings['LIBRARY_SEARCH_PATHS'].push(file);
1266
+ } else {
1267
+ buildSettings['LIBRARY_SEARCH_PATHS'].push(searchPathForFile(file, this));
1268
+ }
1269
+ }
1270
+ }
1271
+
1272
+ pbxProject.prototype.removeFromHeaderSearchPaths = function(file) {
1273
+ var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
1274
+ INHERITED = '"$(inherited)"',
1275
+ SEARCH_PATHS = 'HEADER_SEARCH_PATHS',
1276
+ config, buildSettings, searchPaths;
1277
+ var new_path = searchPathForFile(file, this);
1278
+
1279
+ for (config in configurations) {
1280
+ buildSettings = configurations[config].buildSettings;
1281
+
1282
+ if (unquote(buildSettings['PRODUCT_NAME']) != this.productName)
1283
+ continue;
1284
+
1285
+ if (buildSettings[SEARCH_PATHS]) {
1286
+ var matches = buildSettings[SEARCH_PATHS].filter(function(p) {
1287
+ return p.indexOf(new_path) > -1;
1288
+ });
1289
+ matches.forEach(function(m) {
1290
+ var idx = buildSettings[SEARCH_PATHS].indexOf(m);
1291
+ buildSettings[SEARCH_PATHS].splice(idx, 1);
1292
+ });
1293
+ }
1294
+
1295
+ }
1296
+ }
1297
+ pbxProject.prototype.addToHeaderSearchPaths = function(file) {
1298
+ var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
1299
+ INHERITED = '"$(inherited)"',
1300
+ config, buildSettings, searchPaths;
1301
+
1302
+ for (config in configurations) {
1303
+ buildSettings = configurations[config].buildSettings;
1304
+
1305
+ if (unquote(buildSettings['PRODUCT_NAME']) != this.productName)
1306
+ continue;
1307
+
1308
+ if (!buildSettings['HEADER_SEARCH_PATHS']) {
1309
+ buildSettings['HEADER_SEARCH_PATHS'] = [INHERITED];
1310
+ }
1311
+
1312
+ if (typeof file === 'string') {
1313
+ buildSettings['HEADER_SEARCH_PATHS'].push(file);
1314
+ } else {
1315
+ buildSettings['HEADER_SEARCH_PATHS'].push(searchPathForFile(file, this));
1316
+ }
1317
+ }
1318
+ }
1319
+
1320
+ pbxProject.prototype.addToOtherLinkerFlags = function (flag) {
1321
+ var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
1322
+ INHERITED = '"$(inherited)"',
1323
+ OTHER_LDFLAGS = 'OTHER_LDFLAGS',
1324
+ config, buildSettings;
1325
+
1326
+ for (config in configurations) {
1327
+ buildSettings = configurations[config].buildSettings;
1328
+
1329
+ if (unquote(buildSettings['PRODUCT_NAME']) != this.productName)
1330
+ continue;
1331
+
1332
+ if (!buildSettings[OTHER_LDFLAGS]
1333
+ || buildSettings[OTHER_LDFLAGS] === INHERITED) {
1334
+ buildSettings[OTHER_LDFLAGS] = [INHERITED];
1335
+ }
1336
+
1337
+ buildSettings[OTHER_LDFLAGS].push(flag);
1338
+ }
1339
+ }
1340
+
1341
+ pbxProject.prototype.removeFromOtherLinkerFlags = function (flag) {
1342
+ var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
1343
+ OTHER_LDFLAGS = 'OTHER_LDFLAGS',
1344
+ config, buildSettings;
1345
+
1346
+ for (config in configurations) {
1347
+ buildSettings = configurations[config].buildSettings;
1348
+
1349
+ if (unquote(buildSettings['PRODUCT_NAME']) != this.productName) {
1350
+ continue;
1351
+ }
1352
+
1353
+ if (buildSettings[OTHER_LDFLAGS]) {
1354
+ var matches = buildSettings[OTHER_LDFLAGS].filter(function (p) {
1355
+ return p.indexOf(flag) > -1;
1356
+ });
1357
+ matches.forEach(function (m) {
1358
+ var idx = buildSettings[OTHER_LDFLAGS].indexOf(m);
1359
+ buildSettings[OTHER_LDFLAGS].splice(idx, 1);
1360
+ });
1361
+ }
1362
+ }
1363
+ }
1364
+
1365
+ pbxProject.prototype.addToBuildSettings = function (buildSetting, value) {
1366
+ var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
1367
+ config, buildSettings;
1368
+
1369
+ for (config in configurations) {
1370
+ buildSettings = configurations[config].buildSettings;
1371
+
1372
+ buildSettings[buildSetting] = value;
1373
+ }
1374
+ }
1375
+
1376
+ pbxProject.prototype.removeFromBuildSettings = function (buildSetting) {
1377
+ var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
1378
+ config, buildSettings;
1379
+
1380
+ for (config in configurations) {
1381
+ buildSettings = configurations[config].buildSettings;
1382
+
1383
+ if (buildSettings[buildSetting]) {
1384
+ delete buildSettings[buildSetting];
1385
+ }
1386
+ }
1387
+ }
1388
+
1389
+ // a JS getter. hmmm
1390
+ pbxProject.prototype.__defineGetter__("productName", function() {
1391
+ var configurations = nonComments(this.pbxXCBuildConfigurationSection()),
1392
+ config, productName;
1393
+
1394
+ for (config in configurations) {
1395
+ productName = configurations[config].buildSettings['PRODUCT_NAME'];
1396
+
1397
+ if (productName) {
1398
+ return unquote(productName);
1399
+ }
1400
+ }
1401
+ });
1402
+
1403
+ // check if file is present
1404
+ pbxProject.prototype.hasFile = function(filePath) {
1405
+ var files = nonComments(this.pbxFileReferenceSection()),
1406
+ file, id;
1407
+ for (id in files) {
1408
+ file = files[id];
1409
+ if (file.path == filePath || file.path == ('"' + filePath + '"')) {
1410
+ return file;
1411
+ }
1412
+ }
1413
+
1414
+ return false;
1415
+ }
1416
+
1417
+ pbxProject.prototype.addTarget = function(name, type, subfolder, bundleId) {
1418
+
1419
+ // Setup uuid and name of new target
1420
+ var targetUuid = this.generateUuid(),
1421
+ targetType = type,
1422
+ targetSubfolder = subfolder || name,
1423
+ targetName = name.trim(),
1424
+ targetBundleId = bundleId;
1425
+
1426
+ // Check type against list of allowed target types
1427
+ if (!targetName) {
1428
+ throw new Error("Target name missing.");
1429
+ }
1430
+
1431
+ // Check type against list of allowed target types
1432
+ if (!targetType) {
1433
+ throw new Error("Target type missing.");
1434
+ }
1435
+
1436
+ // Check type against list of allowed target types
1437
+ if (!producttypeForTargettype(targetType)) {
1438
+ throw new Error("Target type invalid: " + targetType);
1439
+ }
1440
+
1441
+ // Build Configuration: Create
1442
+ var buildConfigurationsList = [
1443
+ {
1444
+ name: 'Debug',
1445
+ isa: 'XCBuildConfiguration',
1446
+ buildSettings: {
1447
+ GCC_PREPROCESSOR_DEFINITIONS: ['"DEBUG=1"', '"$(inherited)"'],
1448
+ INFOPLIST_FILE: '"' + path.join(targetSubfolder, targetSubfolder + '-Info.plist' + '"'),
1449
+ LD_RUNPATH_SEARCH_PATHS: '"$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"',
1450
+ PRODUCT_NAME: '"' + targetName + '"',
1451
+ SKIP_INSTALL: 'YES'
1452
+ }
1453
+ },
1454
+ {
1455
+ name: 'Release',
1456
+ isa: 'XCBuildConfiguration',
1457
+ buildSettings: {
1458
+ INFOPLIST_FILE: '"' + path.join(targetSubfolder, targetSubfolder + '-Info.plist' + '"'),
1459
+ LD_RUNPATH_SEARCH_PATHS: '"$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"',
1460
+ PRODUCT_NAME: '"' + targetName + '"',
1461
+ SKIP_INSTALL: 'YES'
1462
+ }
1463
+ }
1464
+ ];
1465
+
1466
+ // Add optional bundleId to build configuration
1467
+ if (targetBundleId) {
1468
+ buildConfigurationsList = buildConfigurationsList.map((elem) => {
1469
+ elem.buildSettings.PRODUCT_BUNDLE_IDENTIFIER = '"' + targetBundleId + '"';
1470
+ return elem;
1471
+ });
1472
+ }
1473
+
1474
+ // Build Configuration: Add
1475
+ var buildConfigurations = this.addXCConfigurationList(buildConfigurationsList, 'Release', 'Build configuration list for PBXNativeTarget "' + targetName +'"');
1476
+
1477
+ // Product: Create
1478
+ var productName = targetName,
1479
+ productType = producttypeForTargettype(targetType),
1480
+ productFileType = filetypeForProducttype(productType),
1481
+ productFile = this.addProductFile(productName, { group: 'Copy Files', 'target': targetUuid, 'explicitFileType': productFileType}),
1482
+ productFileName = productFile.basename;
1483
+
1484
+
1485
+ // Product: Add to build file list
1486
+ this.addToPbxBuildFileSection(productFile);
1487
+
1488
+ // Target: Create
1489
+ var target = {
1490
+ uuid: targetUuid,
1491
+ pbxNativeTarget: {
1492
+ isa: 'PBXNativeTarget',
1493
+ name: '"' + targetName + '"',
1494
+ productName: '"' + targetName + '"',
1495
+ productReference: productFile.fileRef,
1496
+ productType: '"' + producttypeForTargettype(targetType) + '"',
1497
+ buildConfigurationList: buildConfigurations.uuid,
1498
+ buildPhases: [],
1499
+ buildRules: [],
1500
+ dependencies: []
1501
+ }
1502
+ };
1503
+
1504
+ // Target: Add to PBXNativeTarget section
1505
+ this.addToPbxNativeTargetSection(target)
1506
+
1507
+ // Product: Embed (only for "extension"-type targets)
1508
+ if (targetType === 'app_extension') {
1509
+
1510
+ // Create CopyFiles phase in first target
1511
+ this.addBuildPhase([], 'PBXCopyFilesBuildPhase', 'Copy Files', this.getFirstTarget().uuid, targetType)
1512
+
1513
+ // Add product to CopyFiles phase
1514
+ this.addToPbxCopyfilesBuildPhase(productFile)
1515
+
1516
+ // this.addBuildPhaseToTarget(newPhase.buildPhase, this.getFirstTarget().uuid)
1517
+ } else if (targetType === 'watch2_app') {
1518
+ // Create CopyFiles phase in first target
1519
+ this.addBuildPhase(
1520
+ [targetName + '.app'],
1521
+ 'PBXCopyFilesBuildPhase',
1522
+ 'Embed Watch Content',
1523
+ this.getFirstTarget().uuid,
1524
+ targetType,
1525
+ '"$(CONTENTS_FOLDER_PATH)/Watch"'
1526
+ );
1527
+ } else if (targetType === 'watch2_extension') {
1528
+ // Create CopyFiles phase in watch target (if exists)
1529
+ var watch2Target = this.getTarget(producttypeForTargettype('watch2_app'));
1530
+ if (watch2Target) {
1531
+ this.addBuildPhase(
1532
+ [targetName + '.appex'],
1533
+ 'PBXCopyFilesBuildPhase',
1534
+ 'Embed App Extensions',
1535
+ watch2Target.uuid,
1536
+ targetType
1537
+ );
1538
+ }
1539
+ }
1540
+
1541
+ // Target: Add uuid to root project
1542
+ this.addToPbxProjectSection(target);
1543
+
1544
+ // Target: Add dependency for this target to other targets
1545
+ if (targetType === 'watch2_extension') {
1546
+ var watch2Target = this.getTarget(producttypeForTargettype('watch2_app'));
1547
+ if (watch2Target) {
1548
+ this.addTargetDependency(watch2Target.uuid, [target.uuid]);
1549
+ }
1550
+ } else {
1551
+ this.addTargetDependency(this.getFirstTarget().uuid, [target.uuid]);
1552
+ }
1553
+
1554
+
1555
+ // Return target on success
1556
+ return target;
1557
+
1558
+ };
1559
+
1560
+ // helper object creation functions
1561
+ function pbxBuildFileObj(file) {
1562
+ var obj = Object.create(null);
1563
+
1564
+ obj.isa = 'PBXBuildFile';
1565
+ obj.fileRef = file.fileRef;
1566
+ obj.fileRef_comment = file.basename;
1567
+ if (file.settings) obj.settings = file.settings;
1568
+
1569
+ return obj;
1570
+ }
1571
+
1572
+ function pbxFileReferenceObj(file) {
1573
+ var fileObject = {
1574
+ isa: "PBXFileReference",
1575
+ name: "\"" + file.basename + "\"",
1576
+ path: "\"" + file.path.replace(/\\/g, '/') + "\"",
1577
+ sourceTree: file.sourceTree,
1578
+ fileEncoding: file.fileEncoding,
1579
+ lastKnownFileType: file.lastKnownFileType,
1580
+ explicitFileType: file.explicitFileType,
1581
+ includeInIndex: file.includeInIndex
1582
+ };
1583
+
1584
+ return fileObject;
1585
+ }
1586
+
1587
+ function pbxGroupChild(file) {
1588
+ var obj = Object.create(null);
1589
+
1590
+ obj.value = file.fileRef;
1591
+ obj.comment = file.basename;
1592
+
1593
+ return obj;
1594
+ }
1595
+
1596
+ function pbxBuildPhaseObj(file) {
1597
+ var obj = Object.create(null);
1598
+
1599
+ obj.value = file.uuid;
1600
+ obj.comment = longComment(file);
1601
+
1602
+ return obj;
1603
+ }
1604
+
1605
+ function pbxCopyFilesBuildPhaseObj(obj, folderType, subfolderPath, phaseName) {
1606
+
1607
+ // Add additional properties for 'CopyFiles' build phase
1608
+ var DESTINATION_BY_TARGETTYPE = {
1609
+ application: 'wrapper',
1610
+ app_extension: 'plugins',
1611
+ bundle: 'wrapper',
1612
+ command_line_tool: 'wrapper',
1613
+ dynamic_library: 'products_directory',
1614
+ framework: 'shared_frameworks',
1615
+ frameworks: 'frameworks',
1616
+ static_library: 'products_directory',
1617
+ unit_test_bundle: 'wrapper',
1618
+ watch_app: 'wrapper',
1619
+ watch2_app: 'products_directory',
1620
+ watch_extension: 'plugins',
1621
+ watch2_extension: 'plugins'
1622
+ }
1623
+ var SUBFOLDERSPEC_BY_DESTINATION = {
1624
+ absolute_path: 0,
1625
+ executables: 6,
1626
+ frameworks: 10,
1627
+ java_resources: 15,
1628
+ plugins: 13,
1629
+ products_directory: 16,
1630
+ resources: 7,
1631
+ shared_frameworks: 11,
1632
+ shared_support: 12,
1633
+ wrapper: 1,
1634
+ xpc_services: 0
1635
+ }
1636
+
1637
+ obj.name = '"' + phaseName + '"';
1638
+ obj.dstPath = subfolderPath || '""';
1639
+ obj.dstSubfolderSpec = SUBFOLDERSPEC_BY_DESTINATION[DESTINATION_BY_TARGETTYPE[folderType]];
1640
+
1641
+ return obj;
1642
+ }
1643
+
1644
+ function pbxShellScriptBuildPhaseObj(obj, options, phaseName) {
1645
+ obj.name = '"' + phaseName + '"';
1646
+ obj.inputPaths = options.inputPaths || [];
1647
+ obj.outputPaths = options.outputPaths || [];
1648
+ obj.shellPath = options.shellPath;
1649
+ obj.shellScript = '"' + options.shellScript.replace(/"/g, '\\"') + '"';
1650
+
1651
+ return obj;
1652
+ }
1653
+
1654
+ function pbxBuildFileComment(file) {
1655
+ return longComment(file);
1656
+ }
1657
+
1658
+ function pbxFileReferenceComment(file) {
1659
+ return file.basename || path.basename(file.path);
1660
+ }
1661
+
1662
+ function pbxNativeTargetComment(target) {
1663
+ return target.name;
1664
+ }
1665
+
1666
+ function longComment(file) {
1667
+ return f("%s in %s", file.basename, file.group);
1668
+ }
1669
+
1670
+ // respect <group> path
1671
+ function correctForPluginsPath(file, project) {
1672
+ return correctForPath(file, project, 'Plugins');
1673
+ }
1674
+
1675
+ function correctForResourcesPath(file, project) {
1676
+ return correctForPath(file, project, 'Resources');
1677
+ }
1678
+
1679
+ function correctForFrameworksPath(file, project) {
1680
+ return correctForPath(file, project, 'Frameworks');
1681
+ }
1682
+
1683
+ function correctForPath(file, project, group) {
1684
+ var r_group_dir = new RegExp('^' + group + '[\\\\/]');
1685
+
1686
+ if (project.pbxGroupByName(group).path)
1687
+ file.path = file.path.replace(r_group_dir, '');
1688
+
1689
+ return file;
1690
+ }
1691
+
1692
+ function searchPathForFile(file, proj) {
1693
+ var plugins = proj.pbxGroupByName('Plugins'),
1694
+ pluginsPath = plugins ? plugins.path : null,
1695
+ fileDir = path.dirname(file.path);
1696
+
1697
+ if (fileDir == '.') {
1698
+ fileDir = '';
1699
+ } else {
1700
+ fileDir = '/' + fileDir;
1701
+ }
1702
+
1703
+ if (file.plugin && pluginsPath) {
1704
+ return '"\\"$(SRCROOT)/' + unquote(pluginsPath) + '\\""';
1705
+ } else if (file.customFramework && file.dirname) {
1706
+ return '"\\"' + file.dirname + '\\""';
1707
+ } else {
1708
+ return '"\\"$(SRCROOT)/' + proj.productName + fileDir + '\\""';
1709
+ }
1710
+ }
1711
+
1712
+ function nonComments(obj) {
1713
+ var keys = Object.keys(obj),
1714
+ newObj = {}, i = 0;
1715
+
1716
+ for (i; i < keys.length; i++) {
1717
+ if (!COMMENT_KEY.test(keys[i])) {
1718
+ newObj[keys[i]] = obj[keys[i]];
1719
+ }
1720
+ }
1721
+
1722
+ return newObj;
1723
+ }
1724
+
1725
+ function unquote(str) {
1726
+ if (str) return str.replace(/^"(.*)"$/, "$1");
1727
+ }
1728
+
1729
+
1730
+ function buildPhaseNameForIsa (isa) {
1731
+
1732
+ BUILDPHASENAME_BY_ISA = {
1733
+ PBXCopyFilesBuildPhase: 'Copy Files',
1734
+ PBXResourcesBuildPhase: 'Resources',
1735
+ PBXSourcesBuildPhase: 'Sources',
1736
+ PBXFrameworksBuildPhase: 'Frameworks'
1737
+ }
1738
+
1739
+ return BUILDPHASENAME_BY_ISA[isa]
1740
+ }
1741
+
1742
+ function producttypeForTargettype (targetType) {
1743
+
1744
+ PRODUCTTYPE_BY_TARGETTYPE = {
1745
+ application: 'com.apple.product-type.application',
1746
+ app_extension: 'com.apple.product-type.app-extension',
1747
+ bundle: 'com.apple.product-type.bundle',
1748
+ command_line_tool: 'com.apple.product-type.tool',
1749
+ dynamic_library: 'com.apple.product-type.library.dynamic',
1750
+ framework: 'com.apple.product-type.framework',
1751
+ static_library: 'com.apple.product-type.library.static',
1752
+ unit_test_bundle: 'com.apple.product-type.bundle.unit-test',
1753
+ watch_app: 'com.apple.product-type.application.watchapp',
1754
+ watch2_app: 'com.apple.product-type.application.watchapp2',
1755
+ watch_extension: 'com.apple.product-type.watchkit-extension',
1756
+ watch2_extension: 'com.apple.product-type.watchkit2-extension'
1757
+ };
1758
+
1759
+ return PRODUCTTYPE_BY_TARGETTYPE[targetType]
1760
+ }
1761
+
1762
+ function filetypeForProducttype (productType) {
1763
+
1764
+ FILETYPE_BY_PRODUCTTYPE = {
1765
+ 'com.apple.product-type.application': '"wrapper.application"',
1766
+ 'com.apple.product-type.app-extension': '"wrapper.app-extension"',
1767
+ 'com.apple.product-type.bundle': '"wrapper.plug-in"',
1768
+ 'com.apple.product-type.tool': '"compiled.mach-o.dylib"',
1769
+ 'com.apple.product-type.library.dynamic': '"compiled.mach-o.dylib"',
1770
+ 'com.apple.product-type.framework': '"wrapper.framework"',
1771
+ 'com.apple.product-type.library.static': '"archive.ar"',
1772
+ 'com.apple.product-type.bundle.unit-test': '"wrapper.cfbundle"',
1773
+ 'com.apple.product-type.application.watchapp': '"wrapper.application"',
1774
+ 'com.apple.product-type.application.watchapp2': '"wrapper.application"',
1775
+ 'com.apple.product-type.watchkit-extension': '"wrapper.app-extension"',
1776
+ 'com.apple.product-type.watchkit2-extension': '"wrapper.app-extension"'
1777
+ };
1778
+
1779
+ return FILETYPE_BY_PRODUCTTYPE[productType]
1780
+ }
1781
+
1782
+ pbxProject.prototype.getFirstProject = function() {
1783
+
1784
+ // Get pbxProject container
1785
+ var pbxProjectContainer = this.pbxProjectSection();
1786
+
1787
+ // Get first pbxProject UUID
1788
+ var firstProjectUuid = Object.keys(pbxProjectContainer)[0];
1789
+
1790
+ // Get first pbxProject
1791
+ var firstProject = pbxProjectContainer[firstProjectUuid];
1792
+
1793
+ return {
1794
+ uuid: firstProjectUuid,
1795
+ firstProject: firstProject
1796
+ }
1797
+ }
1798
+
1799
+ pbxProject.prototype.getFirstTarget = function() {
1800
+ // Get first target's UUID
1801
+ var firstTargetUuid = this.getFirstProject()['firstProject']['targets'][0].value;
1802
+
1803
+ // Get first pbxNativeTarget
1804
+ var firstTarget = this.pbxNativeTargetSection()[firstTargetUuid];
1805
+
1806
+ return {
1807
+ uuid: firstTargetUuid,
1808
+ firstTarget: firstTarget
1809
+ }
1810
+ }
1811
+
1812
+ pbxProject.prototype.getTarget = function(productType) {
1813
+ // Find target by product type
1814
+ var targets = this.getFirstProject()['firstProject']['targets'];
1815
+ var nativeTargets = this.pbxNativeTargetSection();
1816
+ for (var i = 0; i < targets.length; i++) {
1817
+ var target = targets[i];
1818
+ var targetUuid = target.value;
1819
+ if (nativeTargets[targetUuid]['productType'] === '"' + productType + '"') {
1820
+ // Get pbxNativeTarget
1821
+ var nativeTarget = this.pbxNativeTargetSection()[targetUuid];
1822
+ return {
1823
+ uuid: targetUuid,
1824
+ target: nativeTarget
1825
+ };
1826
+ }
1827
+ }
1828
+
1829
+ return null;
1830
+ }
1831
+
1832
+ /*** NEW ***/
1833
+
1834
+ pbxProject.prototype.addToPbxGroupType = function (file, groupKey, groupType) {
1835
+ var group = this.getPBXGroupByKeyAndType(groupKey, groupType);
1836
+ if (group && group.children !== undefined) {
1837
+ if (typeof file === 'string') {
1838
+ //Group Key
1839
+ var childGroup = {
1840
+ value:file,
1841
+ };
1842
+ if (this.getPBXGroupByKey(file)) {
1843
+ childGroup.comment = this.getPBXGroupByKey(file).name;
1844
+ }
1845
+ else if (this.getPBXVariantGroupByKey(file)) {
1846
+ childGroup.comment = this.getPBXVariantGroupByKey(file).name;
1847
+ }
1848
+
1849
+ group.children.push(childGroup);
1850
+ }
1851
+ else {
1852
+ //File Object
1853
+ group.children.push(pbxGroupChild(file));
1854
+ }
1855
+ }
1856
+ }
1857
+
1858
+ pbxProject.prototype.addToPbxVariantGroup = function (file, groupKey) {
1859
+ this.addToPbxGroupType(file, groupKey, 'PBXVariantGroup');
1860
+ }
1861
+
1862
+ pbxProject.prototype.addToPbxGroup = function (file, groupKey) {
1863
+ this.addToPbxGroupType(file, groupKey, 'PBXGroup');
1864
+ }
1865
+
1866
+
1867
+
1868
+ pbxProject.prototype.pbxCreateGroupWithType = function(name, pathName, groupType) {
1869
+ //Create object
1870
+ var model = {
1871
+ isa: '"' + groupType + '"',
1872
+ children: [],
1873
+ name: name,
1874
+ sourceTree: '"<group>"'
1875
+ };
1876
+ if (pathName) model.path = pathName;
1877
+ var key = this.generateUuid();
1878
+
1879
+ //Create comment
1880
+ var commendId = key + '_comment';
1881
+
1882
+ //add obj and commentObj to groups;
1883
+ var groups = this.hash.project.objects[groupType];
1884
+ if (!groups) {
1885
+ groups = this.hash.project.objects[groupType] = new Object();
1886
+ }
1887
+ groups[commendId] = name;
1888
+ groups[key] = model;
1889
+
1890
+ return key;
1891
+ }
1892
+
1893
+ pbxProject.prototype.pbxCreateVariantGroup = function(name) {
1894
+ return this.pbxCreateGroupWithType(name, undefined, 'PBXVariantGroup')
1895
+ }
1896
+
1897
+ pbxProject.prototype.pbxCreateGroup = function(name, pathName) {
1898
+ return this.pbxCreateGroupWithType(name, pathName, 'PBXGroup');
1899
+ }
1900
+
1901
+
1902
+
1903
+ pbxProject.prototype.removeFromPbxGroupAndType = function (file, groupKey, groupType) {
1904
+ var group = this.getPBXGroupByKeyAndType(groupKey, groupType);
1905
+ if (group) {
1906
+ var groupChildren = group.children, i;
1907
+ for(i in groupChildren) {
1908
+ if(pbxGroupChild(file).value == groupChildren[i].value &&
1909
+ pbxGroupChild(file).comment == groupChildren[i].comment) {
1910
+ groupChildren.splice(i, 1);
1911
+ break;
1912
+ }
1913
+ }
1914
+ }
1915
+ }
1916
+
1917
+ pbxProject.prototype.removeFromPbxGroup = function (file, groupKey) {
1918
+ this.removeFromPbxGroupAndType(file, groupKey, 'PBXGroup');
1919
+ }
1920
+
1921
+ pbxProject.prototype.removeFromPbxVariantGroup = function (file, groupKey) {
1922
+ this.removeFromPbxGroupAndType(file, groupKey, 'PBXVariantGroup');
1923
+ }
1924
+
1925
+
1926
+
1927
+ pbxProject.prototype.getPBXGroupByKeyAndType = function(key, groupType) {
1928
+ return this.hash.project.objects[groupType][key];
1929
+ };
1930
+
1931
+ pbxProject.prototype.getPBXGroupByKey = function(key) {
1932
+ return this.hash.project.objects['PBXGroup'][key];
1933
+ };
1934
+
1935
+ pbxProject.prototype.getPBXVariantGroupByKey = function(key) {
1936
+ return this.hash.project.objects['PBXVariantGroup'][key];
1937
+ };
1938
+
1939
+
1940
+
1941
+ pbxProject.prototype.findPBXGroupKeyAndType = function(criteria, groupType) {
1942
+ var groups = this.hash.project.objects[groupType];
1943
+ var target;
1944
+
1945
+ for (var key in groups) {
1946
+ // only look for comments
1947
+ if (COMMENT_KEY.test(key)) continue;
1948
+
1949
+ var group = groups[key];
1950
+ if (criteria && criteria.path && criteria.name) {
1951
+ if (criteria.path === group.path && criteria.name === group.name) {
1952
+ target = key;
1953
+ break
1954
+ }
1955
+ }
1956
+ else if (criteria && criteria.path) {
1957
+ if (criteria.path === group.path) {
1958
+ target = key;
1959
+ break
1960
+ }
1961
+ }
1962
+ else if (criteria && criteria.name) {
1963
+ if (criteria.name === group.name) {
1964
+ target = key;
1965
+ break
1966
+ }
1967
+ }
1968
+ }
1969
+
1970
+ return target;
1971
+ }
1972
+
1973
+ pbxProject.prototype.findPBXGroupKey = function(criteria) {
1974
+ return this.findPBXGroupKeyAndType(criteria, 'PBXGroup');
1975
+ }
1976
+
1977
+ pbxProject.prototype.findPBXVariantGroupKey = function(criteria) {
1978
+ return this.findPBXGroupKeyAndType(criteria, 'PBXVariantGroup');
1979
+ }
1980
+
1981
+ pbxProject.prototype.addLocalizationVariantGroup = function(name) {
1982
+ var groupKey = this.pbxCreateVariantGroup(name);
1983
+
1984
+ var resourceGroupKey = this.findPBXGroupKey({name: 'Resources'});
1985
+ this.addToPbxGroup(groupKey, resourceGroupKey);
1986
+
1987
+ var localizationVariantGroup = {
1988
+ uuid: this.generateUuid(),
1989
+ fileRef: groupKey,
1990
+ basename: name
1991
+ }
1992
+ this.addToPbxBuildFileSection(localizationVariantGroup); // PBXBuildFile
1993
+ this.addToPbxResourcesBuildPhase(localizationVariantGroup); //PBXResourcesBuildPhase
1994
+
1995
+ return localizationVariantGroup;
1996
+ };
1997
+
1998
+ pbxProject.prototype.addKnownRegion = function (name) {
1999
+ if (!this.pbxProjectSection()[this.getFirstProject()['uuid']]['knownRegions']) {
2000
+ this.pbxProjectSection()[this.getFirstProject()['uuid']]['knownRegions'] = [];
2001
+ }
2002
+ if (!this.hasKnownRegion(name)) {
2003
+ this.pbxProjectSection()[this.getFirstProject()['uuid']]['knownRegions'].push(name);
2004
+ }
2005
+ }
2006
+
2007
+ pbxProject.prototype.removeKnownRegion = function (name) {
2008
+ var regions = this.pbxProjectSection()[this.getFirstProject()['uuid']]['knownRegions'];
2009
+ if (regions) {
2010
+ for (var i = 0; i < regions.length; i++) {
2011
+ if (regions[i] === name) {
2012
+ regions.splice(i, 1);
2013
+ break;
2014
+ }
2015
+ }
2016
+ this.pbxProjectSection()[this.getFirstProject()['uuid']]['knownRegions'] = regions;
2017
+ }
2018
+ }
2019
+
2020
+ pbxProject.prototype.hasKnownRegion = function (name) {
2021
+ var regions = this.pbxProjectSection()[this.getFirstProject()['uuid']]['knownRegions'];
2022
+ if (regions) {
2023
+ for (var i in regions) {
2024
+ if (regions[i] === name) {
2025
+ return true;
2026
+ }
2027
+ }
2028
+ }
2029
+ return false;
2030
+ }
2031
+
2032
+ pbxProject.prototype.getPBXObject = function(name) {
2033
+ return this.hash.project.objects[name];
2034
+ }
2035
+
2036
+
2037
+
2038
+ pbxProject.prototype.addFile = function (path, group, opt) {
2039
+ var file = new pbxFile(path, opt);
2040
+
2041
+ // null is better for early errors
2042
+ if (this.hasFile(file.path)) return null;
2043
+
2044
+ file.fileRef = this.generateUuid();
2045
+
2046
+ this.addToPbxFileReferenceSection(file); // PBXFileReference
2047
+
2048
+ if (this.getPBXGroupByKey(group)) {
2049
+ this.addToPbxGroup(file, group); // PBXGroup
2050
+ }
2051
+ else if (this.getPBXVariantGroupByKey(group)) {
2052
+ this.addToPbxVariantGroup(file, group); // PBXVariantGroup
2053
+ }
2054
+
2055
+ return file;
2056
+ }
2057
+
2058
+ pbxProject.prototype.removeFile = function (path, group, opt) {
2059
+ var file = new pbxFile(path, opt);
2060
+
2061
+ this.removeFromPbxFileReferenceSection(file); // PBXFileReference
2062
+
2063
+ if (this.getPBXGroupByKey(group)) {
2064
+ this.removeFromPbxGroup(file, group); // PBXGroup
2065
+ }
2066
+ else if (this.getPBXVariantGroupByKey(group)) {
2067
+ this.removeFromPbxVariantGroup(file, group); // PBXVariantGroup
2068
+ }
2069
+
2070
+ return file;
2071
+ }
2072
+
2073
+
2074
+
2075
+ pbxProject.prototype.getBuildProperty = function(prop, build, targetName) {
2076
+ var target;
2077
+ let validConfigs = [];
2078
+
2079
+ if (targetName) {
2080
+ const target = this.pbxTargetByName(targetName);
2081
+ const targetBuildConfigs = target && target.buildConfigurationList;
2082
+
2083
+ const xcConfigList = this.pbxXCConfigurationList();
2084
+
2085
+ // Collect the UUID's from the configuration of our target
2086
+ for (const configName in xcConfigList) {
2087
+ if (!COMMENT_KEY.test(configName) && targetBuildConfigs === configName) {
2088
+ const buildVariants = xcConfigList[configName].buildConfigurations;
2089
+
2090
+ for (const item of buildVariants) {
2091
+ validConfigs.push(item.value);
2092
+ }
2093
+
2094
+ break;
2095
+ }
2096
+ }
2097
+ }
2098
+
2099
+ var configs = this.pbxXCBuildConfigurationSection();
2100
+ for (var configName in configs) {
2101
+ if (!COMMENT_KEY.test(configName)) {
2102
+ if (targetName && !validConfigs.includes(configName)) continue;
2103
+ var config = configs[configName];
2104
+ if ( (build && config.name === build) || (build === undefined) ) {
2105
+ if (config.buildSettings[prop] !== undefined) {
2106
+ target = config.buildSettings[prop];
2107
+ }
2108
+ }
2109
+ }
2110
+ }
2111
+ return target;
2112
+ }
2113
+
2114
+ pbxProject.prototype.getBuildConfigByName = function(name) {
2115
+ var target = {};
2116
+ var configs = this.pbxXCBuildConfigurationSection();
2117
+ for (var configName in configs) {
2118
+ if (!COMMENT_KEY.test(configName)) {
2119
+ var config = configs[configName];
2120
+ if (config.name === name) {
2121
+ target[configName] = config;
2122
+ }
2123
+ }
2124
+ }
2125
+ return target;
2126
+ }
2127
+
2128
+ pbxProject.prototype.addDataModelDocument = function(filePath, group, opt) {
2129
+ if (!group) {
2130
+ group = 'Resources';
2131
+ }
2132
+ if (!this.getPBXGroupByKey(group)) {
2133
+ group = this.findPBXGroupKey({ name: group });
2134
+ }
2135
+
2136
+ var file = new pbxFile(filePath, opt);
2137
+
2138
+ if (!file || this.hasFile(file.path)) return null;
2139
+
2140
+ file.fileRef = this.generateUuid();
2141
+ this.addToPbxGroup(file, group);
2142
+
2143
+ if (!file) return false;
2144
+
2145
+ file.target = opt ? opt.target : undefined;
2146
+ file.uuid = this.generateUuid();
2147
+
2148
+ this.addToPbxBuildFileSection(file);
2149
+ this.addToPbxSourcesBuildPhase(file);
2150
+
2151
+ file.models = [];
2152
+ var currentVersionName;
2153
+ var modelFiles = fs.readdirSync(file.path);
2154
+ for (var index in modelFiles) {
2155
+ var modelFileName = modelFiles[index];
2156
+ var modelFilePath = path.join(filePath, modelFileName);
2157
+
2158
+ if (modelFileName == '.xccurrentversion') {
2159
+ currentVersionName = plist.readFileSync(modelFilePath)._XCCurrentVersionName;
2160
+ continue;
2161
+ }
2162
+
2163
+ var modelFile = new pbxFile(modelFilePath);
2164
+ modelFile.fileRef = this.generateUuid();
2165
+
2166
+ this.addToPbxFileReferenceSection(modelFile);
2167
+
2168
+ file.models.push(modelFile);
2169
+
2170
+ if (currentVersionName && currentVersionName === modelFileName) {
2171
+ file.currentModel = modelFile;
2172
+ }
2173
+ }
2174
+
2175
+ if (!file.currentModel) {
2176
+ file.currentModel = file.models[0];
2177
+ }
2178
+
2179
+ this.addToXcVersionGroupSection(file);
2180
+
2181
+ return file;
2182
+ }
2183
+
2184
+ pbxProject.prototype.addTargetAttribute = function(prop, value, target) {
2185
+ var attributes = this.getFirstProject()['firstProject']['attributes'];
2186
+ if (attributes['TargetAttributes'] === undefined) {
2187
+ attributes['TargetAttributes'] = {};
2188
+ }
2189
+ target = target || this.getFirstTarget();
2190
+ if (attributes['TargetAttributes'][target.uuid] === undefined) {
2191
+ attributes['TargetAttributes'][target.uuid] = {};
2192
+ }
2193
+ attributes['TargetAttributes'][target.uuid][prop] = value;
2194
+ }
2195
+
2196
+ pbxProject.prototype.removeTargetAttribute = function(prop, target) {
2197
+ var attributes = this.getFirstProject()['firstProject']['attributes'];
2198
+ target = target || this.getFirstTarget();
2199
+ if (attributes['TargetAttributes'] &&
2200
+ attributes['TargetAttributes'][target.uuid]) {
2201
+ delete attributes['TargetAttributes'][target.uuid][prop];
2202
+ }
2203
+ }
2204
+
2205
+ module.exports = pbxProject;