0 Comments

Title Image showing a library

When it comes to file handling and Xamarin Forms you can find all you need in the official Documentation. However, when it comes to where the data should be stored the documentation leaves some points open. Moreover, might even lead to, dear I say it, your rejection in the App Store...

Also when writing Cross-Platform Code, with .Net Standard, it does depend on the Operating System (OS) that the app is being executed on, where to store your data. So let's dive into the platforms which are primarily supported by Xamarin Forms.

Xamarin.Essentials

The most comfortable way is using Xamarin.Essentials. Currently still in preview and requires Android 7.1 or higher. However, if that does not make you blink, these are the options:

  • Local Storage which is also backed up.
  • Cache Storage which is, well for caching files but are more on the non-permanent side.
  • Files bundled with the app, i.e. read-only files.

You can access the folder paths as follows:

var rootDirectory = FileSystem.AppDataDirectory;

Currently Xamarin.Essentials supports Android, iOS and UWP. So if all the platforms your app requires are named, and you do not need any other location. Be sure to check out Xamarin.Essentials and check the documentation for more details.

Do it yourself

In most cases the folders offered by Xamarin.Essentials will suffice, but if you require a different folder, i.e. the document folder under iOS, you can always set the path to the location on your own. So let's have a look at how you can achieve this.

Android

Now when it comes to Android, you can follow the documentation from Microsoft and use the following path for storing your files:

_rootDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

Looking to cache some files, then we can take the path and with Path.Combine set the path to the cache folder:

_cacheDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "..", "cache");

If you want to store files to the SD-Card, i.e. external storage under Android. You have to pass in the path from a platform Project to the .Net Standard project or use multitargeting to achieve this. You can get the path as follows:

var sdCardPath = Environment.ExternalStorageDirectory.AbsolutePath;

Note that the Environment here is Android.OS.Environment and not System.Environment. So if Android is this easy how hard can iOS be?

iOS

When storing files under iOS, there is a bit more documentation to read. The reason being that Apple uses multiple subfolders within the Sandbox container. The ones that are important for file storage are the following:

  • Documents: In this folder, only user-created files should be stored. No application data, which includes that JSON file of your app, should be stored here. This folder is backed up automatically.

  • Library: The ideal spot for any application data you do not want the user should have direct access to. This folder is backed up automatically.

    • Library/Preferences: A subdirectory which you should not directly access. Better use the Xamarin.Essentials library for storing any key/value data. This data is backed up automatically.
    • Library/Caches: This is an excellent place to store data which can easily be re-created. This data is not backed up.
  • tmp: Good for temporary files, which you should delete when no longer used.

For a more detailed listing check out the docs. So when storing files, you are under iOS this code will point to the Documents folder:

_rootDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

If you intend to store information into the Library folder you can change the directory as follows:

_rootDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "..", "Library");

Equally, you can change your path to one of the other locations described above.

UWP

UWP apps usually also store their data in a sandbox. Only if in the app's metadata the permission is set and a good reason was given, which Microsofts validates on submission to the store, can the app access other file locations outside of the sandbox. UWP apps also live in a sandbox. The ApplicationData offers to store data locally, roaming or in a temporary location. The local folder can be accessed as follows:

_rootDirectory = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);

Similar the other locations can be accessed for example the roaming folder (which is synced automatically across all of your different devices):

_rootDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "..", "RoamingState");

The following directories are present in a UWP apps sandbox:

UWP Container folders: AC, AppData, LocalCache, LocalState, RoamingState, Settings, SystemAppData, TempState

However, what if I am targeting other platforms?

Since Xamarin Forms is no longer limited to Android, iOS and UWP, you might find yourself wanting to write files on another system such as Tizen. The best solution is to check the documentation of the given platform where data should be stored and then run the following code on the platform:

