4

I've seen solutions to doing this with Xcode and even Xamarin Studio, but nothing with Visual Studio.

Ideally, I'd like for every single build of the project to auto-increment the CFBundleVersion value within the Info.plist file.

<key>CFBundleVersion</key>
<string>9</string>

I don't even know where to start and haven't been able to find an article / blog post / tutorial on anything that includes Visual Studio.

Is this possible?

Just wanted to add that I am using Visual Studio 2015 on Windows 8.1.

bdrelling
  • 770
  • 1
  • 9
  • 24
  • See the following SO posts: [here][1] [here][2] [1]: http://stackoverflow.com/questions/826777/how-to-have-an-auto-incrementing-version-number-visual-studio [2]: http://stackoverflow.com/questions/356543/can-i-automatically-increment-the-file-build-version-when-using-visual-studio – rholmes Aug 12 '15 at 19:13
  • Oh may not be relevant to iOS - what's your VS configuration and setup? – rholmes Aug 12 '15 at 19:14
  • @rholmes Yeah, saw those posts but they aren't directly compatible, unfortunately. Sorry I forgot to include the setup--edited my post. VS 2015 and Windows 8.1. Thanks! – bdrelling Aug 14 '15 at 18:48

1 Answers1

0

Being in the same boat as you, as in not finding a proper solution, I decided to create my own. Maybe better late than never! :)

In my case I used the very useful Automatic Versions Settings tool (available on NuGet) to automatically update my assembly info, but wanted that to also update the Info.plist information as that's what HockeyApp uses to track and notify of new releases.

In the end, I kludged together a minimal C# program, which reads AssemblyInfo.cs, grabs the version info from there and edits the Info.plist XML file and writes it back.

It'd be a 20 line program if I hadn't put in a lot of paranoid checks, so as not to risk mangling Info.plist irretrievably (and even then it creates a backup of that file).

The "magic" comes down to two methods, the first one which I found here on SO:

Read AssemblyInfo.cs:

private static string GetVersionFromAssemblyInfo(string infile)
{
    var version = String.Empty;

    var readText = File.ReadAllLines(infile);
    var versionInfoLines = readText.Where(t => t.Contains("[assembly: AssemblyVersion"));
    foreach (var item in versionInfoLines)
    {
        version = item.Substring(item.IndexOf('(') + 2, item.LastIndexOf(')') - item.IndexOf('(') - 3);
    }
    return version;
}

Edit Info.plist, where the first 3 elements of the assembly info tuple becomes the CFBundleShortVersionString and the last element becomes CFBundleVersion which HockeyApp uses for build number.

The wonkiness in the LINQ is due to the slight weirdness of Apple's way of presenting the key/value pairs in that file:

 private static bool SetVersionInInfoPlist(string infoplistFile, string version, string number)
    {
        var xelements = XDocument.Load(infoplistFile);
        var dict = from el in xelements.Root?.Elements() select el;
        // ReSharper disable once ConditionIsAlwaysTrueOrFalse
        if (dict == null) return false;

        var cfshortversion =
            from el in dict.Descendants("key")
            where el.Value == "CFBundleShortVersionString"
            select el.ElementsAfterSelf().FirstOrDefault();
        ;
        // ReSharper disable once ConditionIsAlwaysTrueOrFalse
        if (cfshortversion == null) return false;
        cfshortversion.FirstOrDefault()?.SetValue(version);

        var cfversion =
            from el in dict.Descendants("key")
            where el.Value == "CFBundleVersion"
            select el.ElementsAfterSelf().FirstOrDefault();

        // ReSharper disable once ConditionIsAlwaysTrueOrFalse
        if (cfversion == null) return false;
        cfversion.FirstOrDefault()?.SetValue(number);

        // Make backup
        try
        {
            File.Copy(infoplistFile, $"{infoplistFile}-backup", true);
        }
        catch (Exception)
        {
            Console.WriteLine($"Failed to create backup of {infoplistFile}. Will not edit.");
            return false;
        }

        try
        {
            using (StringWriter sw = new StringWriter())
            {
                using (XmlWriter xWrite = XmlWriter.Create(sw))
                {
                    xelements.Save(xWrite);
                }
            }
            xelements.Save(infoplistFile);
        }
        catch (Exception)
        {
            Console.WriteLine($"Failed to save the edited {infoplistFile}.");
            return false;
        }

        Console.WriteLine($"Successfully edited and saved new {infoplistFile}.");
        return true;

    }

EDIT: I should have also added that I use Bamboo for CI and build automation. This program therefore becomes a capability for the remote build agent and then I can add it as a Task in the Bamboo build Plan.

Marakai
  • 934
  • 8
  • 21