nativescript 8.9.0-dev.1 → 8.9.0-dev.3

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.
@@ -1 +1 @@
1
- {"@jsdevtools/coverage-istanbul-loader":"3.0.5","karma":"6.4.4","karma-coverage":"2.2.1","karma-nativescript-launcher":"0.4.0","mocha":"11.1.0","karma-mocha":"2.0.1","karma-chai":"0.1.0","karma-jasmine":"4.0.2","karma-qunit":"4.2.1","@types/karma-chai":"0.1.7","@types/mocha":"10.0.10","@types/jasmine":"5.1.5","@types/qunit":"2.19.12","nyc":"17.1.0"}
1
+ {"@jsdevtools/coverage-istanbul-loader":"3.0.5","karma":"6.4.4","karma-coverage":"2.2.1","karma-nativescript-launcher":"0.4.0","mocha":"11.1.0","karma-mocha":"2.0.1","karma-chai":"0.1.0","karma-jasmine":"4.0.2","karma-qunit":"4.2.1","@types/karma-chai":"0.1.7","@types/mocha":"10.0.10","@types/jasmine":"5.1.7","@types/qunit":"2.19.12","nyc":"17.1.0"}
package/lib/.d.ts CHANGED
@@ -48,6 +48,7 @@
48
48
  /// <reference path="commands/typings.ts" />
49
49
  /// <reference path="commands/update-platform.ts" />
50
50
  /// <reference path="commands/update.ts" />
51
+ /// <reference path="commands/widget.ts" />
51
52
  /// <reference path="common/bootstrap.ts" />
52
53
  /// <reference path="common/child-process.ts" />
53
54
  /// <reference path="common/codeGeneration/code-entity.ts" />
package/lib/bootstrap.js CHANGED
@@ -208,4 +208,5 @@ yok_1.injector.requireCommand([
208
208
  "native|add|swift",
209
209
  "native|add|objective-c",
210
210
  ], "./commands/native-add");
211
+ yok_1.injector.requireCommand(["widget", "widget|ios"], "./commands/widget");
211
212
  require("./key-commands/bootstrap");
