Xamarin iOS build error with Couchbase Lite .NET 1.3.1

I update my CouchbaseLite .NET library from version 1.2.0.3 to 1.3.1 and now I am running into a build error:
Error MT2002: Failed to resolve assembly: ‘Couchbase.Lite.Storage.SystemSQLite, Version=1.3.1.0, Culture=neutral, PublicKeyToken=null’ (MT2002)

Based on the information here (https://github.com/couchbase/couchbase-lite-net/wiki/Error-Dictionary#cblcs0001). I made sure to reference Couchbase.Lite.Storage.SystemSQLite as well as add the following line to the code:
Couchbase.Lite.Storage.SystemSQLite.Plugin.Register ();

I also made sure the linker setting is set to “Don’t Link”.

Is anyone else seeing this error or know of how to work around this?

This is something I’ve worked hard to prevent and is quite unusual to happen. Can you try removing all the references and re adding the packages? Couchbase lite itself will pull system SQLite as a dependency. As long as you also have the registration call then things should work. In fact even without it should work with the don’t link setting so I suspect something else is going on here.

@borrrden I removed all references, removed nuget packages, and then readded the packages and rebuild. I still see the issue.

Note i am using the following setup:
Xamarin Studio Version 6.1.1 (build 17)
Xamarin.iOS Version: 10.2.0.4

If you can have an empty project that reproduces this then post it onto a github issue. I haven’t run into this issue since before 1.3.0 so I can’t say why this would be the case without some context in how the project is setup.

I was unable to reproduce the issue with a test sample app. I did determine that if I don’t include the line to register SystemSQLite in both my sample app and actual app, I will always get a runtime error at startup even my my linker setting is set to “Don’t Link”.

  1. If the sample app uses Couchbase 1.3.1 but if I don’t include the line Couchbase.Lite.Storage.SystemSQLite.Plugin.Register ();
    I get the following error:
    INFO) DATABASE (Database): [7] 2016-11-16 03:22:48.934-06:00 Opening Database[/var/mobile/Containers/Data/Application/424B684D-A9A9-4AA4-ABA9-618DC5E30A9B/Documents/Library/usermemories_cb.cblite2]
    ERROR) DATABASE (Database): [6] 2016-11-16 03:22:48.938-06:00 No implementation found for SQLite storage. For more information, see https://github.com/couchbase/couchbase-lite-net/wiki/Error-Dictionary#cblcs0001, throwing CouchbaseLiteException (InvalidStorageType)
    2016-11-16 15:22:48.940 xFUI[312:28619] [2016-11-16T15:22:48.9382880-06:00] [debug] No implementation found for SQLite storage. For more information, see https://github.com/couchbase/couchbase-lite-net/wiki/Error-Dictionary#cblcs0001

  2. In the sample app, if I include the line to register SystemSQLite, then the app starts up without any issues.

  3. I went back to my original app and tried the same two settings. If I include the line to register SystemSQLite, then I get a build error that SystemSQLite can’t be resolve. If I exclude the line to register then my solution builds but fails at run time with the error “No implementation found for SQLite storage.” and points me back to the documentation to register SystemSQLite.

I was able to resolve the build issue. I had a second project in my solution that also had a reference to SharpZipLib but with a different/older version. Updating that library to use the latest version that matches with the version Couchbase.Lite uses or forcing the references to be version specific resolved the build error.

Keeping the line of code to register the SystemSQLite plugin resolved the runtime error regardless of linker settings.

Excellent, I’m glad you figured it out because I was stumped. I will remember this situation in case someone else asks though. In regards to the register line, I am not totally sure of the exact circumstances under which it will fail or succeed which is part of the reason I made it mandatory on AOT platforms in 1.3.0 (as opposed to the automatic way of doing things before which caused a slew of problems once linker settings were activated). Here is a bit of info on the ultimate problem that this line resolves:

The core assembly does not make explicit references to the plugin assemblies because A) It doesn’t make sense to reference them if we are not sure they will be needed and B) It would cause circular referencing anyway since the plugins reference the core. Thus, they need to be referenced at the app level instead. Perfect, nuget can take care of that part for us but there is still a problem. The app doesn’t directly use the plugins and neither does the core (since it cannot due to the lack of an explicit reference, it will load it dynamically) and so the .NET compiler will strip out that reference since it regards it as extraneous (there might be a way to prevent this but I haven’t found a way to do it reliably yet). On most platforms this will not cause a problem because it will be JIT compiled later, but on iOS is not a JIT platform. It needs to AOT compile all its assemblies, but the way it figures out which ones to compile is by recursively walking the references of the app being compiled. Since that reference to the plugin gets stripped out, the plugin does not get AOT compiled and you are left with a broken build.

In 1.2 I got creative and compiled dummy assemblies that would force the .NET compiler to see that it was being used, and then swapped those out for the real assemblies at build time. This kept the references intact, but the “dummy” function that I used was missing since it wasn’t in the real build. This caused issues when linker settings were used (it would complain that the dummy function was missing). I would have preferred to keep this process automatic but in the end it just caused too many problems and so I went with a compromise that the user is required to call a register method to use the plugin. This makes an explicit reference from app to plugin which preserves the reference at build time. On JIT platforms it is optional for system sqlite since the core library will attempt to dynamically load it by default but this is not possible to do reliably on AOT platforms. It also provides a handy way for me to ensure that an app is not switching between implementations at runtime ;).