class Gnabber 
{ 
    // ... 
    private IEnumerable<DirectoryDesc> DirectoryDescriptions() 
    { 
        var specialFolders = Enum.GetValues(typeof(Environment.SpecialFolder)).Cast<Environment.SpecialFolder>(); 
        return specialFolders.Select(s => new DirectoryDesc(s.ToString(), Environment.GetFolderPath(s))).Where(d => !string.IsNullOrEmpty(d.Path)); 
    } 
    // ... 
} 
 
class DirectoryDesc 
{ 
    public DirectoryDesc(string key, string path) 
    { 
        Key = key; 
 
        Path = path == null 
            ? "" 
            : string.Join(System.IO.Path.DirectorySeparatorChar.ToString(), path.Split(System.IO.Path.DirectorySeparatorChar).Select(s => s.Length > 18 ? s.Substring(0, 5) + "..." + s.Substring(s.Length - 8, 8) : s)); 
    } 
 
    public string Key { get; set; } 
    public string Path { get; set; } 
}

The code above lists all used folders from the System.Environment.SpecialFolder for the given environment, and also provides you with the absolute path. If none of the special folders is the target location, you desired. Try using Path.Combine and a path nearby to get to your desired location.

Conclusion

Storing files is not tricky but put some thought into where to store your applications data can go a long way. Xamarin.Essentials may provide all the functionality you need, but if not you usually use the System.Environment.SpecialFolders and the System.Environment.GetFolderPath to get access to different folders offered by the platform you are on.

0 Comments

Image showing a laptop with graphs on it -looking fancy that's all...

Did you know that with Visual Studio 2017 there was an update in the target project files of your Xamarin Projects? They no longer contain a packages.config file but contain the NuGet references directly in the csproj. Using NuGet references instead of the packages.config file has numerous benefits ranging from performance improvements to only showing your top-level dependencies (no longer will you have a scajilion package references ).

They also fix a pesky bug I have experienced since partially migrating to .Net Standard. Migrating to .Net Standard is not something new in the Xamarin World. There are many blog posts out there which will guide you through how to migrate your Portable Class Libraries (PCLs) to .Net Standard. My personal favorite is the approach I first read on James Montemagnos blog, which is pretty straightforward and will allow you to keep your version history.

But depending on when you have created your project you will start running into compile errors after the migration telling you that a dll from a NuGet which you have only referenced in the .Net Standard project(s) can not be found in the output folder. As is the case in our sample app which I have created for this blog post:

Showing compile error that ReactiveUI dll was not found in app output folder

In the sample project, we are referencing the ReactiveUI NuGet only in our .Net Standard code and therefore do not have a direct NuGet reference in our platform projects. When looking closely at the platform projects we can see that the Android and iOS project are using a packages.config file to reference their NuGet packages.

One solution you will find on the internet is to add these NuGet packages to your target projects. While this works it has got that yucky feel to it. Furthermore, this can lead to quite a bit of bloat since some projects come with sub-dependencies which will all show up in your NuGet package manager. But there is an easier way which is also less nauseous

Migrating to the newer Package Reference style can be done by simply right clicking on your packages.config and selecting Migrate packages.config to PackageReference...:

Visual Studio dialog showing Migrate To PackageReference

