diff --git a/CHANGELOG.md b/CHANGELOG.md
index ccbe589aa..48ef673de 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,9 @@
## Next Version
+#### Fixed
+- Fixed issue which caused watch app schemes to be generated incorrectly, preventing these apps from launching. [#798](https://github.com/yonaskolb/XcodeGen/pull/798) @daltonclaybrook
+
## 2.15.0
#### Added
diff --git a/Sources/XcodeGenKit/SchemeGenerator.swift b/Sources/XcodeGenKit/SchemeGenerator.swift
index afd240be8..4df57ff39 100644
--- a/Sources/XcodeGenKit/SchemeGenerator.swift
+++ b/Sources/XcodeGenKit/SchemeGenerator.swift
@@ -57,14 +57,15 @@ public class SchemeGenerator {
let debugConfig = suitableConfig(for: .debug, in: project)
let releaseConfig = suitableConfig(for: .release, in: project)
- let scheme = Scheme.init(
+ let scheme = Scheme(
name: schemeName,
target: target,
targetScheme: targetScheme,
+ project: project,
debugConfig: debugConfig.name,
releaseConfig: releaseConfig.name
)
- let xcscheme = try generateScheme(scheme)
+ let xcscheme = try generateScheme(scheme, for: target)
xcschemes.append(xcscheme)
} else {
for configVariant in targetScheme.configVariants {
@@ -80,10 +81,11 @@ public class SchemeGenerator {
name: schemeName,
target: target,
targetScheme: targetScheme,
+ project: project,
debugConfig: debugConfig.name,
releaseConfig: releaseConfig.name
)
- let xcscheme = try generateScheme(scheme)
+ let xcscheme = try generateScheme(scheme, for: target)
xcschemes.append(xcscheme)
}
}
@@ -93,7 +95,7 @@ public class SchemeGenerator {
return xcschemes
}
- public func generateScheme(_ scheme: Scheme) throws -> XCScheme {
+ public func generateScheme(_ scheme: Scheme, for target: Target? = nil) throws -> XCScheme {
func getBuildableReference(_ target: TargetReference) throws -> XCScheme.BuildableReference {
let pbxProj: PBXProj
@@ -159,11 +161,11 @@ public class SchemeGenerator {
return XCScheme.ExecutionAction(scriptText: action.script, title: action.name, environmentBuildable: environmentBuildable)
}
- let target = project.getTarget(scheme.build.targets.first!.target.name)
- let shouldExecuteOnLaunch = target?.type.isExecutable == true
+ let schemeTarget = target ?? project.getTarget(scheme.build.targets.first!.target.name)
+ let shouldExecuteOnLaunch = schemeTarget?.type.isExecutable == true
let buildableReference = buildActionEntries.first!.buildableReference
- let productRunable = XCScheme.BuildableProductRunnable(buildableReference: buildableReference)
+ let runnables = makeProductRunnables(for: schemeTarget, buildableReference: buildableReference)
let buildAction = XCScheme.BuildAction(
buildActionEntries: buildActionEntries,
@@ -225,7 +227,7 @@ public class SchemeGenerator {
locationScenarioReference = XCScheme.LocationScenarioReference(identifier: identifier, referenceType: referenceType.rawValue)
}
let launchAction = XCScheme.LaunchAction(
- runnable: shouldExecuteOnLaunch ? productRunable : nil,
+ runnable: shouldExecuteOnLaunch ? runnables.launch : nil,
buildConfiguration: scheme.run?.config ?? defaultDebugConfig.name,
preActions: scheme.run?.preActions.map(getExecutionAction) ?? [],
postActions: scheme.run?.postActions.map(getExecutionAction) ?? [],
@@ -243,7 +245,7 @@ public class SchemeGenerator {
)
let profileAction = XCScheme.ProfileAction(
- buildableProductRunnable: productRunable,
+ buildableProductRunnable: runnables.profile,
buildConfiguration: scheme.profile?.config ?? defaultReleaseConfig.name,
preActions: scheme.profile?.preActions.map(getExecutionAction) ?? [],
postActions: scheme.profile?.postActions.map(getExecutionAction) ?? [],
@@ -274,6 +276,20 @@ public class SchemeGenerator {
archiveAction: archiveAction
)
}
+
+ private func makeProductRunnables(for target: Target?, buildableReference: XCScheme.BuildableReference) -> (launch: XCScheme.Runnable, profile: XCScheme.BuildableProductRunnable) {
+ let buildable = XCScheme.BuildableProductRunnable(buildableReference: buildableReference)
+ if target?.type.isWatchApp == true {
+ let remote = XCScheme.RemoteRunnable(
+ buildableReference: buildableReference,
+ bundleIdentifier: "com.apple.Carousel",
+ runnableDebuggingMode: "2"
+ )
+ return (remote, buildable)
+ } else {
+ return (buildable, buildable)
+ }
+ }
}
enum SchemeGenerationError: Error, CustomStringConvertible {
@@ -292,11 +308,11 @@ enum SchemeGenerationError: Error, CustomStringConvertible {
}
extension Scheme {
- public init(name: String, target: Target, targetScheme: TargetScheme, debugConfig: String, releaseConfig: String) {
+ public init(name: String, target: Target, targetScheme: TargetScheme, project: Project, debugConfig: String, releaseConfig: String) {
self.init(
name: name,
build: .init(
- targets: [Scheme.BuildTarget(target: TargetReference.local(target.name))],
+ targets: Scheme.buildTargets(for: target, project: project),
buildImplicitDependencies: targetScheme.buildImplicitDependencies
),
run: .init(
@@ -339,4 +355,30 @@ extension Scheme {
)
)
}
+
+ private static func buildTargets(for target: Target, project: Project) -> [BuildTarget] {
+ let buildTarget = Scheme.BuildTarget(target: TargetReference.local(target.name))
+ switch target.type {
+ case .watchApp, .watch2App:
+ let hostTarget = project.targets
+ .first { projectTarget in
+ projectTarget.dependencies.contains { $0.reference == target.name }
+ }
+ .map { BuildTarget(target: TargetReference.local($0.name)) }
+ return hostTarget.map { [buildTarget, $0] } ?? [buildTarget]
+ default:
+ return [buildTarget]
+ }
+ }
+}
+
+extension PBXProductType {
+ var isWatchApp: Bool {
+ switch self {
+ case .watchApp, .watch2App:
+ return true
+ default:
+ return false
+ }
+ }
}
diff --git a/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/App_watchOS.xcscheme b/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/App_watchOS.xcscheme
index 5daa2ee99..c4928b9f8 100644
--- a/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/App_watchOS.xcscheme
+++ b/Tests/Fixtures/TestProject/Project.xcodeproj/xcshareddata/xcschemes/App_watchOS.xcscheme
@@ -20,6 +20,20 @@
ReferencedContainer = "container:Project.xcodeproj">
+
+
+
+
-
+
-
+
diff --git a/Tests/XcodeGenKitTests/SchemeGeneratorTests.swift b/Tests/XcodeGenKitTests/SchemeGeneratorTests.swift
index b3459ff3c..81ddb5e9a 100644
--- a/Tests/XcodeGenKitTests/SchemeGeneratorTests.swift
+++ b/Tests/XcodeGenKitTests/SchemeGeneratorTests.swift
@@ -336,6 +336,61 @@ class SchemeGeneratorTests: XCTestCase {
try expect(buildableReference?.blueprintName) == "ExternalTarget"
try expect(buildableReference?.referencedContainer) == "container:\(externalProject.string)"
}
+
+ $0.it("generates scheme with buildable product runnable for ios app target") {
+ let app = Target(
+ name: "MyApp",
+ type: .application,
+ platform: .iOS,
+ scheme: TargetScheme()
+ )
+ let project = Project(name: "ios_test", targets: [app])
+ let xcodeProject = try project.generateXcodeProject()
+ let xcscheme = try unwrap(xcodeProject.sharedData?.schemes.first)
+ try expect(xcscheme.launchAction?.runnable).beOfType(XCScheme.BuildableProductRunnable.self)
+ }
+
+ $0.it("generates scheme with remote runnable for watch app target") {
+ let xcscheme = try self.makeWatchScheme(appType: .watch2App, extensionType: .watch2Extension)
+ try expect(xcscheme.launchAction?.runnable).beOfType(XCScheme.RemoteRunnable.self)
+ }
+
+ $0.it("generates scheme with host target build action for watch") {
+ let xcscheme = try self.makeWatchScheme(appType: .watch2App, extensionType: .watch2Extension)
+ let buildEntries = xcscheme.buildAction?.buildActionEntries ?? []
+ try expect(buildEntries.count) == 2
+ try expect(buildEntries.first?.buildableReference.blueprintName) == "WatchApp"
+ try expect(buildEntries.last?.buildableReference.blueprintName) == "HostApp"
+ }
}
}
+
+ // MARK: - Helpers
+
+ private func makeWatchScheme(appType: PBXProductType, extensionType: PBXProductType) throws -> XCScheme {
+ let watchExtension = Target(
+ name: "WatchExtension",
+ type: extensionType,
+ platform: .watchOS
+ )
+ let watchApp = Target(
+ name: "WatchApp",
+ type: appType,
+ platform: .watchOS,
+ dependencies: [Dependency(type: .target, reference: watchExtension.name)],
+ scheme: TargetScheme()
+ )
+ let hostApp = Target(
+ name: "HostApp",
+ type: .application,
+ platform: .iOS,
+ dependencies: [Dependency(type: .target, reference: watchApp.name)]
+ )
+ let project = Project(
+ name: "watch_test",
+ targets: [hostApp, watchApp, watchExtension]
+ )
+ let xcodeProject = try project.generateXcodeProject()
+ return try unwrap(xcodeProject.sharedData?.schemes.first)
+ }
}