Development - James Montemagno https://staging.montemagno.com/tag/development/ https://staging.montemagno.com/content/images/2017/08/MonogramIcon_Orange_NoBuffer.png Development - James Montemagno https://staging.montemagno.com/tag/development/ Hugo -- gohugo.io Tue, 13 Oct 2026 23:21:57 GMT 60 Unique Device & App Installation Identifiers for Mobile Apps https://staging.montemagno.com/unique-device-id-for-mobile-apps/ 5c0ec14158056200bf4c259f Fri, 14 Dec 2018 20:00:00 GMT One common query I get all the time from developers is how to get a unique device or application identifier for their mobile app. After deciding to not include an API for this in Xamarin.Essentials we heard a lot of different use cases for it including:

  • Client/Server Communication
  • Login Validation
  • In-App Purchase Verification
  • Licensing Purposes

The next obvious question that you may have is why didn't we include it in Xamarin.Essentials if everyone seemed to want it? The answer is actually pretty straight forward: There is truly no consistent API across every platform to deliver an identifier that is unique to the device or application. On top of that developers all wanted to use the API a bit differently and in some cases make it a core part of processing transactions and that is something we don't want to get in the middle of.

So what can you do if you need this identifier? I would first stop and ask yourself if you can use some sort of User Identifier based on login information as this will ensure uniqueness that you control. If not then there are a few options to go down then trying to implement this.

Platform APIs Available

Each platform offers up something a bit different:

iOS: IdentifierForDevice

This API is pretty interesting as it will give you an alphanumeric string that uniquely identifies a device to the app's vendor. Based on your Bundle ID that you set Apple will return back the same identifier. The issue that I always found is that this can be spoofed by someone else setting a similar Bundle ID, but it is in general a decent API:

public string Id => UIDevice.CurrentDevice.IdentifierForVendor.AsString();

Android: Serial, getSerial & AndroidId

What a mess Android is as there are now three APIs that do similar things and even Google's documentation is all over the place on which to use!

A recommended route would be to attempt to grab the Serial number when available. In the world of Xamarin we will automatically figure out whether to use the static property or the new getSerial() method introduced in Android 9.0 (which requires the READ_PHONE_STATE). The issue here is that this can be modified via rooted devices and sometimes is not available on all hardware. This means that you need to attempt to figure out something else to use, which would be the ANDROID_ID. This is a unique number that is randomly generated when the device is setup, unless you are on API 26+ which is a number that is unique for each combination of app-signing key, user, and device.... As you can see now we have like 4 different identifiers here that all mean different things. If you went this route then your code would look like this:

string id = string.Empty;
public string Id
{
    get
    {
        if (!string.IsNullOrWhiteSpace(id))
            return id;

        id = Android.OS.Build.Serial;
        if(string.IsNullOrWhiteSpace(id) || id == Build.Unknown || id == "0")
        {
            try
            {
                var context = Android.App.Application.Context;
                id = Secure.GetString(context.ContentResolver, Secure.AndroidId);
            }
            catch(Exception ex)
            {
                Android.Util.Log.Warn("DeviceInfo", "Unable to get id: " + ex.ToString());
            }
        }

        return id;
    }
}

Before going down this route think about the implications here of your code and what you are trying to accomplish.

UWP: GetPackageSpecificToken or GetSystemIdForPublisher

We are in a tricky place for UWP as well depending on what we are trying to do. GetSystemIdForPublisher is very similar to what iOS does, but it may not be available in all versions of Windows. The next level is trying to grab a unique hardware identifier for the specific app with GetPackageSpecificToken. So in this case your code may look like:

string id = null;
public string Id
{
    get
    {

        if (id != null)
            return id;

        try
        {
            if (ApiInformation.IsTypePresent("Windows.System.Profile.SystemIdentification"))
            {
                var systemId = SystemIdentification.GetSystemIdForPublisher();

                // Make sure this device can generate the IDs
                if (systemId.Source != SystemIdentificationSource.None)
                {
                    // The Id property has a buffer with the unique ID
                    var hardwareId = systemId.Id;
                    var dataReader = Windows.Storage.Streams.DataReader.FromBuffer(hardwareId);

                    var bytes = new byte[hardwareId.Length];
                    dataReader.ReadBytes(bytes);

                    id = Convert.ToBase64String(bytes);
                }
            }

            if (id == null && ApiInformation.IsTypePresent("Windows.System.Profile.HardwareIdentification"))
            {
                var token = HardwareIdentification.GetPackageSpecificToken(null);
                var hardwareId = token.Id;
                var dataReader = Windows.Storage.Streams.DataReader.FromBuffer(hardwareId);

                var bytes = new byte[hardwareId.Length];
                dataReader.ReadBytes(bytes);

                id = Convert.ToBase64String(bytes);
            }

            if(id == null)
            {
                id = "unsupported";
            }

        }
        catch (Exception)
        {
            id = "unsupported";
        }

        return id;
    }
}

Just like Android you may get different results based on what you are looking for.

A True Cross-Platform Identifier

Now that we have seen each of the platforms and how inconsistent they are, let's talk about a better way of doing this. What most developers are attempting to do is ensure that when an application is installed they can get some unique identifier for that installation. If the app is transfered to a new phone then the identifier is the same, but if it is un-installed then a new identifier is generated. This can easily be accomplished by using Xamarin.Essentials Preferences API to save a Guid when your app starts if it doesn't exist yet. The code is super simple:

var id = Preferences.Get("my_id", string.Empty);
if(string.IsNullOrWhitespace(id))
{
   id = System.Guid.NewGuid().ToString();
   Preferences.Set("my_id", id);
}

This will ensure a unique identifier for the installed application and it is actually the same mechanism that App Center uses for their SDK across each platform! You should of course be aware that this Guid will sync across the user in some instances based on their device and account setup, which is probably what you want to have happen.

There you have it a full break down of each platform with the complexity that each offer. However, my recommendation remains that you should just generate a Guid and go from there :)

]]>
Building Azure DevOps (VSTS) Tasks with TypeScript and VS Code https://staging.montemagno.com/building-vsts-tasks-with-typescript-and-vs-code/ 5a36c138538da90021c4b260 Tue, 30 Jan 2018 17:55:40 GMT Over the last few years I have fallen in love with Azure DevOps (VSTS) for handling all of my application and backend continuous integration, deployment, and release management. Each of these services offer a task based build and release definition that enable you to easily start automating anything. There are a ton of built in tasks, and a whole marketplace where you can install them into your Azure DevOps (VSTS) project. At some point though, you may need to do something custom that isn't a task that has been built. Don't worry, because there is a task called Command Line, which enables you to run any script that you could possibly want. While this is super powerful it also means that these scripts aren't re-usable between applications without copy and pasting them around and don't offer up a friendly user interface since it it just script.This is why Azure DevOps (VSTS) allows anyone to create re-usable tasks that can be shared privately or publicly with the world in the marketplace.

IQsRzovW

Shared Tasks with TypeScript

Wait, why is James talking about TypeScript? We all know that everything JavaScript annoys him and he loves C# with all of his heart and soul. While you can run any custom command in Azure DevOps (VSTS) if you want to write a shareable task that runs on any platform (such as macOS or Linux), then it must be written in JavaScript or TypeScript that is compiled down to JavaScript.

download

Don't run away, it really isn't that bad I promise!!! I tried everything to write tasks in any other language including bash and PowerShell Core, but nope you really need TS/JS for now. Luckily, once you get everything setup inside of VS Code, it is a nice experience. So, let's do it!!!

Development Setup

To get started you will need a Azure DevOps (VSTS) account, VS Code, the latest version of node, the Azure DevOps (VSTS) CLI, and a scaffolded out structure for your code. The Microsoft Documentation on Adding a Task has this entire structure laid out perfectly, so go do that right now! Then come back and we will pick up from this point.

Multi-Task Structure

The documentation above sets you up to build a single task, which is okay, but a unique ability is to create a bunch of tasks into a shareable task extension. So, before we start crushing some code and npm packages, let's structure our code a bit.

Update vss-extension.json

The vss-extension.json lists out all of the tasks that you want bundled into the extension that is compiled. There are two sections, files and contributions, which need to be updated with the multiple tasks and paths that we are creating. You can see what my vss-extension.json looks like with multiple tasks on my GitHub.

Development == Start

Now it is finally time to start development yay! Before we do, we need to insure that we have the TypeScript compiler installed. It is as easy as running:

npm install -g typescript

There is great documentation over on the VS Code site.

Inside of each tak folder we need to create a tsconfig.json file that tells TypeScript what the compiler options should be.

Here is what mine looks like:

{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es6",
        "noImplicitAny": false,
        "outDir": "built",
        "rootDir": ".",
        "sourceMap": true,
        "moduleResolution": "node"
    },
    "exclude": [
        "node_modules"
    ]
}

Again, there is great documentation over on the VS Code website that goes over all of this.

We will create one more file called package.json which will have information about our task and what to point to when finished and what dependencies exist:

{
  "name": "hello world",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "MIT",
  "dependencies": {
    "util": "^0.11.1",
    "azure-pipelines-task-lib": "^2.7.7"
  },
  "devDependencies": {
    "@types/node": "^8.5.1",
    "@types/q": "^1.0.6"
  }
}

Now we can create our actual TypeScript file that will be run by Azure DevOps (VSTS). Here is what the base looks like:

//Grab the Azure DevOps (VSTS) task library helpers
import tl = require('azure-pipelines-task-lib/task');
import trm = require('azure-pipelines-task-lib/toolrunner');
import { isNullOrUndefined } from 'util';

//This is the function that get run
async function run() {
    try {
        let tool: trm.ToolRunner;

        // Grab any variables the user set
        let message: string = tl.getInput("key_from_task");

        if(isNullOrUndefined(message))
        {
            message = "No message defined";
        }

        console.log(message);
    }
    catch (err) {
        tl.setResult(tl.TaskResult.Failed, err.message);
    }
}

run();

Before we try to build and run this task, we must restore our packages. Down in the terminal window of VS Code we can navigate to our Task folder and run npm commands to install the packages we need.

npm install @types/node --save-dev
npm install @types/q --save-dev
npm install azure-pipelines-task-lib --save
npm install util
echo node_modules > .gitignore

NPM-install

Build and Debug!

Now, it is time to compile our TypeScript down into JavaScript and actually debug it.

VS Code has some nifty commands built in for TypeScript. The command palette can be brought up with Ctrl + Shift + P or under View:

Run-Build

Then we can find Tasks: Run Build which will list all of the TypeScript files that can be compiled:

Run-Build-2

This can be brought up also with a keyboard shortcut of Ctrl + Shift + B.

We can select the tsconfig that we want to build and hopefully we get no errors.

This compilation will output some nice JavaScript that will be executed under the hood:

JavaScript

VS Code should have generated a launch.json file at the root of the solution. Here is what mine looks like:

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "type": "node",
            "request": "launch",
            "name": "Launch Program",
            "program": "${workspaceFolder}/tasks\\HelloWorld\\task.ts",
            "outFiles": [
                "${workspaceFolder}/**/*.js"
            ]
        }
    ]
}

We can now set a breakpoint and start to debug our task.

Debugging

The final thing to do is to package up our task and ship it to the marketplace for testing. This can be done with a simple command line at the root of the project to first install tfx-cli and then bundle the package:

npm i -g tfx-cli
tfx extension create --manifest-globs vss-extension.json

Learn More

There you have it from scratch to a fully built Azure DevOps (VSTS) tasks ready to be re-used in any project. Be sure to checkout my Azure DevOps (VSTS) tasks that are fully open source on GitHub and browse through the VSTS documentation for building tasks.

]]>
Data Caching Made Simple with Monkey Cache 🐒 for .NET https://staging.montemagno.com/data-caching-made-simple-with-monkey-cache/ 5a6226bb6115b1002d4563b4 Fri, 19 Jan 2018 20:00:00 GMT Let's be honest, every application needs to store data. Sometimes it is settings, configuration, HTTP requests, or a full database. What I was recently looking to accomplish was to make a web request, cache the request locally, and have it expire after a given amount of time. There are a lot of great solutions out there for storing data, but nothing seemed to fit exactly what I was looking for with a minimal amount of dependencies. So I figured I would tweet about it:

From the results (and controversy) there still wasn't anything that completely fit the bill. And of course my good friend Frank told me that he basically had an implementation of what I was looking for and to create a GitHub repo so we could collaborate. Just like that Monkey Cache was born! The easiest way to cache data in any .NET application.

MonkeyCacheSmall

What is Monkey Cache?

When we were building Monkey Cache we wanted to make a drop dead simple way to store any type of data with a minimal amount of dependencies. In addition to this, we also wanted to give developers an easy way to make web requests that would automatically use the ETag headers to smartly cache data only when needed.

Installing Monkey Cache

Monkey Cache consists of one core NuGet package with three backing store providers. That's right, you can pick the backend that best fits your needs and application. As of today we support SQLite-net, LiteDB (supports encryption), and a simple File Store (no additional dependencies). If you are already using one of these databases in your application then it is a clear choice to pick that implementation, else the choice is yours!

MonkeyCacheNuGets

When you pick a backend it will automatically pull in the Monkey Cache core library which contains the core data models and all of the HTTP helpers. Monkey Cache is built against .NET Standard 2.0, which means it can be installed essentially in any .NET application, not just mobile!

Storing Objects in Barrels

Once Monkey Cache is installed in your project, it is time to start storing objects. Everything is stored in a "Barrel" or IBarrel. There is only one thing to do before we can store items, which is setup our application Id on the barrel:

Barrel.ApplicationId = "your_unique_name_here";

There is 1 barrel that items get stored in and Monkey Cache provides a singleton method that returns an IBarrel:

var currentBarrel = Barrel.Current;

What is an IBarrel?

Great question! This is where items get cached, updated, deleted, and checked for expiration:

    public interface IBarrel
    {
        void Add(string key, string data, TimeSpan expireIn, string eTag = null);
        void Add(string key, T data, TimeSpan expireIn, string eTag = null);
        void Empty(params string[] key);
        void EmptyAll();
        void EmptyExpired();
        bool Exists(string key);
        string Get(string key);
        T Get(string key);
        string GetETag(string key);
        bool IsExpired(string key);
    }

The Add method (which also updates the item) takes in a unique key, some data, a time span, and an optional eTag for metadata (nifty for web requests). Notice that there is a generic version of the method. This is so we can automatically serialize the object with Json.NET. I like to store the string manually for the web request itself, but the option is yours. On top of the normal Add and Get there are options to empty specific items, expired items, or the entire barrel. We have also added on some nifty helpers to check if the key is expired.

Caching a Web Request

Monkey Cache offers a nice drop in solution for caching data, which means there is no need to re-structure your entire app. Here is what an updated generic Get would look like for caching a web request:

There you have it! So simple! Now all of our web requests will be cached for any amount of time that we want and can optionally force a refresh if we need to!

You can learn more over on the Monkey Cache GitHub page, and also be sure to listen to our Merge Conflict episode on the how and why of Monkey Cache:

]]>
So we started a podcast... Merge Conflict https://staging.montemagno.com/so-we-started-a-podcast-merge-conflict/ 598df8f2c121b60019bf2fed Mon, 11 Jul 2016 23:32:55 GMT Two full years in the making with the amazing Frank Krueger, it finally happened, we started a podcast. I am not sure why or how it took two full years, but the time seemed just right to bring everyone a weekly show. Aptly named Merge Conflict, the podcast will be delivered right to your podcast listening device every single Monday at 7AM GMT, where we will be chatting on development, technology, and whatever else Frank and I are up to.

We spent a lot of time… two years… thinking about how to craft the perfect development podcast and we really think you will love it. 

You can subscribe today on the following services:

or simply visit http://www.mergeconflict.fm

Here are the first 3 episodes:

0: Merge Conflict

1: Build up to an app release

2: Augmented Reality #PokemonGo

]]>
App releases and what happens to those developers https://staging.montemagno.com/app-releases-and-what-happens-to-those-developers/ 598df8f2c121b60019bf3054 Mon, 21 Jan 2013 00:14:00 GMT Development cycles are extremely interesting, you have an idea and a core problem that you are out to solve. Early on in development you will study other apps, core user interface designs, and really map out your application. After this it is time to code and get serious. The following months you set a core foundation that you will build the apps off of, core user interface principles you want to implement, and of course start knocking features off one at a time.

Fast forward a few months, you have finished most of the core features, done internal testing and it is time to do a little alpha or beta testing with your users. If you are lucky you will have some great testers that really provide feedback, bug reporting, and will take time out their day to help you personally. I have been pretty lucky that my testers have been fantastic, although there is no possible way to ever catch any and all bugs unfortunately. During this period of beta testing and pushing towards a release candidate it is basically non stop. Welcome to the weeks of 60 to 70 hours at the office plus weekends. Not to mention that you have already been working 50 to 60 hour weeks before this. You do this though because you love it and this is kind of what software engineers do.

So now you are grinding away, fixing bugs, putting on layers upon layers of polish onto your app. Using it non stop at home, on the bus, and any spare time you possibly can. Then the day comes… it is done. This is the RC, you have done it… alright well probably not as you will most likely continue to grid out and pump out a few more RCs and to the extent that you are working so much and so fast that your testers can not possibly keep up. Alright for real this time RTM it is time to submit.

At this point in the development cycle you are the most nervous, anxious, and overwhelmed as you have just submitted your app to be certified by 1 or more app stores. You are handing your app over to someone that has NEVER seen it before, never heard of you, and now you have given the power over to someone that can simply with a click of a mouse destroy all of your hopes and dreams and reject your app from certification. Oh did I mention that this could take anywhere from 24 hours to a full week. Your app is most likely to get rejected for no good reason and then you will try to figure out what the tester found as they most likely didn’t provide you any information at all to even try to help you.

Grind, grind, grind and then boom you submit again, and again until you get that golden ticket “Your app has been approved!” Yes you are soooooo excited you made it, it is your time to shine. You plan out your release for a specific day and time and prep your website, press, twitter, and whatever you can to get ready.

Now it is an interesting time for the developer. You have done it all your hard work is about to pay off. However you now more than ever are scared. Scared that your app is going to come out and get slaughter, sell nothing, no one will care, or something will go horribly wrong with you app. This is the most stress you will ever experience throughout the entire development cycle. What will people think, is it good enough, what about this tiny bug that you just found that will probably effect no one but you are freaking out. This app is you, your hours, days, and life pour into this app that you are about to give away for free or for a few dollars.

The day comes and your app is released, anyone can get it, all the stress is off your shoulders? Correct? Probably not, as now it is time for that backlash from users. Your users that you love so much because they downloaded your app. Things wont work, something they didn’t like, that little bug might pop up, who knows what else. Then it happens the reviews & emails pour in as by far you first day and week will be your highest sales ever. 1 star, 2 star, you read through taking each and every word personal. Like a stab to the heart all you want to do is reach out and say PLEASE EMAIL ME, PLEASE TWEET ME, LET ME HELP YOU with the issue that you might experience!!! Then there is the feedback that really hurts and there is nothing you could possible do about.

Now is the time where you want to run away. You question your development talent and if what you have sunk your time and energy is worth it. You enter a pretty deep state of depression and frustration. You want to help everyone and not read all the feedback so you can clear up any issues people are having and push out updates… updates that will force you to suffer the same certification process over and over again. Your app will stabilize, your users will be happy, and then you will start work on new features, or a new app and live it all over again.

You remember that you do this because you love it. Each 5 star review, positive comment from a user reinforce the fact why you pour in the hours that you do.

*This entry is not only from a bit of personal experience, but also from a large number of developers that I personally follow on twitter and have talked to myself over the last few years I have been developing applications.

]]>