Search Unity

How can you add items to the Xcode project targets Info.plist using the XcodeAPI?

Discussion in 'Unity Build Automation' started by Simurr, Jun 3, 2015.

  1. Simurr

    Simurr

    Joined:
    Jan 2, 2012
    Posts:
    9
    Is this possible with the XcodeAPI?

    I need to add an API key and some boolean values to the generated xcode projects Info.plist. I don't see any examples on how to do that.

    Perhaps it's outside the scope of the XcodeAPI? Any suggestions on how to do this using C# in a post build process function?

    sim
     
    hammondt likes this.
  2. David-Berger

    David-Berger

    Unity Technologies

    Joined:
    Jul 16, 2014
    Posts:
    745
    It's not Unity Cloud Build Specific, but as I wanted to make a tutorial already for a while, I'll post it here. This is useful when you want to edit the Info.plist of an Xcode project built by Unity when building for iOS. This can be used for example to change the values of Version (CFBundleShortVersionString) and Build (CFBundleVersion) in an Xcode project from within Unity, as a build post-process.

    Step-by-step guide
    1. Create/Have a Unity project
    2. Create a folder inside Assets called Editor
    3. Download the Xcode Manipulation API files from https://bitbucket.org/Unity-Technologies/xcodeapi/src/ and place them inside a sub-folder inside Assets > Editor such as Assets > Editor > xcodeapi
    4. You can now create a script such as the one below to alter CFBundleVersion in Xcode plist
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3. using UnityEditor.Callbacks;
    4. using System.Collections;
    5. using UnityEditor.iOS.Xcode;
    6. using System.IO;
    7.  
    8. public class ChangeIOSBuildNumber {
    9.  
    10.     [PostProcessBuild]
    11.     public static void ChangeXcodePlist(BuildTarget buildTarget, string pathToBuiltProject) {
    12.  
    13.         if (buildTarget == BuildTarget.iOS) {
    14.        
    15.             // Get plist
    16.             string plistPath = pathToBuiltProject + "/Info.plist";
    17.             PlistDocument plist = new PlistDocument();
    18.             plist.ReadFromString(File.ReadAllText(plistPath));
    19.        
    20.             // Get root
    21.             PlistElementDict rootDict = plist.root;
    22.        
    23.             // Change value of CFBundleVersion in Xcode plist
    24.             var buildKey = "CFBundleVersion";
    25.             rootDict.SetString(buildKey,"2.3.4");
    26.        
    27.             // Write to file
    28.             File.WriteAllText(plistPath, plist.WriteToString());
    29.         }
    30.     }
    31. }
    32.  
    Which sets the CFBundleVersion in your Info.plist in a post process step to "2.3.4" (works also in Unity Cloud Build in all Tiers).

    I hope this makes sense and helps. Please let me know how it goes! :)
     
  3. Simurr

    Simurr

    Joined:
    Jan 2, 2012
    Posts:
    9
    Thank you! That worked perfectly.

    We are using it for background location and fetch stuff in a native plugin.

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3. using UnityEditor.Callbacks;
    4. using System.Collections;
    5. using UnityEditor.iOS.Xcode;
    6. using System.IO;
    7.  
    8. public class MyPluginPostProcessBuild
    9. {
    10.     [PostProcessBuild]
    11.     public static void ChangeXcodePlist(BuildTarget buildTarget, string pathToBuiltProject)
    12.     {
    13.         if ( buildTarget == BuildTarget.iOS )
    14.         {
    15.             // Get plist
    16.             string plistPath = pathToBuiltProject + "/Info.plist";
    17.             PlistDocument plist = new PlistDocument();
    18.             plist.ReadFromString(File.ReadAllText(plistPath));
    19.            
    20.             // Get root
    21.             PlistElementDict rootDict = plist.root;
    22.            
    23.             // background location useage key (new in iOS 8)
    24.             rootDict.SetString("NSLocationAlwaysUsageDescription", "Uses background location");
    25.            
    26.             // background modes
    27.             PlistElementArray bgModes = rootDict.CreateArray("UIBackgroundModes");
    28.             bgModes.AddString("location");
    29.             bgModes.AddString("fetch");
    30.            
    31.             // Write to file
    32.             File.WriteAllText(plistPath, plist.WriteToString());
    33.         }
    34.     }
    35. }
    Need some clarification.
    Do I need to download the XcodeAPI and include it in my project if we are already using Unity 5 (5.0.2p3)? It already seems to be included, though I'm guessing it's not the newest version.

    Would this work with 4.6.5? Would that require including the XcodeAPI like you instructed?

    Am I correct in assuming this would also work with Unity Cloud Build?
     
    jgmakes likes this.
  4. David-Berger

    David-Berger

    Unity Technologies

    Joined:
    Jul 16, 2014
    Posts:
    745
    Yes, there is also a XCode API for 4.6 and 5. If you use source code of the repro you can alter the framework if necessary and it can be optimised. Yes, it also works with Unity Cloud Build. :cool:
     
    DsevenZ and liortal like this.
  5. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,562
    Thanks, i used the sample code shown above and was able to modify the Info.plist to add what i wanted.
    The major issue was only understanding what's the name of the element i need to be added :)

    In my book, a good API is one that can be used straight away without reading through docs, etc.... (naming conventions are good and it's hard to find your way around).

    So, good work guys, Thanks !!!! :)
     
    David-Berger likes this.
  6. joonturbo

    joonturbo

    Joined:
    Jun 24, 2013
    Posts:
    77
    Found the answer a bit confusing until I realized I didn't need to actually download the API in Unity version 5.1.1
     
  7. David-Berger

    David-Berger

    Unity Technologies

    Joined:
    Jul 16, 2014
    Posts:
    745
    Yes, the guide was made for 4.6, in 5.x you don't need to add the XCode API into your project.
     
    Emmanuel_Charon likes this.
  8. joonturbo

    joonturbo

    Joined:
    Jun 24, 2013
    Posts:
    77
    it works great.
    I already made a hack, that I set the version to 1.0.0#1, and then parse it in the post-build-processor.

    Is there a way to make cloudbuild auto-increment everytime it builds?
     
  9. David-Berger

    David-Berger

    Unity Technologies

    Joined:
    Jul 16, 2014
    Posts:
    745
    Sweet!
    You can use the Build Manifest to determine the build number! I added a sample about the Build Manifest here. :)
     
  10. urfx

    urfx

    Joined:
    Dec 12, 2013
    Posts:
    12
    hey David, some useful posts again. many thanks (you answered my iOS question too!)...

    can I ask two things.

    1. is there a similar routine for Android's manifest.xml ?
    2. Alternatively, is there anyway to just include hard coded plist or manifest files for iOS and Android respectively ?...

    I'm extending both to include a custom url scheme e.g. mygamename://
     
  11. David-Berger

    David-Berger

    Unity Technologies

    Joined:
    Jul 16, 2014
    Posts:
    745
    1. But if you need something more fancy, you will have to write some code in a post process script. The xml format is easy to modify, compared to the xcodeproj, so you don’t really need a “manipulation api" if that makes sense?
    2. You can have multiple manifests in your project and Unity merges them I think. That's probably not possible on iOS, but there you have the XCode manipulation API :)
     
  12. urfx

    urfx

    Joined:
    Dec 12, 2013
    Posts:
    12
    thanks

    just to confirm...

    so for android we just add a manifest to the Assets/Plugins/Android folder

    and RTM for Xcode manipulation API ! ;)
     
  13. peterworth

    peterworth

    Joined:
    Mar 4, 2014
    Posts:
    7
    I'm getting lots of errors in PBXProject.cs, like:

    Error CS0234: The type or namespace name `CombinePaths' does not exist in the namespace `UnityEditor.Utils'. Are you missing an assembly reference? (CS0234) (Assembly-CSharp-Editor)

    In Unity 4.6.8p3

    Any ideas?
     
  14. chrs1885

    chrs1885

    Joined:
    Jun 4, 2015
    Posts:
    4
    Hi,

    I'm trying to add URL Schemes to my Info.plist.

    While adding frameworks is working just fine, accessing the root dictionary of my plist doesn't seem to work as expected:

    Code (CSharp):
    1. // Add url schema to plist file
    2. string plistPath = path + "/Info.plist";
    3. PlistDocument plist = new PlistDocument();
    4. plist.ReadFromString(File.ReadAllText(plistPath));
    5.            
    6. // Get root
    7. PlistElementDict rootDict = plist.root;
    When I step debug the code, Mono Develop shows me

    FYI, the plistPath is correct.

    Am I doing anything wrong?
     
  15. David-Berger

    David-Berger

    Unity Technologies

    Joined:
    Jul 16, 2014
    Posts:
    745
    @urfx yes
    @peterworth make sure you update the XCode Manipulation API to the correct version for 4.6
    @chrs1885 What's the plistPath pointing to? I assume path is the path handed over from the post process method?
     
  16. soloneer

    soloneer

    Joined:
    Nov 23, 2010
    Posts:
    62
    Which is the correct version for 4.6?
     
  17. David-Berger

    David-Berger

    Unity Technologies

    Joined:
    Jul 16, 2014
    Posts:
    745
  18. soloneer

    soloneer

    Joined:
    Nov 23, 2010
    Posts:
    62
    That's the version (latest commit) that gives me errors in Unity 4.6. Same ones that @peterworth mentioned: Namespace CombinePaths does not exist in namespace UnityEditor.Utils, etc. That's why I thought when you mentioned right version you meant a commit version, I just assumed the latest was only written for Unity 5.
     
  19. David-Berger

    David-Berger

    Unity Technologies

    Joined:
    Jul 16, 2014
    Posts:
    745
    The latest commit should work properly. Did you add the source directly into Unity? That might be broken and might not work without modification I fear, best is to build the dll separately and include the resulting dll into the project. I'll forward it to the devs, maybe they can fix it for the next release of the public API.

    In the meantime if you need precompiled dlls with a working Unity Cloud Build project check out the examples in the UCB Demos for Unity 4 and Unity 5. It's the Library Demo project which includes the dlls in the Editor folder of the subfolder project.
     
  20. chrs1885

    chrs1885

    Joined:
    Jun 4, 2015
    Posts:
    4
    Yes! :)
     
  21. David-Berger

    David-Berger

    Unity Technologies

    Joined:
    Jul 16, 2014
    Posts:
    745
    @chrs1885 could you print the final plistPath? Something is wrong, I know that adding urlschemes is working properly from other projects. :confused:
     
  22. arTk_ev

    arTk_ev

    Joined:
    Mar 24, 2013
    Posts:
    11
    Try insert "using Utils = PBX.Utils;"
     
    bluemike likes this.
  23. zero_null

    zero_null

    Joined:
    Mar 11, 2014
    Posts:
    159
    how to get the path of Classes/UI/UnityViewControllerBase.mm
     
  24. yakandco

    yakandco

    Joined:
    Dec 3, 2014
    Posts:
    90
    I totally missed that this had been added.. awesome work guys. So great to have a tidy compile process again and not have to worry about manually editing a plist. The whole API looks so well written, thanks!!

    Are their some docs somewhere or more examples? I'd love to know how to add in a folder reference of assets to the xcode project - something I need to manually do every build, and to also add frame works. Thanks again!! =)
     
  25. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,562
  26. yakandco

    yakandco

    Joined:
    Dec 3, 2014
    Posts:
    90
    Thanks! =)
     
  27. mopsicus

    mopsicus

    Joined:
    Feb 15, 2016
    Posts:
    13
    How can I add embedded framework via XCode api?
     
  28. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,562
    Is this a framework that you added to your project (e.g: plugin), or something that is build-in and should already exist in Xcode ?
     
  29. mopsicus

    mopsicus

    Joined:
    Feb 15, 2016
    Posts:
    13
    I want to add not build-in framework
     
  30. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,562
    And did you try to add it using the XcodeAPI (like the examples linked above) ?
     
  31. mopsicus

    mopsicus

    Joined:
    Feb 15, 2016
    Posts:
    13
  32. somed0nkus

    somed0nkus

    Joined:
    Apr 2, 2014
    Posts:
    2
    Hey, so far this is great example working with making new array elements! Works for me.

    Could it be possible to get an example of how to create a new boolean in the info.plist?
    For example, "Application supports iTunes file sharing" and set it to "YES".

    I try to write the script, and I can't find a way to ".CreateBool", so I guess it some kind of conversion from a Dict/Array, but for me all combinations I try with .AsBoolean result in error.

    Here is my code fail based on the original example:

    Code (CSharp):
    1. PlistElementBoolean iTunesFileSupport = rootDict.CreateArray("Application supports iTunes file sharing");
    2.             iTunesFileSupport.AsBoolean(true);
    What am I doing wrong?
    Help/Tips/Examples appreciated!
     
    Last edited: Jul 18, 2016
  33. VsevolodPiletski

    VsevolodPiletski

    Joined:
    Jul 18, 2016
    Posts:
    1
    I don't know if this post is correct for my question, but i'll write it anyway, just correct me if i'm wrong.

    I'm interested in adding new target to XCode project. Can I do it with code, adn if yes, how can I?
     
    alejobrainz likes this.
  34. alejobrainz

    alejobrainz

    Joined:
    Sep 10, 2005
    Posts:
    288
    We are also very interested in automatically adding targets, especially App Extensions (Sticker Packs, iMessage Extensions and Notification Content Extensions). With the release of iOS 10 App extensions have become a very important part to the user interaction and experience with the apps.

    There is an open thread at:
    http://forum.unity3d.com/threads/ho...sions-to-unity-ios-build.287183/#post-2793951

    But there is no response from UT yet. @David-Berger, do you know if integrating App extensions is feasible via Xcode API and Cloud Build?
     
  35. l3aronsansgland

    l3aronsansgland

    Joined:
    Oct 1, 2016
    Posts:
    1
    I am following all the described steps and I have the following error in Unity:
    PXPorject.cs [...]: The type or namespace 'CombinePaths' does not exist in the namespace 'UnityEditor.Utils'

    Am I missing something?
     
    Noor-Ali likes this.
  36. Noor-Ali

    Noor-Ali

    Joined:
    Feb 26, 2015
    Posts:
    9
    @peterworth

    Somehow, Utils are giving errors. Double click on errors and replace Utils with PBX.Utils
     
  37. jaydipsinh

    jaydipsinh

    Joined:
    Mar 31, 2017
    Posts:
    2
    hi david sir.
    please guide me for make printing functionality in IOS and Android device
     
  38. jason_yak

    jason_yak

    Joined:
    Aug 25, 2016
    Posts:
    531
  39. jason_yak

    jason_yak

    Joined:
    Aug 25, 2016
    Posts:
    531
  40. Cromfeli

    Cromfeli

    Joined:
    Oct 30, 2014
    Posts:
    202
    Is it possible to use the same for OSX?

    https://docs.unity3d.com/ScriptReference/iOS.Xcode.PBXProject.html
     
  41. philmuse

    philmuse

    Joined:
    Dec 29, 2017
    Posts:
    3
    Update for people seeing this article going forward.

    At some point in Unity 5.x, Unity included the code directly in the iOS support install, so no need to download the extra code referenced above. However, doing that there is one requirement not explicitly stated in the docs. Your C# class file should be placed under /Assets/Editor folder. That was the only way to get Visual Studio Mac to recognize the UnityEditor.iOS.Xcode namespace and ultimately for the code to compile.

    Great example from David-Berger, very helpful.
     
  42. Fattie

    Fattie

    Joined:
    Jul 5, 2012
    Posts:
    476
  43. philmuse

    philmuse

    Joined:
    Dec 29, 2017
    Posts:
    3
  44. Fattie

    Fattie

    Joined:
    Jul 5, 2012
    Posts:
    476
    Cromfeli likes this.
  45. bgulanowski

    bgulanowski

    Joined:
    Feb 9, 2018
    Posts:
    36
    This process does not seem to work for standalone Mac builds.

    I installed the iOS build support, to no avail. There is nothing of use in the documentation that explicitly mentions Mac support.

    I would like to have access to editing the Info.plist for my Mac build.

    Which dll/assembly contains the specific classes? I cannot find a dll named "UnityEditor.iOS.Xcode", but there is one called "UnityEditor.Extensions.iOS.Xcode". I'm not sure if it's safe to assume that namespaces and dll names usually match or not. I would assume that they don't have to, but usually would.
     
  46. affinixy

    affinixy

    Joined:
    Nov 2, 2017
    Posts:
    13
    r137 likes this.
  47. wowcrazyguy

    wowcrazyguy

    Joined:
    Sep 24, 2018
    Posts:
    49
  48. AlexNanomonx

    AlexNanomonx

    Joined:
    Jan 4, 2017
    Posts:
    41
    @bgulanowski Did you manage to get it working on macOS? If so how?
     
  49. skoteskote

    skoteskote

    Joined:
    Feb 15, 2017
    Posts:
    87
    I'm trying to add a string array to plist for Bonjour Services. It writes it to the plist, but I still get an error in Xcode while building. Deleting the plist entry for Bonjour Services and adding the exact same information in Xcode resolves the issue.

    Code (CSharp):
    1. rootDict.SetString("Privacy - Local Network Usage Description", "For shared AR");
    2. PlistElementArray b = rootDict.CreateArray("Bonjour services");
    3. b.AddString("_myCollabSession._tcp");
    4. b.AddString("_myCollabSession._udp");
    5. File.WriteAllText(plistPath, plist.WriteToString());
    Off these, both are written to the plist, and the first entry (Privacy..) throws no errors while building, while Bonjour services does. Anyone has an idea of what I'm doing wrong?
     
    Last edited: Oct 24, 2020
  50. tonemcbride

    tonemcbride

    Joined:
    Sep 7, 2010
    Posts:
    1,089
    Perhaps it's just the syntax you're using? It looks correct but the Unity examples do it slightly differently, like this:

    Code (CSharp):
    1. var ser = (rootDict["Bonjour services"] = new PlistElementArray()) as PlistElementArray;
    2. ser.values.Add(new PlistElementString("_myCollabSession._tcp"));
    3. ser.values.Add(new PlistElementString("_myCollabSession._udp"));
    Also, I think the array name has to be "NSBonjourServices" rather than "Bonjour services" which is what you had. (see pic)
     

    Attached Files:

    stefanob and skoteskote like this.