@@ -0,0 +1,805 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WidgetIOSCommand = exports.WidgetCommand = void 0;
4
+ const fs = require("fs");
5
+ const prompts = require("prompts");
6
+ const path = require("path");
7
+ const plist = require("plist");
8
+ const yok_1 = require("../common/yok");
9
+ const utils_1 = require("../common/utils");
10
+ const os_1 = require("os");
11
+ class WidgetCommand {
12
+ constructor($projectData, $projectConfigService, $logger, $errors) {
13
+ this.$projectData = $projectData;
14
+ this.$projectConfigService = $projectConfigService;
15
+ this.$logger = $logger;
16
+ this.$errors = $errors;
17
+ this.allowedParameters = [];
18
+ this.$projectData.initializeProjectData();
19
+ }
20
+ async execute(args) {
21
+ this.failWithUsage();
22
+ return Promise.resolve();
23
+ }
24
+ failWithUsage() {
25
+ this.$errors.failWithHelp("Usage: ns widget ios");
26
+ }
27
+ async canExecute(args) {
28
+ this.failWithUsage();
29
+ return false;
30
+ }
31
+ getIosSourcePathBase() {
32
+ const resources = this.$projectData.getAppResourcesDirectoryPath();
33
+ return path.join(resources, "iOS", "src");
34
+ }
35
+ }
36
+ exports.WidgetCommand = WidgetCommand;
37
+ class WidgetIOSCommand extends WidgetCommand {
38
+ constructor($projectData, $projectConfigService, $logger, $errors) {
39
+ super($projectData, $projectConfigService, $logger, $errors);
40
+ }
41
+ async canExecute(args) {
42
+ return true;
43
+ }
44
+ async execute(args) {
45
+ this.startPrompt(args);
46
+ }
47
+ async startPrompt(args) {
48
+ let result = await prompts.prompt({
49
+ type: "text",
50
+ name: "name",
51
+ message: `What name would you like for this widget? (Default is 'widget')`,
52
+ });
53
+ const name = (result.name || "widget").toLowerCase();
54
+ result = await prompts.prompt({
55
+ type: "select",
56
+ name: "value",
57
+ message: `What type of widget would you like?`,
58
+ choices: [
59
+ {
60
+ title: "Live Activity",
61
+ description: "This will create a Live Activity that will display on the iOS Lock Screen.",
62
+ value: 0,
63
+ },
64
+ {
65
+ title: "Live Activity with Home Screen Widget",
66
+ description: "This will create a Live Activity that will display on the iOS Lock Screen with an optional Widget.",
67
+ value: 1,
68
+ },
69
+ {
70
+ title: "Home Screen Widget",
71
+ description: "This will create just a Home Screen Widget.",
72
+ value: 2,
73
+ },
74
+ ],
75
+ initial: 1,
76
+ });
77
+ const bundleId = this.$projectConfigService.getValue(`id`, "");
78
+ switch (result.value) {
79
+ case 0:
80
+ this.$logger.info("TODO");
81
+ break;
82
+ case 1:
83
+ await this.generateSharedWidgetPackage(this.$projectData.projectDir, name);
84
+ this.generateWidget(this.$projectData.projectDir, name, bundleId, result.value);
85
+ this.generateAppleUtility(this.$projectData.projectDir, name, bundleId);
86
+ break;
87
+ case 2:
88
+ this.$logger.info("TODO");
89
+ break;
90
+ }
91
+ }
92
+ async generateSharedWidgetPackage(projectDir, name) {
93
+ var _a;
94
+ const sharedWidgetDir = "Shared_Resources/iOS/SharedWidget";
95
+ const sharedWidgetPath = path.join(projectDir, sharedWidgetDir);
96
+ const sharedWidgetSourceDir = "Sources/SharedWidget";
97
+ const sharedWidgetPackagePath = path.join(projectDir, `${sharedWidgetDir}/Package.swift`);
98
+ const sharedWidgetSourcePath = path.join(sharedWidgetPath, `${sharedWidgetSourceDir}/${(0, utils_1.capitalizeFirstLetter)(name)}Model.swift`);
99
+ const gitIgnorePath = path.join(projectDir, ".gitignore");
100
+ if (!fs.existsSync(sharedWidgetPackagePath)) {
101
+ fs.mkdirSync(sharedWidgetPath, { recursive: true });
102
+ fs.mkdirSync(path.join(sharedWidgetPath, sharedWidgetSourceDir), {
103
+ recursive: true,
104
+ });
105
+ let content = `// swift-tools-version:5.9
106
+ import PackageDescription
107
+
108
+ let package = Package(
109
+ name: "SharedWidget",
110
+ platforms: [
111
+ .iOS(.v13)
112
+ ],
113
+ products: [
114
+ .library(
115
+ name: "SharedWidget",
116
+ targets: ["SharedWidget"])
117
+ ],
118
+ dependencies: [
119
+ // Dependencies declare other packages that this package depends on.
120
+ ],
121
+ targets: [
122
+ .target(
123
+ name: "SharedWidget",
124
+ dependencies: []
125
+ )
126
+ ]
127
+ )${os_1.EOL}`;
128
+ fs.writeFileSync(sharedWidgetPackagePath, content);
129
+ content = `import ActivityKit
130
+ import WidgetKit
131
+
132
+ public struct ${(0, utils_1.capitalizeFirstLetter)(name)}Model: ActivityAttributes {
133
+ public typealias DeliveryStatus = ContentState
134
+
135
+ public struct ContentState: Codable, Hashable {
136
+ // Dynamic stateful properties about your activity go here!
137
+ public var message: String
138
+ public var deliveryTime: Double
139
+
140
+ public init(message: String, deliveryTime: Double) {
141
+ self.message = message
142
+ self.deliveryTime = deliveryTime
143
+ }
144
+ }
145
+
146
+ // Fixed non-changing properties about your activity go here!
147
+ public var numberOfPizzas: Int
148
+ public var totalAmount: String
149
+
150
+ public init(numberOfPizzas: Int, totalAmount: String) {
151
+ self.numberOfPizzas = numberOfPizzas
152
+ self.totalAmount = totalAmount
153
+ }
154
+ }${os_1.EOL}`;
155
+ fs.writeFileSync(sharedWidgetSourcePath, content);
156
+ const configData = this.$projectConfigService.readConfig(projectDir);
157
+ if (!configData.ios) {
158
+ configData.ios = {};
159
+ }
160
+ if (!configData.ios.SPMPackages) {
161
+ configData.ios.SPMPackages = [];
162
+ }
163
+ const spmPackages = configData.ios.SPMPackages;
164
+ const sharedWidgetPackage = spmPackages === null || spmPackages === void 0 ? void 0 : spmPackages.find((p) => p.name === "SharedWidget");
165
+ if (!sharedWidgetPackage) {
166
+ spmPackages.push({
167
+ name: "SharedWidget",
168
+ libs: ["SharedWidget"],
169
+ path: "./Shared_Resources/iOS/SharedWidget",
170
+ targets: [name],
171
+ });
172
+ }
173
+ else {
174
+ if (!((_a = sharedWidgetPackage.targets) === null || _a === void 0 ? void 0 : _a.includes(name))) {
175
+ sharedWidgetPackage.targets.push(name);
176
+ }
177
+ }
178
+ configData.ios.SPMPackages = spmPackages;
179
+ await this.$projectConfigService.setValue("", configData);
180
+ if (fs.existsSync(gitIgnorePath)) {
181
+ const gitIgnore = fs.readFileSync(gitIgnorePath, {
182
+ encoding: "utf-8",
183
+ });
184
+ const swiftBuildIgnore = `# Swift
185
+ .build
186
+ .swiftpm`;
187
+ if (gitIgnore.indexOf(swiftBuildIgnore) === -1) {
188
+ content = `${gitIgnore}${os_1.EOL}${swiftBuildIgnore}${os_1.EOL}`;
189
+ fs.writeFileSync(gitIgnorePath, content);
190
+ }
191
+ }
192
+ console.log(`\nCreated Shared Resources: ${sharedWidgetDir}.\n`);
193
+ }
194
+ }
195
+ generateWidget(projectDir, name, bundleId, type) {
196
+ const appResourcePath = this.$projectData.appResourcesDirectoryPath;
197
+ const capitalName = (0, utils_1.capitalizeFirstLetter)(name);
198
+ const appInfoPlistPath = path.join(appResourcePath, "iOS", "Info.plist");
199
+ const extensionDir = path.join(appResourcePath, "iOS", "extensions");
200
+ const widgetPath = path.join(extensionDir, name);
201
+ const extensionProvisionPath = path.join(extensionDir, `provisioning.json`);
202
+ const extensionsInfoPath = path.join(widgetPath, `Info.plist`);
203
+ const extensionsPrivacyPath = path.join(widgetPath, `PrivacyInfo.xcprivacy`);
204
+ const extensionsConfigPath = path.join(widgetPath, `extension.json`);
205
+ const entitlementsPath = path.join(widgetPath, `${name}.entitlements`);
206
+ const widgetBundlePath = path.join(widgetPath, `${capitalName}Bundle.swift`);
207
+ const widgetHomeScreenPath = path.join(widgetPath, `${capitalName}HomeScreenWidget.swift`);
208
+ const widgetLiveActivityPath = path.join(widgetPath, `${capitalName}LiveActivity.swift`);
209
+ const appEntitlementsPath = path.join(appResourcePath, "iOS", "app.entitlements");
210
+ if (!fs.existsSync(extensionsConfigPath)) {
211
+ fs.mkdirSync(widgetPath, { recursive: true });
212
+ let content = `<?xml version="1.0" encoding="UTF-8"?>
213
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
214
+ <plist version="1.0">
215
+ <dict>
216
+ <key>NSExtension</key>
217
+ <dict>
218
+ <key>NSExtensionPointIdentifier</key>
219
+ <string>com.apple.widgetkit-extension</string>
220
+ </dict>
221
+ <key>CFBundleShortVersionString</key>
222
+ <string>1.0</string>
223
+ <key>CFBundleVersion</key>
224
+ <string>1.0</string>
225
+ </dict>
226
+ </plist>${os_1.EOL}`;
227
+ fs.writeFileSync(extensionsInfoPath, content);
228
+ content = `<?xml version="1.0" encoding="UTF-8"?>
229
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
230
+ <plist version="1.0">
231
+ <dict>
232
+ <key>NSPrivacyAccessedAPITypes</key>
233
+ <array>
234
+ <dict>
235
+ <key>NSPrivacyAccessedAPIType</key>
236
+ <string>NSPrivacyAccessedAPICategoryUserDefaults</string>
237
+ <key>NSPrivacyAccessedAPITypeReasons</key>
238
+ <array>
239
+ <string>CA92.1</string>
240
+ </array>
241
+ </dict>
242
+ </array>
243
+ <key>NSPrivacyCollectedDataTypes</key>
244
+ <array/>
245
+ <key>NSPrivacyTracking</key>
246
+ <false/>
247
+ </dict>
248
+ </plist>${os_1.EOL}`;
249
+ fs.writeFileSync(extensionsPrivacyPath, content);
250
+ content = `import WidgetKit
251
+ import SwiftUI
252
+
253
+ @main
254
+ struct ${capitalName}Bundle: SwiftUI.WidgetBundle {
255
+ var body: some Widget {
256
+ ${[1, 2].includes(type) ? capitalName + "HomeScreenWidget()" : ""}
257
+ ${[0, 1].includes(type) ? capitalName + "LiveActivity()" : ""}
258
+ }
259
+ }${os_1.EOL}`;
260
+ fs.writeFileSync(widgetBundlePath, content);
261
+ if ([0, 1].includes(type)) {
262
+ content = `import ActivityKit
263
+ import SwiftUI
264
+ import WidgetKit
265
+ import Foundation
266
+ import SharedWidget
267
+ import os
268
+
269
+ struct ${capitalName}LiveActivity: Widget {
270
+
271
+ var body: some WidgetConfiguration {
272
+ ActivityConfiguration(for: ${capitalName}Model.self) { context in
273
+
274
+ LockScreenView(message: context.state.message, deliveryTime: context.state.deliveryTime)
275
+ .activityBackgroundTint(Color.black)
276
+ .activitySystemActionForegroundColor(Color.white)
277
+
278
+ } dynamicIsland: { context in
279
+ DynamicIsland {
280
+ DynamicIslandExpandedRegion(.leading) {
281
+ Image(systemName: context.state.deliveryTime >= 0 ? "car.side.arrowtriangle.up.fill" : "face.smiling.inverse")
282
+ .resizable()
283
+ .scaledToFit()
284
+ .frame(width: 50, height: 50)
285
+ .foregroundColor(context.state.deliveryTime >= 0 ? Color.green : Color.blue)
286
+ }
287
+ DynamicIslandExpandedRegion(.trailing) {
288
+ if (context.state.deliveryTime >= 0) {
289
+ ZStack {
290
+ ProgressView(value: context.state.deliveryTime, total: 60)
291
+ .progressViewStyle(.circular)
292
+ .tint(Color.green)
293
+ .frame(width: 75, height: 75)
294
+ Text("\\(formatter.string(for: context.state.deliveryTime) ?? "") mins")
295
+ .font(.system(size: 11))
296
+ .foregroundStyle(.white)
297
+ }.frame(width: 75, height: 75)
298
+ } else {
299
+ Image(systemName: "checkmark.circle.fill")
300
+ .resizable()
301
+ .scaledToFit()
302
+ .frame(width: 50, height: 50)
303
+ .foregroundColor(.blue)
304
+ }
305
+ }
306
+ DynamicIslandExpandedRegion(.bottom) {
307
+ Text("\\(context.state.message)")
308
+ }
309
+ } compactLeading: {
310
+ Image(systemName: context.state.deliveryTime >= 0 ? "car.side.arrowtriangle.up.fill" : "face.smiling.inverse")
311
+ .resizable()
312
+ .scaledToFit()
313
+ .frame(width: 20, height: 20)
314
+ .foregroundColor(context.state.deliveryTime >= 0 ? .green : .blue)
315
+ } compactTrailing: {
316
+ Image(systemName: context.state.deliveryTime >= 0 ? "timer.circle.fill" : "checkmark.circle.fill")
317
+ .resizable()
318
+ .scaledToFit()
319
+ .frame(width: 20, height: 20)
320
+ .foregroundColor(context.state.deliveryTime >= 0 ? .green : .blue)
321
+ } minimal: {
322
+ Text(context.state.message).font(.system(size: 12))
323
+ }
324
+ .widgetURL(URL(string: "http://www.apple.com"))
325
+ .keylineTint(Color.red)
326
+ }
327
+ }
328
+
329
+ private let formatter: NumberFormatter = {
330
+ let formatter = NumberFormatter()
331
+ formatter.maximumFractionDigits = 0
332
+ formatter.minimumFractionDigits = 0
333
+ return formatter
334
+ }()
335
+ }
336
+
337
+ struct LockScreenView: View {
338
+ @State private var message = ""
339
+ @State private var deliveryTime: Double = 0
340
+ // for console debugging
341
+ let logger = Logger(subsystem: "${bundleId}.${name}", category: "Widget")
342
+
343
+ var body: some View {
344
+ ZStack {
345
+ LinearGradient(
346
+ gradient: Gradient(colors: [Color.gray.opacity(0.3), Color.black]),
347
+ startPoint: .top,
348
+ endPoint: .bottom
349
+ )
350
+ VStack {
351
+ Spacer()
352
+ Image(systemName: deliveryTime >= 0 ? "car.side.arrowtriangle.up.fill" : "face.smiling.inverse")
353
+ .resizable()
354
+ .scaledToFit()
355
+ .frame(width: 50, height: 50)
356
+ .foregroundColor(deliveryTime >= 0 ? .green : .blue)
357
+ Spacer()
358
+ Text("\\(message)")
359
+ .foregroundStyle(.white)
360
+ Spacer()
361
+ }
362
+ }.frame(maxWidth: .infinity, maxHeight: .infinity)
363
+ }
364
+
365
+ init(message: String = "", deliveryTime: Double = 0) {
366
+ _message = State(initialValue: message)
367
+ _deliveryTime = State(initialValue: deliveryTime)
368
+
369
+ // Logs the deliveryTime at init for debugging purposes if needed
370
+ logger.log("deliveryTime: \\(deliveryTime)")
371
+ }
372
+ }${os_1.EOL}`;
373
+ fs.writeFileSync(widgetLiveActivityPath, content);
374
+ }
375
+ if ([1, 2].includes(type)) {
376
+ content = `import SwiftUI
377
+ import WidgetKit
378
+
379
+ /**
380
+ * Widget data shared between the app and the widget extension.
381
+ */
382
+ struct WidgetData: Codable {
383
+ let pizzas: [String]
384
+ let orderTime: Double
385
+ let delivered: Bool
386
+ }
387
+
388
+ struct Provider: TimelineProvider {
389
+
390
+ func placeholder(in context: Context) -> SimpleEntry {
391
+ SimpleEntry(date: Date(), pizza: "Pepperoni", delivered: false, orderTime: Date())
392
+ }
393
+
394
+ func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) {
395
+ let entry = SimpleEntry(date: Date(), pizza: "Pepperoni", delivered: false, orderTime: Date())
396
+ completion(entry)
397
+ }
398
+
399
+ func getTimeline(in context: Context, completion: @escaping @Sendable (Timeline<Entry>) -> ()) {
400
+ var entries: [SimpleEntry] = []
401
+
402
+ if let sharedDefaults = UserDefaults(suiteName: "group.${bundleId}") {
403
+ let currentDate = Date()
404
+ if let jsonString = sharedDefaults.string(forKey: "widgetData") {
405
+ if let jsonData = jsonString.data(using: .utf8) {
406
+ do {
407
+ let widgetData = try JSONDecoder().decode(WidgetData.self, from: jsonData)
408
+ let pizzas = widgetData.pizzas
409
+ let orderTime = Date(timeIntervalSince1970: widgetData.orderTime/1000)
410
+ let delivered = widgetData.delivered
411
+
412
+ // Generate a timeline of entries 1 second apart, starting from the current date.
413
+ for secondOffset in 0..<pizzas.count {
414
+ let entryDate = Calendar.current.date(
415
+ byAdding: .second, value: secondOffset, to: currentDate)!
416
+ let entry = SimpleEntry(date: entryDate, pizza: secondOffset < pizzas.count ? pizzas[secondOffset] : pizzas[0], delivered: delivered, orderTime: orderTime)
417
+ entries.append(entry)
418
+ }
419
+ } catch {
420
+ print("Failed to decode JSON: (error)")
421
+ }
422
+ }
423
+ } else {
424
+ let entry = SimpleEntry(date: currentDate, pizza: "", delivered: false, orderTime: nil)
425
+ entries.append(entry)
426
+ }
427
+ }
428
+
429
+ let timeline = Timeline(entries: entries, policy: .atEnd)
430
+ completion(timeline)
431
+ }
432
+
433
+ // func relevances() async -> WidgetRelevances<Void> {
434
+ // // Generate a list containing the contexts this widget is relevant in.
435
+ // }
436
+ }
437
+
438
+ struct SimpleEntry: TimelineEntry {
439
+ let date: Date
440
+ let pizza: String
441
+ let delivered: Bool
442
+ let orderTime: Date?
443
+ }
444
+
445
+ struct WidgetView: View {
446
+ @Environment(\\.widgetFamily) var widgetFamily
447
+ var entry: Provider.Entry
448
+
449
+ var body: some View {
450
+ VStack {
451
+ if (entry.pizza != "") {
452
+ Spacer()
453
+ Image(systemName: entry.delivered ? "face.smiling.inverse" : "car.side")
454
+ .resizable()
455
+ .scaledToFit()
456
+ .frame(width: iconSize(for: widgetFamily), height: iconSize(for: widgetFamily))
457
+ .foregroundColor(entry.delivered ? .blue : .green)
458
+ Spacer()
459
+ if (entry.delivered) {
460
+ Text("Pizza Delivered!")
461
+ .font(.system(size: fontSize(for: widgetFamily), weight: .bold))
462
+ .foregroundStyle(.white)
463
+ } else {
464
+ HStack(spacing: 4) {
465
+ Text("Ordered:")
466
+ .font(.system(size: fontSize(for: widgetFamily)))
467
+ .foregroundStyle(.white)
468
+ Text(entry.orderTime!, style: .time)
469
+ .font(.system(size: fontSize(for: widgetFamily), weight: .bold))
470
+ .foregroundStyle(.white)
471
+ }
472
+ HStack(spacing: 4) {
473
+ Text("Pizza:")
474
+ .font(.system(size: fontSize(for: widgetFamily)))
475
+ .foregroundStyle(.white)
476
+ Text(entry.pizza)
477
+ .font(.system(size: fontSize(for: widgetFamily), weight: .bold))
478
+ .foregroundStyle(.white)
479
+ }
480
+ }
481
+ Spacer()
482
+ } else {
483
+ Spacer()
484
+ Image(systemName: "car.side.rear.open")
485
+ .resizable()
486
+ .scaledToFit()
487
+ .frame(width: iconSize(for: widgetFamily), height: iconSize(for: widgetFamily))
488
+ .foregroundColor(.gray)
489
+ Spacer()
490
+ Text("Awaiting orders...")
491
+ .foregroundStyle(.white)
492
+ Spacer()
493
+ }
494
+ }.frame(maxWidth: .infinity, maxHeight: .infinity)
495
+ }
496
+
497
+ private func iconSize(for family: WidgetFamily) -> CGFloat {
498
+ switch family {
499
+ case .systemSmall:
500
+ return 65
501
+ case .systemMedium:
502
+ return 85
503
+ case .systemLarge:
504
+ return 150
505
+ default:
506
+ return 65
507
+ }
508
+ }
509
+
510
+ private func fontSize(for family: WidgetFamily) -> CGFloat {
511
+ switch family {
512
+ case .systemSmall:
513
+ return 12
514
+ case .systemMedium:
515
+ return 14
516
+ case .systemLarge:
517
+ return 18
518
+ default:
519
+ return 14
520
+ }
521
+ }
522
+ }
523
+
524
+ @available(iOSApplicationExtension 17.0, *)
525
+ struct ${capitalName}HomeScreenWidget: Widget {
526
+ let kind: String = "widget"
527
+
528
+ var body: some WidgetConfiguration {
529
+ StaticConfiguration(kind: kind, provider: Provider()) { entry in
530
+ WidgetView(entry: entry)
531
+ .containerBackground(for: .widget) {
532
+ LinearGradient(
533
+ gradient: Gradient(colors: [Color.black.opacity(0.6), Color.black]),
534
+ startPoint: .top,
535
+ endPoint: .bottom
536
+ )
537
+ }
538
+ }
539
+ .configurationDisplayName("${capitalName} Widget")
540
+ .description("${capitalName} delivery service.")
541
+ }
542
+ }
543
+
544
+ #Preview(as: .systemSmall) {
545
+ ${capitalName}HomeScreenWidget()
546
+ } timeline: {
547
+ SimpleEntry(date: .now, pizza: "Pepperoni", delivered: false, orderTime: Date())
548
+ SimpleEntry(date: .now, pizza: "Hawaiian", delivered: false, orderTime: Date())
549
+ }${os_1.EOL}`;
550
+ fs.writeFileSync(widgetHomeScreenPath, content);
551
+ }
552
+ content = `{
553
+ "${bundleId}.${name}": "{set-your-provision-profile-id}"
554
+ }`;
555
+ fs.writeFileSync(extensionProvisionPath, content);
556
+ content = `<?xml version="1.0" encoding="UTF-8"?>
557
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
558
+ <plist version="1.0">
559
+ <dict>
560
+ <key>com.apple.security.application-groups</key>
561
+ <array>
562
+ <string>group.${bundleId}</string>
563
+ </array>
564
+ </dict>
565
+ </plist>${os_1.EOL}`;
566
+ fs.writeFileSync(entitlementsPath, content);
567
+ if (fs.existsSync(appInfoPlistPath)) {
568
+ const appSupportLiveActivity = "NSSupportsLiveActivities";
569
+ const appInfoPlist = plist.parse(fs.readFileSync(appInfoPlistPath, {
570
+ encoding: "utf-8",
571
+ }));
572
+ if (!appInfoPlist[appSupportLiveActivity]) {
573
+ appInfoPlist[appSupportLiveActivity] = true;
574
+ const appPlist = plist.build(appInfoPlist);
575
+ fs.writeFileSync(appInfoPlistPath, appPlist);
576
+ }
577
+ }
578
+ const appGroupKey = "com.apple.security.application-groups";
579
+ if (fs.existsSync(appEntitlementsPath)) {
580
+ const appEntitlementsPlist = plist.parse(fs.readFileSync(appEntitlementsPath, {
581
+ encoding: "utf-8",
582
+ }));
583
+ if (!appEntitlementsPlist[appGroupKey]) {
584
+ appEntitlementsPlist[appGroupKey] = [`group.${bundleId}`];
585
+ const appEntitlements = plist.build(appEntitlementsPlist);
586
+ console.log("appentitlement:", appEntitlements);
587
+ fs.writeFileSync(appEntitlementsPath, appEntitlements);
588
+ }
589
+ }
590
+ else {
591
+ content = `<?xml version="1.0" encoding="UTF-8"?>
592
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
593
+ <plist version="1.0">
594
+ <dict>
595
+ <key>com.apple.security.application-groups</key>
596
+ <array>
597
+ <string>group.${bundleId}</string>
598
+ </array>
599
+ </dict>
600
+ </plist>${os_1.EOL}`;
601
+ fs.writeFileSync(appEntitlementsPath, content);
602
+ }
603
+ content = `{
604
+ "frameworks": [
605
+ "SwiftUI.framework",
606
+ "WidgetKit.framework"
607
+ ],
608
+ "targetBuildConfigurationProperties": {
609
+ "ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME": "AccentColor",
610
+ "ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME": "WidgetBackground",
611
+ "CLANG_ANALYZER_NONNULL": "YES",
612
+ "CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION": "YES_AGGRESSIVE",
613
+ "CLANG_CXX_LANGUAGE_STANDARD": "\\"gnu++20\\"",
614
+ "CLANG_ENABLE_OBJC_WEAK": "YES",
615
+ "CLANG_WARN_DOCUMENTATION_COMMENTS": "YES",
616
+ "CLANG_WARN_UNGUARDED_AVAILABILITY": "YES_AGGRESSIVE",
617
+ "CURRENT_PROJECT_VERSION": 1,
618
+ "GCC_C_LANGUAGE_STANDARD": "gnu11",
619
+ "GCC_WARN_UNINITIALIZED_AUTOS": "YES_AGGRESSIVE",
620
+ "GENERATE_INFOPLIST_FILE": "YES",
621
+ "INFOPLIST_KEY_CFBundleDisplayName": "widget",
622
+ "INFOPLIST_KEY_NSHumanReadableCopyright": "\\"Copyright © All rights reserved.\\"",
623
+ "IPHONEOS_DEPLOYMENT_TARGET": 18.0,
624
+ "MARKETING_VERSION": "1.0",
625
+ "MTL_FAST_MATH": "YES",
626
+ "PRODUCT_NAME": "widget",
627
+ "SWIFT_EMIT_LOC_STRINGS": "YES",
628
+ "SWIFT_VERSION": "5.0",
629
+ "TARGETED_DEVICE_FAMILY": "\\"1,2\\"",
630
+ "MTL_ENABLE_DEBUG_INFO": "NO",
631
+ "SWIFT_OPTIMIZATION_LEVEL": "\\"-O\\"",
632
+ "COPY_PHASE_STRIP": "NO",
633
+ "SWIFT_COMPILATION_MODE": "wholemodule",
634
+ "CODE_SIGN_ENTITLEMENTS": "../../App_Resources/iOS/extensions/${name}/${name}.entitlements"
635
+ },
636
+ "targetNamedBuildConfigurationProperties": {
637
+ "debug": {
638
+ "DEBUG_INFORMATION_FORMAT": "dwarf",
639
+ "GCC_PREPROCESSOR_DEFINITIONS": "(\\"DEBUG=1\\",\\"$(inherited)\\",)",
640
+ "MTL_ENABLE_DEBUG_INFO": "INCLUDE_SOURCE",
641
+ "SWIFT_ACTIVE_COMPILATION_CONDITIONS": "DEBUG",
642
+ "SWIFT_OPTIMIZATION_LEVEL": "\\"-Onone\\""
643
+ },
644
+ "release": {
645
+ "CODE_SIGN_STYLE": "Manual",
646
+ "MTL_ENABLE_DEBUG_INFO": "NO",
647
+ "SWIFT_OPTIMIZATION_LEVEL": "\\"-O\\"",
648
+ "COPY_PHASE_STRIP": "NO",
649
+ "SWIFT_COMPILATION_MODE": "wholemodule"
650
+ }
651
+ }
652
+ }${os_1.EOL}`;
653
+ fs.writeFileSync(extensionsConfigPath, content);
654
+ console.log(`🚀 Your widget is now ready to develop: App_Resources/iOS/extensions/${name}.\n`);
655
+ console.log(`Followup steps:\n
656
+ - Check App_Resources/iOS/build.xcconfig uses IPHONEOS_DEPLOYMENT_TARGET=17 or higher.
657
+ - Update App_Resources/iOS/extensions/provisioning.json with your profile id.
658
+ - Customize App_Resources/iOS/extensions/${name}/${(0, utils_1.capitalizeFirstLetter)(name)}LiveActivity.swift for your display.
659
+ - Customize Shared_Resources/iOS/SharedWidget/Sources/SharedWidget/${(0, utils_1.capitalizeFirstLetter)(name)}Model.swift for your data.
660
+ `);
661
+ }
662
+ }
663
+ generateAppleUtility(projectDir, name, bundleId) {
664
+ const capitalName = (0, utils_1.capitalizeFirstLetter)(name);
665
+ const appResourcePath = this.$projectData.appResourcesDirectoryPath;
666
+ const appResourceSrcPath = path.join(appResourcePath, "iOS", "src");
667
+ const appleUtilityPath = path.join(appResourceSrcPath, `AppleWidgetUtils.swift`);
668
+ const referenceTypesPath = path.join(projectDir, "references.d.ts");
669
+ if (!fs.existsSync(appleUtilityPath)) {
670
+ fs.mkdirSync(appResourceSrcPath, { recursive: true });
671
+ }
672
+ if (!fs.existsSync(appleUtilityPath)) {
673
+ }
674
+ let content = `import Foundation
675
+ import UIKit
676
+ import ActivityKit
677
+ import WidgetKit
678
+ import SharedWidget
679
+
680
+ @objcMembers
681
+ public class AppleWidgetUtils: NSObject {
682
+
683
+ // Live Activity Handling
684
+ public static func startActivity(_ data: NSDictionary) {
685
+ if ActivityAuthorizationInfo().areActivitiesEnabled {
686
+ let numberOfPizzas = data.object(forKey: "numberOfPizzas") as! Int
687
+ let totalAmount = data.object(forKey: "totalAmount") as! String
688
+ let attrs = ${capitalName}Model(numberOfPizzas: numberOfPizzas, totalAmount: totalAmount)
689
+
690
+ let message = data.object(forKey: "message") as! String
691
+ let deliveryTime = data.object(forKey: "deliveryTime") as! Double
692
+ let initialStatus = ${capitalName}Model.DeliveryStatus(
693
+ message: message, deliveryTime: deliveryTime)
694
+ let content = ActivityContent(state: initialStatus, staleDate: nil)
695
+
696
+ do {
697
+ let activity = try Activity<${capitalName}Model>.request(
698
+ attributes: attrs,
699
+ content: content,
700
+ pushType: nil)
701
+ print("Requested a Live Activity \\(activity.id)")
702
+ } catch (let error) {
703
+ print("Error requesting Live Activity \\(error.localizedDescription)")
704
+ }
705
+ }
706
+ }
707
+ public static func updateActivity(_ data: NSDictionary) {
708
+ if ActivityAuthorizationInfo().areActivitiesEnabled {
709
+ Task {
710
+ let message = data.object(forKey: "message") as! String
711
+ let deliveryTime = data.object(forKey: "deliveryTime") as! Double
712
+ let status = ${capitalName}Model.DeliveryStatus(
713
+ message: message, deliveryTime: deliveryTime)
714
+ let content = ActivityContent(state: status, staleDate: nil)
715
+
716
+ for activity in Activity<${capitalName}Model>.activities {
717
+ await activity.update(content)
718
+ }
719
+ }
720
+ }
721
+ }
722
+ public static func cancelActivity(_ data: NSDictionary) {
723
+ if ActivityAuthorizationInfo().areActivitiesEnabled {
724
+ Task {
725
+ let message = data.object(forKey: "message") as! String
726
+ let status = ${capitalName}Model.DeliveryStatus(
727
+ message: message, deliveryTime: 0)
728
+ let content = ActivityContent(state: status, staleDate: nil)
729
+
730
+ for activity in Activity<${capitalName}Model>.activities {
731
+ await activity.end(content, dismissalPolicy: .immediate)
732
+ }
733
+ }
734
+ }
735
+ }
736
+ public static func getData(key: String) -> String? {
737
+ guard let sharedDefaults = UserDefaults(suiteName: "group.${bundleId}") else {
738
+ return nil
739
+ }
740
+ return sharedDefaults.object(forKey: key) as? String
741
+ }
742
+ public static func updateData(key: String, _ data: String) {
743
+ guard let sharedDefaults = UserDefaults(suiteName: "group.${bundleId}") else {
744
+ return
745
+ }
746
+ sharedDefaults.set(data, forKey: key)
747
+ sharedDefaults.synchronize()
748
+ }
749
+ public static func removeData(key: String) {
750
+ guard let sharedDefaults = UserDefaults(suiteName: "group.${bundleId}") else {
751
+ return
752
+ }
753
+ sharedDefaults.removeObject(forKey: key)
754
+ sharedDefaults.synchronize()
755
+ }
756
+
757
+ // Home Screen Widget Handling
758
+ public static func updateWidget() {
759
+ if #available(iOS 14.0, *) {
760
+ Task.detached(priority: .userInitiated) {
761
+ WidgetCenter.shared.reloadAllTimelines()
762
+ }
763
+ }
764
+ }
765
+ }${os_1.EOL}`;
766
+ fs.writeFileSync(appleUtilityPath, content);
767
+ content = `/**
768
+ * Customize for your own Apple Widget Data
769
+ */
770
+ declare interface AppleWidgetModelData {
771
+ numberOfPizzas: number;
772
+ totalAmount: string;
773
+ driverName: string;
774
+ deliveryTime: number;
775
+ }
776
+ declare class AppleWidgetUtils extends NSObject {
777
+ static startActivity(data: AppleWidgetModelData): void;
778
+ static updateActivity(
779
+ data: Pick<AppleWidgetModelData, "message" | "deliveryTime">
780
+ ): void;
781
+ static cancelActivity(data: Pick<AppleWidgetModelData, "message">): void;
782
+ static updateWidget(): void;
783
+ static updateDataWithKey(key: string, data: string): void;
784
+ static getDataWithKey(key: string): string;
785
+ static removeDataWithKey(key: string): void;
786
+ }${os_1.EOL}`;
787
+ if (!fs.existsSync(referenceTypesPath)) {
788
+ const references = `/// <reference path="./node_modules/@nativescript/types-android/index.d.ts" />
789
+ /// <reference path="./node_modules/@nativescript/types-ios/complete.d.ts" />${os_1.EOL}${content}`;
790
+ fs.writeFileSync(referenceTypesPath, references);
791
+ }
792
+ else {
793
+ const references = fs.readFileSync(referenceTypesPath, {
794
+ encoding: "utf-8",
795
+ });
796
+ if ((references === null || references === void 0 ? void 0 : references.indexOf("AppleWidgetUtils")) === -1) {
797
+ content = `${references.toString()}${os_1.EOL}${content}`;
798
+ fs.writeFileSync(referenceTypesPath, content);
799
+ }
800
+ }
801
+ }
802
+ }
803
+ exports.WidgetIOSCommand = WidgetIOSCommand;
804
+ yok_1.injector.registerCommand(["widget"], WidgetCommand);
805
+ yok_1.injector.registerCommand(["widget|ios"], WidgetIOSCommand);
package/lib/constants.js CHANGED
@@ -68,7 +68,7 @@ exports.APK_EXTENSION_NAME = ".apk";
68
68
  exports.AAB_EXTENSION_NAME = ".aab";