This action will change the csproj from this:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="..\..\packages\Xamarin.Forms.3.1.0.637273\build\netstandard2.0\Xamarin.Forms.props" Condition="Exists('..\..\packages\Xamarin.Forms.3.1.0.637273\build\netstandard2.0\Xamarin.Forms.props')" />
  <!-- File includes and that stuff -->
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Xml" />
    <Reference Include="System.Core" />
    <Reference Include="Xamarin.Forms.Core, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
      <HintPath>..\..\packages\Xamarin.Forms.3.1.0.637273\lib\Xamarin.iOS10\Xamarin.Forms.Core.dll</HintPath>
    </Reference>
    <Reference Include="Xamarin.Forms.Platform, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
      <HintPath>..\..\packages\Xamarin.Forms.3.1.0.637273\lib\Xamarin.iOS10\Xamarin.Forms.Platform.dll</HintPath>
    </Reference>
    <Reference Include="Xamarin.Forms.Platform.iOS, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
      <HintPath>..\..\packages\Xamarin.Forms.3.1.0.637273\lib\Xamarin.iOS10\Xamarin.Forms.Platform.iOS.dll</HintPath>
    </Reference>
    <Reference Include="Xamarin.Forms.Xaml, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
      <HintPath>..\..\packages\Xamarin.Forms.3.1.0.637273\lib\Xamarin.iOS10\Xamarin.Forms.Xaml.dll</HintPath>
    </Reference>
    <Reference Include="Xamarin.iOS" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\HelloReactiveUI\HelloNetStandard.csproj">
      <Project>{984433BA-6DB2-4606-8AB1-E5E070C60D44}</Project>
      <Name>HelloNetStandard</Name>
    </ProjectReference>
  </ItemGroup>
  <Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
    <PropertyGroup>
      <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
    </PropertyGroup>
    <Error Condition="!Exists('..\..\packages\Xamarin.Forms.3.1.0.637273\build\netstandard2.0\Xamarin.Forms.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Xamarin.Forms.3.1.0.637273\build\netstandard2.0\Xamarin.Forms.props'))" />
    <Error Condition="!Exists('..\..\packages\Xamarin.Forms.3.1.0.637273\build\netstandard2.0\Xamarin.Forms.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Xamarin.Forms.3.1.0.637273\build\netstandard2.0\Xamarin.Forms.targets'))" />
  </Target>
  <Import Project="..\..\packages\Xamarin.Forms.3.1.0.637273\build\netstandard2.0\Xamarin.Forms.targets" Condition="Exists('..\..\packages\Xamarin.Forms.3.1.0.637273\build\netstandard2.0\Xamarin.Forms.targets')" />
</Project>

To this:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!-- File includes and that stuff -->
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Xml" />
    <Reference Include="System.Core" />
    <Reference Include="Xamarin.iOS" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\HelloReactiveUI\HelloNetStandard.csproj">
      <Project>{984433BA-6DB2-4606-8AB1-E5E070C60D44}</Project>
      <Name>HelloNetStandard</Name>
    </ProjectReference>
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="Xamarin.Forms">
      <Version>3.1.0.637273</Version>
    </PackageReference>
  </ItemGroup>
  <Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
</Project>

Which only references the top level package. Further the package.config file will be removed. Since this method is still in development you might run into some bumps. So be sure to check out the limitations which might apply to the packages you are currently using. If that is the case make sure to let the team know. But once you have migrated, you will be able to use your project without the compile issue and all the benefits from using Nuget package references.

Conclusion

In this post, you saw how to migrate your Xamarin Projects to use package references. Using package references is intended to be the new adding NuGet package references directly to your csproj. With this migration, an error introduced when migrating your PCL projects to .Net Standard will also be solved. So be sure to check out this option when updating your Xamarin apps that have been out in the wild for a while.

Note: that all new projects created with Visual Studio 2017 will automatically use the package reference approach.

To see the sample app in full you can check them out on GitHub packages.config and package reference.

Thank you to Pierce Bogganwho pointed me in the right direction to get this problem solved.

0 Comments

pexels-photo-64774

Update: Hey there thank you for reading my blog, since I wrote this post I have learnt a lot and have found a better way to migrate your Xamarin Apps so please check out my latest blogpost on this matter.

If you haven’t heard or dived into .Net Standard you are in for a treat. In short it provides a way to share code across platforms but in contrast to the PCL it gives you so many more platform specific features. For an in depth overview check out the official docs.

Note: If you are starting a new Project today with Visual Studio (VS) 2015.4, you will not be able to select .Net Standard by default. But the new templates for .Net Standard will be included in Visual Studio 15.5.

Creating the .Net Standard Class Library

Migrating an existing app to .Net Standard is pretty straight forward. Step one add a .Net Standard Library to replace your PCL project.

The VS Add New Project dialog, under Visual C# select Class Library (.Net Standard)

Migrating your source code

