How to get Sqlite Connection in Xamarin Forms in iOS? - ios

I've created a Xamarin.Forms PCL project and trying to access the local data stored in sqlite database which is working file in Android but not working in iOS. Whenever I'm trying to call the iOS specific code using DependencyService it throws System.NullReferenceException: Object reference not set to an instance of an object.
Here is my calling statement
var db = DependencyService.Get<IDBPath>().GetDBPath();
Here is my iOS specific code for getting Sqlite Connection
using SQLite.Net;
using SQLite.Net.Async;
using SQLite.Net.Platform.XamarinIOS;
using SwachhParyatanApp.iOS;
using System;
using System.IO;
[assembly: Xamarin.Forms.Dependency(typeof(DBPath_iOS))]
namespace SwachhParyatanApp.iOS
{
class DBPath_iOS
{
public SQLiteAsyncConnection GetDBPath()
{
var sqliteFilename = "localData.db";
string folder = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
string libraryPath = Path.Combine(folder, "..", "Library");
var path = Path.Combine(libraryPath, sqliteFilename);
var platform = new SQLitePlatformIOS();
var param = new SQLiteConnectionString(path, false);
var connection = new SQLiteAsyncConnection(() => new SQLiteConnectionWithLock(platform, param));
return connection;
}
}
}
I don't think the calling method is going to reach the iOS specific code because I used the break point in iOS specific code but it never came to the break point and it immediately gives the error. I've also tried going to the exception for details but there is no inner exception and in stacktrace it only points to the line which called the method.

Using SQLite.Net PCL below is a working example of an iOS dependency injection recipient for SQLite. A couple of differences I noticed are your db extension .db instead of .db3 and your 'assembly' header does not implement the full namespace. I am not sure if that matters.
[assembly: Dependency(typeof(NameSpace.iOS.SQLiteUtility.SQLite_iOS))]
namespace NameSpace.iOS.SQLiteUtility
{
class SQLite_iOS : ISQLite
{
public SQLiteConnection GetConnection()
{
try
{
var sqliteFilename = "MyDB.db3";
string documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal); // Documents folder
string libraryPath = Path.Combine(documentsPath, "..", "Library"); // Library folder
var path = Path.Combine(libraryPath, sqliteFilename);
var plat = new SQLite.Net.Platform.XamarinIOS.SQLitePlatformIOS();
var conn = new SQLite.Net.SQLiteConnection(plat, path,
SQLite.Net.Interop.SQLiteOpenFlags.ReadWrite |
SQLite.Net.Interop.SQLiteOpenFlags.Create |
SQLite.Net.Interop.SQLiteOpenFlags.FullMutex, true);
return conn;
}
catch (SQLiteException ex)
{
Helpers.Helper_ErrorHandling.SendErrorToServer(ex);
return null;
}
catch (System.Exception ex)
{
Helpers.Helper_ErrorHandling.SendErrorToServer(ex);
return null;
}
}
}
If it must be the async version you may want to look at How to use SQLiteAsyncConnection from the async PCL version of SQLite?

Related

HttpClient doesn't works in IOs app builded in VS with Xamarin

I try to make an awaitable request in my PCL/Android/iOs project. Function for request is in PCL
public class DataService
{
private static string _requestUri = "https://requesturi.com/";
public static async Task<LocalizationData> GetLocalization(string code)
{
string queryString = _requestUri + "get_localization.php" + "?code=" + code;
HttpClient client = new HttpClient();
var response = await client.GetAsync(queryString);
dynamic data = null;
if (response != null)
{
string json = response.Content.ReadAsStringAsync().Result;
data = JsonConvert.DeserializeObject(json);
if (data["status"] == "success")
{
List<string> aliases = new List<string>();
List<string> translations = new List<string>();
foreach (var localization in data["localizations"])
{
aliases.Add((string)localization["alias"]);
translations.Add((string)localization["translation"]);
}
LocalizationData localizationData = new LocalizationData(code, aliases.ToArray(), translations.ToArray());
return localizationData;
}
else
{
return null;
}
}
return null;
}
}
Both in Android and iOS I call this function with
localizationData = await DataService.GetLocalization(langCode);
In Android it works without problems both on simulator and on real device.
But when I try run it in iOS, on simulator it works fine, on real device app crash on
var response = await client.GetAsync(queryString);
Is it something about permissions? Or something else?
Can anybody help me with this problem?
UPDATED
There are exception for client.GetAsync(queryString) I get in app on real device:
"Operation is not valid due to the current state of the object"
According to the thread in Xamarin forum this is issue with Reference. Seems like httpClient instance was created in mono memory but not in iOS memory, due to a difference between an iOS device(AOT) and simulator(JIT) build nature.
Try :
1) Go to References of ios Project
2) Edit References
3) Check 'System.Net.Http'
In general - use ModernHttpClient - it provides wrappers for native networking API, it is secure and faster then default .Net HttpClient.

PDF not loading from documents folder on App relaunch iOS

