Skip to content

Commit a9ed61f

Browse files
committed
feat(publish): Enable publishing of iOS apps with Xcode 11
As `Application Loader` is no longer included in Xcode 11, CLI is not able to find the path to itmsTransporter and throws an error. However, itmsTransporter app is already part of ContentDeliveryServices.framework which is part of SharedFrameworks which are included in Xcode 11 itself. So this PR returns the correct path to itmsTransporter app based on xcode's version.
1 parent 50d1af6 commit a9ed61f

File tree

4 files changed

+21
-16
lines changed

4 files changed

+21
-16
lines changed

lib/commands/appstore-upload.ts

+7-12
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,12 @@ export class PublishIOS implements ICommand {
2525
}
2626

2727
public async execute(args: string[]): Promise<void> {
28-
let username = args[0];
29-
let password = args[1];
28+
await this.$itmsTransporterService.validate();
29+
30+
const username = args[0] || await this.$prompter.getString("Apple ID", { allowEmpty: false });
31+
const password = args[1] || await this.$prompter.getPassword("Apple ID password");
3032
const mobileProvisionIdentifier = args[2];
3133
const codeSignIdentity = args[3];
32-
let ipaFilePath = this.$options.ipa ? path.resolve(this.$options.ipa) : null;
33-
34-
if (!username) {
35-
username = await this.$prompter.getString("Apple ID", { allowEmpty: false });
36-
}
37-
38-
if (!password) {
39-
password = await this.$prompter.getPassword("Apple ID password");
40-
}
4134

4235
const user = await this.$applePortalSessionService.createUserSession({ username, password }, {
4336
applicationSpecificPassword: this.$options.appleApplicationSpecificPassword,
@@ -49,6 +42,8 @@ export class PublishIOS implements ICommand {
4942
this.$errors.fail(`Invalid username and password combination. Used '${username}' as the username.`);
5043
}
5144

45+
let ipaFilePath = this.$options.ipa ? path.resolve(this.$options.ipa) : null;
46+
5247
if (!mobileProvisionIdentifier && !ipaFilePath) {
5348
this.$logger.warn("No mobile provision identifier set. A default mobile provision will be used. You can set one in app/App_Resources/iOS/build.xcconfig");
5449
}
@@ -58,9 +53,9 @@ export class PublishIOS implements ICommand {
5853
}
5954

6055
this.$options.release = true;
61-
const platform = this.$devicePlatformsConstants.iOS.toLowerCase();
6256

6357
if (!ipaFilePath) {
58+
const platform = this.$devicePlatformsConstants.iOS.toLowerCase();
6459
// No .ipa path provided, build .ipa on out own.
6560
if (mobileProvisionIdentifier || codeSignIdentity) {
6661
// This is not very correct as if we build multiple targets we will try to sign all of them using the signing identity here.

lib/declarations.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -639,6 +639,7 @@ interface IITMSData {
639639
* Used for communicating with Xcode's iTMS Transporter tool.
640640
*/
641641
interface IITMSTransporterService {
642+
validate(): Promise<void>;
642643
/**
643644
* Uploads an .ipa package to iTunes Connect.
644645
* @param {IITMSData} data Data needed to upload the package

lib/services/itmstransporter-service.ts

+12-4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ export class ITMSTransporterService implements IITMSTransporterService {
1919
return this.$injector.resolve("projectData");
2020
}
2121

22+
public async validate(): Promise<void> {
23+
const itmsTransporterPath = await this.getITMSTransporterPath();
24+
if (!this.$fs.exists(itmsTransporterPath)) {
25+
this.$errors.fail('iTMS Transporter not found on this machine - make sure your Xcode installation is not damaged.');
26+
}
27+
}
28+
2229
public async upload(data: IITMSData): Promise<void> {
2330
temp.track();
2431
const itmsTransporterPath = await this.getITMSTransporterPath();
@@ -96,11 +103,12 @@ export class ITMSTransporterService implements IITMSTransporterService {
96103
@cache()
97104
private async getITMSTransporterPath(): Promise<string> {
98105
const xcodePath = await this.$xcodeSelectService.getContentsDirectoryPath();
99-
const loaderAppContentsPath = path.join(xcodePath, "Applications", "Application Loader.app", "Contents");
100-
const itmsTransporterPath = path.join(loaderAppContentsPath, ITMSConstants.iTMSDirectoryName, "bin", ITMSConstants.iTMSExecutableName);
106+
let itmsTransporterPath = path.join(xcodePath, "..", "Contents", "SharedFrameworks", "ContentDeliveryServices.framework", "Versions", "A", "itms", "bin", ITMSConstants.iTMSExecutableName);
101107

102-
if (!this.$fs.exists(itmsTransporterPath)) {
103-
this.$errors.fail('iTMS Transporter not found on this machine - make sure your Xcode installation is not damaged.');
108+
const xcodeVersionData = await this.$xcodeSelectService.getXcodeVersion();
109+
if (+xcodeVersionData.major < 11) {
110+
const loaderAppContentsPath = path.join(xcodePath, "Applications", "Application Loader.app", "Contents");
111+
itmsTransporterPath = path.join(loaderAppContentsPath, ITMSConstants.iTMSDirectoryName, "bin", ITMSConstants.iTMSExecutableName);
104112
}
105113

106114
return itmsTransporterPath;

test/tns-appstore-upload.ts

+1
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ class AppStore {
124124

125125
expectITMSTransporterUpload() {
126126
this.expectedItmsTransporterServiceUploadCalls = 1;
127+
this.itmsTransporterService.validate = () => Promise.resolve();
127128
this.itmsTransporterService.upload = (options: IITMSData) => {
128129
this.itmsTransporterServiceUploadCalls++;
129130
chai.assert.equal(options.ipaFilePath, "/Users/person/git/MyProject/platforms/ios/archive/MyProject.ipa");

0 commit comments

Comments
 (0)