Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Add file to XCode Copy Bundle Resources Phase via Script

Discussion in 'iOS and tvOS' started by ocnenued, Jun 16, 2020.

  1. ocnenued

    ocnenued

    Joined:
    Apr 4, 2018
    Posts:
    26
    Is there a way to add files to "Copy Bundle Resources" phase in XCode from unity via scripting API in a Postprocess script?

    We're trying to add Tapjoy's resources bundle to make it compatible with 2019.3.x, and we couldn't find a way to copy it from another destination into the Xcode.

    Screen Shot 2020-06-16 at 11.31.48 AM.png
     
    Last edited: Jun 16, 2020
    SweatyChair likes this.
  2. andymads

    andymads

    Joined:
    Jun 16, 2011
    Posts:
    1,614
    Not a solution but it appears the API has methods to add and insert a "Copy Bundle Resources" phase but I can't see how you would actually add any files to that phase.
     
  3. ocnenued

    ocnenued

    Joined:
    Apr 4, 2018
    Posts:
    26
    Unfortunately, that method only adds a new "phase", not a file to an existing phase.

     
  4. andymads

    andymads

    Joined:
    Jun 16, 2011
    Posts:
    1,614
    Yes, that's what I was inferring.
     
    ocnenued likes this.
  5. tonemcbride

    tonemcbride

    Joined:
    Sep 7, 2010
    Posts:
    1,077
    There is a way to do it (I did it to add the storyboard launch files to the 'copy bundle resources' for a cloud build). Unfortunately the way I did it is really horrible and not really something I would recommend.

    If you're willing to pollute your codebase with this abomination then here's how to do it:

    1) Add the files you want to include in your xCode project to the 'StreamingAssets' folder. In my example I added 2 files:

    Assets\StreamingAssets\AA_Gradient.png
    Assets\StreamingAssets\AA_Logo.png

    This has the effect of telling Unity to copy the files into your xCode project and makes them accessible via:

    Data/Raw/AA_Gradient.png
    Data/Raw/AA_Logo.png

    2) Setup a [PostProcessBuild] script to run when the build is complete

    3) In the post process script retrieve the xCode project file, convert it to a string

    4) Do a search and replace on the string and inject references to your new files in the correct place to add them to the 'copy bundle resources' part (make up some random GUIDs for the files to keep xCode happy).

    Here's a code example that shows the xCode project getting retrieved as a string, injecting the references and then writing it back out again:

    Code (CSharp):
    1. [PostProcessBuild]
    2. public static void ChangeXcodePlist(BuildTarget buildTarget, string pathToBuiltProject)
    3. {
    4.     // xCode workspace modifications
    5.     {
    6.         // Open the xCode Project
    7.         string projectPath = pathToBuiltProject + "/Unity-iPhone.xcodeproj/project.pbxproj";
    8.         PBXProject project = new PBXProject();
    9.         project.ReadFromFile( projectPath );
    10.         string targetGuid = project.GetUnityMainTargetGuid();
    11.        
    12.         // Retrieve the xCode project as a string
    13.         string projectString = project.WriteToString();
    14.        
    15.         // Add Launch screen storyboard references
    16.         {
    17.             // Add images and image references
    18.             projectString = projectString.Replace(    "/* Begin PBXBuildFile section */",
    19.                                                                                             "/* Begin PBXBuildFile section */\n\t\tEA8C3480243F64CC002A3835 /* AA_Gradient.png in Resources */ = {isa = PBXBuildFile; fileRef = EA8C347E243F64CC002A3835 /* AA_Gradient.png */; };" +
    20.                                                                                                                                                             "\n\t\tEA8C3481243F64CC002A3835 /* AA_Logo.png in Resources */ = {isa = PBXBuildFile; fileRef = EA8C347F243F64CC002A3835 /* AA_Logo.png */; };" );
    21.  
    22.             projectString = projectString.Replace(    "/* Begin PBXFileReference section */",
    23.                                                                                             "/* Begin PBXFileReference section */\n\t\tEA8C347E243F64CC002A3835 /* AA_Gradient.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = AA_Gradient.png; path = Data/Raw/AA_Gradient.png; sourceTree = \"<group>\"; };" +
    24.                                                                                                                                                                     "\n\t\tEA8C347F243F64CC002A3835 /* AA_Logo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = AA_Logo.png; path = Data/Raw/AA_Logo.png; sourceTree = \"<group>\"; };" );
    25.  
    26.             // Add images/refs to the 'Copy Bundle Resources' section
    27.             projectString = projectString.Replace(    "/* CustomTemplate */ = {\n            isa = PBXGroup;\n            children = (",
    28.                                                                                             "/* CustomTemplate */ = {\n            isa = PBXGroup;\n            children = (\n                EA8C347E243F64CC002A3835 /* AA_Gradient.png */,\n                EA8C347F243F64CC002A3835 /* AA_Logo.png */,");
    29.                
    30.                
    31.             projectString = projectString.Replace(    "/* LaunchScreen.storyboard in Resources */,",
    32.                                                                                             "/* LaunchScreen.storyboard in Resources */,\n\t\t\t\tEA8C3480243F64CC002A3835 /* AA_Gradient.png in Resources */," +
    33.                                                                                                                                                                                  "\n\t\t\t\tEA8C3481243F64CC002A3835 /* AA_Logo.png in Resources */," );
    34.         }
    35.  
    36.         // Save the xCode project file
    37.         File.WriteAllText( projectPath , projectString );
    38.     }
    39. }
    40.  
     
  6. ocnenued

    ocnenued

    Joined:
    Apr 4, 2018
    Posts:
    26
    Haha, this really is a crazy way of getting it done, but hats off at the same time! Thanks for sharing @tonemcbride
     
  7. YTFGames

    YTFGames

    Joined:
    Nov 27, 2018
    Posts:
    6
    I've worked on this for days. And finally get it done. These are my codes. Hope that they can help you.
    Tested under Unity 2019.4.1
    check My V2 codes out from Here https://forum.unity.com/threads/add...sources-phase-via-script.913187/#post-6050723


    Code (CSharp):
    1.  
    2. --------------------------Don't User these V1 Codes anymore-------------------
    3. using UnityEngine;
    4. using UnityEditor;
    5. using System.IO;
    6. using UnityEditor.Callbacks;
    7. using UnityEditor.iOS.Xcode;
    8. using System.Collections.Generic;
    9.  
    10. public class XcodeProjectMod : MonoBehaviour
    11. {
    12.    internal static void CopyAndReplaceDirectory(string srcPath, string dstPath)
    13.    {
    14.        if (Directory.Exists(dstPath))
    15.            Directory.Delete(dstPath);
    16.        if (File.Exists(dstPath))
    17.            File.Delete(dstPath);
    18.  
    19.        Directory.CreateDirectory(dstPath);
    20.  
    21.        foreach (var file in Directory.GetFiles(srcPath))
    22.            File.Copy(file, Path.Combine(dstPath, Path.GetFileName(file)));
    23.  
    24.        foreach (var dir in Directory.GetDirectories(srcPath))
    25.            CopyAndReplaceDirectory(dir, Path.Combine(dstPath, Path.GetFileName(dir)));
    26.    }
    27.  
    28.    [PostProcessBuild]
    29.    public static void OnPostprocessBuild(BuildTarget buildTarget, string path)
    30.    {
    31.        if (buildTarget == BuildTarget.iOS)
    32.        {
    33.            string projPath = PBXProject.GetPBXProjectPath(path);
    34.            PBXProject pBXProject = new PBXProject();
    35.  
    36.            pBXProject.ReadFromString(File.ReadAllText(projPath));
    37.            string targetGuid = pBXProject.GetUnityMainTargetGuid();
    38.            string projectGuid = pBXProject.ProjectGuid();
    39.  
    40. //copy bundle resources start from here
    41.            CopyAndReplaceDirectory("Assets/Plugins/iOS/resources", Path.Combine(path, "iOS/resources"));
    42.            List<string> resources = new List<string>();
    43.            GetDirs("Assets/Plugins/iOS/resources", ref resources);
    44.            foreach (string resource in resources)
    45.            {
    46.                Debug.Log(resource);
    47.                string resourcesBuildPhase = pBXProject.GetResourcesBuildPhaseByTarget(targetGuid);
    48.                string resourcesFilesGuid = pBXProject.AddFolderReference(resource, resource, PBXSourceTree.Source);
    49.                pBXProject.AddFileToBuildSection(targetGuid, resourcesBuildPhase, resourcesFilesGuid);
    50.            }
    51.  
    52.  
    53.            File.WriteAllText(projPath, pBXProject.WriteToString());
    54.  
    55.        }
    56.    }
    57.  
    58.    public static void GetDirs(string dirPath, ref List<string> dirs)
    59.    {
    60.        foreach (string path in Directory.GetFiles(dirPath))
    61.        {
    62.            if (path.IndexOf(".") != 0 && System.IO.Path.GetExtension(path) != ".meta")
    63.            {
    64.                dirs.Add(path.Substring(path.IndexOf("iOS")));
    65.                // dirs.Add(path);
    66.            }
    67.        }
    68.  
    69.        if (Directory.GetDirectories(dirPath).Length > 0)
    70.        {
    71.            foreach (string path in Directory.GetDirectories(dirPath))
    72.            {
    73.                GetDirs(path, ref dirs);
    74.            }
    75.        }
    76.  
    77.    }
    78.  
    79. }
     
    Last edited: Sep 25, 2020
    ocnenued and SweatyChair like this.
  8. qmpaden

    qmpaden

    Joined:
    Feb 2, 2019
    Posts:
    9
    Unfortunately after trying these solutions, Xcode keeps giving errors with SplashScreen.mm inside of the Unity project, that error is for some reason negating the fact that the Xcode Project regonizes the storyboard, it just fails the build because Apple updated submission does not allow the splash screen setup like the builds before..

    I have tried changing Build Player Settings to not include splash screen and only enable the link to the LaunchScreen.storyboard file inside of unity asset folder to no avail,

    Is there any way to get unity to not include SplashScreen.mm in the ios build xcode project and just recognize launchscreen.storyboard and bundle resources?

    Maybe I should try just removing that file that Xcode doesnt like "SplashScreen.mm" and try again..
     
  9. YTFGames

    YTFGames

    Joined:
    Nov 27, 2018
    Posts:
    6
    Line 46 of my codes is error.
    It should be:
    Code (CSharp):
    1. string resourcesFilesGuid = pBXProject.AddFile(resource, resource, PBXSourceTree.Source);
    Sorry about that.
     
  10. YTFGames

    YTFGames

    Joined:
    Nov 27, 2018
    Posts:
    6
    This is my Version 2 code. I think it's more usable than V1.
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3. using System.IO;
    4. using UnityEditor.Callbacks;
    5. using UnityEditor.iOS.Xcode;
    6. using System.Collections.Generic;
    7. using System.Linq;
    8.  
    9. public class XcodeProjectMod : MonoBehaviour
    10. {
    11.  
    12.     static string[] resourceExts = { ".png", ".jpg", ".jpeg", ".storyboard" };
    13.  
    14.     internal static void CopyAndReplaceDirectory(string srcPath, string dstPath, string[] enableExts)
    15.     {
    16.         if (Directory.Exists(dstPath))
    17.             Directory.Delete(dstPath);
    18.         if (File.Exists(dstPath))
    19.             File.Delete(dstPath);
    20.  
    21.         Directory.CreateDirectory(dstPath);
    22.  
    23.         foreach (var file in Directory.GetFiles(srcPath))
    24.         {
    25.             if (enableExts.Contains(System.IO.Path.GetExtension(file)))
    26.             {
    27.                 File.Copy(file, Path.Combine(dstPath, Path.GetFileName(file)));
    28.             }
    29.         }
    30.  
    31.         foreach (var dir in Directory.GetDirectories(srcPath))
    32.         {
    33.             CopyAndReplaceDirectory(dir, Path.Combine(dstPath, Path.GetFileName(dir)), enableExts);
    34.         }
    35.     }
    36.  
    37.     public static void GetDirFileList(string dirPath, ref List<string> dirs, string[] enableExts, string subPathFrom="")
    38.     {
    39.         foreach (string path in Directory.GetFiles(dirPath))
    40.         {
    41.             if (enableExts.Contains(System.IO.Path.GetExtension(path)))
    42.             {
    43.                 if(subPathFrom != ""){
    44.                     dirs.Add(path.Substring(path.IndexOf(subPathFrom)));
    45.                 }else{
    46.                     dirs.Add(path);
    47.                 }
    48.             }
    49.         }
    50.  
    51.         if (Directory.GetDirectories(dirPath).Length > 0)
    52.         {
    53.             foreach (string path in Directory.GetDirectories(dirPath))
    54.             {
    55.                 GetDirFileList(path, ref dirs, enableExts, subPathFrom);
    56.             }
    57.         }
    58.  
    59.     }
    60.  
    61.     [PostProcessBuild]
    62.     public static void OnPostprocessBuild(BuildTarget buildTarget, string path)
    63.     {
    64.         if (buildTarget == BuildTarget.iOS)
    65.         {
    66.             string projPath = PBXProject.GetPBXProjectPath(path);
    67.             PBXProject pBXProject = new PBXProject();
    68.  
    69.             pBXProject.ReadFromString(File.ReadAllText(projPath));
    70.             string targetGuid = pBXProject.GetUnityMainTargetGuid();
    71.             string projectGuid = pBXProject.ProjectGuid();
    72.  
    73.             pBXProject.AddBuildProperty(projectGuid, "OTHER_LDFLAGS", "-ObjC -all_load -licucore");
    74.             pBXProject.SetBuildProperty(projectGuid, "ENABLE_BITCODE", "NO");
    75.             List<string> resources = new List<string>();
    76.             CopyAndReplaceDirectory("Assets/Plugins/iOS/resources", Path.Combine(path, "resources"), resourceExts);
    77.             GetDirFileList("Assets/Plugins/iOS/resources", ref resources, resourceExts, "resources");
    78.             foreach (string resource in resources)
    79.             {
    80.                 Debug.Log(resource);
    81.                 string resourcesBuildPhase = pBXProject.GetResourcesBuildPhaseByTarget(targetGuid);
    82.                 string resourcesFilesGuid = pBXProject.AddFile(resource, resource, PBXSourceTree.Source);
    83.                 pBXProject.AddFileToBuildSection(targetGuid, resourcesBuildPhase, resourcesFilesGuid);
    84.             }
    85.  
    86.             File.WriteAllText(projPath, pBXProject.WriteToString());
    87.  
    88.         }
    89.     }
    90.  
    91. }
     
  11. NSWell

    NSWell

    Joined:
    Jul 30, 2017
    Posts:
    89
    Code (CSharp):
    1.  tmp_PBXProject.AddFileToBuild(tmp_FrameworkTarget, tmp_DataGuid);
    Try this code. It's work for me!