Then drag and drop all of your existing files from the PCL project to your .Net Standard library. Note that you don’t want to copy the packages.config or any of the files under properties.

CopyFiles

Now you can delete your PCL project that you have migrated. If you do so from Visual Studio note that the Project is still available in the file system. Which means you still have it if you forgot something, but also means that you will have to delete it later on if you want to remove it from the source control workspace.

Next step is to add all the NuGet packages you have used in the PCL project. If you are having trouble adding some of the packages look out for NuGet packages that are no longer needed due to .Net Standard support such as file system access. In other cases it could be because the NuGet package has not (hopefully yet) migrated to .Net Standard. In that case check out this post and don’t forget to ask the maintainers of the project when the project will be available for .Net Standard Winking smile

Hooking up the projects

You can now add the reference to the .Net Standard in your Android and iOS project. If you created a new namespace I strongly recommend you refactor them after adding them to your projects. Or else your refactoring tool of choice will only do half the magic and you will still have some work left to do.

If you are using a UWP project please read the section bellow as you will need to make some additional steps to make it work.

Fixing the csproj for Xamarin Forms

Unfortunately when moving a Xamarin Forms app over to .Net Standard you will get weird compilation errors. The cause of this is that the XAML files are referenced in the csproj file:

02_1_RemoveEmbeddedResources

Simply remove them as they are not needed and the compile errors should be history.

When using UWP

If you are using UWP as a target (I.e. using the default Project provided up to VS 2015.4). You will have to remove and add the project anew:

If you are unsure if you really have to update your UWP project. Check if you have a project.json file in your UWP project. If the answer is yes, I’m afraid you will have to follow the following steps.

  1. Remove the UWP project from the solution in Visual Studio
  2. Rename the UWP project folder in the file explorer
  3. Add a new UWP project in Visual Studio (with the same name as the one just removed)
  4. Set the minimal supported Windows 10 version to the Fall creators update

    VS Dialog Window with Target and Minimum Version set to Fall Creators Update
  5. Add any NuGet references the just removed project had (you can peek into the project.json file in the renamed location if you are unsure which packages to add)
  6. Copy and paste all your UWP files (except the project.json)
  7. Add a reference to the Standard Library project

If you know an easier way to upgrade a UWP project, please let me know in the comments bellow Smile

Conclusion

In this post we went over the steps required to migrate an existing PCL project to .Net Standard. All the steps were done with Visual Studio 15.4.

0 Comments

NetStandardLogo

The wait will soon be over and we will all be able to use .Net Standards for sharing all our code across .Net run-times. But for the current time we are still in a transition state, not all libraries support .Net Standard out of the box. One option would to rely on good old Portable Class Libraries (PCLs) for now. But the PCL comes with many restrictions compared to a .Net Standard library. Plus .Net Standard is where all the action is (and will be) happening. So it seems like a no brainer to choose .Net Standard as our library container to share code.

But what when we are using a library that does not support .Net Standard out of the box? For instance let’s say we want to use Reactive UI as our MVVM framework for a Xamarin.iOS and Xamarin.Android app. When trying to add Reactive UI version 7.4.0 (latest stable version as of writing) via NuGet to our .Net Standard Project. We get the following error message:

Error message that .Net Standard library is not among the supported .Net Frameworks of this NuGet package

The solution to our problem is to define the required targets in our .Net Standard Project. The targets which the Reactive UI NuGet package defines. We can edit a .Net Standard library by right clicking on it in Visual Studio and selecting Edit Project name:

Right-Click menu showing the edit option of a .Net Standard Project

Now we can define the targets by adding the PackageTargetFallback line to our DotNetStandard.Core.csproj file:

Providing the targets provides NuGet with the information needed to install the package. Rerunning the NuGet installation once more leads to a success Smile

Successfull installation of Reactive UI after changing the project

Conclusion

.Net Standard is the future of writing .Net Libraries which runs on multiple .Net runtime Environments.

Happy coding and don’t let those not-yet ported libraries stop you from achieving your goals!