Skip to content

Commit 1ad9786

Browse files
authored
Merge pull request #4568 from NativeScript/kddimitrov/watch-app
Kddimitrov/watch app
2 parents c32139a + 74ace6b commit 1ad9786

11 files changed

+326
-89
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/constants.ts

+19
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ export const TNS_NATIVE_SOURCE_GROUP_NAME = "TNSNativeSource";
4949
export const NATIVE_SOURCE_FOLDER = "src";
5050
export const APPLICATION_RESPONSE_TIMEOUT_SECONDS = 60;
5151
export const NATIVE_EXTENSION_FOLDER = "extensions";
52+
export const IOS_WATCHAPP_FOLDER = "watchapp";
53+
export const IOS_WATCHAPP_EXTENSION_FOLDER = "watchextension";
5254

5355
export class PackageVersion {
5456
static NEXT = "next";
@@ -280,3 +282,20 @@ export const LiveSyncEvents = {
280282
liveSyncStarted: "liveSyncStarted",
281283
liveSyncNotification: "notify"
282284
};
285+
286+
export enum IOSDeviceTargets {
287+
ios = "1,2",
288+
watchos = 4
289+
}
290+
291+
export enum IOSNativeTargetProductTypes {
292+
watchApp = "com.apple.product-type.application.watchapp2",
293+
watchExtension = "com.apple.product-type.watchkit2-extension",
294+
appExtension = "com.apple.product-type.app-extension"
295+
}
296+
297+
export enum IOSNativeTargetTypes {
298+
watchApp = "watch_app",
299+
watchExtension = "watch_extension",
300+
appExtension = "app_extension"
301+
}

lib/definitions/project.d.ts

+24-2
Original file line numberDiff line numberDiff line change
@@ -592,17 +592,39 @@ interface IIOSExtensionsService {
592592
removeExtensions(options: IRemoveExtensionsOptions): void;
593593
}
594594

595-
interface IAddExtensionsFromPathOptions {
596-
extensionsFolderPath: string;
595+
/**
596+
* Describes a service used to add and remove iOS extension
597+
*/
598+
interface IIOSExtensionsService {
599+
addExtensionsFromPath(options: IAddExtensionsFromPathOptions): Promise<boolean>;
600+
removeExtensions(options: IRemoveExtensionsOptions): void;
601+
}
602+
603+
interface IIOSWatchAppService {
604+
addWatchAppFromPath(options: IAddWatchAppFromPathOptions): Promise<boolean>;
605+
removeWatchApp(options: IRemoveWatchAppOptions): void;
606+
}
607+
608+
interface IAddTargetFromPathOptions {
597609
projectData: IProjectData;
598610
platformData: IPlatformData;
599611
pbxProjPath: string;
600612
}
601613

614+
interface IAddExtensionsFromPathOptions extends IAddTargetFromPathOptions {
615+
extensionsFolderPath: string;
616+
}
617+
618+
interface IAddWatchAppFromPathOptions extends IAddTargetFromPathOptions {
619+
watchAppFolderPath: string;
620+
}
621+
602622
interface IRemoveExtensionsOptions {
603623
pbxProjPath: string
604624
}
605625

626+
interface IRemoveWatchAppOptions extends IRemoveExtensionsOptions{}
627+
606628
interface IRubyFunction {
607629
functionName: string;
608630
functionParameters?: string;

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-extensions-service.ts

+21-64
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import * as path from "path";
2-
3-
export class IOSExtensionsService implements IIOSExtensionsService {
4-
constructor(private $fs: IFileSystem,
5-
private $pbxprojDomXcode: IPbxprojDomXcode,
6-
private $xcode: IXcode) {
2+
import { NativeTargetServiceBase } from "./ios-native-target-service-base";
3+
import { IOSNativeTargetProductTypes, IOSNativeTargetTypes } from "../constants";
4+
5+
export class IOSExtensionsService extends NativeTargetServiceBase implements IIOSExtensionsService {
6+
constructor(protected $fs: IFileSystem,
7+
protected $pbxprojDomXcode: IPbxprojDomXcode,
8+
protected $xcode: IXcode) {
9+
super($fs, $pbxprojDomXcode, $xcode);
710
}
811

912
public async addExtensionsFromPath({extensionsFolderPath, projectData, platformData, pbxProjPath}: IAddExtensionsFromPathOptions): Promise<boolean> {
@@ -14,81 +17,35 @@ export class IOSExtensionsService implements IIOSExtensionsService {
1417
}
1518
const project = new this.$xcode.project(pbxProjPath);
1619
project.parseSync();
17-
this.$fs.readDirectory(extensionsFolderPath)
18-
.filter(fileName => {
19-
const filePath = path.join(extensionsFolderPath, fileName);
20-
const stats = this.$fs.getFsStats(filePath);
21-
22-
return stats.isDirectory() && !fileName.startsWith(".");
23-
})
20+
this.getTargetDirectories(extensionsFolderPath)
2421
.forEach(extensionFolder => {
25-
const targetUuid = this.addExtensionToProject(extensionsFolderPath, extensionFolder, project, projectData, platformData);
26-
targetUuids.push(targetUuid);
22+
const target = this.addTargetToProject(extensionsFolderPath, extensionFolder, IOSNativeTargetTypes.appExtension, project, platformData);
23+
this.configureTarget(extensionFolder, path.join(extensionsFolderPath, extensionFolder), target, project, projectData);
24+
targetUuids.push(target.uuid);
2725
addedExtensions = true;
2826
});
2927

3028
this.$fs.writeFile(pbxProjPath, project.writeSync({omitEmptyValues: true}));
31-
this.prepareExtensionSigning(targetUuids, projectData, pbxProjPath);
29+
this.prepareSigning(targetUuids, projectData, pbxProjPath);
3230

3331
return addedExtensions;
3432
}
3533

36-
private addExtensionToProject(extensionsFolderPath: string, extensionFolder: string, project: IXcode.project, projectData: IProjectData, platformData: IPlatformData): string {
37-
const extensionPath = path.join(extensionsFolderPath, extensionFolder);
38-
const extensionRelativePath = path.relative(platformData.projectRoot, extensionPath);
39-
const files = this.$fs.readDirectory(extensionPath)
40-
.filter(filePath => !filePath.startsWith("."))
41-
.map(filePath => path.join(extensionPath, filePath));
42-
const target = project.addTarget(extensionFolder, 'app_extension', extensionRelativePath);
43-
project.addBuildPhase([], 'PBXSourcesBuildPhase', 'Sources', target.uuid);
44-
project.addBuildPhase([], 'PBXResourcesBuildPhase', 'Resources', target.uuid);
45-
project.addBuildPhase([], 'PBXFrameworksBuildPhase', 'Frameworks', target.uuid);
46-
47-
const extJsonPath = path.join(extensionsFolderPath, extensionFolder, "extension.json");
48-
if (this.$fs.exists(extJsonPath)) {
49-
const extensionJson = this.$fs.readJson(extJsonPath);
50-
_.forEach(extensionJson.frameworks, framework => {
51-
project.addFramework(
52-
framework,
53-
{ target: target.uuid }
54-
);
55-
});
56-
if (extensionJson.assetcatalogCompilerAppiconName) {
57-
project.addToBuildSettings("ASSETCATALOG_COMPILER_APPICON_NAME", extensionJson.assetcatalogCompilerAppiconName, target.uuid);
58-
}
59-
}
34+
private configureTarget(extensionName: string, extensionPath: string, target: IXcode.target, project: IXcode.project, projectData: IProjectData) {
35+
const extJsonPath = path.join(extensionPath, "extension.json");
6036

61-
project.addPbxGroup(files, extensionFolder, extensionPath, null, { isMain: true, target: target.uuid, filesRelativeToProject: true });
62-
project.addBuildProperty("PRODUCT_BUNDLE_IDENTIFIER", `${projectData.projectIdentifiers.ios}.${extensionFolder}`, "Debug", extensionFolder);
63-
project.addBuildProperty("PRODUCT_BUNDLE_IDENTIFIER", `${projectData.projectIdentifiers.ios}.${extensionFolder}`, "Release", extensionFolder);
64-
project.addToHeaderSearchPaths(extensionPath, target.pbxNativeTarget.productName);
37+
this.setXcodeTargetBuildConfigurationProperties(
38+
[{name: "PRODUCT_BUNDLE_IDENTIFIER", value: `${projectData.projectIdentifiers.ios}.${extensionName}`}],
39+
extensionName,
40+
project);
6541

66-
return target.uuid;
67-
}
68-
69-
private prepareExtensionSigning(targetUuids: string[], projectData:IProjectData, projectPath: string) {
70-
const xcode = this.$pbxprojDomXcode.Xcode.open(projectPath);
71-
const signing = xcode.getSigning(projectData.projectName);
72-
if (signing !== undefined) {
73-
_.forEach(targetUuids, targetUuid => {
74-
if (signing.style === "Automatic") {
75-
xcode.setAutomaticSigningStyleByTargetKey(targetUuid, signing.team);
76-
} else {
77-
for (const config in signing.configurations) {
78-
const signingConfiguration = signing.configurations[config];
79-
xcode.setManualSigningStyleByTargetKey(targetUuid, signingConfiguration);
80-
break;
81-
}
82-
}
83-
});
84-
}
85-
xcode.save();
42+
this.setConfigurationsFromJsonFile(extJsonPath, target.uuid, extensionName, project);
8643
}
8744

8845
public removeExtensions({pbxProjPath}: IRemoveExtensionsOptions): void {
8946
const project = new this.$xcode.project(pbxProjPath);
9047
project.parseSync();
91-
project.removeTargetsByProductType("com.apple.product-type.app-extension");
48+
project.removeTargetsByProductType(IOSNativeTargetProductTypes.appExtension);
9249
this.$fs.writeFile(pbxProjPath, project.writeSync({omitEmptyValues: true}));
9350
}
9451
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import * as path from "path";
2+
3+
export enum BuildNames {
4+
debug = "Debug",
5+
release = "Release"
6+
}
7+
8+
export interface IXcodeTargetBuildConfigurationProperty {
9+
name: string;
10+
value: any;
11+
buildNames?: BuildNames[];
12+
}
13+
14+
export abstract class NativeTargetServiceBase {
15+
constructor(protected $fs: IFileSystem,
16+
protected $pbxprojDomXcode: IPbxprojDomXcode,
17+
protected $xcode: IXcode) {
18+
}
19+
20+
protected addTargetToProject(extensionsFolderPath: string, extensionFolder: string, targetType: string, project: IXcode.project, platformData: IPlatformData, parentTarget?: string): IXcode.target {
21+
const extensionPath = path.join(extensionsFolderPath, extensionFolder);
22+
const extensionRelativePath = path.relative(platformData.projectRoot, extensionPath);
23+
const files = this.$fs.readDirectory(extensionPath)
24+
.filter(filePath => !filePath.startsWith("."))
25+
.map(filePath => path.join(extensionPath, filePath));
26+
const target = project.addTarget(extensionFolder, targetType, extensionRelativePath, parentTarget);
27+
project.addBuildPhase([], 'PBXSourcesBuildPhase', 'Sources', target.uuid);
28+
project.addBuildPhase([], 'PBXResourcesBuildPhase', 'Resources', target.uuid);
29+
project.addBuildPhase([], 'PBXFrameworksBuildPhase', 'Frameworks', target.uuid);
30+
31+
project.addPbxGroup(files, extensionFolder, extensionPath, null, { isMain: true, target: target.uuid, filesRelativeToProject: true });
32+
project.addToHeaderSearchPaths(extensionPath, target.pbxNativeTarget.productName);
33+
return target;
34+
}
35+
36+
protected prepareSigning(targetUuids: string[], projectData:IProjectData, projectPath: string) {
37+
const xcode = this.$pbxprojDomXcode.Xcode.open(projectPath);
38+
const signing = xcode.getSigning(projectData.projectName);
39+
if (signing !== undefined) {
40+
_.forEach(targetUuids, targetUuid => {
41+
if (signing.style === "Automatic") {
42+
xcode.setAutomaticSigningStyleByTargetKey(targetUuid, signing.team);
43+
} else {
44+
for (const config in signing.configurations) {
45+
const signingConfiguration = signing.configurations[config];
46+
xcode.setManualSigningStyleByTargetKey(targetUuid, signingConfiguration);
47+
break;
48+
}
49+
}
50+
});
51+
}
52+
xcode.save();
53+
}
54+
55+
protected getTargetDirectories(folderPath: string): string[] {
56+
return this.$fs.readDirectory(folderPath)
57+
.filter(fileName => {
58+
const filePath = path.join(folderPath, fileName);
59+
const stats = this.$fs.getFsStats(filePath);
60+
61+
return stats.isDirectory() && !fileName.startsWith(".");
62+
});
63+
}
64+
65+
protected setXcodeTargetBuildConfigurationProperties(properties: IXcodeTargetBuildConfigurationProperty[], targetName: string, project: IXcode.project): void {
66+
properties.forEach(property => {
67+
const buildNames = property.buildNames || [BuildNames.debug, BuildNames.release];
68+
buildNames.forEach((buildName) => {
69+
project.addBuildProperty(property.name, property.value, buildName, targetName);
70+
});
71+
});
72+
}
73+
74+
protected setConfigurationsFromJsonFile(jsonPath: string, targetUuid: string, targetName: string, project: IXcode.project) {
75+
if (this.$fs.exists(jsonPath)) {
76+
const configurationJson = this.$fs.readJson(jsonPath) || {};
77+
78+
_.forEach(configurationJson.frameworks, framework => {
79+
project.addFramework(
80+
framework,
81+
{ target: targetUuid }
82+
);
83+
});
84+
85+
if (configurationJson.assetcatalogCompilerAppiconName) {
86+
project.addToBuildSettings("ASSETCATALOG_COMPILER_APPICON_NAME", configurationJson.assetcatalogCompilerAppiconName, targetUuid);
87+
}
88+
89+
if (configurationJson.targetBuildConfigurationProperties) {
90+
const properties: IXcodeTargetBuildConfigurationProperty[] = [];
91+
_.forEach(configurationJson.targetBuildConfigurationProperties, (value, name: string) => properties.push({value, name}));
92+
this.setXcodeTargetBuildConfigurationProperties(properties, targetName, project);
93+
}
94+
}
95+
}
96+
}

0 commit comments

Comments
 (0)