Can I auto-increment the CFBundleVersion value in the Info.plist file using Visual Studio? - ios

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.

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.

Related

Xamarin & Multiple Filepicker

i'm building a project on Xamarin. Right now i have a big issue. I need to browse user's computer for upload any file. He can of course upload multiple files. As i know Xamarin does not provide browsing of all the system but just its. So i tried to find a way with some drag n drop, i didn't find. I tried a filepicker but he let me pick just one file (my client would upload 100 files at once) so it doesn't fit to what i need. Finally i decided to do my own browsing system but it takes forever to browse because of the UI. Do you have any solution for me ? I would appreciate a package with a filepicker that allow multiple files.
Thanks
Have you tried the class FileOpenPicker in UWP ?
It supports to pick multiple files , check the method FileOpenPicker.PickMultipleFilesAsync.
Sample
Define interface in Forms project
public interface MyFilePicker
{
Task OpenFilePickerAsync();
}
Implement in UWP project
[assembly: Dependency(typeof(UWPFilePicker))]
namespace App24.UWP
{
class UWPFilePicker : MyFilePicker
{
public async Task OpenFilePickerAsync()
{
var openPicker = new FileOpenPicker();
openPicker.ViewMode = PickerViewMode.Thumbnail;
openPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
openPicker.FileTypeFilter.Add(".jpg");
openPicker.FileTypeFilter.Add(".jpeg");
openPicker.FileTypeFilter.Add(".png");
IReadOnlyList<StorageFile> files = await openPicker.PickMultipleFilesAsync();
if (files.Count > 0)
{
StringBuilder output = new StringBuilder("Picked files:\n");
// Application now has read/write access to the picked file(s)
}
else
{
return;
}
}
}
}
Call it in Forms project
private async void Button_Clicked(object sender, EventArgs e)
{
MyFilePicker service = DependencyService.Get<MyFilePicker>();
await service.OpenFilePickerAsync();
}

GLib, is there a reliable way to test icons existence ? (Vala)

