Support for multiple deployment targets with xcode 14 (#1336)

* platformFilters on Dependecies

* platformFilters on sources

* fixed current unit tests

* renamed enum to SupportedPlatforms

* supportedPlatforms field for target

* errors

* renamed errors

* inferPlatformFiltersByPath flag

* changed priority to generate filter

* fixed parsing

* fixed init

* unit test supportedPlatforms

* unit tests for errors

* fixing build settings and unit tests

* added new settingsPresets

* new check errors and unit tests

* case insensitive match

* fixed skipping cross platform target

* json decode

* unit tests inferPlatformFiltersByPath and platformFilters for sources

* mocked files

* fixing unit tests

* first test on dependecies

* unit tests completed

* fixed unit tests

* changelog

* doc changes

* doc changes

* doc changes

* doc changes

* doc changes

* doc changes

* doc changes

* doc changes

* fixed doc

* fixed unti tests style

* fixed regex

* fixed doc

* addressing comments

* Added TestProject, moved unit tests resources in another folder

* Raising error if platform is an array

* unit test on new error

* fixed error enum

* Integrated in TestProject

* committed TestProject

* unit test error

* fixing spm deps in test project

* pushed testProject

* pushed testProject

* pushed testProject fix

* comment on isResolved property

* renameing supportedPlatforms to supportedDestinations

* renameing supportedPlatforms to supportedDestinations

* renameing test app

* checked out old file

* fixing test app

* working on auto baseSDK

* fixed deploymentTarget

* renamed errors

* fixed presets

* remamed index to priority

* small comments

* removed isResolved in target and fixed error check

* added unit tests

* fixed doc

* fixed doc

* fixed doc

* fixed doc

* fixed test app

* add visionOS and more error check and testing

* fixed supported destinations priority and tests

* fixed doc

* solved conflicts

* fixed conflicts

* renamed everything

---------

Co-authored-by: Giovanni Amati <giovanni.amati@sky.uk>
This commit is contained in:
Giovanni Amati 2023-10-31 09:55:38 +00:00 committed by GitHub
parent ec050cd5cb
commit 97d36fd1d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 1255 additions and 46 deletions

View File

@ -2,6 +2,15 @@
## Next Version
### Feature support for multiple deployment targets with xcode 14
- Added `supportedDestinations` for target
- Added a new platform value `auto` that we can use only with `supportedDestinations`
- Added the possiblity to avoid the definition of plaform, only when we use `supportedDestinations`, that fallbacks to 'auto'
- Added `destinationFilters` for sources and dependecies
- Added `inferDestinationFiltersByPath`, a convenience filter for sources
- Added visionOS support
### Added
- `.mlpackage` files now default to being a source type #1398 @aaron-foreflight

View File

@ -22,6 +22,7 @@ You can also use environment variables in your configuration file, by using `${S
- [Target](#target)
- [Product Type](#product-type)
- [Platform](#platform)
- [Supported Destinations](#supported-destinations)
- [Sources](#sources)
- [Target Source](#target-source)
- [Dependency](#dependency)
@ -356,6 +357,7 @@ Settings are merged in the following order: `groups`, `base`, `configs` (simple
- [x] **type**: **[Product Type](#product-type)** - Product type of the target
- [x] **platform**: **[Platform](#platform)** - Platform of the target
- [ ] **supportedDestinations**: **[[Supported Destinations](#supported-destinations)]** - List of supported platform destinations for the target.
- [ ] **deploymentTarget**: **String** - The deployment target (eg `9.2`). If this is not specified the value from the project set in [Options](#options)`.deploymentTarget.PLATFORM` will be used.
- [ ] **sources**: **[Sources](#sources)** - Source directories of the target
- [ ] **configFiles**: **[Config Files](#config-files)** - `.xcconfig` files per config
@ -433,12 +435,15 @@ This will provide default build settings for a certain product type. It can be a
This will provide default build settings for a certain platform. It can be any of the following:
- `auto` (available only when we use `supportedDestinations`)
- `iOS`
- `macOS`
- `tvOS`
- `macOS`
- `watchOS`
- `visionOS` (`visionOS` doesn't support Carthage usage)
Note that when we use supported destinations with Xcode 14+ we can avoid the definition of platform that fallbacks to the `auto` value.
**Multi Platform targets**
You can also specify an array of platforms. This will generate a target for each platform.
@ -469,6 +474,33 @@ targets:
The above will generate 2 targets named `MyFramework_iOS` and `MyFramework_tvOS`, with all the relevant platform build settings. They will both have a `PRODUCT_NAME` of `MyFramework`
### Supported Destinations
This will provide a mix of default build settings for the chosen platform destinations. It can be any of the following:
- `iOS`
- `tvOS`
- `macOS`
- `macCatalyst`
- `visionOS`
```yaml
targets:
MyFramework:
type: framework
supportedDestinations: [iOS, tvOS]
deploymentTarget:
iOS: 9.0
tvOS: 10.0
sources:
- path: MySources
inferDestinationFiltersByPath: true
- path: OtherSources
destinationFilters: [iOS]
```
Note that the definition of supported destinations can be applied to every type of bundle making everything more easy to manage (app targets, unit tests, UI tests etc).
### Sources
Specifies the source directories for a target. This can either be a single source or a list of sources. Applicable source files, resources, headers, and `.lproj` files will be parsed appropriately.
@ -483,7 +515,9 @@ A source can be provided via a string (the path) or an object of the form:
- [ ] **compilerFlags**: **[String]** or **String** - A list of compilerFlags to add to files under this specific path provided as a list or a space delimited string. Defaults to empty.
- [ ] **excludes**: **[String]** - A list of [global patterns](https://en.wikipedia.org/wiki/Glob_(programming)) representing the files to exclude. These rules are relative to `path` and _not the directory where `project.yml` resides_. XcodeGen uses Bash 4's Glob behaviors where globstar (**) is enabled.
- [ ] **includes**: **[String]** - A list of global patterns in the same format as `excludes` representing the files to include. These rules are relative to `path` and _not the directory where `project.yml` resides_. If **excludes** is present and file conflicts with **includes**, **excludes** will override the **includes** behavior.
- [ ] **createIntermediateGroups**: **Bool** - This overrides the value in [Options](#options)
- [ ] **destinationFilters**: **[[Supported Destinations](#supported-destinations)]** - List of supported platform destinations the files should filter to. Defaults to all supported destinations.
- [ ] **inferDestinationFiltersByPath**: **Bool** - This is a convenience filter that helps you to filter the files if their paths match these patterns `**/<supportedDestination>/*` or `*_<supportedDestination>.swift`. Note, if you use `destinationFilters` this flag will be ignored.
- [ ] **createIntermediateGroups**: **Bool** - This overrides the value in [Options](#options).
- [ ] **optional**: **Bool** - Disable missing path check. Defaults to false.
- [ ] **buildPhase**: **String** - This manually sets the build phase this file or files in this directory will be added to, otherwise XcodeGen will guess based on the file extension. Note that `Info.plist` files will never be added to any build phases, no matter what this setting is. Possible values are:
- `sources` - Compile Sources phase
@ -519,9 +553,11 @@ targets:
MyTarget:
sources: MyTargetSource
MyOtherTarget:
supportedDestinations: [iOS, tvOS]
sources:
- MyOtherTargetSource1
- path: MyOtherTargetSource2
inferDestinationFiltersByPath: true
name: MyNewName
excludes:
- "ios/*.[mh]"
@ -533,6 +569,7 @@ targets:
- "-Werror"
- "-Wextra"
- path: MyOtherTargetSource3
destinationFilters: [iOS]
compilerFlags: "-Werror -Wextra"
- path: ModuleMaps
buildPhase:
@ -560,10 +597,11 @@ A dependency can be one of a 6 types:
- [ ] **embed**: **Bool** - Whether to embed the dependency. Defaults to true for application target and false for non application targets.
- [ ] **link**: **Bool** - Whether to link the dependency. Defaults to `true` depending on the type of the dependency and the type of the target (e.g. static libraries will only link to executables by default).
- [ ] **codeSign**: **Bool** - Whether the `codeSignOnCopy` setting is applied when embedding framework. Defaults to true
- [ ] **removeHeaders**: **Bool** - Whether the `removeHeadersOnCopy` setting is applied when embedding the framework. Defaults to true
- [ ] **weak**: **Bool** - Whether the `Weak` setting is applied when linking the framework. Defaults to false
- [ ] **platformFilter**: **String** - This field is specific to Mac Catalyst. It corresponds to the "Platforms" dropdown in the Frameworks & Libraries section of Target settings in Xcode. Available options are: **iOS**, **macOS** and **all**. Defaults is **all**
- [ ] **codeSign**: **Bool** - Whether the `codeSignOnCopy` setting is applied when embedding framework. Defaults to true.
- [ ] **removeHeaders**: **Bool** - Whether the `removeHeadersOnCopy` setting is applied when embedding the framework. Defaults to true.
- [ ] **weak**: **Bool** - Whether the `Weak` setting is applied when linking the framework. Defaults to false.
- [ ] **platformFilter**: **String** - This field is specific to Mac Catalyst. It corresponds to the "Platforms" dropdown in the Frameworks & Libraries section of Target settings in Xcode. Available options are: **iOS**, **macOS** and **all**. Defaults is **all**.
- [ ] **destinationFilters**: **[[Supported Destinations](#supported-destinations)]** - List of supported platform destinations this dependency should filter to. Defaults to all supported destinations.
- [ ] **platforms**: **[[Platform](#platform)]** - List of platforms this dependency should apply to. Defaults to all applicable platforms.
- **copy** - Copy Files Phase for this dependency. This only applies when `embed` is true. Must be specified as an object with the following fields:
- [x] **destination**: **String** - Destination of the Copy Files phase. This can be one of the following values:
@ -613,13 +651,17 @@ projectReferences:
path: path/to/FooLib.xcodeproj
targets:
MyTarget:
supportedDestinations: [iOS, tvOS]
dependencies:
- target: MyFramework
destinationFilters: [iOS]
- target: FooLib/FooTarget
- framework: path/to/framework.framework
destinationFilters: [tvOS]
- carthage: Result
findFrameworks: false
linkType: static
destinationFilters: [iOS]
- sdk: Contacts.framework
- sdk: libc++.tbd
- sdk: libz.dylib

View File

@ -1,3 +1,3 @@
TARGETED_DEVICE_FAMILY: 3
LD_RUNPATH_SEARCH_PATHS: ["$(inherited)", "@executable_path/Frameworks"]
SDKROOT: appletvos
TARGETED_DEVICE_FAMILY: 3

View File

@ -0,0 +1,5 @@
SUPPORTED_PLATFORMS: iphoneos iphonesimulator
TARGETED_DEVICE_FAMILY: '1,2'
SUPPORTS_MACCATALYST: NO
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD: YES
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD: YES

View File

@ -0,0 +1,2 @@
SUPPORTS_MACCATALYST: YES
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD: NO

View File

@ -0,0 +1,3 @@
SUPPORTED_PLATFORMS: macosx
SUPPORTS_MACCATALYST: NO
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD: NO

View File

@ -0,0 +1,2 @@
SUPPORTED_PLATFORMS: appletvos appletvsimulator
TARGETED_DEVICE_FAMILY: '3'

View File

@ -0,0 +1,3 @@
SUPPORTED_PLATFORMS: xros xrsimulator
TARGETED_DEVICE_FAMILY: '7'
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD: NO

View File

@ -16,6 +16,7 @@ public struct Dependency: Equatable {
public var implicit: Bool = implicitDefault
public var weakLink: Bool = weakLinkDefault
public var platformFilter: PlatformFilter = platformFilterDefault
public var destinationFilters: [SupportedDestination]?
public var platforms: Set<Platform>?
public var copyPhase: BuildPhaseSpec.CopyFilesSettings?
@ -28,6 +29,7 @@ public struct Dependency: Equatable {
implicit: Bool = implicitDefault,
weakLink: Bool = weakLinkDefault,
platformFilter: PlatformFilter = platformFilterDefault,
destinationFilters: [SupportedDestination]? = nil,
platforms: Set<Platform>? = nil,
copyPhase: BuildPhaseSpec.CopyFilesSettings? = nil
) {
@ -39,6 +41,7 @@ public struct Dependency: Equatable {
self.implicit = implicit
self.weakLink = weakLink
self.platformFilter = platformFilter
self.destinationFilters = destinationFilters
self.platforms = platforms
self.copyPhase = copyPhase
}
@ -48,7 +51,7 @@ public struct Dependency: Equatable {
case iOS
case macOS
}
public enum CarthageLinkType: String {
case dynamic
case `static`
@ -142,7 +145,11 @@ extension Dependency: JSONObjectConvertible {
} else {
self.platformFilter = .all
}
if let destinationFilters: [SupportedDestination] = jsonDictionary.json(atKeyPath: "destinationFilters") {
self.destinationFilters = destinationFilters
}
if let platforms: [ProjectSpec.Platform] = jsonDictionary.json(atKeyPath: "platforms") {
self.platforms = Set(platforms)
}
@ -161,6 +168,7 @@ extension Dependency: JSONEncodable {
"link": link,
"platforms": platforms?.map(\.rawValue).sorted(),
"copy": copyPhase?.toJSONValue(),
"destinationFilters": destinationFilters?.map { $0.rawValue },
]
if removeHeaders != Dependency.removeHeadersDefault {

View File

@ -26,6 +26,7 @@ public struct DeploymentTarget: Equatable {
public func version(for platform: Platform) -> Version? {
switch platform {
case .auto: return nil
case .iOS: return iOS
case .tvOS: return tvOS
case .watchOS: return watchOS
@ -39,6 +40,7 @@ extension Platform {
public var deploymentTargetSetting: String {
switch self {
case .auto: return ""
case .iOS: return "IPHONEOS_DEPLOYMENT_TARGET"
case .tvOS: return "TVOS_DEPLOYMENT_TARGET"
case .watchOS: return "WATCHOS_DEPLOYMENT_TARGET"
@ -49,6 +51,7 @@ extension Platform {
public var sdkRoot: String {
switch self {
case .auto: return "auto"
case .iOS: return "iphoneos"
case .tvOS: return "appletvos"
case .watchOS: return "watchos"

View File

@ -1,9 +1,10 @@
import Foundation
public enum Platform: String, Hashable, CaseIterable {
case auto
case iOS
case watchOS
case tvOS
case macOS
case watchOS
case visionOS
}

View File

@ -30,7 +30,7 @@ extension Plist: JSONEncodable {
[
"path": path,
"properties": properties,
]
] as [String : Any]
}
}

View File

@ -113,7 +113,7 @@ extension Settings: JSONEncodable {
"base": buildSettings,
"groups": groups,
"configs": configSettings.mapValues { $0.toJSONValue() },
]
] as [String : Any]
}
return buildSettings
}

View File

@ -7,6 +7,7 @@ public enum SpecParsingError: Error, CustomStringConvertible {
case unknownPackageRequirement([String: Any])
case invalidSourceBuildPhase(String)
case invalidTargetReference(String)
case invalidTargetPlatformAsArray
case invalidVersion(String)
case unknownBreakpointType(String)
case unknownBreakpointScope(String)
@ -27,6 +28,8 @@ public enum SpecParsingError: Error, CustomStringConvertible {
return "Invalid Source Build Phase: \(error)"
case let .invalidTargetReference(targetReference):
return "Invalid Target Reference Syntax: \(targetReference)"
case .invalidTargetPlatformAsArray:
return "Invalid Target platform: Array not allowed with supported destinations"
case let .invalidVersion(version):
return "Invalid version: \(version)"
case let .unknownPackageRequirement(package):

View File

@ -180,6 +180,29 @@ extension Project {
errors.append(.invalidTargetSource(target: target.name, source: sourcePath.string))
}
}
if target.supportedDestinations != nil, target.platform == .watchOS {
errors.append(.unexpectedTargetPlatformForSupportedDestinations(target: target.name, platform: target.platform))
}
if target.supportedDestinations?.contains(.macOS) == true,
target.supportedDestinations?.contains(.macCatalyst) == true {
errors.append(.multipleMacPlatformsInSupportedDestinations(target: target.name))
}
if target.supportedDestinations?.contains(.macCatalyst) == true,
target.platform != .iOS, target.platform != .auto {
errors.append(.invalidTargetPlatformForSupportedDestinations(target: target.name))
}
if target.platform != .auto, target.platform != .watchOS,
let supportedDestination = SupportedDestination(rawValue: target.platform.rawValue),
target.supportedDestinations?.contains(supportedDestination) == false {
errors.append(.missingTargetPlatformInSupportedDestinations(target: target.name, platform: target.platform))
}
}
for projectReference in projectReferences {

View File

@ -17,6 +17,10 @@ public struct SpecValidationError: Error, CustomStringConvertible {
case invalidTargetConfigFile(target: String, configFile: String, config: String)
case invalidTargetSchemeConfigVariant(target: String, configVariant: String, configType: ConfigType)
case invalidTargetSchemeTest(target: String, testTarget: String)
case invalidTargetPlatformForSupportedDestinations(target: String)
case unexpectedTargetPlatformForSupportedDestinations(target: String, platform: Platform)
case multipleMacPlatformsInSupportedDestinations(target: String)
case missingTargetPlatformInSupportedDestinations(target: String, platform: Platform)
case invalidSchemeTarget(scheme: String, target: String, action: String)
case invalidSchemeConfig(scheme: String, config: String)
case invalidSwiftPackage(name: String, target: String)
@ -54,6 +58,14 @@ public struct SpecValidationError: Error, CustomStringConvertible {
return "Target \(target.quoted) has an invalid scheme config variant which requires a config that has a \(configType.rawValue.quoted) type and contains the name \(configVariant.quoted)"
case let .invalidTargetSchemeTest(target, test):
return "Target \(target.quoted) scheme has invalid test \(test.quoted)"
case let .invalidTargetPlatformForSupportedDestinations(target):
return "Target \(target.quoted) has supported destinations that require a target platform iOS or auto"
case let .unexpectedTargetPlatformForSupportedDestinations(target, platform):
return "Target \(target.quoted) has platform \(platform.rawValue.quoted) that does not expect supported destinations"
case let .multipleMacPlatformsInSupportedDestinations(target):
return "Target \(target.quoted) has multiple definitions of mac platforms in supported destinations"
case let .missingTargetPlatformInSupportedDestinations(target, platform):
return "Target \(target.quoted) has platform \(platform.rawValue.quoted) that is missing in supported destinations"
case let .invalidConfigFile(configFile, config):
return "Invalid config file \(configFile.quoted) for config \(config.quoted)"
case let .invalidSchemeTarget(scheme, target, action):

View File

@ -0,0 +1,45 @@
import Foundation
public enum SupportedDestination: String, CaseIterable {
case iOS
case tvOS
case macOS
case macCatalyst
case visionOS
}
extension SupportedDestination {
public var string: String {
switch self {
case .iOS:
return "ios"
case .tvOS:
return "tvos"
case .macOS:
return "macos"
case .macCatalyst:
return "maccatalyst"
case .visionOS:
return "xros"
}
}
// This is used to:
// 1. Get the first one and apply SettingPresets 'Platforms' and 'Product_Platform' if the platform is 'auto'
// 2. Sort, loop and merge together SettingPresets 'SupportedDestinations'
public var priority: Int {
switch self {
case .iOS:
return 0
case .tvOS:
return 1
case .visionOS:
return 2
case .macOS:
return 3
case .macCatalyst:
return 4
}
}
}

View File

@ -37,6 +37,7 @@ public struct Target: ProjectTarget {
public var name: String
public var type: PBXProductType
public var platform: Platform
public var supportedDestinations: [SupportedDestination]?
public var settings: Settings
public var sources: [TargetSource]
public var dependencies: [Dependency]
@ -58,7 +59,7 @@ public struct Target: ProjectTarget {
public var productName: String
public var onlyCopyFilesOnInstall: Bool
public var putResourcesBeforeSourcesBuildPhase: Bool
public var isLegacy: Bool {
legacy != nil
}
@ -78,6 +79,7 @@ public struct Target: ProjectTarget {
name: String,
type: PBXProductType,
platform: Platform,
supportedDestinations: [SupportedDestination]? = nil,
productName: String? = nil,
deploymentTarget: Version? = nil,
settings: Settings = .empty,
@ -103,6 +105,7 @@ public struct Target: ProjectTarget {
self.name = name
self.type = type
self.platform = platform
self.supportedDestinations = supportedDestinations
self.deploymentTarget = deploymentTarget
self.productName = productName ?? name
self.settings = settings
@ -162,15 +165,17 @@ extension Target {
guard let targetsDictionary: [String: JSONDictionary] = jsonDictionary["targets"] as? [String: JSONDictionary] else {
return jsonDictionary
}
var crossPlatformTargets: [String: JSONDictionary] = [:]
for (targetName, target) in targetsDictionary {
if let platforms = target["platform"] as? [String] {
for platform in platforms {
var platformTarget = target
/// This value is set to help us to check, in Target init, that there are no conflicts in the definition of the platforms. We want to ensure that the user didn't define, at the same time,
/// the new Xcode 14 supported destinations and the XcodeGen generation of Multiple Platform Targets (when you define the platform field as an array).
platformTarget["isMultiPlatformTarget"] = true
platformTarget = platformTarget.expand(variables: ["platform": platform])
@ -202,8 +207,8 @@ extension Target {
crossPlatformTargets[targetName] = target
}
}
var merged = jsonDictionary
merged["targets"] = crossPlatformTargets
return merged
}
@ -268,19 +273,41 @@ extension Target: NamedJSONDictionaryConvertible {
let resolvedName: String = jsonDictionary.json(atKeyPath: "name") ?? name
self.name = resolvedName
productName = jsonDictionary.json(atKeyPath: "productName") ?? resolvedName
let typeString: String = try jsonDictionary.json(atKeyPath: "type")
let typeString: String = jsonDictionary.json(atKeyPath: "type") ?? ""
if let type = PBXProductType(string: typeString) {
self.type = type
} else {
throw SpecParsingError.unknownTargetType(typeString)
}
let platformString: String = try jsonDictionary.json(atKeyPath: "platform")
if let supportedDestinations: [SupportedDestination] = jsonDictionary.json(atKeyPath: "supportedDestinations") {
self.supportedDestinations = supportedDestinations
}
let isResolved = jsonDictionary.json(atKeyPath: "isMultiPlatformTarget") ?? false
if isResolved, supportedDestinations != nil {
throw SpecParsingError.invalidTargetPlatformAsArray
}
var platformString: String = jsonDictionary.json(atKeyPath: "platform") ?? ""
// platform defaults to 'auto' if it is empty and we are using supported destinations
if supportedDestinations != nil, platformString.isEmpty {
platformString = Platform.auto.rawValue
}
// we add 'iOS' in supported destinations if it contains only 'macCatalyst'
if supportedDestinations?.contains(.macCatalyst) == true,
supportedDestinations?.contains(.iOS) == false {
supportedDestinations?.append(.iOS)
}
if let platform = Platform(rawValue: platformString) {
self.platform = platform
} else {
throw SpecParsingError.unknownTargetPlatform(platformString)
}
if let string: String = jsonDictionary.json(atKeyPath: "deploymentTarget") {
deploymentTarget = try Version.parse(string)
} else if let double: Double = jsonDictionary.json(atKeyPath: "deploymentTarget") {
@ -351,6 +378,7 @@ extension Target: JSONEncodable {
var dict: [String: Any?] = [
"type": type.name,
"platform": platform.rawValue,
"supportedDestinations": supportedDestinations?.map { $0.rawValue },
"settings": settings.toJSONValue(),
"configFiles": configFiles,
"attributes": attributes,

View File

@ -23,6 +23,8 @@ public struct TargetSource: Equatable {
public var createIntermediateGroups: Bool?
public var attributes: [String]
public var resourceTags: [String]
public var inferDestinationFiltersByPath: Bool?
public var destinationFilters: [SupportedDestination]?
public enum HeaderVisibility: String {
case `public`
@ -51,7 +53,9 @@ public struct TargetSource: Equatable {
headerVisibility: HeaderVisibility? = nil,
createIntermediateGroups: Bool? = nil,
attributes: [String] = [],
resourceTags: [String] = []
resourceTags: [String] = [],
inferDestinationFiltersByPath: Bool? = nil,
destinationFilters: [SupportedDestination]? = nil
) {
self.path = (path as NSString).standardizingPath
self.name = name
@ -66,6 +70,8 @@ public struct TargetSource: Equatable {
self.createIntermediateGroups = createIntermediateGroups
self.attributes = attributes
self.resourceTags = resourceTags
self.inferDestinationFiltersByPath = inferDestinationFiltersByPath
self.destinationFilters = destinationFilters
}
}
@ -112,6 +118,12 @@ extension TargetSource: JSONObjectConvertible {
createIntermediateGroups = jsonDictionary.json(atKeyPath: "createIntermediateGroups")
attributes = jsonDictionary.json(atKeyPath: "attributes") ?? []
resourceTags = jsonDictionary.json(atKeyPath: "resourceTags") ?? []
inferDestinationFiltersByPath = jsonDictionary.json(atKeyPath: "inferDestinationFiltersByPath")
if let destinationFilters: [SupportedDestination] = jsonDictionary.json(atKeyPath: "destinationFilters") {
self.destinationFilters = destinationFilters
}
}
}
@ -129,6 +141,8 @@ extension TargetSource: JSONEncodable {
"createIntermediateGroups": createIntermediateGroups,
"resourceTags": resourceTags,
"path": path,
"inferDestinationFiltersByPath": inferDestinationFiltersByPath,
"destinationFilters": destinationFilters?.map { $0.rawValue },
]
if optional != TargetSource.optionalDefault {

View File

@ -25,7 +25,7 @@ extension TestPlan: JSONEncodable {
[
"path": path,
"defaultPlan": defaultPlan,
]
] as [String : Any]
}
}

View File

@ -82,6 +82,7 @@ extension Platform {
public var emoji: String {
switch self {
case .auto: return "🤖"
case .iOS: return "📱"
case .watchOS: return "⌚️"
case .tvOS: return "📺"

View File

@ -151,6 +151,9 @@ extension Platform {
public var carthageName: String {
switch self {
case .auto:
// This is a dummy value
return "auto"
case .iOS:
return "iOS"
case .tvOS:

View File

@ -340,7 +340,7 @@ public class PBXProjGenerator {
return addObject(buildConfig)
}
var dependencies = target.targets.map { generateTargetDependency(from: target.name, to: $0, platform: nil) }
var dependencies = target.targets.map { generateTargetDependency(from: target.name, to: $0, platform: nil, platforms: nil) }
let defaultConfigurationName = project.options.defaultConfig ?? project.configs.first?.name ?? ""
let buildConfigList = addObject(XCConfigurationList(
@ -359,7 +359,7 @@ public class PBXProjGenerator {
aggregateTarget.dependencies = dependencies
}
func generateTargetDependency(from: String, to target: String, platform: String?) -> PBXTargetDependency {
func generateTargetDependency(from: String, to target: String, platform: String?, platforms: [String]?) -> PBXTargetDependency {
guard let targetObject = targetObjects[target] ?? targetAggregateObjects[target] else {
fatalError("Target dependency not found: from ( \(from) ) to ( \(target) )")
}
@ -376,6 +376,7 @@ public class PBXProjGenerator {
let targetDependency = addObject(
PBXTargetDependency(
platformFilter: platform,
platformFilters: platforms,
target: targetObject,
targetProxy: targetProxy
)
@ -727,7 +728,7 @@ public class PBXProjGenerator {
return !linkingAttributes.isEmpty ? ["ATTRIBUTES": linkingAttributes] : nil
}
func processTargetDependency(_ dependency: Dependency, dependencyTarget: Target, embedFileReference: PBXFileElement?, platform: String?) {
func processTargetDependency(_ dependency: Dependency, dependencyTarget: Target, embedFileReference: PBXFileElement?, platform: String?, platforms: [String]?) {
let dependencyLinkage = dependencyTarget.defaultLinkage
let link = dependency.link ??
((dependencyLinkage == .dynamic && target.type != .staticLibrary) ||
@ -736,6 +737,7 @@ public class PBXProjGenerator {
if link, let dependencyFile = embedFileReference {
let pbxBuildFile = PBXBuildFile(file: dependencyFile, settings: getDependencyFrameworkSettings(dependency: dependency))
pbxBuildFile.platformFilter = platform
pbxBuildFile.platformFilters = platforms
let buildFile = addObject(pbxBuildFile)
targetFrameworkBuildFiles.append(buildFile)
@ -752,6 +754,7 @@ public class PBXProjGenerator {
settings: getEmbedSettings(dependency: dependency, codeSign: dependency.codeSign ?? !dependencyTarget.type.isExecutable)
)
pbxBuildFile.platformFilter = platform
pbxBuildFile.platformFilters = platforms
let embedFile = addObject(pbxBuildFile)
if dependency.copyPhase != nil {
@ -787,6 +790,7 @@ public class PBXProjGenerator {
let embed = dependency.embed ?? target.shouldEmbedDependencies
let platform = makePlatformFilter(for: dependency.platformFilter)
let platforms = makeDestinationFilters(for: dependency.destinationFilters)
switch dependency.type {
case .target:
@ -795,15 +799,15 @@ public class PBXProjGenerator {
switch dependencyTargetReference.location {
case .local:
let dependencyTargetName = dependency.reference
let targetDependency = generateTargetDependency(from: target.name, to: dependencyTargetName, platform: platform)
let targetDependency = generateTargetDependency(from: target.name, to: dependencyTargetName, platform: platform, platforms: platforms)
dependencies.append(targetDependency)
guard let dependencyTarget = project.getTarget(dependencyTargetName) else { continue }
processTargetDependency(dependency, dependencyTarget: dependencyTarget, embedFileReference: targetFileReferences[dependencyTarget.name], platform: platform)
processTargetDependency(dependency, dependencyTarget: dependencyTarget, embedFileReference: targetFileReferences[dependencyTarget.name], platform: platform, platforms: platforms)
case .project(let dependencyProjectName):
let dependencyTargetName = dependencyTargetReference.name
let (targetDependency, dependencyTarget, dependencyProductProxy) = try generateExternalTargetDependency(from: target.name, to: dependencyTargetName, in: dependencyProjectName, platform: target.platform)
dependencies.append(targetDependency)
processTargetDependency(dependency, dependencyTarget: dependencyTarget, embedFileReference: dependencyProductProxy, platform: platform)
processTargetDependency(dependency, dependencyTarget: dependencyTarget, embedFileReference: dependencyProductProxy, platform: platform, platforms: platforms)
}
case .framework:
@ -829,6 +833,7 @@ public class PBXProjGenerator {
if dependency.link ?? (target.type != .staticLibrary) {
let pbxBuildFile = PBXBuildFile(file: fileReference, settings: getDependencyFrameworkSettings(dependency: dependency))
pbxBuildFile.platformFilter = platform
pbxBuildFile.platformFilters = platforms
let buildFile = addObject(pbxBuildFile)
targetFrameworkBuildFiles.append(buildFile)
@ -841,6 +846,7 @@ public class PBXProjGenerator {
if embed {
let pbxBuildFile = PBXBuildFile(file: fileReference, settings: getEmbedSettings(dependency: dependency, codeSign: dependency.codeSign ?? true))
pbxBuildFile.platformFilter = platform
pbxBuildFile.platformFilters = platforms
let embedFile = addObject(pbxBuildFile)
if dependency.copyPhase != nil {
@ -891,12 +897,14 @@ public class PBXProjGenerator {
settings: getDependencyFrameworkSettings(dependency: dependency)
)
pbxBuildFile.platformFilter = platform
pbxBuildFile.platformFilters = platforms
let buildFile = addObject(pbxBuildFile)
targetFrameworkBuildFiles.append(buildFile)
if dependency.embed == true {
let pbxBuildFile = PBXBuildFile(file: fileReference, settings: getEmbedSettings(dependency: dependency, codeSign: dependency.codeSign ?? true))
pbxBuildFile.platformFilter = platform
pbxBuildFile.platformFilters = platforms
let embedFile = addObject(pbxBuildFile)
if dependency.copyPhase != nil {
@ -926,6 +934,7 @@ public class PBXProjGenerator {
if dependency.link ?? (!isStaticLibrary && !isCarthageStaticLink) {
let pbxBuildFile = PBXBuildFile(file: fileReference, settings: getDependencyFrameworkSettings(dependency: dependency))
pbxBuildFile.platformFilter = platform
pbxBuildFile.platformFilters = platforms
let buildFile = addObject(pbxBuildFile)
targetFrameworkBuildFiles.append(buildFile)
}
@ -954,11 +963,12 @@ public class PBXProjGenerator {
if link {
let file = PBXBuildFile(product: packageDependency, settings: getDependencyFrameworkSettings(dependency: dependency))
file.platformFilter = platform
file.platformFilters = platforms
let buildFile = addObject(file)
targetFrameworkBuildFiles.append(buildFile)
} else {
let targetDependency = addObject(
PBXTargetDependency(platformFilter: platform, product: packageDependency)
PBXTargetDependency(platformFilter: platform, platformFilters: platforms, product: packageDependency)
)
dependencies.append(targetDependency)
}
@ -967,6 +977,7 @@ public class PBXProjGenerator {
let pbxBuildFile = PBXBuildFile(product: packageDependency,
settings: getEmbedSettings(dependency: dependency, codeSign: dependency.codeSign ?? true))
pbxBuildFile.platformFilter = platform
pbxBuildFile.platformFilters = platforms
let embedFile = addObject(pbxBuildFile)
if dependency.copyPhase != nil {
@ -999,6 +1010,7 @@ public class PBXProjGenerator {
settings: embed ? getEmbedSettings(dependency: dependency, codeSign: dependency.codeSign ?? true) : nil
)
pbxBuildFile.platformFilter = platform
pbxBuildFile.platformFilters = platforms
let buildFile = addObject(pbxBuildFile)
copyBundlesReferences.append(buildFile)
@ -1445,7 +1457,12 @@ public class PBXProjGenerator {
return "ios"
}
}
private func makeDestinationFilters(for filters: [SupportedDestination]?) -> [String]? {
guard let filters = filters, !filters.isEmpty else { return nil }
return filters.map { $0.string }
}
/// Make `Build Tools Plug-ins` as a dependency to the target
/// - Parameter target: ProjectTarget
/// - Returns: Elements for referencing other targets through content proxies.
@ -1466,7 +1483,7 @@ public class PBXProjGenerator {
return targetDependency
}
}
func getInfoPlists(for target: Target) -> [Config: String] {
var searchForDefaultInfoPlist: Bool = true
var defaultInfoPlist: String?
@ -1600,7 +1617,7 @@ extension Platform {
/// - returns: `true` for platforms that the app store requires simulator slices to be stripped.
public var requiresSimulatorStripping: Bool {
switch self {
case .iOS, .tvOS, .watchOS, .visionOS:
case .auto, .iOS, .tvOS, .watchOS, .visionOS:
return true
case .macOS:
return false

View File

@ -41,16 +41,63 @@ extension Project {
public func getTargetBuildSettings(target: Target, config: Config) -> BuildSettings {
var buildSettings = BuildSettings()
// list of supported destination sorted by priority
let specSupportedDestinations = target.supportedDestinations?.sorted(by: { $0.priority < $1.priority }) ?? []
if options.settingPresets.applyTarget {
buildSettings += SettingsPresetFile.platform(target.platform).getBuildSettings()
let platform: Platform
if target.platform == .auto,
let firstDestination = specSupportedDestinations.first,
let firstDestinationPlatform = Platform(rawValue: firstDestination.rawValue) {
platform = firstDestinationPlatform
} else {
platform = target.platform
}
buildSettings += SettingsPresetFile.platform(platform).getBuildSettings()
buildSettings += SettingsPresetFile.product(target.type).getBuildSettings()
buildSettings += SettingsPresetFile.productPlatform(target.type, target.platform).getBuildSettings()
buildSettings += SettingsPresetFile.productPlatform(target.type, platform).getBuildSettings()
if target.platform == .auto {
// this fix is necessary because the platform preset overrides the original value
buildSettings["SDKROOT"] = Platform.auto.rawValue
}
}
if !specSupportedDestinations.isEmpty {
var supportedPlatforms: [String] = []
var targetedDeviceFamily: [String] = []
for supportedDestination in specSupportedDestinations {
let supportedPlatformBuildSettings = SettingsPresetFile.supportedDestination(supportedDestination).getBuildSettings()
buildSettings += supportedPlatformBuildSettings
if let value = supportedPlatformBuildSettings?["SUPPORTED_PLATFORMS"] as? String {
supportedPlatforms += value.components(separatedBy: " ")
}
if let value = supportedPlatformBuildSettings?["TARGETED_DEVICE_FAMILY"] as? String {
targetedDeviceFamily += value.components(separatedBy: ",")
}
}
buildSettings["SUPPORTED_PLATFORMS"] = supportedPlatforms.joined(separator: " ")
buildSettings["TARGETED_DEVICE_FAMILY"] = targetedDeviceFamily.joined(separator: ",")
}
// apply custom platform version
if let version = target.deploymentTarget {
buildSettings[target.platform.deploymentTargetSetting] = version.deploymentTarget
if !specSupportedDestinations.isEmpty {
for supportedDestination in specSupportedDestinations {
if let platform = Platform(rawValue: supportedDestination.rawValue) {
buildSettings[platform.deploymentTargetSetting] = version.deploymentTarget
}
}
} else {
buildSettings[target.platform.deploymentTargetSetting] = version.deploymentTarget
}
}
// Prevent setting presets from overrwriting settings in target xcconfig files
@ -205,7 +252,7 @@ extension SettingsPresetFile {
guard let settingsPath = possibleSettingsPaths.first(where: { $0.exists }) else {
switch self {
case .base, .config, .platform:
case .base, .config, .platform, .supportedDestination:
print("No \"\(name)\" settings found")
case .product, .productPlatform:
break

View File

@ -5,6 +5,7 @@ import XcodeProj
public enum SettingsPresetFile {
case config(ConfigType)
case platform(Platform)
case supportedDestination(SupportedDestination)
case product(PBXProductType)
case productPlatform(PBXProductType, Platform)
case base
@ -13,6 +14,7 @@ public enum SettingsPresetFile {
switch self {
case let .config(config): return "Configs/\(config.rawValue)"
case let .platform(platform): return "Platforms/\(platform.rawValue)"
case let .supportedDestination(supportedDestination): return "SupportedDestinations/\(supportedDestination.rawValue)"
case let .product(product): return "Products/\(product.name)"
case let .productPlatform(product, platform): return "Product_Platform/\(product.name)_\(platform.rawValue)"
case .base: return "base"
@ -23,6 +25,7 @@ public enum SettingsPresetFile {
switch self {
case let .config(config): return "\(config.rawValue) config"
case let .platform(platform): return platform.rawValue
case let .supportedDestination(supportedDestination): return supportedDestination.rawValue
case let .product(product): return product.name
case let .productPlatform(product, platform): return "\(platform) \(product)"
case .base: return "base"

View File

@ -109,7 +109,23 @@ class SourceGenerator {
return nil
}
}
private func makeDestinationFilters(for path: Path, with filters: [SupportedDestination]?, or inferDestinationFiltersByPath: Bool?) -> [String]? {
if let filters = filters, !filters.isEmpty {
return filters.map { $0.string }
} else if inferDestinationFiltersByPath == true {
for supportedDestination in SupportedDestination.allCases {
let regex1 = try? NSRegularExpression(pattern: "\\/\(supportedDestination)\\/", options: .caseInsensitive)
let regex2 = try? NSRegularExpression(pattern: "\\_\(supportedDestination)\\.swift$", options: .caseInsensitive)
if regex1?.isMatch(to: path.string) == true || regex2?.isMatch(to: path.string) == true {
return [supportedDestination.string]
}
}
}
return nil
}
func generateSourceFile(targetType: PBXProductType, targetSource: TargetSource, path: Path, fileReference: PBXFileElement? = nil, buildPhases: [Path: BuildPhaseSpec]) -> SourceFile {
let fileReference = fileReference ?? fileReferencesByPath[path.string.lowercased()]!
var settings: [String: Any] = [:]
@ -174,8 +190,10 @@ class SourceGenerator {
if chosenBuildPhase == .resources && !assetTags.isEmpty {
settings["ASSET_TAGS"] = assetTags
}
let buildFile = PBXBuildFile(file: fileReference, settings: settings.isEmpty ? nil : settings)
let platforms = makeDestinationFilters(for: path, with: targetSource.destinationFilters, or: targetSource.inferDestinationFiltersByPath)
let buildFile = PBXBuildFile(file: fileReference, settings: settings.isEmpty ? nil : settings, platformFilters: platforms)
return SourceFile(
path: path,
fileReference: fileReference,

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>TestApp</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1.0.0</string>
</dict>
</plist>

View File

@ -0,0 +1,10 @@
import SwiftUI
@main
struct MyAppApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}

View File

@ -0,0 +1,13 @@
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
Text("Hello, world! on iOS")
}
.padding()
}
}

View File

@ -0,0 +1,13 @@
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Image(systemName: "house")
.imageScale(.large)
.foregroundColor(.accentColor)
Text("Hello, world! tvOS")
}
.padding()
}
}

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>

View File

@ -0,0 +1 @@
import Foundation

View File

@ -0,0 +1 @@
import Foundation

View File

@ -0,0 +1 @@
import Foundation

View File

@ -0,0 +1 @@
import Foundation

View File

@ -0,0 +1 @@
import Foundation

View File

@ -0,0 +1 @@
import Foundation

View File

@ -41,6 +41,7 @@
0F99AECCB4691803C791CDCE /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2FC2A8A829CE71B1CF415FF7 /* Main.storyboard */; };
15129B8D9ED000BDA1FEEC27 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23A2F16890ECF2EE3FED72AE /* AppDelegate.swift */; };
1551370B0ACAC632E15C853B /* SwiftyJSON.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FF47010E7368583405AA50CB /* SwiftyJSON.framework */; };
1B485D6584C3B47AC58831C6 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18722C61B05FFF4CC63D5755 /* ContentView.swift */; platformFilters = (tvos, ); };
1BC891D89980D82738D963F3 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 74FBDFA5CB063F6001AD8ACD /* Main.storyboard */; };
1E03FC7312293997599C6435 /* Empty.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 068EDF47F0B087F6A4052AC0 /* Empty.h */; };
1E2A4D61E96521FF7123D7B0 /* XPC Service.xpc in CopyFiles */ = {isa = PBXBuildFile; fileRef = 22237B8EBD9E6BE8EBC8735F /* XPC Service.xpc */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
@ -56,6 +57,7 @@
21CA04F29CD0DEB0DD27B808 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0C5AC2545AE4D4F7F44E2E9B /* Result.framework */; };
262891CCD5F74316610437FA /* Framework2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3EF21DF245F66BEF5446AAEF /* Framework2.framework */; platformFilter = ios; settings = {ATTRIBUTES = (Weak, ); }; };
265B6A05C0198FD2EB485173 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93C033648A37D95027845BD3 /* main.swift */; };
2698ED273D0A5820B28CAD20 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D52EC9AA9FFD3B690C355068 /* LaunchScreen.storyboard */; platformFilters = (ios, ); };
2730C6D0A35AED4ADD6EDF17 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0704B6CAFBB53E0EBB08F6B3 /* ViewController.swift */; settings = {COMPILER_FLAGS = "-Werror"; }; };
28A96EBC76D53817AABDA91C /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 8AF20308873AEEEC4D8C45D1 /* Settings.bundle */; };
2A5356FCC03EE312F1738C61 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B82F603D981398F38D762E /* AppDelegate.swift */; };
@ -70,6 +72,7 @@
339578307B9266AB3D7722D9 /* File2.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC56891DA7446EAC8C2F27EB /* File2.swift */; };
3535891EC86283BB5064E7E1 /* Headers in Headers */ = {isa = PBXBuildFile; fileRef = 2E1E747C7BC434ADB80CC269 /* Headers */; settings = {ATTRIBUTES = (Public, ); }; };
3788E1382B38DF4ACE3D2BB1 /* MyFramework.h in Headers */ = {isa = PBXBuildFile; fileRef = 6A58A16491CDDF968B0D56DE /* MyFramework.h */; settings = {ATTRIBUTES = (Public, ); }; };
3A3BA9F91994D8B472C71F04 /* Swinject in Frameworks */ = {isa = PBXBuildFile; platformFilters = (tvos, ); productRef = C7F9B7EDE85527EFEA85D46D /* Swinject */; };
3BBCA6F76E5F212E9C55FB78 /* BundleX.bundle in Copy Bundle Resources */ = {isa = PBXBuildFile; fileRef = 45C12576F5AA694DD0CE2132 /* BundleX.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
3C5134EE524310ACF7B7CD6E /* ExtensionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CAF6C55B555E3E1352645B6 /* ExtensionDelegate.swift */; };
3DF22C477446669094AC7C8C /* ExternalTarget.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = F6ADE654A3459AFDA2CC0CD3 /* ExternalTarget.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
@ -148,6 +151,7 @@
B18C121B0A4D43ED8149D8E2 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 79325B44B19B83EC6CEDBCC5 /* LaunchScreen.storyboard */; };
B20617116B230DED1F7AF5E5 /* libStaticLibrary_ObjC.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B221F5A689AD7D3AD52F56B8 /* libStaticLibrary_ObjC.a */; };
B2D43A31C184E34EF9CB743C /* Framework.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 8A9274BE42A03DC5DA1FAD04 /* Framework.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
B358AB913543E62237FCC4E3 /* MyAppApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15A4363D659A58DA835DE8BA /* MyAppApp.swift */; };
B47F2629BFE5853767C8BB5E /* Contacts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FDB2B6A77D39CD5602F2125F /* Contacts.framework */; };
B49D3A51787E362DE4D0E78A /* SomeXPCService.xpc in CopyFiles */ = {isa = PBXBuildFile; fileRef = 70A8E15C81E454DC950C59F0 /* SomeXPCService.xpc */; };
B502EF8F7605CBD038298F23 /* CrossOverlayFramework.swiftcrossimport in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8DD7A61B07AD2F91BDECC255 /* CrossOverlayFramework.swiftcrossimport */; };
@ -163,6 +167,7 @@
C836F09B677937EFF69B1FCE /* NotificationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C934C1F7A68CCD0AB6B38478 /* NotificationController.swift */; };
C88598A49087A212990F4E8B /* ResourceFolder in Resources */ = {isa = PBXBuildFile; fileRef = 6B1603BA83AA0C7B94E45168 /* ResourceFolder */; };
CAE18A2194B57C830A297F83 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6680EFE4E908CDBDCE405C8 /* main.swift */; };
CAF8470C7F1BF207DBE6AEE3 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEEFDE76B5FEC833403C0869 /* ContentView.swift */; platformFilters = (ios, ); };
CCA17097382757012B58C17C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1BC32A813B80A53962A1F365 /* Assets.xcassets */; };
D058D241BDF5FB0C919BBECA /* CrossOverlayFramework.swiftcrossimport in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8DD7A61B07AD2F91BDECC255 /* CrossOverlayFramework.swiftcrossimport */; };
D5458D67C3596943114C3205 /* Standalone.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F5BD97AF0F94A15A5B7DDB7 /* Standalone.swift */; };
@ -738,8 +743,10 @@
108BB29172D27BE3BD1E7F35 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
13EEAB58665D79C15184D9D0 /* App_iOS_UITests.xctest */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = App_iOS_UITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
148B7C933698BCC4F1DBA979 /* XPC_Service.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = XPC_Service.m; sourceTree = "<group>"; };
15A4363D659A58DA835DE8BA /* MyAppApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyAppApp.swift; sourceTree = "<group>"; };
16AA52945B70B1BF9E246350 /* FilterDataProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterDataProvider.swift; sourceTree = "<group>"; };
16D662EE577E4CD6AFF39D66 /* config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = config.xcconfig; sourceTree = "<group>"; };
18722C61B05FFF4CC63D5755 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
187E665975BB5611AF0F27E1 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
1BC32A813B80A53962A1F365 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
1D0C79A8C750EC0DE748C463 /* StaticLibrary_ObjC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StaticLibrary_ObjC.m; sourceTree = "<group>"; };
@ -766,6 +773,7 @@
3ED831531AA349CCC19B258B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
3EF21DF245F66BEF5446AAEF /* Framework2.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Framework2.framework; sourceTree = BUILT_PRODUCTS_DIR; };
3FC04772130400920D68A167 /* App_Clip_Tests.xctest */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = App_Clip_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
407C3F0009FDCE5B1B7DC2A8 /* App_supportedDestinations.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = App_supportedDestinations.app; sourceTree = BUILT_PRODUCTS_DIR; };
40863AE6202CFCD0529D8438 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
41FC82ED1C4C3B7B3D7B2FB7 /* Framework.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Framework.framework; sourceTree = BUILT_PRODUCTS_DIR; };
432E2C071A4B6B3757BEA13E /* Driver.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Driver.cpp; sourceTree = "<group>"; };
@ -830,6 +838,7 @@
AAA49985DFFE797EE8416887 /* inputList.xcfilelist */ = {isa = PBXFileReference; lastKnownFileType = text.xcfilelist; path = inputList.xcfilelist; sourceTree = "<group>"; };
AB055761199DF36DB0C629A6 /* Framework2.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Framework2.framework; sourceTree = BUILT_PRODUCTS_DIR; };
AEBCA8CFF769189C0D52031E /* App_iOS.xctestplan */ = {isa = PBXFileReference; path = App_iOS.xctestplan; sourceTree = "<group>"; };
AEEFDE76B5FEC833403C0869 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
B17B8D9C9B391332CD176A35 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LocalizedStoryboard.storyboard; sourceTree = "<group>"; };
B198242976C3395E31FE000A /* MessagesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesViewController.swift; sourceTree = "<group>"; };
B1C33BB070583BE3B0EC0E68 /* App_iOS.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = App_iOS.app; sourceTree = BUILT_PRODUCTS_DIR; };
@ -855,6 +864,7 @@
D21BB1B6FA5A025305B223BA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
D296BB7355994040E197A1EE /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = "<group>"; };
D51CC8BCCBD68A90E90A3207 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
D52EC9AA9FFD3B690C355068 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
D629E142AB87C681D4EC90F7 /* iMessageExtension.appex */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = "wrapper.app-extension"; path = iMessageExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
D6C89D80B5458D8929F5C127 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
D70BE0C05E5779A077793BE6 /* Model 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Model 2.xcdatamodel"; sourceTree = "<group>"; };
@ -909,6 +919,14 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
36418B6CABA06BA9B206556E /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
3A3BA9F91994D8B472C71F04 /* Swinject in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
5EFF61D0A49AA8EABD72DF44 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@ -1102,6 +1120,7 @@
0D039F2E62354C7C8E283BE6 /* App_iOS_UITests */,
EE78B4FBD0137D1975C47D76 /* App_macOS */,
6DE1C805DC13547F27FD86C6 /* App_macOS_Tests */,
D27117FBA2920408002F0B4C /* App_supportedDestinations */,
BAE6C12745737019DC9E98BF /* App_watchOS */,
795B8D70B674C850B57DD39D /* App_watchOS Extension */,
6DBE0EE90642BB3F6E58AD43 /* Configs */,
@ -1194,6 +1213,23 @@
path = UnderFileGroup;
sourceTree = "<group>";
};
69C61547C081D04364A5DE42 /* Storyboards */ = {
isa = PBXGroup;
children = (
D52EC9AA9FFD3B690C355068 /* LaunchScreen.storyboard */,
);
name = Storyboards;
path = Storyboards;
sourceTree = "<group>";
};
6A90AFD865B13D26DA108CAB /* tvOS */ = {
isa = PBXGroup;
children = (
18722C61B05FFF4CC63D5755 /* ContentView.swift */,
);
path = tvOS;
sourceTree = "<group>";
};
6BD8F0932CCAD4BBE752866B /* App_Clip_UITests */ = {
isa = PBXGroup;
children = (
@ -1266,6 +1302,17 @@
path = "XPC Service";
sourceTree = "<group>";
};
85FEBB7D2103B020423407A2 /* Sources */ = {
isa = PBXGroup;
children = (
9E5249A284275B0CDF4E5DDA /* iOS */,
6A90AFD865B13D26DA108CAB /* tvOS */,
15A4363D659A58DA835DE8BA /* MyAppApp.swift */,
);
name = Sources;
path = Sources;
sourceTree = "<group>";
};
8CFD8AD4820FAB9265663F92 /* Tool */ = {
isa = PBXGroup;
children = (
@ -1301,6 +1348,14 @@
path = StandaloneFiles;
sourceTree = "<group>";
};
9E5249A284275B0CDF4E5DDA /* iOS */ = {
isa = PBXGroup;
children = (
AEEFDE76B5FEC833403C0869 /* ContentView.swift */,
);
path = iOS;
sourceTree = "<group>";
};
9EDF27BB8A57733E6639D36D /* Resources */ = {
isa = PBXGroup;
children = (
@ -1343,6 +1398,7 @@
B1C33BB070583BE3B0EC0E68 /* App_iOS.app */,
0B9D98D935F2C69A1F5BA539 /* App_macOS_Tests.xctest */,
33F6DCDC37D2E66543D4965D /* App_macOS.app */,
407C3F0009FDCE5B1B7DC2A8 /* App_supportedDestinations.app */,
0D09D243DBCF9D32E239F1E8 /* App_watchOS Extension.appex */,
A680BE9F68A255B0FB291AE6 /* App_watchOS.app */,
84317819C92F78425870E483 /* BundleX.bundle */,
@ -1432,6 +1488,15 @@
path = StaticLibrary_Swift;
sourceTree = "<group>";
};
D27117FBA2920408002F0B4C /* App_supportedDestinations */ = {
isa = PBXGroup;
children = (
85FEBB7D2103B020423407A2 /* Sources */,
69C61547C081D04364A5DE42 /* Storyboards */,
);
path = App_supportedDestinations;
sourceTree = "<group>";
};
D557819B1EE5B42A0A3DD4D1 /* tvOS */ = {
isa = PBXGroup;
children = (
@ -2205,6 +2270,26 @@
productReference = 7D700FA699849D2F95216883 /* EntitledApp.app */;
productType = "com.apple.product-type.application";
};
C0570E2FB50D830D8D423396 /* App_supportedDestinations */ = {
isa = PBXNativeTarget;
buildConfigurationList = 5EC789CCE1928A4CDA00DD1E /* Build configuration list for PBXNativeTarget "App_supportedDestinations" */;
buildPhases = (
CF8BAE171BAAFB2E5DDB9C19 /* Sources */,
97119CD9F01D9A9522EF3526 /* Resources */,
36418B6CABA06BA9B206556E /* Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = App_supportedDestinations;
packageProductDependencies = (
C7F9B7EDE85527EFEA85D46D /* Swinject */,
);
productName = App_supportedDestinations;
productReference = 407C3F0009FDCE5B1B7DC2A8 /* App_supportedDestinations.app */;
productType = "com.apple.product-type.application";
};
C7F90FD0FAAF232B3E015D38 /* CrossOverlayFramework_watchOS */ = {
isa = PBXNativeTarget;
buildConfigurationList = C483BD5456B09C276DE6EFC1 /* Build configuration list for PBXNativeTarget "CrossOverlayFramework_watchOS" */;
@ -2415,6 +2500,7 @@
F674B2CFC4738EEC49BAD0DA /* App_iOS_UITests */,
020A320BB3736FCDE6CC4E70 /* App_macOS */,
71E2BDAC4B8E8FC2BBF75C55 /* App_macOS_Tests */,
C0570E2FB50D830D8D423396 /* App_supportedDestinations */,
208179651927D1138D19B5AD /* App_watchOS */,
307AE3FA155FFD09B74AE351 /* App_watchOS Extension */,
DA40AB367B606CCE2FDD398D /* BundleX */,
@ -2520,6 +2606,14 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
97119CD9F01D9A9522EF3526 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2698ED273D0A5820B28CAD20 /* LaunchScreen.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
AA12B5909FEE45016F469C78 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@ -3000,6 +3094,16 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
CF8BAE171BAAFB2E5DDB9C19 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
CAF8470C7F1BF207DBE6AEE3 /* ContentView.swift in Sources */,
1B485D6584C3B47AC58831C6 /* ContentView.swift in Sources */,
B358AB913543E62237FCC4E3 /* MyAppApp.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
D1F422E9C4DD531AA88418C9 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@ -4108,6 +4212,26 @@
};
name = "Staging Debug";
};
24CFBB3ABB9E14E85035B892 /* Production Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer";
INFOPLIST_FILE = App_supportedDestinations/Info.generated.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.test.TestApp;
SDKROOT = auto;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator appletvos appletvsimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = YES;
TARGETED_DEVICE_FAMILY = "1,2,3";
};
name = "Production Debug";
};
2569D399CA3C4828EF87AD78 /* Test Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@ -4815,6 +4939,26 @@
};
name = "Production Release";
};
4C11A3E7EA48B5FB556D4614 /* Staging Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer";
INFOPLIST_FILE = App_supportedDestinations/Info.generated.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.test.TestApp;
SDKROOT = auto;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator appletvos appletvsimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = YES;
TARGETED_DEVICE_FAMILY = "1,2,3";
};
name = "Staging Debug";
};
4D5DC2028DC046B8AF0B9B83 /* Test Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@ -5558,6 +5702,26 @@
};
name = "Staging Debug";
};
75B3C83C754AA9C12ABF5E54 /* Staging Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer";
INFOPLIST_FILE = App_supportedDestinations/Info.generated.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.test.TestApp;
SDKROOT = auto;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator appletvos appletvsimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = YES;
TARGETED_DEVICE_FAMILY = "1,2,3";
};
name = "Staging Release";
};
77B8B41EBA5D778EB3AF89DC /* Production Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@ -5757,6 +5921,26 @@
};
name = "Production Release";
};
7F3F6A813F8B126258780E8D /* Production Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer";
INFOPLIST_FILE = App_supportedDestinations/Info.generated.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.test.TestApp;
SDKROOT = auto;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator appletvos appletvsimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = YES;
TARGETED_DEVICE_FAMILY = "1,2,3";
};
name = "Production Release";
};
7F86E00770E76CA3412A03BD /* Staging Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
@ -6288,6 +6472,26 @@
};
name = "Staging Debug";
};
98EE00BF46FAE62F16C25E9C /* Test Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer";
INFOPLIST_FILE = App_supportedDestinations/Info.generated.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.test.TestApp;
SDKROOT = auto;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator appletvos appletvsimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = YES;
TARGETED_DEVICE_FAMILY = "1,2,3";
};
name = "Test Release";
};
9A891313A139893990989BDD /* Test Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@ -6469,6 +6673,26 @@
};
name = "Production Debug";
};
A4015127D0A01FE60CF5621B /* Test Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer";
INFOPLIST_FILE = App_supportedDestinations/Info.generated.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.test.TestApp;
SDKROOT = auto;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator appletvos appletvsimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = YES;
TARGETED_DEVICE_FAMILY = "1,2,3";
};
name = "Test Debug";
};
A59DDFBFCF18C44A993CFB00 /* Test Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@ -8721,6 +8945,19 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = "Production Debug";
};
5EC789CCE1928A4CDA00DD1E /* Build configuration list for PBXNativeTarget "App_supportedDestinations" */ = {
isa = XCConfigurationList;
buildConfigurations = (
24CFBB3ABB9E14E85035B892 /* Production Debug */,
7F3F6A813F8B126258780E8D /* Production Release */,
4C11A3E7EA48B5FB556D4614 /* Staging Debug */,
75B3C83C754AA9C12ABF5E54 /* Staging Release */,
A4015127D0A01FE60CF5621B /* Test Debug */,
98EE00BF46FAE62F16C25E9C /* Test Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = "Production Debug";
};
62C52A55CB8D3BD9A055FD14 /* Build configuration list for PBXNativeTarget "App_macOS_Tests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
@ -9125,6 +9362,11 @@
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
C7F9B7EDE85527EFEA85D46D /* Swinject */ = {
isa = XCSwiftPackageProductDependency;
package = 4EDA79334592CBBA0E507AD2 /* XCRemoteSwiftPackageReference "Swinject" */;
productName = Swinject;
};
D7917D10F77DA9D69937D493 /* Swinject */ = {
isa = XCSwiftPackageProductDependency;
package = 4EDA79334592CBBA0E507AD2 /* XCRemoteSwiftPackageReference "Swinject" */;

View File

@ -232,6 +232,29 @@ targets:
properties:
com.apple.security.application-groups: group.com.app
App_supportedDestinations:
type: application
supportedDestinations: [iOS, tvOS]
info:
path: App_supportedDestinations/Info.generated.plist
properties:
CFBundleDisplayName: "TestApp"
CFBundleVersion: "1.0.0"
settings:
base:
PRODUCT_BUNDLE_IDENTIFIER: com.test.TestApp
sources:
- path: App_supportedDestinations/Sources
group: App_supportedDestinations
inferDestinationFiltersByPath: true
- path: App_supportedDestinations/Storyboards
group: App_supportedDestinations
destinationFilters: [iOS]
dependencies:
- package: Swinject
product: Swinject
destinationFilters: [tvOS]
App_watchOS:
type: application.watchapp2
platform: watchOS
@ -482,7 +505,7 @@ schemes:
enableGPUFrameCaptureMode: "disabled"
enableGPUValidationMode: "disabled"
storeKitConfiguration: "App_iOS/Configuration.storekit"
macroExpansion: App_iOS
macroExpansion: App_iOS
test:
gatherCoverageData: true
targets:

View File

@ -73,12 +73,23 @@ class ProjectSpecTests: XCTestCase {
describe {
$0.it("has correct build setting") {
try expect(Platform.auto.deploymentTargetSetting) == ""
try expect(Platform.iOS.deploymentTargetSetting) == "IPHONEOS_DEPLOYMENT_TARGET"
try expect(Platform.tvOS.deploymentTargetSetting) == "TVOS_DEPLOYMENT_TARGET"
try expect(Platform.watchOS.deploymentTargetSetting) == "WATCHOS_DEPLOYMENT_TARGET"
try expect(Platform.macOS.deploymentTargetSetting) == "MACOSX_DEPLOYMENT_TARGET"
try expect(Platform.visionOS.deploymentTargetSetting) == "XROS_DEPLOYMENT_TARGET"
}
$0.it("has correct sdk root") {
try expect(Platform.auto.sdkRoot) == "auto"
try expect(Platform.iOS.sdkRoot) == "iphoneos"
try expect(Platform.tvOS.sdkRoot) == "appletvos"
try expect(Platform.watchOS.sdkRoot) == "watchos"
try expect(Platform.macOS.sdkRoot) == "macosx"
try expect(Platform.visionOS.sdkRoot) == "xros"
}
$0.it("parses version correctly") {
try expect(Version.parse("2").deploymentTarget) == "2.0"
try expect(Version.parse("2.0").deploymentTarget) == "2.0"
@ -178,7 +189,59 @@ class ProjectSpecTests: XCTestCase {
try expectNoValidationError(project, .duplicateDependencies(target: "target1", dependencyReference: "package1"))
}
$0.it("unexpected supported destinations for watch app") {
var project = baseProject
project.targets = [
Target(
name: "target1",
type: .application,
platform: .watchOS,
supportedDestinations: [.macOS]
)
]
try expectValidationError(project, .unexpectedTargetPlatformForSupportedDestinations(target: "target1", platform: .watchOS))
}
$0.it("multiple definitions of mac platform in supported destinations") {
var project = baseProject
project.targets = [
Target(
name: "target1",
type: .application,
platform: .iOS,
supportedDestinations: [.macOS, .macCatalyst]
)
]
try expectValidationError(project, .multipleMacPlatformsInSupportedDestinations(target: "target1"))
}
$0.it("invalid target platform for macCatalyst supported destinations") {
var project = baseProject
project.targets = [
Target(
name: "target1",
type: .application,
platform: .tvOS,
supportedDestinations: [.tvOS, .macCatalyst]
)
]
try expectValidationError(project, .invalidTargetPlatformForSupportedDestinations(target: "target1"))
}
$0.it("missing target platform in supported destinations") {
var project = baseProject
project.targets = [
Target(
name: "target1",
type: .application,
platform: .iOS,
supportedDestinations: [.tvOS]
)
]
try expectValidationError(project, .missingTargetPlatformInSupportedDestinations(target: "target1", platform: .iOS))
}
$0.it("allows non-existent configurations") {
var project = baseProject
project.options = SpecOptions(disabledValidations: [.missingConfigs])

View File

@ -604,10 +604,66 @@ class SpecLoadingTests: XCTestCase {
target_iOS.deploymentTarget = Version(major: 9, minor: 0, patch: 0)
target_tvOS.deploymentTarget = Version(major: 10, minor: 0, patch: 0)
try expect(project.targets.count) == 2
try expect(project.targets) == [target_iOS, target_tvOS]
}
$0.it("parses no platform fallbacks to auto if we are using supported destinations") {
let targetDictionary: [String: Any] = [
"type": "framework",
"supportedDestinations": ["iOS", "tvOS"]
]
let project = try getProjectSpec(["targets": ["Framework": targetDictionary]])
let target = Target(name: "Framework", type: .framework, platform: .auto)
try expect(project.targets) == [target]
}
$0.it("parses no platform fails if we are not using supported destinations") {
let expectedError = SpecParsingError.unknownTargetPlatform("")
let projectDictionary: [String: Any] = [
"name": "test",
"targets": ["target1": [
"type": "application"
] as [String : Any]]
]
try expectError(expectedError) {
_ = try Project(jsonDictionary: projectDictionary)
}
}
$0.it("parses supported destinations with macCatalyst but not iOS, we add iOS") {
let targetDictionary: [String: Any] = [
"type": "framework",
"supportedDestinations": ["macCatalyst"]
]
let project = try getProjectSpec(["targets": ["Framework": targetDictionary]])
let target = Target(name: "Framework", type: .framework, platform: .auto)
try expect(project.targets) == [target]
try expect(project.targets.first?.supportedDestinations) == [.macCatalyst, .iOS]
}
$0.it("invalid target platform because platform is an array and supported destinations is in use") {
let expectedError = SpecParsingError.invalidTargetPlatformAsArray
let projectDictionary: [String: Any] = [
"name": "test",
"targets": ["target1": [
"type": "application",
"platform": ["iOS", "tvOS"],
"supportedDestinations": ["iOS", "tvOS"]
] as [String : Any]]
]
try expectError(expectedError) {
_ = try Project(jsonDictionary: projectDictionary)
}
}
$0.it("parses target templates") {
let targetDictionary: [String: Any] = [

View File

@ -335,7 +335,156 @@ class ProjectGeneratorTests: XCTestCase {
try expect(targetConfig.buildSettings["WATCHOS_DEPLOYMENT_TARGET"] as? String) == "2.0"
try expect(targetConfig.buildSettings["TVOS_DEPLOYMENT_TARGET"]).beNil()
}
$0.it("supportedPlaforms merges settings - iOS, tvOS") {
let target = Target(name: "Target", type: .application, platform: .auto, supportedDestinations: [.tvOS, .iOS])
let project = Project(name: "", targets: [target])
let pbxProject = try project.generatePbxProj()
let targetConfig1 = try unwrap(pbxProject.nativeTargets.first?.buildConfigurationList?.buildConfigurations.first)
try expect(targetConfig1.buildSettings["SUPPORTED_PLATFORMS"] as? String) == "iphoneos iphonesimulator appletvos appletvsimulator"
try expect(targetConfig1.buildSettings["TARGETED_DEVICE_FAMILY"] as? String) == "1,2,3"
try expect(targetConfig1.buildSettings["SUPPORTS_MACCATALYST"] as? Bool) == false
try expect(targetConfig1.buildSettings["SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD"] as? Bool) == true
try expect(targetConfig1.buildSettings["SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD"] as? Bool) == true
try expect(targetConfig1.buildSettings["LD_RUNPATH_SEARCH_PATHS"] as? [String]) == ["$(inherited)", "@executable_path/Frameworks"]
try expect(targetConfig1.buildSettings["SDKROOT"] as? String) == "auto"
try expect(targetConfig1.buildSettings["ASSETCATALOG_COMPILER_APPICON_NAME"] as? String) == "AppIcon"
try expect(targetConfig1.buildSettings["CODE_SIGN_IDENTITY"] as? String) == "iPhone Developer"
}
$0.it("supportedPlaforms merges settings - iOS, visionOS") {
let target = Target(name: "Target", type: .application, platform: .auto, supportedDestinations: [.visionOS, .iOS])
let project = Project(name: "", targets: [target])
let pbxProject = try project.generatePbxProj()
let targetConfig1 = try unwrap(pbxProject.nativeTargets.first?.buildConfigurationList?.buildConfigurations.first)
try expect(targetConfig1.buildSettings["SUPPORTED_PLATFORMS"] as? String) == "iphoneos iphonesimulator xros xrsimulator"
try expect(targetConfig1.buildSettings["TARGETED_DEVICE_FAMILY"] as? String) == "1,2,7"
try expect(targetConfig1.buildSettings["SUPPORTS_MACCATALYST"] as? Bool) == false
try expect(targetConfig1.buildSettings["SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD"] as? Bool) == true
try expect(targetConfig1.buildSettings["SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD"] as? Bool) == false
try expect(targetConfig1.buildSettings["LD_RUNPATH_SEARCH_PATHS"] as? [String]) == ["$(inherited)", "@executable_path/Frameworks"]
try expect(targetConfig1.buildSettings["SDKROOT"] as? String) == "auto"
try expect(targetConfig1.buildSettings["ASSETCATALOG_COMPILER_APPICON_NAME"] as? String) == "AppIcon"
try expect(targetConfig1.buildSettings["CODE_SIGN_IDENTITY"] as? String) == "iPhone Developer"
}
$0.it("supportedPlaforms merges settings - iOS, tvOS, macOS") {
let target = Target(name: "Target", type: .application, platform: .auto, supportedDestinations: [.iOS, .tvOS, .macOS])
let project = Project(name: "", targets: [target])
let pbxProject = try project.generatePbxProj()
let targetConfig1 = try unwrap(pbxProject.nativeTargets.first?.buildConfigurationList?.buildConfigurations.first)
try expect(targetConfig1.buildSettings["SUPPORTED_PLATFORMS"] as? String) == "iphoneos iphonesimulator appletvos appletvsimulator macosx"
try expect(targetConfig1.buildSettings["TARGETED_DEVICE_FAMILY"] as? String) == "1,2,3"
try expect(targetConfig1.buildSettings["SUPPORTS_MACCATALYST"] as? Bool) == false
try expect(targetConfig1.buildSettings["SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD"] as? Bool) == false
try expect(targetConfig1.buildSettings["SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD"] as? Bool) == true
try expect(targetConfig1.buildSettings["LD_RUNPATH_SEARCH_PATHS"] as? [String]) == ["$(inherited)", "@executable_path/Frameworks"]
try expect(targetConfig1.buildSettings["SDKROOT"] as? String) == "auto"
try expect(targetConfig1.buildSettings["ASSETCATALOG_COMPILER_APPICON_NAME"] as? String) == "AppIcon"
try expect(targetConfig1.buildSettings["CODE_SIGN_IDENTITY"] as? String) == "iPhone Developer"
}
$0.it("supportedPlaforms merges settings - iOS, tvOS, macCatalyst") {
let target = Target(name: "Target", type: .application, platform: .auto, supportedDestinations: [.iOS, .tvOS, .macCatalyst])
let project = Project(name: "", targets: [target])
let pbxProject = try project.generatePbxProj()
let targetConfig1 = try unwrap(pbxProject.nativeTargets.first?.buildConfigurationList?.buildConfigurations.first)
try expect(targetConfig1.buildSettings["SUPPORTED_PLATFORMS"] as? String) == "iphoneos iphonesimulator appletvos appletvsimulator"
try expect(targetConfig1.buildSettings["TARGETED_DEVICE_FAMILY"] as? String) == "1,2,3"
try expect(targetConfig1.buildSettings["SUPPORTS_MACCATALYST"] as? Bool) == true
try expect(targetConfig1.buildSettings["SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD"] as? Bool) == false
try expect(targetConfig1.buildSettings["SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD"] as? Bool) == true
try expect(targetConfig1.buildSettings["LD_RUNPATH_SEARCH_PATHS"] as? [String]) == ["$(inherited)", "@executable_path/Frameworks"]
try expect(targetConfig1.buildSettings["SDKROOT"] as? String) == "auto"
try expect(targetConfig1.buildSettings["ASSETCATALOG_COMPILER_APPICON_NAME"] as? String) == "AppIcon"
try expect(targetConfig1.buildSettings["CODE_SIGN_IDENTITY"] as? String) == "iPhone Developer"
}
$0.it("supportedPlaforms merges settings - iOS, macOS") {
let target = Target(name: "Target", type: .application, platform: .auto, supportedDestinations: [.iOS, .macOS])
let project = Project(name: "", targets: [target])
let pbxProject = try project.generatePbxProj()
let targetConfig1 = try unwrap(pbxProject.nativeTargets.first?.buildConfigurationList?.buildConfigurations.first)
try expect(targetConfig1.buildSettings["SUPPORTED_PLATFORMS"] as? String) == "iphoneos iphonesimulator macosx"
try expect(targetConfig1.buildSettings["TARGETED_DEVICE_FAMILY"] as? String) == "1,2"
try expect(targetConfig1.buildSettings["SUPPORTS_MACCATALYST"] as? Bool) == false
try expect(targetConfig1.buildSettings["SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD"] as? Bool) == false
try expect(targetConfig1.buildSettings["SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD"] as? Bool) == true
try expect(targetConfig1.buildSettings["LD_RUNPATH_SEARCH_PATHS"] as? [String]) == ["$(inherited)", "@executable_path/Frameworks"]
try expect(targetConfig1.buildSettings["SDKROOT"] as? String) == "auto"
try expect(targetConfig1.buildSettings["ASSETCATALOG_COMPILER_APPICON_NAME"] as? String) == "AppIcon"
try expect(targetConfig1.buildSettings["CODE_SIGN_IDENTITY"] as? String) == "iPhone Developer"
}
$0.it("supportedPlaforms merges settings - tvOS, macOS") {
let target = Target(name: "Target", type: .application, platform: .auto, supportedDestinations: [.tvOS, .macOS])
let project = Project(name: "", targets: [target])
let pbxProject = try project.generatePbxProj()
let targetConfig1 = try unwrap(pbxProject.nativeTargets.first?.buildConfigurationList?.buildConfigurations.first)
try expect(targetConfig1.buildSettings["SUPPORTED_PLATFORMS"] as? String) == "appletvos appletvsimulator macosx"
try expect(targetConfig1.buildSettings["TARGETED_DEVICE_FAMILY"] as? String) == "3"
try expect(targetConfig1.buildSettings["SUPPORTS_MACCATALYST"] as? Bool) == false
try expect(targetConfig1.buildSettings["SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD"] as? Bool) == false
try expect(targetConfig1.buildSettings["LD_RUNPATH_SEARCH_PATHS"] as? [String]) == ["$(inherited)", "@executable_path/Frameworks"]
try expect(targetConfig1.buildSettings["SDKROOT"] as? String) == "auto"
try expect(targetConfig1.buildSettings["ASSETCATALOG_COMPILER_APPICON_NAME"] as? String) == "App Icon & Top Shelf Image"
try expect(targetConfig1.buildSettings["ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME"] as? String) == "LaunchImage"
}
$0.it("supportedPlaforms merges settings - visionOS, macOS") {
let target = Target(name: "Target", type: .application, platform: .auto, supportedDestinations: [.visionOS, .macOS])
let project = Project(name: "", targets: [target])
let pbxProject = try project.generatePbxProj()
let targetConfig1 = try unwrap(pbxProject.nativeTargets.first?.buildConfigurationList?.buildConfigurations.first)
try expect(targetConfig1.buildSettings["SUPPORTED_PLATFORMS"] as? String) == "xros xrsimulator macosx"
try expect(targetConfig1.buildSettings["TARGETED_DEVICE_FAMILY"] as? String) == "7"
try expect(targetConfig1.buildSettings["SUPPORTS_MACCATALYST"] as? Bool) == false
try expect(targetConfig1.buildSettings["SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD"] as? Bool) == false
try expect(targetConfig1.buildSettings["SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD"] as? Bool) == false
try expect(targetConfig1.buildSettings["SDKROOT"] as? String) == "auto"
try expect(targetConfig1.buildSettings["ASSETCATALOG_COMPILER_APPICON_NAME"] as? String) == "AppIcon"
}
$0.it("supportedPlaforms merges settings - iOS, macCatalyst") {
let target = Target(name: "Target", type: .application, platform: .auto, supportedDestinations: [.iOS, .macCatalyst])
let project = Project(name: "", targets: [target])
let pbxProject = try project.generatePbxProj()
let targetConfig1 = try unwrap(pbxProject.nativeTargets.first?.buildConfigurationList?.buildConfigurations.first)
try expect(targetConfig1.buildSettings["SUPPORTED_PLATFORMS"] as? String) == "iphoneos iphonesimulator"
try expect(targetConfig1.buildSettings["TARGETED_DEVICE_FAMILY"] as? String) == "1,2"
try expect(targetConfig1.buildSettings["SUPPORTS_MACCATALYST"] as? Bool) == true
try expect(targetConfig1.buildSettings["SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD"] as? Bool) == false
try expect(targetConfig1.buildSettings["SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD"] as? Bool) == true
try expect(targetConfig1.buildSettings["LD_RUNPATH_SEARCH_PATHS"] as? [String]) == ["$(inherited)", "@executable_path/Frameworks"]
try expect(targetConfig1.buildSettings["SDKROOT"] as? String) == "auto"
try expect(targetConfig1.buildSettings["ASSETCATALOG_COMPILER_APPICON_NAME"] as? String) == "AppIcon"
try expect(targetConfig1.buildSettings["CODE_SIGN_IDENTITY"] as? String) == "iPhone Developer"
}
$0.it("generates dependencies") {
let pbxProject = try project.generatePbxProj()
@ -1066,7 +1215,108 @@ class ProjectGeneratorTests: XCTestCase {
try expect(app2OtherLinkerSettings.contains("-ObjC")) == false
try expect(app3OtherLinkerSettings.contains("-ObjC")) == true
}
$0.it("filter sources with inferDestinationFiltersByPath") {
let sourceFiles = TargetSource(path: "App_supportedDestinations/TestResources", inferDestinationFiltersByPath: true)
let target = Target(
name: "test",
type: .application,
platform: .auto,
sources: [sourceFiles],
dependencies: []
)
let project = Project(
basePath: fixturePath + "TestProject",
name: "test",
targets: [target]
)
let pbxProject = try project.generatePbxProj()
let buildFiles = pbxProject.buildFiles
try expect(buildFiles.count) == 8
for buildFile in buildFiles {
let name = buildFile.file?.nameOrPath
if buildFile.platformFilters == [SupportedDestination.iOS.string] &&
(name == "File_ios.swift" || name == "File_A.swift") {
continue
} else if buildFile.platformFilters == [SupportedDestination.tvOS.string] &&
(name == "File_tvOs.swift" || name == "File_B.swift") {
continue
} else if buildFile.platformFilters == [SupportedDestination.macOS.string] &&
(name == "File_macOS.swift" || name == "File_C.swift") {
continue
} else if buildFile.platformFilters == [SupportedDestination.macCatalyst.string] &&
(name == "File_MACCATALYST.swift" || name == "File_D.swift") {
continue
}
throw failure("Unexpected source file / destinationFilters")
}
}
$0.it("filter sources with destinationFilters") {
let sourceFile1 = TargetSource(path: "App_supportedDestinations/TestResources/iOs",
destinationFilters: [.iOS])
let sourceFile2 = TargetSource(path: "App_supportedDestinations/TestResources/TVOS",
destinationFilters: [.tvOS])
let sourceFile3 = TargetSource(path: "App_supportedDestinations/TestResources/macos",
destinationFilters: [.macOS, .macCatalyst])
let sourceFile4 = TargetSource(path: "App_supportedDestinations/TestResources/macCatalyst",
destinationFilters: [.macOS, .macCatalyst])
let sourceFile5 = TargetSource(path: "App_supportedDestinations/TestResources/File_ios.swift",
destinationFilters: [.iOS])
let sourceFile6 = TargetSource(path: "App_supportedDestinations/TestResources/File_tvOs.swift",
destinationFilters: [.tvOS])
let sourceFile7 = TargetSource(path: "App_supportedDestinations/TestResources/File_macOS.swift",
destinationFilters: [.macOS, .macCatalyst])
let sourceFile8 = TargetSource(path: "App_supportedDestinations/TestResources/File_MACCATALYST.swift",
destinationFilters: [.macOS, .macCatalyst])
let target = Target(
name: "test",
type: .application,
platform: .auto,
sources: [sourceFile1, sourceFile2, sourceFile3, sourceFile4, sourceFile5, sourceFile6, sourceFile7, sourceFile8],
dependencies: []
)
let project = Project(
basePath: fixturePath + "TestProject",
name: "test",
targets: [target]
)
let pbxProject = try project.generatePbxProj()
let buildFiles = pbxProject.buildFiles
try expect(buildFiles.count) == 8
for buildFile in buildFiles {
let name = buildFile.file?.nameOrPath
if buildFile.platformFilters == [SupportedDestination.iOS.string] &&
(name == "File_ios.swift" || name == "File_A.swift") {
continue
} else if buildFile.platformFilters == [SupportedDestination.tvOS.string] &&
(name == "File_tvOs.swift" || name == "File_B.swift") {
continue
} else if buildFile.platformFilters == [SupportedDestination.macOS.string, SupportedDestination.macCatalyst.string] &&
(name == "File_C.swift" || name == "File_D.swift") {
continue
} else if buildFile.platformFilters == [SupportedDestination.macOS.string, SupportedDestination.macCatalyst.string] &&
(name == "File_macOS.swift" || name == "File_MACCATALYST.swift") {
continue
}
throw failure("Unexpected source file / destinationFilters")
}
}
$0.it("copies Swift Objective-C Interface Header") {
let swiftStaticLibraryWithHeader = Target(
name: "swiftStaticLibraryWithHeader",
@ -1645,7 +1895,197 @@ class ProjectGeneratorTests: XCTestCase {
}
}
}
func testGenerateXcodeProjectWithPlatformFilteredDependencies() throws {
describe("generateXcodeProject with destinationFilters") {
func generateProjectForApp(withDependencies: [Dependency], targets: [Target], packages: [String: SwiftPackage] = [:]) throws -> PBXProj {
let app = Target(
name: "App",
type: .application,
platform: .iOS,
dependencies: withDependencies
)
let project = Project(
name: "test",
targets: targets + [app],
packages: packages
)
return try project.generatePbxProj()
}
func expectLinkedDependecies(_ expectedLinkedFiles: [String: [String]], in project: PBXProj) throws {
let buildPhases = project.buildPhases
let frameworkPhases = project.frameworksBuildPhases.filter { buildPhases.contains($0) }
var linkedFiles: [String: [String]] = [:]
for link in frameworkPhases[0].files ?? [] {
if let name = link.file?.nameOrPath ?? link.product?.productName {
linkedFiles[name] = link.platformFilters
}
}
try expect(linkedFiles) == expectedLinkedFiles
}
func expectCopiedBundles(_ expectedCopiedBundleFiles: [String: [String]], in project: PBXProj) throws {
let buildPhases = project.buildPhases
let copyBundlesPhase = project.copyFilesBuildPhases.filter { buildPhases.contains($0) }
var copiedFiles: [String: [String]] = [:]
for copy in copyBundlesPhase[0].files ?? [] {
if let name = copy.file?.nameOrPath {
copiedFiles[name] = copy.platformFilters
}
}
try expect(copiedFiles) == expectedCopiedBundleFiles
}
$0.it("target dependencies") {
let frameworkA = Target(
name: "frameworkA",
type: .framework,
platform: .iOS
)
let frameworkB = Target(
name: "frameworkB",
type: .framework,
platform: .iOS
)
let expectedLinkedFiles = [
"frameworkA.framework": [SupportedDestination.iOS.string],
"frameworkB.framework": [SupportedDestination.iOS.string, SupportedDestination.tvOS.string]
]
// given
let dependencies = [
Dependency(type: .target, reference: frameworkA.name, destinationFilters: [.iOS]),
Dependency(type: .target, reference: frameworkB.name, destinationFilters: [.iOS, .tvOS]),
]
// when
let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [frameworkA, frameworkB])
// then ensure that everything is linked
try expectLinkedDependecies(expectedLinkedFiles, in: pbxProject)
}
$0.it("framework dependencies") {
let expectedLinkedFiles = [
"frameworkA.framework": [SupportedDestination.iOS.string],
"frameworkB.framework": [SupportedDestination.iOS.string, SupportedDestination.tvOS.string]
]
// given
let dependencies = [
Dependency(type: .framework, reference: "frameworkA.framework", destinationFilters: [.iOS]),
Dependency(type: .framework, reference: "frameworkB.framework", destinationFilters: [.iOS, .tvOS]),
]
// when
let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [])
// then ensure that everything is linked
try expectLinkedDependecies(expectedLinkedFiles, in: pbxProject)
}
$0.it("carthage dependencies") {
let expectedLinkedFiles = [
"frameworkA.framework": [SupportedDestination.iOS.string],
"frameworkB.framework": [SupportedDestination.iOS.string, SupportedDestination.tvOS.string]
]
// given
let dependencies = [
Dependency(type: .carthage(findFrameworks: false, linkType: .dynamic), reference: "frameworkA.framework", destinationFilters: [.iOS]),
Dependency(type: .carthage(findFrameworks: false, linkType: .dynamic), reference: "frameworkB.framework", destinationFilters: [.iOS, .tvOS]),
]
// when
let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [])
// then ensure that everything is linked
try expectLinkedDependecies(expectedLinkedFiles, in: pbxProject)
}
$0.it("sdk dependencies") {
let expectedLinkedFiles = [
"sdkA.framework": [SupportedDestination.iOS.string],
"sdkB.framework": [SupportedDestination.iOS.string, SupportedDestination.tvOS.string]
]
// given
let dependencies = [
Dependency(type: .sdk(root: nil), reference: "sdkA.framework", destinationFilters: [.iOS]),
Dependency(type: .sdk(root: nil), reference: "sdkB.framework", destinationFilters: [.iOS, .tvOS]),
]
// when
let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [])
// then ensure that everything is linked
try expectLinkedDependecies(expectedLinkedFiles, in: pbxProject)
}
$0.it("package dependencies") {
let packages: [String: SwiftPackage] = [
"RxSwift": .remote(url: "https://github.com/ReactiveX/RxSwift", versionRequirement: .upToNextMajorVersion("5.1.1")),
]
let expectedLinkedFiles = [
"RxSwift": [SupportedDestination.iOS.string],
"RxCocoa": [SupportedDestination.iOS.string, SupportedDestination.tvOS.string]
]
// given
let dependencies = [
Dependency(type: .package(products: ["RxSwift"]), reference: "RxSwift", destinationFilters: [.iOS]),
Dependency(type: .package(products: ["RxCocoa"]), reference: "RxSwift", destinationFilters: [.iOS, .tvOS]),
]
// when
let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [], packages: packages)
// then ensure that everything is linked
try expectLinkedDependecies(expectedLinkedFiles, in: pbxProject)
}
$0.it("bundle dependencies") {
let expectedCopiedBundleFiles = [
"bundleA.bundle": [SupportedDestination.iOS.string],
"bundleB.bundle": [SupportedDestination.iOS.string, SupportedDestination.tvOS.string]
]
// given
let dependencies = [
Dependency(type: .bundle, reference: "bundleA.bundle", destinationFilters: [.iOS]),
Dependency(type: .bundle, reference: "bundleB.bundle", destinationFilters: [.iOS, .tvOS]),
]
// when
let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [])
// then ensure that everything is linked
try expectCopiedBundles(expectedCopiedBundleFiles, in: pbxProject)
}
}
}
func testGenerateXcodeProjectWithCustomDependencyDestinations() throws {
describe("generateXcodeProject") {