I have a pdf that I download from the server and save it. Next I open the file from the file path within a UIWebView. This works the first time I launch the app. When I relaunch the app again, even thought the file path is the same, the document does not open. Also, the document does exist in the document folder of the app.
I am doing something like :-
SaveToFolder.cs
var filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), fileName);
using (FileStream destinationStream = File.Create(filePath))
{
await documentStream.CopyToAsync(destinationStream);
}
File path after saving the document first time :-
/var/mobile/Containers/Data/Application/C3EA2325-81CA-4EC9-8C03-479ACF7EE330/Documents/Insufficiency.pdf
File Path on app relaunch
/var/mobile/Containers/Data/Application/C3EA2325-81CA-4EC9-8C03-479ACF7EE330/Documents/Insufficiency.pdf
Is there something Iam doing wrong?
I have created a file in iOS for reading & writing file. Please have a look in iOS
using System;
using Xamarin.Forms;
using FileReader.iOS;
using System.IO;
using FileReader;
using Foundation;
using System.Linq;
using System.Threading.Tasks;
[assembly: Dependency(typeof(SaveAndLoadiOS))]
namespace FileReader.iOS
{
public class SaveAndLoadiOS : LoadAndSave
{
public static string DocumentPath
{
get
{
var documentURL = NSFileManager.DefaultManager.GetUrls(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomain.User).Last();
return documentURL.Path;
}
}
public string CreatePath(string fileName)
{
return Path.Combine(DocumentPath, fileName);
}
public async Task SaveTextAsync(string fileName, string text)
{
string path = CreatePath(fileName);
if (IsFileExits(fileName))
{
File.Delete(path);
}
using (StreamWriter sw = File.CreateText(path))
await sw.WriteAsync(text);
}
public async Task<string> LaodTextAsync(string fileName)
{
string path = CreatePath(fileName);
using (StreamReader sr = File.OpenText(path))
return await sr.ReadToEndAsync();
}
public bool IsFileExits(string fileName)
{
return File.Exists (CreatePath(fileName));
}
}
}
For reading from my .CS class (subclass of ContentPage), Below is the code
var tempFileService = DependencyService.Get<LoadAndSave>();
var itemFile = await tempFileService.LaodTextAsync(tempFile.StoredFileName);
var rootobject = JsonConvert.DeserializeObject<Rootobject>(itemFile);
Where LoadAndSave is an interface as below
using System;
using System.Threading.Tasks;
namespace FileReader
{
public interface LoadAndSave
{
Task SaveTextAsync(string fileName, string text);
Task<string> LaodTextAsync(string fileName);
bool IsFileExits(string fileName);
}
}
Hope it helps.
I ran into the same issue a while ago. You can refer Can't find saved file (in device) after restarting the app
According to the answer
You shouldn't store raw file paths for persistence (or if you do, know that the root can move on you). A better practice would be to only store the relative part of the path and always attach it to the current "root" path in question (particularly if you might be sharing data across devices as with iCloud).
Maybe your root is changing as well. You can change your approach and append the filename with the default path to your documents folder like so in Xamarin:-
var docsPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
filePath = docsPath +"/" + "Insuffeciency.pdf";
Also, consider changing your Personal folder to MyDocuments folder while saving the file.

Xamarin iOS not finding path for sqlite database

I am working on an iOS app that needs to have a simple sqlite db. This is in a portable class library using Xamarin. In the code I'm attempting to get the connection to the DB, but I'm not sure where I should be placing the database in my project folder nor if the #if __ IOS__ is even working honestly, but I'm using based on the Xamarin docs here: http://bit.ly/1MxSYey
public static SQLiteConnection GetConnection()
{
#if __IOS__
var sqliteFilename = "messages.db";
var docs = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
var db = Path.Combine(docs, "..", "Library", sqliteFilename);
return new SQLiteConnection(db);
#endif
return null;
}
IN PCL you should be using interfaces and dependency injection instead using IF directives as in shared solution.
Eg. Xamarin Forms has dependency injection build in (but you can also use another library):
PCL shared library:
public interface ISqlite {
SQLiteConnection GetConnection();
}
iOS specific project:
[assembly: Dependency (typeof (SqliteApple))]
public class SqliteApple : ISqlite
{
public SQLite.SQLiteConnection GetConnection ()
{
var sqliteFilename = "messages.db";
var docs = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
var db = Path.Combine(docs, "..", "Library", sqliteFilename);
return new SQLiteConnection(db);
}
}
and then use it like that:
var database = DependencyService.Get<ISqlite>().GetConnection();

Why is the response content in this streaming service example empty?