69
69
  exports.APKS_EXTENSION_NAME = ".apks";
70
70
  exports.HASHES_FILE_NAME = ".nshashes";
71
- exports.TNS_NATIVE_SOURCE_GROUP_NAME = "NSNativeSources";
71
+ exports.TNS_NATIVE_SOURCE_GROUP_NAME = "AppResourcesSrc";
72
72
  exports.NATIVE_SOURCE_FOLDER = "src";
73
73
  exports.APPLICATION_RESPONSE_TIMEOUT_SECONDS = 60;
74
74
  exports.NATIVE_EXTENSION_FOLDER = "extensions";
@@ -101,8 +101,34 @@ interface INsConfigPlaform {
101
101
  id?: string;
102
102
  }
103
103
 
104
+ interface IOSSPMPackageBase {
105
+ name: string;
106
+ libs: string[];
107
+ /**
108
+ * Optional: If you have more targets (like widgets for example)
109
+ * you can list their names here to include the Swift Package with them
110
+ */
111
+ targets?: string[];
112
+ }
113
+
114
+ export interface IOSRemoteSPMPackage extends IOSSPMPackageBase {
115
+ repositoryURL: string;
116
+ version: string;
117
+ }
118
+
119
+ export interface IOSLocalSPMPackage extends IOSSPMPackageBase {
120
+ path: string;
121
+ }
122
+
123
+ export type IOSSPMPackage = IOSRemoteSPMPackage | IOSLocalSPMPackage;
124
+
104
125
  interface INsConfigIOS extends INsConfigPlaform {
105
126
  discardUncaughtJsExceptions?: boolean;
127
+ /**
128
+ * Swift Package Manager
129
+ * List packages to be included in the iOS build.
130
+ */
131
+ SPMPackages?: Array<IOSSPMPackage>;
106
132
  }
