Hi,
I am trying to integrate the SQLCipher component version 3.3.1 into the iOS build of my Xamarin forms application and when I call the CreateTableAsync() method the app just hangs without any messages or exceptions. The exact same code works against the android version of the component. Here is the gist of what I am doing, nothing out of the ordinary:
'> public Task<'CreateStoragesResult> CreateStoragesAsync<'T>() where T : new()
{
var task = Task.Run(() =>
{
Type temp = typeof(T);
System.Diagnostics.Debug.WriteLine(string.Format("TypeMap:{0}",temp.FullName));
CreateStoragesResult dummyResult = null;
try
{
var result = _connection.CreateTableAsync<T>().Result;
dummyResult = new CreateStoragesResult();
dummyResult.Results = result.Results;
}
catch(Exception e)
{
System.Diagnostics.Debug.WriteLine(string.Format("CreateStorageException:{0}",e.Message));
}
return dummyResult;
});
return task;
}`
The SQLiteAsyncConnection (_connection) is a singleton created in the constructor of this wrapper class. Like so:
public DataAsyncConnection(string filePath, string password)
{
_filePath = filePath;
_password = password;
_connection = new SQLiteAsyncConnection(filePath, password);
}
Like I said this code works properly while using the android version of this component, is there something unique to the iOS version of this component that I should know about? Any help would be appreciated.
P.S - Also, what is the proper way to get code with generics into a post?
Hello @RWilkinson
I just modified a demo application based on the sample we ship with Xamarin for iOS using the 3.3.1 version and was successful in getting it to run. Could you change your portion where it captures the Result
to use a ContinueWith
instead. The relevant portion of the example is below:
public class t1{
public string a {get; set;}
public string b { get; set; }
}
private SQLite.SQLiteAsyncConnection connection;
private SQLite.SQLiteAsyncConnection GetConnection(){
if (connection == null) {
var databaseName = "demo.db";
var password = "test";
var databasePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), databaseName);
connection = new SQLite.SQLiteAsyncConnection (databasePath, password);
connection.CreateTableAsync<t1> ().ContinueWith (t => {
Console.WriteLine("t1 table created");
});
}
return connection;
}
With regard to formatting code sample, you can use Markdown to format posts, or use the inline editor. Could you give that a try and let us know your results? Thanks!
Hi @developernotes,
I tried simplifying my code to just fire off the async create table call (like your example) but ultimately I need to wait for all the table creation to complete before I continue on, and where ever I attempted to wait on these tasks the application would hang. In the end, the only way I was able to get the tables created where I needed them was by doing it one by one synchronously on the UI thread which hurts app startup time.
I did some more research and figured out why only in IOS does this occur. I am using the trial version of the Xamarin Component; as many developers who are considering your product will surely do to see if the product integrates well with their application. In order to show the trial version message the IOS version is executing code on the main UI thread. Showing this message is causing a deadlock, since the UI thread is waiting on this task and the task is ultimately waiting on the UI thread to show a message.
I have worked around this issue while I wait for licenses, but I thought this post may be helpful for other developers that encounter this issue.
I also realize that mixing synchronous and asynchronous code can be problematic and difficult. I have found the following link very helpful.
https://msdn.microsoft.com/en-us/magazine/jj991977.aspx
Hello @RWilkinson
Thank you for giving us an update, I am glad to hear you were able to work around the issue while you await the licenses. What solution did you employ?
Hi @developernotes,
The offending code was being called from the constructor of the SQLiteConnection class, but the SQLiteAsyncConnection uses a pool of these. Luckily, there is one synchronous method on the SQLiteAsyncConnection class that causes the pool to instantiate a class and fire this UI thread message code: Table().
In addition to calling this method on the UI thread, I also had to add the ConfigureAwait method on the async call in order to give it a new synchronization context when the task returns. This is explained better in the link I provided above. Here is a simplified version of the changes I made to make this work.
Here is the storage layer, where I added this Table call to the constructor:
private SQLiteAsyncConnection _connection;
public DataStorage(string filePath, string password)
{
_filePath = filePath;
_password = password;
_connection = new SQLiteAsyncConnection(filePath, password);
_connection.Table<DisplayItem>();
}
public async Task<CreateTablesResult> CreateStoragesAsync<T>() where T : new()
{
var temp = await _connection.CreateTableAsync<T>().ConfigureAwait(false);
return temp;
}
And here is the calling code:
this.repository = new DataStorage(databasePath, password);
var createTableTask = this.repository.CreateStoragesAsync<DisplayItem>();
createTableTask.Wait();
Now the createTableTask will complete and I have created my table in a separate thread from the threadpool. I hope this helps.
Thanks for sharing @RWilkinson