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:
- You can generate only 1 Info.plist, declared in
.target()
- 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 
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) 
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’ 
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
. 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 