107
133
 
108
134
  interface INSConfigVisionOS extends INsConfigIOS {}
@@ -206,7 +232,7 @@ interface IProjectData extends ICreateProjectData {
206
232
  initializeProjectData(projectDir?: string): void;
207
233
  initializeProjectDataFromContent(
208
234
  packageJsonContent: string,
209
- projectDir?: string
235
+ projectDir?: string,
210
236
  ): void;
211
237
  getAppDirectoryPath(projectDir?: string): string;
212
238
  getAppDirectoryRelativePath(): string;
@@ -296,7 +322,7 @@ interface IProjectDataService {
296
322
  */
297
323
  getRuntimePackage(
298
324
  projectDir: string,
299
- platform: SupportedPlatform
325
+ platform: SupportedPlatform,
300
326
  ): IBasePluginData;
301
327
 
302
328
  /**
@@ -316,7 +342,7 @@ interface IProjectCleanupService {
316
342
  */
317
343
  clean(
318
344
  pathsToClean: string[],
319
- options?: IProjectCleanupOptions
345
+ options?: IProjectCleanupOptions,
320
346
  ): Promise<IProjectCleanupResult>;
321
347
 
322
348
  /**
@@ -325,7 +351,7 @@ interface IProjectCleanupService {
325
351
  */
326
352
  cleanPath(
327
353
  pathToClean: string,
328
- options?: IProjectCleanupOptions
354
+ options?: IProjectCleanupOptions,
329
355
  ): Promise<IProjectCleanupResult>;
330
356
  }
331
357
 
@@ -427,7 +453,7 @@ interface IProjectConfigService {
427
453
 
428
454
  writeLegacyNSConfigIfNeeded(
429
455
  projectDir: string,
430
- runtimePackage: IBasePluginData
456
+ runtimePackage: IBasePluginData,
431
457
  ): Promise<void>;
432
458
  }
433
459
 
@@ -530,14 +556,14 @@ interface IProjectTemplatesService {
530
556
  */
531
557
  prepareTemplate(
532
558
  templateName: string,
533
- projectDir: string
559
+ projectDir: string,
534
560
  ): Promise<ITemplateData>;
535
561
  }
536
562
 
537
563
  interface IPlatformProjectServiceBase {
538
564
  getPluginPlatformsFolderPath(
539
565
  pluginData: IPluginData,
540
- platform: string
566
+ platform: string,
541
567
  ): string;
542
568
  getFrameworkVersion(projectData: IProjectData): string;
543
569
  }
@@ -599,7 +625,7 @@ interface ILocalBuildService {
599
625
  */
600
626
  build(
601
627
  platform: string,
602
- platformBuildOptions: IPlatformBuildData
628
+ platformBuildOptions: IPlatformBuildData,
603
629
  ): Promise<string>;
604
630
  /**
605
631
  * Removes build artifacts specific to the platform
@@ -619,7 +645,7 @@ interface ITestExecutionService {
619
645
  startKarmaServer(
620
646
  platform: string,
621
647
  liveSyncInfo: ILiveSyncInfo,
622
- deviceDescriptors: ILiveSyncDeviceDescriptor[]
648
+ deviceDescriptors: ILiveSyncDeviceDescriptor[],
623
649
  ): Promise<void>;
624
650
  canStartKarmaServer(projectData: IProjectData): Promise<boolean>;
625
651
  }
@@ -653,17 +679,17 @@ interface ICocoaPodsService {
653
679
  */
654
680
  applyPodfileFromAppResources(
655
681
  projectData: IProjectData,
656
- platformData: IPlatformData
682
+ platformData: IPlatformData,
657
683
  ): Promise<void>;
658
684
 
659
685
  applyPodfileArchExclusions(
660
686
  projectData: IProjectData,
661
- platformData: IPlatformData
687
+ platformData: IPlatformData,
662
688
  ): Promise<void>;
663
689
 
664
690
  applyPodfileFromExtensions(
665
691
  projectData: IProjectData,
666
- platformData: IPlatformData
692
+ platformData: IPlatformData,
667
693
  ): Promise<void>;
668
694
 
669
695
  /**
@@ -678,7 +704,7 @@ interface ICocoaPodsService {
678
704
  moduleName: string,
679
705
  podfilePath: string,
680
706
  projectData: IProjectData,
681
- platformData: IPlatformData
707
+ platformData: IPlatformData,
682
708
  ): Promise<void>;
683
709
 
684
710
  /**
@@ -700,7 +726,7 @@ interface ICocoaPodsService {
700
726
  moduleName: string,
701
727
  podfilePath: string,
702
728
  projectData: IProjectData,
703
- nativeProjectPath: string
729
+ nativeProjectPath: string,
704
730
  ): void;
705
731
 
706
732
  /**
@@ -718,7 +744,7 @@ interface ICocoaPodsService {
718
744
  */
719
745
  executePodInstall(
720
746
  projectRoot: string,
721
- xcodeProjPath: string
747
+ xcodeProjPath: string,
722
748
  ): Promise<ISpawnResult>;
723
749
 
724
750
  /**
@@ -730,7 +756,7 @@ interface ICocoaPodsService {
730
756
  mergePodXcconfigFile(
731
757
  projectData: IProjectData,
732
758
  platformData: IPlatformData,
733
- opts: IRelease
759
+ opts: IRelease,
734
760
  ): Promise<void>;
735
761
  }
736
762
 
@@ -738,16 +764,16 @@ interface ICocoaPodsPlatformManager {
738
764
  addPlatformSection(
739
765
  projectData: IProjectData,
740
766
  podfilePlatformData: IPodfilePlatformData,
741
- projectPodfileContent: string
767
+ projectPodfileContent: string,
742
768
  ): string;
743
769
  removePlatformSection(
744
770
  moduleName: string,
745
771
  projectPodFileContent: string,
746
- podfilePath: string
772
+ podfilePath: string,
747
773
  ): string;
748
774
  replacePlatformRow(
749
775
  podfileContent: string,
750
- podfilePath: string
776
+ podfilePath: string,
751
777
  ): { replacedContent: string; podfilePlatformData: IPodfilePlatformData };
752
778
  }
753
779
 
@@ -772,24 +798,24 @@ interface IIOSNativeTargetService {
772
798
  targetType: string,
773
799
  project: IXcode.project,
774
800
  platformData: IPlatformData,
775
- parentTarget?: string
801
+ parentTarget?: string,
776
802
  ): IXcode.target;
777
803
  prepareSigning(
778
804
  targetUuids: string[],
779
805
  projectData: IProjectData,
780
- projectPath: string
806
+ projectPath: string,
781
807
  ): void;
782
808
  getTargetDirectories(folderPath: string): string[];
783
809
  setXcodeTargetBuildConfigurationProperties(
784
810
  properties: IXcodeTargetBuildConfigurationProperty[],
785
811
  targetName: string,
786
- project: IXcode.project
812
+ project: IXcode.project,
787
813
  ): void;
788
814
  setConfigurationsFromJsonFile(
789
815
  jsonPath: string,
790
816
  targetUuid: string,
791
817
  targetName: string,
792
- project: IXcode.project
818
+ project: IXcode.project,
793
819
  ): void;
794
820
  }
795
821
 
@@ -798,7 +824,7 @@ interface IIOSNativeTargetService {
798
824
  */
799
825
  interface IIOSExtensionsService {
800
826
  addExtensionsFromPath(
801
- options: IAddExtensionsFromPathOptions
827
+ options: IAddExtensionsFromPathOptions,
802
828
  ): Promise<boolean>;
803
829
  removeExtensions(options: IRemoveExtensionsOptions): void;
804
830
  }
@@ -18,6 +18,7 @@ const projectServiceBaseLib = require("./platform-project-service-base");
18
18
  const plist_merge_patch_1 = require("plist-merge-patch");
19
19
  const os_1 = require("os");
20
20
  const plist = require("plist");
21
+ const fastGlob = require("fast-glob");
21
22
  const constants_2 = require("../constants");
22
23
  const helpers_2 = require("../common/helpers");
23
24
  const yok_1 = require("../common/yok");
@@ -363,6 +364,12 @@ class IOSProjectService extends projectServiceBaseLib.PlatformProjectServiceBase
363
364
  resourcesNativeCodePath = path.join(resourcesDirectoryPath, this.$devicePlatformsConstants.iOS, constants.NATIVE_SOURCE_FOLDER);
364
365
  }
365
366
  await this.prepareNativeSourceCode(constants.TNS_NATIVE_SOURCE_GROUP_NAME, resourcesNativeCodePath, projectData);
367
+ const nativeSource = this.$projectConfigService.getValue(`${this._platformData.platformNameLowerCase}.NativeSource`, []);
368
+ if (nativeSource === null || nativeSource === void 0 ? void 0 : nativeSource.length) {
369
+ for (const source of nativeSource) {
370
+ await this.prepareNativeSourceCode(source.name, source.path, projectData);
371
+ }
372
+ }
366
373
  }
