Multiple Info.Plist generation

Question or problem

How to generate multiple Info.plist files for the same Target?

Expectation

Sometimes you want to have 2 different Info.plist files in the same target.
There’re various reasons, one of them is e. g. to distinct Staging Info.plist from Production Info.plist via Build Configuration.

In Xcode you can achieve that by changing INFOPLIST_FILE build setting (and you can also modify it via Tuist).
The problem is with Info.plist file generation:

  1. You can generate only 1 Info.plist, declared in .target()
  2. You still should point to some Info.plist when defining a project. If you won’t, it’ll use a ProjectDescription.InfoPlist value (and generate that).

What I tried

There’s a workaround/hack in Xcode - you can define some InfoPlist key-value pairs in Project Settings, via INFOPLIST_KEY_NAMEOFYOURKEY setting name. But seems I can’t assign a dictionary type here, only String and Array are acceptable.

Context

  • Tuist version: 4.40.0
  • Xcode 16

Reproduction (mandatory for problems)

Not sure if it’s a missing feature or a bug, but hope definition of problem is enough.

Expectation

I’d expect a syntax like this maybe:

.target(
  name: "MyTarget",
  infoPlist: .multiple([...])
)

Same is applicable for Entitlements (afaik we can generate only one), and maybe something more

Hey @denisgaskov :wave:

Have you tried this suggestion by @kas?

It does mean you couldn’t generate an Info.plist file by Tuist and it would need to be present on disk, but that should hopefully not affect you too much.

1 Like

Hey @marekfort!
Didn’t see this exact message, but I came up with nearly same idea myself (it’s in initial message, but without nil for generated plist).

The biggest disadvantage is that - yes, I can’t reuse lots of duplicated Info.plist content (common parts between configurations) :frowning_face:

Particularly, I want to keep my custom URL schemes separated for Staging and Production. Maybe I’m thinking in wrong direction, and the solution is not about having 2 Info.plists.

P.S

Obviously, it’s not possible to achieve that reusability with vanilla Xcode Projects too, so it’s not a ‘downgrade’ :v:

I was thinking maybe we can create some kind of additional optional generationOptions for Project/Target, which can emit such files (Info.plist, Entitlements.plist from the top of my head, maybe something else)

P.P.S

Technically I can even now write some Swift code which will write Info.plist into the disk using FileManager (I already have similar stuff in code):

  targets: [
    .target(
      name: {
        RunOnce.run()
        return consts.appName
      }()

enum RunOnce {
  static fun run() {
    // to be able to use Swift Concurrency & actor isolation
    MainActor.assumeIsolated {
      // some icon generation stuff via SwiftUI view rendering
    }
  }
}

which will solve the issue. But ideally I’d like to have something built-into-Tuist, type-safe, easy-to-use. Just sharing an idea, maybe someone find this useful.

There is a PR that tries to tackle this at the Project.swift level: Support Info.plist for configurations by lieuvu · Pull Request #6499 · tuist/tuist · GitHub

Technically I can even now write some Swift code which will write Info.plist into the disk using FileManager (I already have similar stuff in code):

Doing file-based operations in the Project.swift is highly discouraged as it easily breaks caching of these manifests.

1 Like

Thanks for noticing :+1:. Apart from manifests caching, what it can break? I noticed this behaviour, but in my workflow I’m ok with that.

There’s another partial workaround for the initial problem though: create a build setting called e. g. “INFOPLIST_URL_SCHEME” and use it as an environment variable in InfoPlist definition:

let appPlist: [String: Plist.Value] = [
  "CFBundleURLTypes": .array([
    .dictionary([
      "CFBundleTypeRole": .string("Editor"),
      "CFBundleURLSchemes": .array(["${INFOPLIST_URL_SCHEME}"])
    ])
  ])
]

It works till you have same number of array elements in all configurations, but still kind of error prone

1 Like

Binary caching or selective testing would have issues if you use this approach, too. Additionally, you’re making tuist generate slower. We strongly advise against doing file operations in Project.swift manifests, but in the end, it’s your choice – just be aware of the consequences :grin: