What tools support editing project.pbxproj files?
Asked Answered
C

4

23

I want to edit project.pbxproj straight up using command line (for CI server script)

what tools can allow me to do this?

I used to use PlistBuddy to edit the output Info.plist; however, what i really want to do is to edit this user defined field, which is used in multiple places, and i really don't want to have to hunt that down in every plist location

Cordiform answered 21/8, 2015 at 6:16 Comment(8)
Hi. Can you explain for what you want to edit a .pbxproj file?Aramaic
a User defined fieldCordiform
You can try a *.xcconfig files, plus a user defined fields may redefine from cmd line. This is a build step from teamcity CI: xcodebuild -project 'MyApp.xcodeproj' -scheme 'MyAppStaticTeamcity' -configuration 'Debug' -sdk 'iphoneos' GCC_PREPROCESSOR_DEFINITIONS='$GCC_PREPROCESSOR_DEFINITIONS NS_BLOCK_ASSERTIONS=1 MY_LOG_LEVEL=2' clean buildAramaic
@Mozilla actually, i didn't realize you can pass in preprocessor flags. would this work directly if i just want to pass in one random field, say something like "USER_DEFINED_VARIABLE=${hi}" ??Cordiform
Maybe you went the wrong way. If you have a frequently changing parameter, it is easier to carry out it in * .plist as a setting. Replacement a value in the * .plist much easier than editing the project file or pass the user defined field (see plistbuddy). In my project I have the setting server_address which placed in the *.plist.Aramaic
Ah i see i see. that makes sense. thanks!! would you like to write your thoughts as an answer so that i can upvote and accet?Cordiform
project.pbxproj is a plist file, too. So you can use /usr/libexec/PlistBuddy to edit it.Suzan
@SergeMaslyakov Yes, you are right.xcconfig is a much better way than project.pbxproj file. And perl is good at editing xcconfig file.Suzan
S
22

project.pbxproj is an old-style ASCII property list file, too. So you can use /usr/libexec/PlistBuddy to edit it.

Print some User-Defined key's value like this,

# Get the key A83311AA20DA4A80004B8C0E in your project.pbxproj
# LZD_NOTIFICATION_SERVICE_BUNDLE_ID is defined by me,
# Replace key paths with your own.
/usr/libexec/PlistBuddy -c 'print :objects:A83311AA20DA4A80004B8C0E:buildSettings:LZD_NOTIFICATION_SERVICE_BUNDLE_ID' LAAppAdapter.xcodeproj/project.pbxproj

Set its value like this,

/usr/libexec/PlistBuddy -c 'set :objects:A83311AA20DA4A80004B8C0E:buildSettings:LZD_NOTIFICATION_SERVICE_BUNDLE_ID com.dawnsong.notification-service' LAAppAdapter.xcodeproj/project.pbxproj

UPDATE
PlistBuddy will automatically convert project.pbxproj into a xml-format plist file since macOS Catalina (or some earlier version). It's better to move the setting item into xcconfig file instead since xcconfig is much smaller and simpler than project.pbxproj and not easy to make mistakes when editing with perl script.

Suzan answered 17/5, 2019 at 3:59 Comment(7)
Thanks. Very helpful. This is the right answer IMO.Gile
printing values with project.pbxproj seems to work fine, but when I use the set command the whole project.pbxproj gets converted to an XML .plist file and can no longer be read by Xcode.Latrell
You just made my day sir !Bailey
Same error for me, have you found a workaround @Mr.Zystem?Bailey
@Bailey I think PlistBuddy's -x option becomes default. It's strange.Suzan
Apple Doc said, "Because you cannot save a property list in the old-style (OpenStep) format, the only valid format parameters for this method are NSPropertyListXMLFormat_v1_0 and NSPropertyListBinaryFormat_v1_0". I think it explains why and what happensSuzan
@Bailey found no solution, so I stopped trying to mess around with the .pbxproj file.Latrell
R
20

I know this has been answered for a while, but since the original question is about tools supporting the manipulation of .pbxproj files, and many other people may be looking for the same information, here's how I do it. It took me quite a while to figure this out because I was very unfamiliar with Xcode when I started attempting this, so I hope this saves others the hours of grief I had to put in.

You can use the plutil command to transform the .pbxproj file from the legacy .plist format into an XML or JSON format you will be able to manipulate more easily. I'm using JSON. To do so, just run:

plutil -convert json project.pbxproj

This will convert the format of project.pbxproj, but be aware that -contrary to common sense- the output won't be another file with a JSON extention such as project.json. What will happen is that project.pbxproj will be converted to JSON format, but retain it's cryptic .pbxproj extension. So even though the file's format has been changed, Xcode will still pick it up and use it in its new JSON format.

Then you can change project.pbxproj with ease using any JSON manipulation tool of your choosing. I'm using Groovy's JsonSlurper class in a Groovy script.

Note I also explored the XML option, but I found the project.pbxproj file in XML format to be cumbersome to parse. The elements are not properly nested to allow for traversing the tree with ease. It's plagued with:

<key>someKey</key>
<dict>
    <!--More elements which provide configuration for the key above-->
</dict>

So it's positional in nature. You have to look for the key element corresponding to the setting you want to manipulate and then jump to the dict element just after it. Which means you have to mount the children of each XML element into an array, in order to index them.

Rebutter answered 25/10, 2017 at 10:2 Comment(6)
Nice explanation. Thanks for sharing this. It saves lots of time for me and may saves time for many other.Nombril
No problem @TheiOSDev, would you mind voting up my answer then? Thanks in advance.Rebutter
Convering the pbxproj file to JSON did not work for me. Xcode 10 says the file is corrupted and won't open after it's converted.Demagogy
@PaulSlocum have you tried upgrading the plutil command? It's possible that you're working with an old plutil version that's not capable of handling the .pbxproj format produced by Xcode 10+ I use Brew. I just tried brew outdated and got this libplist (1.12) < 2.0.0_1. I looked it up and found it here. Sadly, there's no -v or --version option in plutil but brew upgrade libplist will either get you the latest version or say Error: libplist 2.0.0_1 already installed.Rebutter
Wow I had no idea that Xcode could support this. Thanks!Sarilda
xcode add file convert project.pbxproj back to legacy formatRickart
F
10

Here are 3 open-source tools which implement .pbxproj file editing:

Personally, I made the best experience with the NodeJS based tool. So far it has covered all our needs reliably.

In the following is listed an example javascript file update-project.js which sets the developer team ID, app entitlements, adds a GoogleService-Info.plist file to the project and checks it as part of the build target. Take it as an inspiration and adapt the scripts and its paths to your needs:

const fs = require('fs')
const xcode = require('xcode')

if (process.argv.length !== 3) {
  console.error("Please pass the development team ID as the first argument")
  process.exit(1)
}
const developmentTeamId = process.argv[2]
const path = 'ios/App/App.xcodeproj/project.pbxproj'
const project = xcode.project(path)

project.parse(error => {
  const targetKey = project.findTargetKey('App')
  const appGroupKey = project.findPBXGroupKey({path: 'App'})
  project.addBuildProperty('CODE_SIGN_ENTITLEMENTS', 'App/App.entitlements')
  project.addBuildProperty('DEVELOPMENT_TEAM', developmentTeamId)
  project.addFile('App.entitlements', appGroupKey)
  project.removeFile('GoogleService-Info.plist', appGroupKey)
  const f = project.addFile('GoogleService-Info.plist', appGroupKey, {target: targetKey})
  f.uuid = project.generateUuid()
  project.addToPbxBuildFileSection(f)
  project.addToPbxResourcesBuildPhase(f)
  fs.writeFileSync(path, project.writeSync())
})

Above script can be executed with

yarn run update-project <arguments...>

given that update-project is registered in package.json:

{
  ...,
  "scripts": {
    ...
    "update-project": "node update-project.js"
  },
  ...
}
Fireboard answered 15/9, 2020 at 15:6 Comment(1)
thats amazing, thank you! the simplest way to workaround installing some SDKs including firebase and setting GoogleService-Info.plist in place.Dreary
L
0

The correct and straight to the point answer is a mix of Downsong and Mig82 ones.

First check your project.pbxproj file and get the different keys to modify in my example AABBCCDDEEFF.

Then use PlistBuddy to set the value you want. Below, the provisioning profile with value :

/usr/libexec/PlistBuddy -c 'set :objects:AABBCCDDEEFF:buildSettings:PROVISIONING_PROFILE_SPECIFIER value' myproject.xcodeproj/project.pbxproj

This will modify the pbxproj file and transform it in a XML format.

The transform it back to the original (json) format use the command provided by Mig82 :

plutil -convert json myproject.xcodeproj/project.pbxproj
Lachish answered 25/7 at 11:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.