I’ve recently been working on a Xamarin.Forms project for Android and
iOS and recently came across an issue that took some experimenting to get
fixed so I thought I would share it for anyone else that might run into it.
Realm is a database designed for mobile applications and offers a safe,
easy, non-ORM replacement for SQLite. The intent is that you can, for the
most part, mark your data model objects as RealmObjects and Realm
will handle all of the persistence for you. As with anything, it gets
more complicated than that but so far it’s still much better for what
I’m doing than SQLite. If I needed the ability to do offline synchronization
with an online data source, I’d be looking at Azure Mobile Apps
, but as I don’t I’m fine with what Realm will do for me.
Adding Realm to your Xamarin.Forms application just requires adding the
Realm NuGet package to your projects.
From there, you should be able to compile your projects without any issues.
However, if you have Continuous Integration set up to auto-build your projects
and run tests with every build, it’s possible to run into an issue. I use
Visual Studio Team Services to
build all of my mobile projects and then deliver them to HockeyApp
for beta testing. I’ll have to post more about setting up the Build Definition
(the steps for building and uploading my compiled applications) but want to
focus on the issue that having Realm in your projects has with the build
When you add Realm to your projects, it adds a Target as part of the package.
That step, called CopyRealmWeaver which copies RealmWeaver.Fody.dll as
part of the build process. I won’t get into what Fody
is but it’s a necessary part of using Realm. The XML found in the Realm.targets
has the following inside of it:
<Target Name="CopyRealmWeaver" BeforeTargets="FodyTarget">
<Message Text="CopyRealmWeaver" />
Text="Solution directory was not set. If you are building via xbuild, specify by adding a /p:SolutionDir=/path/to/solution/folder argument. See github.com/realm/realm-dotnet/issues/656"
Condition="'$(SolutionDir)' == ''" />
<Copy SourceFiles="$(MSBuildThisFileDirectory)../tools/RealmWeaver.Fody.dll" DestinationFolder="$(SolutionDir)/Tools" />
What this does is check to make sure $(SolutionDir) has been specified
as part of the call to xbuild, or in the case of VSTS, msbuild. Provided that
value has been specified, it then copies the RealmWeaver.Fody.dll from a tools
directory in the packages folder into the Solution directory.
What’s wrong here
The problem here is that when compiling with VSTS, there is some step that changes
the SolutionDir value to *undefined*. When I ran into this, it was
especially weird as I couldn’t seem to find a reference to VSTS, build, and
*undefined* anywhere. I did find a Stack Overflow post
about Visual Studio replacing $(SolutionDir) with *undefined* which
wasn’t the solution to my problem exactly but did get me on the right path. From there,
I took a look at the call to MSBuild to see what was showing up:
2016-12-13T19:23:38.1392477Z ##[command]"C:\Program Files (x86)\MSBuild\14.0\bin\msbuild.exe" "C:\a\1\s\src\Droid\MyApp.Android.csproj" /nologo /nr:false /dl:CentralLogger,"C:\a\_tasks\XamarinAndroid_27edd013-36fd-43aa-96a3-7d73e1e35285\1.1.1\ps_modules\MSBuildHelpers\Microsoft.TeamFoundation.DistributedTask.MSBuild.Logger.dll";"RootDetailId=39592de0-fb1c-4156-a73e-b2e8ee77129e|SolutionDir=C:\a\1\s\src\Droid"*ForwardingLogger,"C:\a\_tasks\XamarinAndroid_27edd013-36fd-43aa-96a3-7d73e1e35285\1.1.1\ps_modules\MSBuildHelpers\Microsoft.TeamFoundation.DistributedTask.MSBuild.Logger.dll" /p:configuration="Release" /p:_MSDeployUserAgent="VSTS_db204fe6-401b-4488-b9c9-02229bdeafd2_build_25_317" /t:PackageForAndroid /p:OutputPath="C:\a\1\b/Release" /p:JavaSdkDirectory=" C:\Program Files (x86)\Java\jdk1.8.0_102"
What I want to highlight from that call to msbuild is SolutionDir=C:\a\1\s\src\Droid. It
seems like we’re specifying a Solution Directory, but the error shows us that somehow,
that is being overwritten. So we need to override that overwrite and ensure a value
is in fact being passed in.
Thankfully, the Build Xamarin project build step has a spot for Additional Arguments at
the bottom. In this case, I set the solution directory to the directory of my overall solution
file and where the tools directory containing the RealmWeaver.Fody.dll is at. The format
of the argument is important so this is what you should be using (adjusting the path for your)
tools folder location of course:
With that done, I was able to run builds without any issue. This seem to be an Android only
issue so you shouldn’t need to change anything on your iOS build.
Update 2-16-2017: A new error
After a recent update (to my code base), I found that my workaround was no longer working. The new error I was seeing was:
Fody: Fody (version 18.104.22.168) Executing
Fody: ProjectDirectory: 'D:\a\1\s\src\MyApp\MyApp.PCL\'.
Fody: AssemblyPath: 'D:\a\1\s\src\MyApp\MyApp.PCL\obj\Release\MyApp.PCL.dll'
Fody: Found path to weavers file 'D:\a\1\s\src\MyApp\MyApp.PCL\FodyWeavers.xml'.
##[error]Fody: SolutionDir "D:\a\1\s\src\MyApp\MyApp.PCL\src" does not exist.
What this appears to be doing is adding the SolutionDir that I specified above to the end of the PCL’s path. I was able to fix this once again by changing my Additional Arguments to:
I suspect this was due to a change in the Realm.targets XML file for the newer version of Realm that I updated to when I updated NuGets.