Typically, I’m gathering info of steam games, which does create game icons if user asked it. So there might be icons for games available like steam_icon_1524 but not for sure.
How could I test if an icon is available ?
This answer may not hold for steam games, but you said it's not specific to steam, so shrug.
Basically, you just need to call GLib.AppInfo.get_icon. It will return null if there is no icon.
To enumerate installed applications you use GLib.AppInfo.get_all ().
Under the hood, what is happening is that the *.desktop files stored in the applications/ subdirectory of $XDG_DATA_DIRS (fallback if not set: "/usr/local/share/:/usr/share/") and $XDG_HOME_DATA_DIR (fallback if not set: "~/.local/share/") are parsed (see Desktop Entry Specification for details on the file format), and the "Icon" key is used determine the icon name.
Technically, this doesn't quite tell you whether or not the icon actually exists with the current icon theme, only if it is supposed to exist. That's where the Icon Theme Specification comes in. There are several implementations, but since you're using Vala I'll assume you're using GTK+…
You can use Gtk.IconTheme.get_default to get the theme, then Gtk.IconTheme.lookup_by_gicon to get the Gtk.IconInfo (or null if it wasn't found).
Putting it all together, here is a quick program to list all the installed applications and their icons:
private static void main (string[] args) {
Gtk.init (ref args);
unowned Gtk.IconTheme theme = Gtk.IconTheme.get_default ();
foreach (unowned GLib.AppInfo appinfo in GLib.AppInfo.get_all ()) {
GLib.Icon? icon = appinfo.get_icon ();
if (icon != null && icon is GLib.ThemedIcon) {
GLib.message ("%s: %s", appinfo.get_display_name (), icon.to_string ());
Gtk.IconInfo? iconinfo = theme.lookup_by_gicon (icon, 48, 0);
if (iconinfo != null) {
GLib.message (iconinfo.get_filename ());
} else {
GLib.message ("No icon.");
}
}
}
}

How to update breeze.js library

I could use some guidance on how to manually update between versions of Breeze and it's dependencies. I do not believe I can just update with the NuGet Package Manager.
I have been developing my Single Page App with Breeze, Knockout, WebAPI and so forth. I have been using Breeze version 0.61 and want to upgrade to the latest version so I can take advantage of the ODataActionFilters and not have to parse the Request.QueryString to pull out parameters and filters. For example when I call
var getMachineById(machineId) {
var query = EntityQuery
.from("Machines")
.where("machineId", "eq", machineId);
return manager.executeQuery(query)
.then(function (data) {
do_something_with(data.results);
})
.fail(queryFailed);
}
There has to be a way for Breeze to handle that for me, so I can just do something like this:
[AcceptVerbs("GET")]
public IQueryable<Machine> Machines()
{
return _contextProvider.Context.Machines;
}
instead of
// eg "?$filter=machineId%20eq%205"
[AcceptVerbs("GET")]
public IQueryable<Machine> Machines()
{
IQueryable<Machine> x = _contextProvider.Context.Machines;
List<ODataQueryString> list = null;
string qs = Request.RequestUri.Query.ToString(CultureInfo.CurrentCulture);
list = new ODataQueryStringParser(qs).Parse();
if (list != null)
{
int machineId = int.Parse(list[0].Value); // covert string to an int
x = x.Where(o => o.MachineId == machineId);
}
return x;
}
I notice that the Attribute decoration in the Controller has changed in the Samples. Do I need to change mine too?
namespace PilotPlantBreeze.Controllers
{
[JsonFormatter, ODataActionFilter]
public class BreezeController : ApiController
{
readonly EFContextProvider<PilotPlantDbContext> _contextProvider =
new EFContextProvider<PilotPlantDbContext>();
[AcceptVerbs("GET")]
public string Metadata()
{
return _contextProvider.Metadata();
}
[AcceptVerbs("POST")]
public SaveResult SaveChanges(JObject saveBundle)
{
return _contextProvider.SaveChanges(saveBundle);
}
... etc.
}
}
I have my 3rd party libraries in a folder ~\Scripts\lib. If I use the NuGet package manager to update, it puts all the replacements in ~\Scripts. How do I move the files into the lib folder without messing up my Team Foundation Server (Azure) source control?
Are the runtime versions of Antlr3.Runtime.dll, Breeze.WebApi.dll, Irony.dll, Newtonsoft.Json.dll, WebActivator.dll and maybe WebGrease.dll compatible between versions. I bet not. Is there something I have to change in Visual Studio?
Can I just change version entries from the package folder in packages.config?
Thanks.
I think that your best approach would be to remove any existing breeze '.js' files and the webApi and irony.dlls from your project and then simply install the latest breeze nuget package. After installing the nuget package you can go ahead and move the files to other locations within the project to match their "old" locations. I'd do the same for the NewtonSoft nuget package as well, just in case ( breeze will add this back for you). Going forward, you can just update to latest nuget and then move the files.
As you noticed you will also need to replace these attributes
[JsonFormatter, ODataActionFilter]
with this attribute
[BreezeController]
This assumes that you are not running a beta version of ASP.MVC4. I think there are posts in other forums that discuss how to migrate away from the beta.

How to find process template applied to a project using tfs Api

i have seen may links on this but none of them works fine.In all cases either templateid comes -1 or processtemplate comes null.
private ProjectProperty[] GetProcessTemplateDetailsForTheSelectedProject(string prjname)
{
var vcs = _tfs.GetService<VersionControlServer>();
var ics = _tfs.GetService<ICommonStructureService>();
ProjectProperty[] ProjectProperties = null;
var p = vcs.GetTeamProject(prjname);
string ProjectName = string.Empty;
string ProjectState = String.Empty;
int templateId = 0;
ProjectProperties = null;
ics.GetProjectProperties(p.ArtifactUri.AbsoluteUri, out ProjectName, out ProjectState, out templateId, out ProjectProperties);
IProcessTemplates processTemplates = (IProcessTemplates)_tfs.GetService(typeof(IProcessTemplates));
XmlNode node = processTemplates.GetTemplateNames();
return ProjectProperties;
}
Please help in this regard.I want to find out the process template applied to a project programitcally.
You can only get the Process Template name, if the process template was given a name before it was uploaded to TFS.
As you can see in the image, there is a property here "Process Template" which has a value i.e. name of the template. If you look for a property that does not exist, the api will return you -1, this is what you are running in to with the code above.
The code in the blog link here will help you get the process template name, again, only if, you gave the process template a name before uploading it. http://geekswithblogs.net/TarunArora/archive/2011/11/15/tfs-api-process-template-currently-applied-to-the-team-project.aspx
Please note, all process templates in TFS 2012 have been named by default. This wasn't the case for TFS 2010.
Hope this answers your question.

How to update a custom TFS field programmatically

We have a custom build process (not using MS Build) and during that process I am adding a "fake" build to the global builds list. The reason I am doing that is so that you can select the build for a given work item (found in build). We have a custom field, build included, which is intended to show which build that work item was fixed in. I am having trouble figuring out how to update this field programmatically. The idea is I will have a small app that does this that I will call during the build process, finding all work items since the last build, then updating the field for those work items. Any ideas?
Something like this should work for you:
public void UpdateTFSValue(string tfsServerUrl, string fieldToUpdate,
string valueToUpdateTo, int workItemID)
{
// Connect to the TFS Server
TfsTeamProjectCollection tfs = new TfsTeamProjectCollection(new Uri(tfsUri));
// Connect to the store of work items.
_store = (WorkItemStore)tfs.GetService(typeof(WorkItemStore));
// Grab the work item we want to update
WorkItem workItem = _store.GetWorkItem(workItemId);
// Open it up for editing. (Sometimes PartialOpen() works too and takes less time.)
workItem.Open();
// Update the field.
workItem.Fields[fieldToUpdate] = valueToUpdateTo;
// Save your changes. If there is a constraint on the field and your value does not
// meet it then this save will fail. (Throw an exception.) I leave that to you to
// deal with as you see fit.
workItem.Save();
}
An example of calling this would be:
UpdateTFSValue("http://tfs2010dev:8080/tfs", "Integration Build", "Build Name", 1234);
The variable fieldToUpdate should be the name of the field, not the refname (ie. Integration Build, not Microsoft.VSTS.Build.IntegrationBuild)
You could probably get away with using PartialOpen(), but I am not sure.
You will probably need to add Microsoft.TeamFoundation.Client to your project. (And maybe Microsoft.TeamFoundation.Common)
This has changed for TFS 2012, basicly you have to add workItem.Fields[fieldToUpdate].Value
Updated Version of what #Vaccano wrote.
public void UpdateTFSValue(string tfsServerUrl, string fieldToUpdate,
string valueToUpdateTo, int workItemID)
{
// Connect to the TFS Server
TfsTeamProjectCollection tfs = new TfsTeamProjectCollection(new Uri(tfsUri));
// Connect to the store of work items.
_store = (WorkItemStore)tfs.GetService(typeof(WorkItemStore));
// Grab the work item we want to update
WorkItem workItem = _store.GetWorkItem(workItemId);
// Open it up for editing. (Sometimes PartialOpen() works too and takes less time.)
workItem.Open();
// Update the field.
workItem.Fields[fieldToUpdate].Value = valueToUpdateTo;
// Save your changes. If there is a constraint on the field and your value does not
// meet it then this save will fail. (Throw an exception.) I leave that to you to
// deal with as you see fit.
workItem.Save();
}

Resources