367
374
  this.$iOSWatchAppService.removeWatchApp({ pbxProjPath });
368
375
  const addedWatchApp = await this.$iOSWatchAppService.addWatchAppFromPath({
@@ -637,7 +644,7 @@ class IOSProjectService extends projectServiceBaseLib.PlatformProjectServiceBase
637
644
  }
638
645
  async prepareNativeSourceCode(groupName, sourceFolderPath, projectData) {
639
646
  const project = this.createPbxProj(projectData);
640
- const group = this.getRootGroup(groupName, sourceFolderPath);
647
+ const group = await this.getRootGroup(groupName, sourceFolderPath);
641
648
  project.addPbxGroup(group.files, group.name, group.path, null, {
642
649
  isMain: true,
643
650
  filesRelativeToProject: true,
@@ -680,20 +687,30 @@ class IOSProjectService extends projectServiceBaseLib.PlatformProjectServiceBase
680
687
  this.$logger.warn("Let us know if there are other Extension features you'd like! https://github.com/NativeScript/NativeScript/issues");
681
688
  }
682
689
  }
683
- getRootGroup(name, rootPath) {
690
+ async getRootGroup(name, rootPath) {
684
691
  const filePathsArr = [];
685
692
  const rootGroup = {
686
693
  name: name,
687
694
  files: filePathsArr,
688
695
  path: rootPath,
689
696
  };
690
- if (this.$fs.exists(rootPath)) {
691
- const stats = this.$fs.getFsStats(rootPath);
692
- if (stats.isDirectory() && !this.$fs.isEmptyDir(rootPath)) {
693
- this.$fs.readDirectory(rootPath).forEach((fileName) => {
694
- const filePath = path.join(rootGroup.path, fileName);
695
- filePathsArr.push(filePath);
696
- });
697
+ if (fastGlob.isDynamicPattern(rootPath)) {
698
+ const projectRoot = this.$projectDataService.getProjectData().projectDir;
699
+ const filePaths = await fastGlob(rootPath);
700
+ for (const filePath of filePaths) {
701
+ const sourceFilePath = path.normalize(path.join(projectRoot, filePath));
702
+ filePathsArr.push(sourceFilePath);
703
+ }
704
+ }
705
+ else {
706
+ if (this.$fs.exists(rootPath)) {
707
+ const stats = this.$fs.getFsStats(rootPath);
708
+ if (stats.isDirectory() && !this.$fs.isEmptyDir(rootPath)) {
709
+ this.$fs.readDirectory(rootPath).forEach((fileName) => {
710
+ const filePath = path.join(rootGroup.path, fileName);
711
+ filePathsArr.push(filePath);
712
+ });
713
+ }
697
714
  }
698
715
  }
699
716
  return rootGroup;
@@ -719,9 +736,9 @@ class IOSProjectService extends projectServiceBaseLib.PlatformProjectServiceBase
719
736
  await this.addStaticLibrary(path.join(pluginPlatformsFolderPath, fileName), projectData);
720
737
  }
721
738
  }
722
- removeNativeSourceCode(pluginPlatformsFolderPath, pluginData, projectData) {
739
+ async removeNativeSourceCode(pluginPlatformsFolderPath, pluginData, projectData) {
723
740
  const project = this.createPbxProj(projectData);
724
- const group = this.getRootGroup(pluginData.name, pluginPlatformsFolderPath);
741
+ const group = await this.getRootGroup(pluginData.name, pluginPlatformsFolderPath);
725
742
  project.removePbxGroup(group.name, group.path);
726
743
  project.removeFromHeaderSearchPaths(group.path);
727
744
  this.savePbxProj(project, projectData);
@@ -167,7 +167,7 @@ export default {
167
167
  if (!this.$fs.exists(configFilePath)) {
168
168
  this.writeDefaultConfig(this.projectHelper.projectDir);
169
169
  }
170
- if (typeof value === "object") {
170
+ if (!Array.isArray(value) && typeof value === "object") {
171
171
  let allSuccessful = true;
172
172
  for (const prop of this.flattenObjectToPaths(value)) {
173
173
  if (!(await this.setValue(prop.key, prop.value))) {
@@ -195,7 +195,7 @@ export default {
195
195
  this.$logger.error(`Failed to update config.` + error);
196
196
  }
197
197
  finally {
198
- if (this.getValue(key) !== value) {
198
+ if (!Array.isArray(this.getValue(key)) && this.getValue(key) !== value) {
199
199
  this.$logger.error(`${os_1.EOL}Failed to update ${hasTSConfig ? constants_1.CONFIG_FILE_NAME_TS : constants_1.CONFIG_FILE_NAME_JS}.${os_1.EOL}`);
200
200
  this.$logger.printMarkdown(`Please manually update \`${hasTSConfig ? constants_1.CONFIG_FILE_NAME_TS : constants_1.CONFIG_FILE_NAME_JS}\` and set \`${key}\` to \`${value}\`.${os_1.EOL}`);
201
201
  this.$fs.writeFile(configFilePath, configContent);
@@ -297,7 +297,16 @@ You may add \`nsconfig.json\` to \`.gitignore\` as the CLI will regenerate it as
297
297
  flattenObjectToPaths(obj, basePath) {
298
298
  const toPath = (key) => [basePath, key].filter(Boolean).join(".");
299
299
  return Object.keys(obj).reduce((all, key) => {
300
- if (typeof obj[key] === "object") {
300
+ if (Array.isArray(obj[key])) {
301
+ return [
302
+ ...all,
303
+ {
304
+ key: toPath(key),
305
+ value: obj[key],
306
+ },
307
+ ];
308
+ }
309
+ else if (typeof obj[key] === "object" && obj[key] !== null) {
301
310
  return [...all, ...this.flattenObjectToPaths(obj[key], toPath(key))];
302
311
  }
303
312
  return [
@@ -102,6 +102,15 @@ class ConfigTransformer {
102
102
  else if (typeof value === "number" || typeof value === "boolean") {
103
103
  return `${value}`;
104
104
  }
105
+ else if (Array.isArray(value)) {
106
+ return `[${value.map((v) => this.createInitializer(v)).join(", ")}]`;
107
+ }
108
+ else if (typeof value === "object" && value !== null) {
109
+ const properties = Object.entries(value)
110
+ .map(([key, val]) => `${key}: ${this.createInitializer(val)}`)
111
+ .join(", ");
112
+ return `{ ${properties} }`;
113
+ }
105
114
  return `{}`;
106
115
  }
107
116
  setInitializerValue(initializer, newValue) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nativescript",
3
3
  "main": "./lib/nativescript-cli-lib.js",
4
- "version": "8.9.0-dev.1",
4
+ "version": "8.9.0-dev.3",
5
5
  "author": "NativeScript <oss@nativescript.org>",
6
6
  "description": "Command-line interface for building NativeScript projects",
7
7
  "bin": {
@@ -84,7 +84,7 @@
84
84
  "minimatch": "10.0.1",
85
85
  "mkdirp": "3.0.1",
86
86
  "mute-stream": "2.0.0",
87
- "nativescript-dev-xcode": "0.8.0",
87
+ "nativescript-dev-xcode": "0.8.1",
88
88
  "open": "10.1.0",
89
89
  "ora": "8.1.1",
90
90
  "pacote": "21.0.0",