Workaround: Xamarin.Android long paths on Windows

Quick answer (TL;DR)

Create a symbolic link from your deeply-nested folder to a shorter path using one of the following commands in an Administrator prompt.

Command Prompt

mklink /D {desired-path-location} {actual-path-location}

PowerShell [Core]

New-Item -ItemType SymbolicLink -Path {desired-path-location} -Value {actual-path-location}

After you create the symbolic link, drag the desired solution into Visual Studio rather than navigating through the link in the Open dialog, which will override to the original path.

Background

If you like to keep all your code in a user directory on Windows, there’s a very good chance you’ve cloned a repo or started a new Xamarin.Android project that ran into issues on the first build. Most likely you have run into a path length issue. Sometimes a project is just so nested, even putting the repo in a low-level folder will still have trouble.

If you read the error messages, sometimes they explicitly say there is a path length issue. Sometimes, though, it will be something unusual like this JavaTypeInfo error. I don’t know enough of the Android build process to explain the issue here, but it’s definitely looking for a file at a path location that is 269 characters long.

Failed to create JavaTypeInfo for class: Android.Support.V4.View.Accessibility.AccessibilityManagerCompat/IAccessibilityStateChangeListenerImplementor due to System.IO.DirectoryNotFoundException: Could not find a part of the path ‘C:\Users\patridge\source\repos\mslearn-create-a-mobile-app-with-xamarin-forms\src\exercise1\final\Phoneword\Phoneword.Android\obj\Debug\81\android\src\mono\android\support\v4\view\accessibility\AccessibilityManagerCompat_AccessibilityStateChangeListenerImplementor.java’. at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost) at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize) at Xamarin.Android.Tools.Files.CopyIfStreamChanged(Stream stream, String destination) at Xamarin.Android.Tasks.Generator.CreateJavaSources(TaskLoggingHelper log, IEnumerable`1 javaTypes, String outputPath, String applicationJavaClass, Boolean useSharedRuntime, Boolean generateOnCreateOverrides, Boolean hasExportReference)

You can definitely run into this issues in a number of situations. Even the Xamarin modules we recently launched on Microsoft Learn have a path like this: {descriptive-module-name}/src/exercise#/{start|final}/{SolutionName}. That’s enough to to get too long for Windows without being in a deeply-nested folder to start.

On Windows, when you start nesting files really deep, you run into a maximum length of 260 characters (drive letter, like “C:\”, plus 256-characters of path from there). While there are ways of circumventing the path length restriction in newer versions of Windows, I haven’t been willing to try them yet. (I doubt I still use any 32-bit applications, but if I did, they wouldn’t be compatible with the long paths.)

Fake a shorter path

I used to copy entire repos to a shallower folder location like C:\dev, or **C:\d** when I got desperate. But sometimes I would forget which location I was working in and leave uncommitted Git changes in the wrong location. Ideally, I would keep all my code in a single location but not have those issues. If you also run into Android build issues that are caused by path lengths, we can trick Windows and Visual Studio into thinking our path is shorter without moving the files from our desired location.

The magic here comes from symbolic links or junctions in the Windows file system. These two tools are a bit like a shortcut, but it steps in the way of the path resolution as you use files and folders it points to. Regardless of the approach, this is how we can make a file at C:\Users\awesomeuser\source\repos\AndroidProjectOfAwesomeness (for Visual Studio 2019; C:\users\awesomeuser\Documents\Visual Studio 2017\Project\AndroidProjectOfAwesomeness) appear like it’s being used from C:\dev\Awesomeness.

The difference a symbolic link and a junction in Windows is subtle, and really only seems to become an issue when dealing with remote connections. Just know that if you are linking from a local path and to a local path, you can probably use either one without seeing any differences.

Create a symbolic link in Windows

Enter the mklink Windows console command. It’s been around for a while, and there was a similar junction command in Windows versions before Windows 8, but it doesn’t get much attention. Open a command or PowerShell prompt from the Start Menu. (Creating a symbolic link requires running this prompt as an administrator unless you have Developer Mode enabled.) Run a command like the following to link from {desired-path-location} (the shortened path) to a location where the code currently exists in {actual-path-location} (the longer path where the Android project isn’t building).

For the Windows Command Prompt, the command looks like this:

mklink /D {desired-path-location} {actual-path-location}

For PowerShell, the command looks like this:

New-Item -ItemType SymbolicLink -Path {desired-path-location} -Value {actual-path-location}

So, if I cloned the intro Xamarin.Forms module exercises from Microsoft Learn to the default location, but I wanted to make sure my path wasn’t too crazy, I could run a command similar to this, where “someuser” is your Windows username.

mklink /D C:\dev\learn-forms C:\Users\someuser\Documents\source\repos\mslearn-create-a-mobile-app-with-xamarin-forms\src\

Or, if you are using PowerShell, the right parameters to New-Item will create a symbolic link as well.

New-Item -ItemType SymbolicLink -Path C:\dev\learn-forms -Value C:\Users\someuser\Documents\source\repos\mslearn-create-a-mobile-app-with-xamarin-forms\src\

From there, I would have access to all the exercise folders, such as the final exercise at C:\dev\learn-forms\exercise1\final\Phoneword.

Using the symbolic link in Visual Studio

For some reason, Visual Studio won’t just play along with the illusion of symbolic links when you navigate through one in the normal open dialog. It will navigate to the original path location, defeating the whole point of all this.

However, if you find the solution file you want in Explorer first and drag it into Visual Studio, it will play along just fine. At this point, you should be happily building your Android projects without issue!

When you’re done with a symbolic link

If a symbolic link ever outlives its usefulness, just delete it like it’s any other shortcut. The original directory will stay where it is. The flip side is also true, tough. If you delete the source directory, the link won’t know any better.

Bonus tip

This can also be a good way to move location-required files from a drive that is running low on space to an external location. Some notable examples include migrating your iTunes music to a different drive, or moving your installed Steam games to another drive. You create a symbolic link from where iTunes or Steam is looking for those file to where you are planning to store them. The app doesn’t know any different, but your C drive can be much less bloated. If you are running Windows from a must-faster-but-smaller SSD, this can be a life saver.

Cross-Platform Images in Xamarin.Forms Using SVGs

Screenshot from the demo app; code available via GitHub, showing the flexibility of SVGs.

For the last four months, I’ve been working on a fast-paced Xamarin.Forms project where almost every image used has been an SVG shared between both the iOS and the Android platform apps. And it has been glorious! If an early UI design needed a few pixels shaved or added to an image we were using, no new images were needed, let alone a handful of pixel-density variations on it. It was just the few lines of code or XAML to change the rendered size. (In fact, I’ve extended our use to include stretchable buttons by adding 9-slice SVG support.) With SVGs, you use one tiny XML file to render the resulting image at any size or for any screen density.

TL;DR

  1. Put SVGs in a PCL as an EmbeddedResource build action
  2. Use an SVG control (I recommend TwinTechsForms.NControl.SvgImageView, but there are other choices.)
  3. Add an SvgImageView to your C# or XAML UI the the right assembly and resource path

Without SVG Images

Images on iOS and Android are normally managed in slightly different ways regarding naming and location. For better or worse, Xamarin.Forms continues using these image conventions, making a detour from the focus on cross-platform sharing. On a normal iOS app, you want three sizes of every image with density-based names: someimage.png, [email protected], and [email protected] On Android, you generate the different-sized images with the same name, but place them in separate drawable folders: mdpi, hdpi, xhdpi, etc.

Typical Android images resource structure

Typical iOS images resource structure

While programs like Sketch have made generating these multiple images from a single vector file much simpler (and I’ll explain later why you will still need this), whenever you add a new one or change an existing one, that means remembering to put six or more images in their proper location. Wouldn’t it be nice if you just exported one file, put it in one location, and had that image available at any size and/or density at any time? This is where SVGs can greatly simplify things for you.

We are going to walk through using SVGs as embedded resources for this post, but you could certainly download SVGs as they are needed. Embedded resources, as the name suggests, are stored within the compiled assembly.

Bringing Your SVGs to Xamarin.Forms

To make your SVG resources easy to use in both Xamarin.Forms platform projects, we will embed them in a common portable class library (PCL). If your Xamarin.Forms app doesn’t use a PCL for code sharing, feel free to make a separate one just for SVG assets. If you are already using a PCL to share code between platform apps, you can simply put them in their own location within that library. You could also create a separate portable library for assets, depending on your organization preference.

Creating a portable class library (PCL) for your shared SVGs.

Once you have your target PCL project, you can start to further organize them however you want. (An additional benefit over Android drawables which must be all at the same directory level.) If we’re putting the SVGs in your Xamarin.Forms shared code, it might be best just to create another directory for them; you can name it whatever you like: Resources, Images, Assets, SVGs, whatever. With a destination set up, you can start adding your SVGs to the project however you prefer.

After adding an image to your project, though, you need to remember one important step. Switch the file’s “Build action” from “None” to “EmbeddedResource” by either the Properties pad or by right-clicking it. Don’t worry. You are bound to forget to do this a few times; the SVG loading code currently throws an exception if the resource stream comes back null.

Make an SVG an embedded resource.

With some SVGs in place, let’s get them showing up. For this, you have a few options, each can have their own trade-offs.

Xamarin.Forms SVG Controls

One of the first Xamarin.Forms SVG controls was SvgImage from GitHub user paulpatarinski. This SvgImage control was created prior to NGraphics offering really solid SVG support, so it uses a custom fork. You may be just fine using this library, but it you are already using NGraphics for something else, you will end up with two almost-identical libraries compiled into your app. Additionally, this version can choke on some SVG elements that others can handle.

With NGraphics’ SVG support vastly improved, while working on Xamarin.Forms projects at Twin Technologies I created a custom version of SvgImage that uses the standard NGraphics NuGet package directly. This variant, TwinTechsForms.SvgImage, is available as a NuGet package. In addition to updating NGraphics, I also added 9-slice SVG support to SvgImage control. This control does not offer Windows Phone or UWP support yet.

NControl.Controls also offers an SvgImage control using NGraphics. (NControl is a Xamarin.Forms control created as a wrapper around NGraphics for custom-drawn cross-platform controls, with NControl.Controls being sample implementations.)

While it isn’t derived from the NControl SvgImage control (yet!), I have also ported the 9-slice SVG implementation found in the TwinTechsForms.SvgImage control to an NControl version: TwinTechsForms.NControl.SvgImageView (also available on NuGet). This control does not offer Windows Phone or UWP support yet.

No matter which SVG control you pick, you’ll want to add the package to both your Xamarin.Forms PCL project(s) as well as all of your platform projects. For the rest of this post, the code is based on TwinTechsForms.NControl.SvgImageView, so the exact code might be different for other SVG control libraries.

Using TwinTechsForms.NControl.SvgImageView

There is some initialization required to make SvgImageView work, but it’s a simple line in the platform projects after Xamarin.Forms initializes (AppDelegate.cs for iOS, MainActivity.cs for Android).

Xamarin.Forms.Init();
SvgImageRenderer.Init();

NOTE: without this line, SvgImageView will silently fail to use the custom renderer. I hope to eliminate the need for it at some point, but it is still needed for now.

Once we have the platform renderer Init in place, we can start putting SvgImageView in our UI code/XAML.

new SvgImageView {
    SvgAssembly = typeof(App).GetTypeInfo().Assembly,
    SvgPath = "some.embedded.resource.path.svg",
    WidthRequest = 48,
    HeightRequest = 48,
}

The SvgAssembly field might look a little unusual, but that is how we point our control to the assembly where the embedded resource is actually found. SvgPath is the string you pulled from the file’s properties.

If you are doing your SvgImageViews in XAML, which you certainly do, you will have to get your SvgAssembly from somewhere. (It may be possible in raw XAML, but my XAML skills were not up to par.)

<controls:SvgImageView
    SvgAssembly="{Binding SvgAssembly}"
    SvgPath="SvgImageDemo.Resources.logo.svg"
    WidthRequest="75"
    HeightRequest="75" />

You can see SvgImageView XAML in action in the demo repo for this blog post. In that case, I used a view model with an Assembly property to bind to SvgImageView.SvgAssembly.

Now we can start getting our SVGs rendering on the screen.

Embedded Resource SVG Gotchas

While you are experimenting with SvgImageView/SvgImage control, you are bound to hit a few mistakes. (There are some mistakes that I make almost every single time.)

Forgetting the SvgAssembly property results in a null reference exception in most controls. If you forget it, this exception will bubble up from the SvgImageView control, so it isn’t immediately obvious what went wrong. In an internal version of our SvgImageView control, we ended up setting a static DefaultSvgAssembly property so that all SVGs could be assumed to come from one location. This saved us from even bothering to add the line.

If you set the SvgPath incorrectly, you’re going to have issues at runtime as well. In some controls, it is an exception. In some, it just doesn’t render anything. It’s definitely worth checking the path you set against the value in the Properties pad for the file you want to use.

Even when you get what would be the correct embedded resource path, it is incredibly easy to forget to flag the file itself as an embedded resource. Every time you add an SVG, you need to do this before you try to render it. This error will also either be a runtime exception or result in nothing being rendered. With as many times as I’ve made this mistake, don’t ever feel bad when you go debugging an issue only to find there isn’t an SVG where you are pointing for this very reason.

Shared SVG Limitations

Remember when I said you would still need to export your images into the various density sizes? That is where one of the limitations of using SVGs comes in. Platform assets like launcher icons still require the various explicit assets because they are handled by the system before your Xamarin code can be used.

Additionally, there are some arguments for custom images when rendering an image at tiny icon sizes (or a separate SVG optimized for that purpose). Not every SVG detail will be discernible at the smallest sizes you need. This isn’t necessarily a limited of SVGs as much as complex graphics in general.

NGraphics has made some great progress toward consuming almost any SVGs I throw at it, but that could be more a symptom of the simplicity of my graphics and the use of Sketch. There are definitely SVGs that can either blow up rendering at runtime or render in unusual ways. Sometimes it is as simple as removing extra SVG non-rendering element cruft that came along from your export. If you run into an SVG that is giving you trouble, you can try exporting them from a different vector graphics application that may result in simpler paths. If you have a background with rendering SVGs, I’m sure NGraphics could use your help adding support for more elements.

Even SVG elements that do render (in some controls), don’t always render as expected. On prime example of this is the text element. If you have a text element, it likely has a typeface defined for it. There may be a way to make this work, but it might require some extra setup. So far, the easiest way to make text work is to output the SVG text as a path instead of a text element. For example, Sketch can convert text to outline paths that will work great in for SVG use.

Converting text to outlines for SVG use in Sketch.

Parting Thoughts

There are definitely limitations to using SVGs, but they can still vastly simplify your image workflow. Adding SVG support to your pre-existing app can be done without breaking any of your current image use, so it is ideal for adding to existing projects without rewriting your UI code. If you happen to give one of the TwinTechsForms NuGet packages, I’d love to hear your thoughts. Should you run into any issues, don’t hesitate to send a GitHub issue my way. Feel free to send any feedback my way on Twitter or leave a comment here. If you have any thoughts for improvements, pull requests are always welcome (or file an issue).

Giving CocosSharp v1.7.0.0-pre1 a Spin

iOS screenshot of a hybrid screen showing a Xamarin.Forms ListView and a CocosSharp game splitting the screen.

If you didn’t see the announcement recently, the awesome people working on CocosSharp put out a new preview of CocosSharp v1.7 (v1.7.0.0-pre1). With it, comes a very cool new way of working with your game content, the ability to mix it with normal native Xamarin.iOS, Xamarin.Android, or Xamarin.Forms content. I wanted to poke at the last of those, because I’ve been buried in Xamarin.Forms code non-stop for quite a few months now.

TL;DR:

Getting the CocosSharp project templates working the the v1.7.0.0-pre1 packages takes a few extra steps: restoring the pre-release CocosSharp.Forms NuGet package on your shared and platform projects. Once you get things in place, though, you can do some amazing things by combining Xamarin.Forms content with your CocosSharp game content.

If you want to just jump right into playing with the final code, the resulting solution is available in a GitHub repo. (I may continue to work on this repo, so if it ever looks too different, here is the exact commit to clone to get you to the end of this post.)

CocosSharp Project Templates Add-in

While we can spin up a new Xamarin.Forms project and add the CocosSharp bits, this also gives us a chance to play with a nice Xamarin Studio add-in. Open up the Add-in Manager from the Xamarin Studio menu and search for “cocos” (technically “CocosSharp project templates”) and give the Install… button a click. Let’s restart Xamarin Studio to let it pick up the new templates.

Adding the CocosSharp project templates add-in.

When Xamarin Studio comes back up, create a New Solution. In the New Project window, under Cross-platform > App, you should now see two new CocosSharp entries: CocosSharp.Forms Game and CocosSharp.Forms.Game Showcase. The former is just an empty project pre-wired to try to get plumbed up for CocosSharp (more on that later), and the later is a similar project but with a little sample game content. We will pick the Showcase one so we have something on the screen immediately. Select that template and hit Next. Give your sample app some basic information like App Name. (I left everything else with the defaults, like PCL for shared code and Git integration, but feel free to customize.)

Package Restore Failure

Once you let Xamarin Studio spin up all the parts of your new solution, you’ll be presented with one of the hiccups with playing around with pre-release libraries.

Error restoring pre-release NuGet packages on the CocosSharp project templates.

By default, the NuGet package restore on our new project will fail because the CocosSharp packages it references are still in pre-release mode. If you open up the Package Console pad, you’ll see a message like this. (Note: when v1.7.0.0-pre1 is updated to go live, this won’t be an issue anymore.)

Adding CocosSharp.Forms… Attempting to resolve dependency ‘Xamarin.Forms’. Attempting to resolve dependency ‘CocosSharp’. Unable to resolve dependency ‘CocosSharp’.

If you don’t even notice this error and try to compile the project anyway, you’ll get various build errors as it tries and fails to resolve various CocosSharp namespaces.

CocosLovesXamarinForms.cs(7,7): Error CS0246: The type or namespace name `CocosSharp’ could not be found. Are you missing an assembly reference? (CS0246) (CocosLovesXamarinForms)

We’ll have to add the NuGet packages ourselves. It’s not that the project templates are broken, really, they just need some help to work with the system as it is.

Adding Pre-Release Packages

Double-click on the Packages folder of your shared PCL project to open the Add Package dialog. Since we know we want a pre-release package, check the bottom-left box to “Show pre-release packages.” Then, search for “CocosSharp.Forms” and add the CocosSharp for Xamarin.Forms package to that project.

Adding the pre-release CocosSharp NuGet  package.

Now, your PCL project will build, but you will still see errors on your platform projects because they are missing the Xamarin.Forms packages. This is slightly confusing because we don’t need pre-release Xamarin.Forms packages. So, why wouldn’t those be restored? Well, they weren’t added to these projects directly. They were supposed to be added by way of the dependencies on the CocosSharp.Forms package. Since that didn’t restore, we didn’t get its dependencies either, apparently.

If You Only See White

As a warning, if you run off and just add Xamarin.Forms to the platform project(s), you will end up with projects that build and run “without errors.” Unfortunately, you won’t have anything but white on the screen either.

Running the CocosSharp template project without restoring the platform CocosSharp.Forms packages.

Because the CocosSharp.Forms package contains the custom platform renderers for the CocosSharpView used in the PCL code, without them you don’t see anything on iOS or Android.

CocosSharp.Forms All the Things

To get your game rendering on the platform projects, we also need to add the CocosSharp.Forms NuGet package to the iOS and Android platform projects the same way we did for the shared PCL project. (Make sure you check the “Show pre-release packages” checkbox each time to find it.)

With those packages in place, you can see your project template game in all its glory.

Android screenshot of the CocosSharp project template game.

iOS screenshot of the CocosSharp project template game.

Mixing UI and Game Worlds

Now that your CocosSharp game isn’t bound to the world of running only in full-screen, you can do some fun and crazy things. For example, if you want to mix a game with a Xamarin.Forms ListView, we can now do that.* (* Some restrictions apply.)

Let’s open up GamePage.cs and start making some adjustments (Cmd+., “GamePage.cs”, Enter). Currently, the constructor is just putting the game in place.

CocosSharpView gameView;

public GamePage ()
{
    gameView = new CocosSharpView () {
        HorizontalOptions = LayoutOptions.FillAndExpand,
        VerticalOptions = LayoutOptions.FillAndExpand,
        // Set the game world dimensions
        DesignResolution = new Size (1024, 768),
        // Set the method to call once the view has been initialised
        ViewCreated = LoadGame
    };

    Content = gameView;
}

Because of the way things work with CocosSharpView, it has to know ahead of time what size to be; you won’t be able to just request something like LayoutOptions.FillAndExpand. When I need to pre-determine dimensions and still handle multiple layouts, I tend to grab for an AbsoluteLayout and then do the necessary layout in the LayoutChildren overrride.

CocosSharpView gameView;
ListView listView;
AbsoluteLayout mainAbsoluteLayout;

public GamePage ()
{
    listView = new ListView () {
        ItemsSource = "These are some words I want to display in a list".Split (new[] { ' ' }),
    };
    gameView = new CocosSharpView () {
        // Set the game world dimensions
        DesignResolution = new Size (1024, 768),
        // Set the method to call once the view has been initialised
        ViewCreated = LoadGame
    };
    mainAbsoluteLayout = new AbsoluteLayout () {
        Children = {
            listView,
            gameView,
        },
    };

    Content = mainAbsoluteLayout;
}

protected override void LayoutChildren (double x, double y, double width, double height)
{
    base.LayoutChildren(x, y, width, height);

    AbsoluteLayout.SetLayoutBounds (listView, new Rectangle (0, 0, width, height / 2));
    AbsoluteLayout.SetLayoutBounds (gameView, new Rectangle (0, height / 2, width, height / 2));
}

With that in place, we get this wonderfully useless “game.”

iOS screenshot of a hybrid screen showing a Xamarin.Forms ListView and a CocosSharp game splitting the screen.

Android screenshot of a hybrid screen showing a Xamarin.Forms ListView and a CocosSharp game splitting the screen.