Skip to content

Commit 7fdb7b7

Browse files
committed
feat: initial work on watch app integration
1 parent 9283d29 commit 7fdb7b7

File tree

6 files changed

+144
-14
lines changed

6 files changed

+144
-14
lines changed

lib/bootstrap.ts

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ $injector.require("androidProjectService", "./services/android-project-service")
1313
$injector.require("androidPluginBuildService", "./services/android-plugin-build-service");
1414
$injector.require("iOSEntitlementsService", "./services/ios-entitlements-service");
1515
$injector.require("iOSExtensionsService", "./services/ios-extensions-service");
16+
$injector.require("iOSWatchAppService", "./services/ios-watch-app-service");
1617
$injector.require("iOSProjectService", "./services/ios-project-service");
1718
$injector.require("iOSProvisionService", "./services/ios-provision-service");
1819
$injector.require("xcconfigService", "./services/xcconfig-service");

lib/common/definitions/mobile.d.ts

+4
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ declare module Mobile {
8181
imageIdentifier?: string;
8282
}
8383

84+
interface IIOSWatchSimulatorDevice extends IDeviceInfo{
85+
86+
}
87+
8488
interface IDeviceError extends Error, IDeviceIdentifier { }
8589

8690
interface IDeviceIdentifier {

lib/common/mobile/ios/simulator/ios-simulator-device.ts

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export class IOSSimulator extends IOSDeviceBase implements Mobile.IiOSDevice {
1010
public applicationManager: Mobile.IDeviceApplicationManager;
1111
public fileSystem: Mobile.IDeviceFileSystem;
1212
public deviceInfo: Mobile.IDeviceInfo;
13+
public watchSimulator: Mobile.IIOSWatchSimulatorDevice;
1314

1415
constructor(private simulator: Mobile.IiSimDevice,
1516
private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants,

lib/definitions/xcode.d.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ declare module "nativescript-dev-xcode" {
2828

2929
pbxXCBuildConfigurationSection(): any;
3030

31-
addTarget(targetName: string, targetType: string, targetPath?: string): target;
31+
addTarget(targetName: string, targetType: string, targetPath?: string, parentTarget?: string): target;
3232
addBuildPhase(filePathsArray: string[],
3333
buildPhaseType: string,
3434
comment: string,
@@ -47,6 +47,7 @@ declare module "nativescript-dev-xcode" {
4747
addBuildProperty(prop: string, value: any, build_name?: string, productName?: string): void;
4848
addToHeaderSearchPaths(file: string|Object, productName?: string): void;
4949
removeTargetsByProductType(targetType: string): void
50+
getFirstTarget(): {uuid: string}
5051
}
5152

5253
class target {

lib/services/ios-project-service.ts

+19-13
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
5353
private $plistParser: IPlistParser,
5454
private $sysInfo: ISysInfo,
5555
private $xcconfigService: IXcconfigService,
56-
private $iOSExtensionsService: IIOSExtensionsService) {
56+
private $iOSExtensionsService: IIOSExtensionsService,
57+
private $iOSWatchAppService: IIOSExtensionsService) {
5758
super($fs, $projectDataService);
5859
}
5960

@@ -414,7 +415,6 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
414415
args = args.concat((buildConfig && buildConfig.architectures) || this.getBuildArchitectures(projectData, buildConfig, ["armv7", "arm64"]));
415416

416417
args = args.concat([
417-
"-sdk", DevicePlatformSdkName,
418418
"BUILD_DIR=" + path.join(projectRoot, constants.BUILD_DIR)
419419
]);
420420

@@ -503,6 +503,8 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
503503

504504
xcode.setAutomaticSigningStyle(projectData.projectName, teamId);
505505
xcode.setAutomaticSigningStyleByTargetProductType("com.apple.product-type.app-extension", teamId);
506+
xcode.setAutomaticSigningStyleByTargetProductType("com.apple.product-type.watchkit2-extension", teamId);
507+
xcode.setAutomaticSigningStyleByTargetProductType("com.apple.product-type.application.watchapp2", teamId);
506508
xcode.save();
507509

508510
this.$logger.trace(`Set Automatic signing style and team id ${teamId}.`);
@@ -584,12 +586,13 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
584586
.concat([
585587
"build",
586588
"-configuration", getConfigurationName(buildConfig.release),
587-
"-sdk", SimulatorPlatformSdkName,
588589
"ONLY_ACTIVE_ARCH=NO",
589590
"BUILD_DIR=" + path.join(projectRoot, constants.BUILD_DIR),
590-
"CODE_SIGN_IDENTITY=",
591+
"CODE_SIGNING_ALLOWED=NO",
592+
"-destination",
593+
"generic/platform=iOS Simulator"
591594
])
592-
.concat(this.xcbuildProjectArgs(projectRoot, projectData));
595+
.concat(this.xcbuildProjectArgs(projectRoot, projectData, "scheme"));
593596

594597
await this.xcodebuild(args, projectRoot, buildConfig.buildOutputStdio);
595598
}
@@ -781,15 +784,18 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
781784
_.each(imagesToRemove, image => project.removeResourceFile(path.join(this.getAppResourcesDestinationDirectoryPath(projectData), image)));
782785

783786
this.savePbxProj(project, projectData);
784-
785-
const resourcesNativeCodePath = path.join(
786-
projectData.getAppResourcesDirectoryPath(),
787-
this.getPlatformData(projectData).normalizedPlatformName,
788-
constants.NATIVE_SOURCE_FOLDER
789-
);
790-
791-
await this.prepareNativeSourceCode(constants.TNS_NATIVE_SOURCE_GROUP_NAME, resourcesNativeCodePath, projectData);
792787
}
788+
789+
const platformData = this.getPlatformData(projectData);
790+
const resourlcesDirectoryPath = projectData.getAppResourcesDirectoryPath();
791+
const pbxProjPath = this.getPbxProjPath(projectData);
792+
const resourcesNativeCodePath = path.join(
793+
projectData.getAppResourcesDirectoryPath(),
794+
platformData.normalizedPlatformName,
795+
constants.NATIVE_SOURCE_FOLDER
796+
);
797+
await this.prepareNativeSourceCode(constants.TNS_NATIVE_SOURCE_GROUP_NAME, resourcesNativeCodePath, projectData);
798+
await this.$iOSWatchAppService.addExtensionsFromPath({ extensionsFolderPath: path.join(resourlcesDirectoryPath, platformData.normalizedPlatformName), projectData, platformData, pbxProjPath });
793799

794800
}
795801

lib/services/ios-watch-app-service.ts

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import * as path from "path";
2+
3+
export class IOSWatchAppService implements IIOSExtensionsService {
4+
constructor(private $fs: IFileSystem,
5+
private $pbxprojDomXcode: IPbxprojDomXcode,
6+
private $xcode: IXcode) {
7+
}
8+
9+
public async addExtensionsFromPath({extensionsFolderPath, projectData, platformData, pbxProjPath}: IAddExtensionsFromPathOptions): Promise<boolean> {
10+
const targetUuids: string[] = [];
11+
let addedExtensions = false;
12+
if (!this.$fs.exists(extensionsFolderPath)) {
13+
return false;
14+
}
15+
const project = new this.$xcode.project(pbxProjPath);
16+
const appPath = path.join(extensionsFolderPath, "watchapp");
17+
const extensionPath = path.join(extensionsFolderPath, "watchextension");
18+
project.parseSync();
19+
const appFolder = this.$fs.readDirectory(appPath)
20+
.filter(fileName => {
21+
const filePath = path.join(appPath, fileName);
22+
const stats = this.$fs.getFsStats(filePath);
23+
24+
return stats.isDirectory() && !fileName.startsWith(".");
25+
})[0];
26+
27+
const extensionFolder = this.$fs.readDirectory(extensionPath)
28+
.filter(fileName => {
29+
const filePath = path.join(extensionPath, fileName);
30+
const stats = this.$fs.getFsStats(filePath);
31+
32+
return stats.isDirectory() && !fileName.startsWith(".");
33+
})[0];
34+
35+
let targetUuid = this.addExtensionToProject(appPath, appFolder, project, projectData, platformData, "watch_app", `${projectData.projectIdentifiers.ios}.watchkitapp`, project.getFirstTarget().uuid);
36+
targetUuids.push(targetUuid);
37+
targetUuid = this.addExtensionToProject(extensionPath, extensionFolder, project, projectData, platformData, "watch_extension", `${projectData.projectIdentifiers.ios}.watchkitapp.watchkitextension`, targetUuid);
38+
targetUuids.push(targetUuid);
39+
addedExtensions = true;
40+
41+
this.$fs.writeFile(pbxProjPath, project.writeSync({omitEmptyValues: true}));
42+
this.prepareExtensionSigning(targetUuids, projectData, pbxProjPath);
43+
44+
return addedExtensions;
45+
}
46+
47+
private addExtensionToProject(extensionsFolderPath: string, extensionFolder: string, project: IXcode.project, projectData: IProjectData, platformData: IPlatformData, targetType: string, identifier: string, parentTarget: string): string {
48+
const extensionPath = path.join(extensionsFolderPath, extensionFolder);
49+
const extensionRelativePath = path.relative(platformData.projectRoot, extensionPath);
50+
const files = this.$fs.readDirectory(extensionPath)
51+
.filter(filePath => !filePath.startsWith("."))
52+
.map(filePath => path.join(extensionPath, filePath));
53+
const target = project.addTarget(extensionFolder, targetType, extensionRelativePath, parentTarget);
54+
project.addBuildPhase([], 'PBXSourcesBuildPhase', 'Sources', target.uuid);
55+
project.addBuildPhase([], 'PBXResourcesBuildPhase', 'Resources', target.uuid);
56+
project.addBuildPhase([], 'PBXFrameworksBuildPhase', 'Frameworks', target.uuid);
57+
58+
const extJsonPath = path.join(extensionsFolderPath, extensionFolder, "extension.json");
59+
if (this.$fs.exists(extJsonPath)) {
60+
const extensionJson = this.$fs.readJson(extJsonPath);
61+
_.forEach(extensionJson.frameworks, framework => {
62+
project.addFramework(
63+
framework,
64+
{ target: target.uuid }
65+
);
66+
});
67+
if (extensionJson.assetcatalogCompilerAppiconName) {
68+
project.addToBuildSettings("ASSETCATALOG_COMPILER_APPICON_NAME", extensionJson.assetcatalogCompilerAppiconName, target.uuid);
69+
}
70+
}
71+
const identifierParts = identifier.split(".");
72+
identifierParts.pop();
73+
const wkAppBundleIdentifier = identifierParts.join(".");
74+
project.addPbxGroup(files, extensionFolder, extensionPath, null, { isMain: true, target: target.uuid, filesRelativeToProject: true });
75+
project.addBuildProperty("PRODUCT_BUNDLE_IDENTIFIER", identifier, "Debug", extensionFolder);
76+
project.addBuildProperty("PRODUCT_BUNDLE_IDENTIFIER", identifier, "Release", extensionFolder);
77+
project.addBuildProperty("SDKROOT", "watchos", "Debug", extensionFolder);
78+
project.addBuildProperty("SDKROOT", "watchos", "Release", extensionFolder);
79+
project.addBuildProperty("TARGETED_DEVICE_FAMILY", 4, "Debug", extensionFolder);
80+
project.addBuildProperty("TARGETED_DEVICE_FAMILY", 4, "Release", extensionFolder);
81+
project.addBuildProperty("WATCHOS_DEPLOYMENT_TARGET", 4.1, "Debug", extensionFolder);
82+
project.addBuildProperty("WATCHOS_DEPLOYMENT_TARGET", 4.1, "Release", extensionFolder);
83+
project.addBuildProperty("WK_APP_BUNDLE_IDENTIFIER", wkAppBundleIdentifier, "Debug", extensionFolder);
84+
project.addBuildProperty("WK_APP_BUNDLE_IDENTIFIER", wkAppBundleIdentifier, "Release", extensionFolder);
85+
project.addToHeaderSearchPaths(extensionPath, target.pbxNativeTarget.productName);
86+
87+
return target.uuid;
88+
}
89+
90+
private prepareExtensionSigning(targetUuids: string[], projectData:IProjectData, projectPath: string) {
91+
const xcode = this.$pbxprojDomXcode.Xcode.open(projectPath);
92+
const signing = xcode.getSigning(projectData.projectName);
93+
if (signing !== undefined) {
94+
_.forEach(targetUuids, targetUuid => {
95+
if (signing.style === "Automatic") {
96+
xcode.setAutomaticSigningStyleByTargetKey(targetUuid, signing.team);
97+
} else {
98+
for (const config in signing.configurations) {
99+
const signingConfiguration = signing.configurations[config];
100+
xcode.setManualSigningStyleByTargetKey(targetUuid, signingConfiguration);
101+
break;
102+
}
103+
}
104+
});
105+
}
106+
xcode.save();
107+
}
108+
109+
public removeExtensions({pbxProjPath}: IRemoveExtensionsOptions): void {
110+
const project = new this.$xcode.project(pbxProjPath);
111+
project.parseSync();
112+
project.removeTargetsByProductType("com.apple.product-type.app-extension");
113+
this.$fs.writeFile(pbxProjPath, project.writeSync({omitEmptyValues: true}));
114+
}
115+
}
116+
117+
$injector.register("iOSWatchAppService", IOSWatchAppService);

0 commit comments

Comments
 (0)