I could really use some help understanding why this unit test is failing. I suspect it's due to the way I'm handling the streams. I have a number of other tests that successfully use this self-hosting server setup, but they all read services that return primitives like strings.
Here's the test in question:
using System.Net.Http;
using System.Threading;
using System.Web.Http.SelfHost;
using AttributeRouting.Web.Http.SelfHost;
using NUnit.Framework;
[TestFixture]
public class StreamControllerTests
{
[Test]
public void Can_get_simple_streaming_service_to_respond()
{
using (var config = new HttpSelfHostConfiguration("http://in-memory"))
{
config.Routes.MapHttpAttributeRoutes();
using (var server = new HttpSelfHostServer(config))
{
// I get the same behavior if I use HttpClient
using (var client = new HttpMessageInvoker(server))
{
using (var request = new HttpRequestMessage(HttpMethod.Get, "http://in-memory/stream/notepad"))
{
using (HttpResponseMessage response = client.SendAsync(request, CancellationToken.None).Result)
{
Assert.IsNotNull(response.Content);
// FAILS, content length is 0
Assert.Greater(response.Content.Headers.ContentLength, 0);
}
}
}
}
}
And here is the controller that feeds the test:
using System;
using System.Drawing.Imaging;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using AttributeRouting.Web.Mvc;
using MyUnitTests.Properties;
[GET("stream/notepad")]
public HttpResponseMessage StreamAnImageFromResources()
{
var imageStream = new MemoryStream(); // closed when stream content is read
Resources.a_jpeg_in_resources.Save(imageStream, ImageFormat.Jpeg);
try
{
HttpResponseMessage response = Request.CreateResponse();
// at this point, imageStream contains about 120K bytes
response.Content = new StreamContent(imageStream);
return response;
}
catch (Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.ServiceUnavailable, e);
}
}
I don't see anything really wrong but your test is more complicated than it needs to be.
Try this,
[Test]
public void Can_get_simple_streaming_service_to_respond2()
{
var config = new HttpConfiguration();
config.Routes.MapHttpAttributeRoutes();
var server = new HttpServer(config);
var client = new HttpClient(server);
var request = new HttpRequestMessage(HttpMethod.Get, "http://in-memory/stream/notepad");
HttpResponseMessage response = client.SendAsync(request, CancellationToken.None).Result;
Assert.IsNotNull(response.Content);
// FAILS, content length is 0
Assert.Greater(response.Content.Headers.ContentLength, 0);
}
EDIT: In the comments, Darrel gave me the true answer, which I'm moving to the answer body for visibility:
Check the position of your image stream after doing Save. You need to reset it back to 0 before passing to StreamContent. Also, you might want to consider doing GetManifestResourceStream instead, it will save copying the bytes into managed memory.

Localization in MonoDroid

My app is localized using the standard .NET RESX methods (ie. String.fr.resx, Strings.de.resx etc.) works great under Windows Phone.
I am porting to Android using MonoDroid and I do not see the localized UI when I switch locales on the phone. If I rename the APK file to ZIP and open it I see that it has not packaged up the locale DLLs produced during the build (ie. the intermediate \.Resources.dll files are under the bin directory but are not packaged into the APK).
What am I missing? I have tried changing the build action on the RESX files from "Embedded Resource" to "Android Resource" and even "Android Asset" but to no avail.
Thanks in advance for any help!
Cheers
Warren
I asked about this on the monodroid irc channel and the official answer was "not supported yet but we do have plans to do it".
You need to convert the resx files to android xml format (see below) and add them to your project as shown here: http://docs.xamarin.com/android/tutorials/Android_Resources/Part_5_-_Application_Localization_and_String_Resources
In my app (game) I needed to look up the localised strings by name. The code to do this was simple but not immediately obvious. Instead of using ResourceManager I swapped in this for android:
class AndroidResourcesProxy : Arands.Core.IResourcesProxy
{
Context _context;
public AndroidResourcesProxy(Context context)
{
_context = context;
}
public string GetString(string key)
{
int resId = _context.Resources.GetIdentifier(key, "string", _context.PackageName);
return _context.Resources.GetString(resId);
}
}
Since I'm not a XSLT guru I made a command line program for converting resx to Android string XML files:
/// <summary>
/// Conerts localisation resx string files into the android xml format
/// </summary>
class Program
{
static void Main(string[] args)
{
string inFile = args[0];
XmlDocument inDoc = new XmlDocument();
using (XmlTextReader reader = new XmlTextReader(inFile))
{
inDoc.Load(reader);
}
string outFile = Path.Combine(Path.GetDirectoryName(inFile), Path.GetFileNameWithoutExtension(inFile)) + ".xml";
XmlDocument outDoc = new XmlDocument();
outDoc.AppendChild(outDoc.CreateXmlDeclaration("1.0", "utf-8", null));
XmlElement resElem = outDoc.CreateElement("resources");
outDoc.AppendChild(resElem);
XmlNodeList stringNodes = inDoc.SelectNodes("root/data");
foreach (XmlNode n in stringNodes)
{
string key = n.Attributes["name"].Value;
string val = n.SelectSingleNode("value").InnerText;
XmlElement stringElem = outDoc.CreateElement("string");
XmlAttribute nameAttrib = outDoc.CreateAttribute("name");
nameAttrib.Value = key;
stringElem.Attributes.Append(nameAttrib);
stringElem.InnerText = val;
resElem.AppendChild(stringElem);
}
XmlWriterSettings xws = new XmlWriterSettings();
xws.Encoding = Encoding.UTF8;
xws.Indent = true;
xws.NewLineChars = "\n";
using (StreamWriter sr = new StreamWriter(outFile))
{
using (XmlWriter writer = XmlWriter.Create(sr, xws))
{
outDoc.Save(writer);
}
}
}
}

Resources