Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for multiple deployment targets with xcode 14 #1336

Conversation

amatig
Copy link
Contributor

@amatig amatig commented Mar 16, 2023

Hi, I am Giovanni Amati.
I am new, I have never contributed to this project but because in my company we are using XcodeGen, and we are starting a new app, it was good to have this new feature to support iOS and AppleTV in one single target.

This fixes #1333 , opened by me then I decided to try to solve it.

What I have done:

  • Introduced an optional supportedDestinations array in target to create the right combination of build settings for multi platform destinations.
  • Introduced a new enum supportedDestination (iOS, tvOS, macOS, macCatalyst and visionOS)
  • Introduced auto has platform value that you can use only with supported destinations
  • The definition of the platform for supported destinations is optional, it fallbacks to auto if it is not defined.
  • Introduced an optional destinationFilters array for sources and dependencies to filter things for platforms
  • Introduced an optional inferDestinationFiltersByPath boolean, this is a convenience filter that helps you to filter sources (and resources) if their pathes match these patterns **/<supportedDestination>/* or *_<supportedDestination>.swift
  • if destinationFilters and inferDestinationFiltersByPath are both defined, inferDestinationFiltersByPath is ignored
  • if supportedDestinations is defined and platform is an array, an error is raised because we are using to ways to use multi platforms
  • Added checks to improve the spec validation
  • Added visionOS support
  • Added unit tests
  • Updated docs / changelog

Ex. of spec

targets:
  MyApp:
    type: application
    supportedDestinations: [iOS, tvOS]
    sources:
      - path: ../Project/GLEAP/Sources
        inferDestinationFiltersByPath: true
      - path: ../Project/GLEAP/Storyboards
        destinationFilters: [iOS]
    dependencies:
      - package: Yams
        product: Yams
        destinationFilters: [tvOS]
      - package: Lottie
        product: Lottie

Everything works with every bundle so it's possible to do the same for UI tests, 1 single target and if you have different file you can filter them by platform. It is very useful.

This spec becomes:

Screenshot 2023-03-20 at 13 48 39

Screenshot 2023-07-19 at 17 17 25

Many thanks!

@amatig amatig changed the title Support for multiple deployment targets with xcode 14 [Feature] Support for multiple deployment targets with xcode 14 Mar 16, 2023
@amatig
Copy link
Contributor Author

amatig commented Aug 25, 2023

Hi, I did a couple of things and now I am investigating about "auto" for baseSDK and visionOS.

About "auto" I have a couple of things to ask. At the moment, a lot of logics are based on the platform that is mandatory. If I add a new case auto in the enum we are going to lose presets that we are applying now (Platform and Product_Platform) so the result will be a project a little bite more empty where the user needs to configure more build settings stuff in the spec because we can not help the generation.

Is this an issue? Or isn't it better to leave to he user the decision to define one "baseSDK" so we can give him few presets and then he needs to customised the extra platforms things if he wants a multi-platform solution?

Or both? You can put what ever you want iOS, tvOS or auto (or nothing that fallbacks to auto). The user will lose presets only in auto.

What do you think? @yonaskolb

I have an idea.
Because the destinations have a priority defined in the enum of SupportedDestination, I can assign to the target the platform preset and product_preset of the destination with higher priority. So the user can have something if the platform is "auto" or undefined.

I pushed the changes have a look, if this approach is ok I will add unit tests and add stuff in the doc.

I have also removed "isResolved" as a property of the Target (the new field that I have added), I am adding that information only in the dictionary to help me to check if the spec is correct. For this reason I moved the error of that check in SpecParsingError.

Added visionOS support and fixed doc.

Everything is done, I have also added more unit tests and checks to prevent issue in the spec and help the user.

@amatig amatig requested a review from yonaskolb August 25, 2023 04:26
Copy link
Owner

@yonaskolb yonaskolb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work @amatig

- [ ] **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**.
- [ ] **platformFilters**: **[[Supported Destinations](#supported-destinations)]** - List of supported platform destinations this dependency should filter to. Defaults to all supported destinations.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reading this perhaps platformFilters should be called destinationFilters to match the new name. Also differentiates it from the existing platformFilter. Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah yes, I can do this. This is ok, as input I can change it but when the value arrives to XcodeProj we must use this field name because that project accept that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


/// 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["isResolved"] = true
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this could have a more specific name like isMultiPlatformTarget?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

if supportedDestinations?.contains(.macCatalyst) == true,
supportedDestinations?.contains(.iOS) == false {

supportedDestinations?.append(.iOS)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a little bit too "magic" in this part of the pipeline for my tastes, but happy to let it in. I can tell you're trying to provide a nice user experience and can appreciate that.

Copy link
Contributor Author

@amatig amatig Sep 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you don't like this I could raise an error instead

@@ -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)
- [ ] **platformFilters**: **[[Supported Destinations](#supported-destinations)]** - List of supported platform destinations the files should filter to. Defaults to all supported destinations.
- [ ] **inferPlatformFiltersByPath**: **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 `platformFilters` this flag will be ignored.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment here as below about a possible rename from filters to destinations for these 2 props?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, ok

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

} else if inferPlatformFiltersByPath == true {
for supportedDestination in SupportedDestination.allCases {
let regex1 = try? NSRegularExpression(pattern: "\\/\(supportedDestination)\\/", options: .caseInsensitive)
let regex2 = try? NSRegularExpression(pattern: "\\_\(supportedDestination)\\.swift$", options: .caseInsensitive)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm slightly worried about the performance implications of this, but it's opt in

Copy link
Contributor Author

@amatig amatig Sep 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know but this thing is very helpful, you can use this instead of define folders one by one for each specific filter.

@amatig
Copy link
Contributor Author

amatig commented Sep 25, 2023

Any news?

@FelixLisczyk
Copy link

This feature would significantly reduce the number of framework targets in my project. 👍 When can we merge it?

@amatig
Copy link
Contributor Author

amatig commented Oct 14, 2023

This feature would significantly reduce the number of framework targets in my project. 👍 When can we merge it?

Hope soon, I made the last changes after the review I am waiting for @yonaskolb

Copy link
Owner

@yonaskolb yonaskolb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your patience @amatig, and all your great work here. I think this is good to merge now 👍

@yonaskolb yonaskolb merged commit 97d36fd into yonaskolb:master Oct 31, 2023
2 checks passed
@FelixLisczyk
Copy link

FelixLisczyk commented Nov 9, 2023

@amatig I've noticed that the supportedDestination enum doesn't contain the watchOS platform. Is there any reason for this? I can add it manually in Xcode.

Screenshot 2023-11-09 at 11 51 01

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support for multiple deployment targets with Xcode 14?
4 participants