Écriture/lecture/modification de fichiers dans un projet Universel app/UWP et cordova

Dans nos applications, nous avons pris le parti d’écrire nos fichiers en JSON. Nous avons donc conçu une abstraction (en WinJS, avec un système de promises) qui nous permet d’utiliser des méthodes claires et simples sur toutes les plateformes (WinRT/UWP ou Cordova (iOS/Android) )

Ce module « DataContainer » est disponible dans notre libraire WinJSContrib (Github)

Dans WinJSContrib.DataContainer, nous avons 4 fichiers qui exposent les mêmes méthodes avec des implémentations différentes :

  • read : lecture d’un élément
  • save : enregistrement d’un élément
  • remove : suppression d’un élément
  • list : liste des fichiers d’un container
  • child : création/accès container enfant

Dans une application WinRT/UWP nous allons naturellement utiliser la couche WinRT/UWP pour écrire/lire les fichiers. Pour cela il suffit d’inclure le fichier « winjscontrib.datacontainer.winrt.file.js »

Dans une application cordova IOS ou Android, nous avons plusieurs choix :

  • Utiliser une base de données : winjscontrib.datacontainer.cordova.database.js
  • Utiliser le système de fichiers avec le plugin file : « winjscontrib.datacontainer.cordova.file.js »
  • Utiliser le localStorage : « winjscontrib.datacontainer.localstorage.js » (utilisable en WinRT aussi)

Ensuite, il faut instancier un container parent et l’utiliser partout dans l’application.

Dans le JS des applications Windows 8/Phone il suffit d’instancier le container (dossier) parent :

MyApp.Data.container = new WinJSContrib.DataContainer.WinRTFilesContainer(undefined, { logger: WinJSContrib.Logger });

Et changer WinRTFilesContainer par notre choix (CordovaDatabaseContainer par exemple) pour l’application cordova en faisant attention de ne l’appeler qu’après l’enclenchement de l’évènement deviceready.

Et c’est tout, la magie s’opère à l’intérieur de notre librairie 🙂

Quelques exemples d’utilisation :

  • Pour lire un fichier :
  • Data.container.read("objKey").then(function (data) { }, function (error) { });
    
  • Supprimer un fichier :
  • Data.container.remove("objKey").then(function () { }, function (error) { });
    
  • Enregistrer un fichier :
  • Data.container.save("objKey",obj).then(function () { }, function (error) { });
    
  • Accès à un container enfant (sous dossier) avec la lecture d’un fichier fichier :
  • Data.container.child(folderid).read("subObjKey").then( function (subObjInFolderID) { }, function (error) { }));
    
  • Liste des fichiers dans un container :
  • Data.container.list().then(function (res) {}, function (error) { });
    

Xamarin – Using SQLite.NET Async with PCL

With Xamarin, it is now easier than ever to share most of the logic between your apps.
Most today’s apps use a lot of data, so deciding how to store them locally can prove a challenge.
SQLite provides a great way to store and query those easily and does support Xamarin. Hurray!

In this post, we’ll see how to setup SQLite in a portable project (PCL) shared by two Xamarin projects (Android, iOS) and a UWP project in Visual Studio.
It should be more or less the same with Xamarin Studio.

If you’re interested in using SQLite.NET with Xamarin.Forms, see the Xamarin.Forms documentation instead.

Portable class library and NuGet packages

First of all, we’ll need to create a portable class library supporting Android, iOS and Windows.
Conveniently, Visual Studio provides a template which exactly fits our needs.

PCL.PNG

If for any reason you can’t or won’t use this template, you can use the generic « Class Library (Portable) » template instead.
See Xamarin’s Introduction to Portable Class Libraries for more information on it.

Then, we’ll need to add references to SQLite.NET via the NuGet package SQLite.NET.Async-PCL v3.1.1 which comes along with its dependency SQLite.Net.Core-PCL.

Nuget1.PNG

That’s all we need to setup the PCL in order to use SQLite.NET.

Using SQLite.NET

Now for the tricky part.

We can now use SQLite in the PCL and thus write it once and be able to use it on each platform. But in order to do so, we’ll need a connection to the SQLite database (and need to create it if it’s not the case).

As we’re targeting three different platforms, each with its own rules on how to access the storage space, SQLite.NET needs several things in order to work : a path to where SQLite should store the database file and a custom implementation of the wrapper around the native sqlite3.

For that, as we’re using SQLite.NET Async, we need to create a SQLiteAsyncConnection.
It’s fairly simple.

public class SQLiteDatabase
{
    public static SQLiteAsyncConnection GetConnection(string path, ISQLitePlatform sqlitePlatform)
    {
        var connectionFactory = new Func<SQLiteConnectionWithLock>(() => new SQLiteConnectionWithLock(sqlitePlatform, new SQLiteConnectionString(path, storeDateTimeAsTicks: false)));
        return new SQLiteAsyncConnection(connectionFactory);
    }
}

See what we did there?
We have created a connection to the SQLite database but all the platform-specific parts are passed as parameters, allowing us to have a common method in the PCL project.

The path parameter is a file path which SQLite will use to store its db file.
The ISQLitePlatform parameter is described in the SQLite NuGet package and represents an internal implementation of SQLite for a given platform. Don’t worry, it has already been done for us for Android, iOS and UWP.

Of course, you can use dependency injection or any architecture that fits your needs instead. The key point here is to leave the implementation detail to the output projects.

We’ll come back shortly after to these platform-specific parameters.

For now enjoy!

public class MyEntityRepository
{
    private SQLiteAsyncConnection _connection;

    public async Task InitializeAsync(string path, ISQLitePlatform sqlitePlatform)
    {
        _connection = SQLiteDatabase.GetConnection(path, sqlitePlatform);

        // Create MyEntity table if need be
        await _connection.CreateTableAsync<MyEntity>();
    }

    public async Task<MyEntity> CreateAsync(string name)
    {
        var entity = new MyEntity()
        {
            Name = name
        };
        var count = await _connection.InsertAsync(entity);
        return (count == 1) ? entity : null;
    }

    public async Task<IEnumerable<MyEntity>> GetAllAsync()
    {
        var entities = await _connection.Table<MyEntity>().OrderBy(m => m.Name).ToListAsync();
        return entities;
    }
}

 

Android / iOS / UWP projects

Xamarin.Android and Xamarin.iOS will both need the exact same configuration regarding SQLite.NET, but UWP will need one extra small step.

Along with the obvious reference to the PCL project, we’ll need to reference the SQLite.NET-PCL v3.1.1 NuGet package as well.
Warning : it is not the same package as the one we used earlier.

This one automatically includes the previously seen ISQLitePlatform implementation of the targeted platform.
When referenced in an output project, it brings another dependency with it where all the platform-specific implementation is present.

  • SQLite.Net.Platform.XamarinAndroid when referenced in a Xamarin.Android project
  • SQLite.Net.Platform.XamarinIOS in a Xamarin.iOS project
  • SQLite.Net.Platform.WinRT in a UWP project

Last step is to use our MyEntityRepository by giving it all the platform specific parameters.

Android

private string GetDbPath()
{
    string documentsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
    return Path.Combine(documentsPath, "Todo.db3");
}

protected override async void OnCreate(Bundle bundle)
{
    base.OnCreate(bundle);

    // Set our view from the "main" layout resource
    SetContentView(Resource.Layout.Main);
    var addButton = FindViewById<Button>(Resource.Id.AddButton);
    var todoListView = FindViewById<ListView>(Resource.Id.TodoListView);
    addButton.Click += OnAddButtonClicked;

    _todoRepository = new TodoRepository();

    var path = GetDbPath();

    await _todoRepository.InitializeAsync(path, new SQLitePlatformAndroid());
    var items = await _todoRepository.GetAllAsync();

    todoListView.Adapter = new ArrayAdapter<string>(this, global::Android.Resource.Layout.SimpleListItem1, items.Select(i => i.Text).ToList());
}

private async void OnAddButtonClicked(object sender, EventArgs e)
{
    var todoEditText = FindViewById<EditText>(Resource.Id.TodoEditText);
    var text = todoEditText.Text;
    todoEditText.Text = string.Empty;

    var todoItem = await _todoRepository.CreateAsync(text);
            
    var todoListView = FindViewById<ListView>(Resource.Id.TodoListView);
    var adapter = todoListView.Adapter as ArrayAdapter<string>;
    adapter.Add(todoItem.Text);
}

 

iOS

private string GetDbPath()
{
    string documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
    return Path.Combine(documentsPath, "..", "Library", "Todo.db3");
}

public override async void ViewDidLoad()
{
    base.ViewDidLoad();
    AddButton.TouchUpInside += OnAddButtonTouchUpInside;
            
    _todoRepository = new TodoRepository();

    var path = GetDbPath();

    await _todoRepository.InitializeAsync(path, new SQLitePlatformIOS());
    var items = await _todoRepository.GetAllAsync();
            
    TodoTable.Source = new TodoTableDataSource(items, TodoTable);
    TodoTable.ReloadData();
}

private async void OnAddButtonTouchUpInside(object sender, EventArgs e)
{
    var text = TodoTextField.Text;
    TodoTextField.Text = string.Empty;

    var todoItem = await _todoRepository.CreateAsync(text);

    var source = TodoTable.Source as TodoTableDataSource;
    source.Add(todoItem);
}

 

Universal Windows Platform

Remember that one extra small step ? Now is the time to take care of it.

Windows, contrary to Android and iOS, doesn’t have SQLite3 installed by default. So we need to bring sqlite3.dll along in our appx package otherwise we’ll get an exception « Unable to load sqlite3.dll ».
To do so, we’ll need to install the Visual Studio extension for UWP which can be found on the SQLite official website.

Then we just have to add two references in our UWP project.

  • SQLite for Universal Windows Platform (that’s our sqlite3.dll added by the VSIX)
  • Visual C++ 2015 Runtime for Universal Windows Platform (it’s a dependency of the former)

 
Capture.PNG
And that’s all for that extra small step.

Now we can use SQLite like in the Android and iOS projects.

public ObservableCollection<TodoItem> Items { get; set; } = new ObservableCollection<TodoItem>();

private async Task<string> GetDbPathAsync()
{
    var folder = Windows.Storage.ApplicationData.Current.LocalFolder;
    var file = await folder.CreateFileAsync("Todo.db3", CreationCollisionOption.OpenIfExists);
    return file.Path;
}

protected override async void OnNavigatedTo(NavigationEventArgs e)
{
    _todoRepository = new TodoRepository();

    var path = await GetDbPathAsync();

    await _todoRepository.InitializeAsync(path, new SQLite.Net.Platform.WinRT.SQLitePlatformWinRT());

    var items = await _todoRepository.GetAllAsync();
    foreach (var todoItem in items)
    {
        Items.Add(todoItem);
    }
}

private async void OnAddTodoItemButtonClicked(object sender, RoutedEventArgs e)
{
    var text = TodoTextBox.Text;
    TodoTextBox.Text = string.Empty;

    var item = await _todoRepository.CreateAsync(text);
    Items.Add(item);
}

 
And there we are! That’s all for today.
Go make some happy users with great apps using SQLite.NET and Xamarin!
 
I’ve put up a sample project on GitHub, it’s a simple Todo list app working on Android, iOS and UWP : https://github.com/TimLariviere/XamarinSQLite

Le Windows Store for Business de Microsoft est enfin là !

Après plusieurs mois d’attente le « Business Store » est enfin disponible et accessible gratuitement avec quelques prérequis :

  • Avoir un Azure Active Directory.
  • Des postes clients sous Windows 10 uniquement (Desktop/Phone)
  • (Énormément de patience pour le déploiement…)

(Pour les clients sous Windows 8 et Windows 8.1, ou ceux qui n’ont pas d’Azure AD, nous avons une solution pour vous !)

Comment s’inscrire ?

La première étape se déroule sur https://businessstore.microsoft.com, l’administrateur général de l’AD se connecte avec le compte de l’organisation.

La création du store est immédiate, mais son déploiement prend du temps.

L’administrateur peut attribuer les droits à d’autres utilisateurs de l’AD de gérer le store (directement dans le BO du store) pour les actes d’achats, d’attributions de licences et de publications.

login.png

Les applications grand public :

Après « l’achat » (visiblement pour l’instant nous ne pouvons acheter que des applications gratuites), l’attribution de la licence peut être globale (au niveau de l’organisation), par groupe ou par utilisateur. Le déploiement prend plusieurs heures (+12h)

achat.png

 Les application LOB :

Le processus est long puisqu’il se passe en plusieurs actes :

  • Premier acte :
    • L’administrateur invite un éditeur LOB avec une adresse mail pour que celui-ci puisse attribuer à l’organisation une ou des applications. (Éditeur LOB = un simple compte de développement qui possède un accès au Windows Dev Center https://dev.windows.com)

lob.png

  • Deuxième acte :
    • L’éditeur publie ou met à jour une application en précisant dans la section « tarifs et disponibilités », le store d’entreprise qu’il souhaite viser (une même app LOB peut être diffusée sur différents Business Stores).
  • Troisième acte :
    • Après la validation de l’application par Microsoft (cette étape dure entre quelques heures à quelques jours), l’application est disponible coté BO du Business Store, et c’est à ce moment là que l’administrateur peut l’ajouter au catalogue, de la même manière qu’une application grand public (avec les droits qui vont avec et l’attente du déploiement qui prend aussi plusieurs heures)

Configuration du poste client (Desktop/Phone):

L’étape la plus simple se passe coté poste client où il suffit de se connecter ou d’ajouter (si ce n’est pas déjà le cas) le compte utilisateur rattaché à l’AD directement dans l’application du store. Un nouvel onglet va apparaitre avec la liste des applications attribuées que l’utilisateur pourra alors installer.

Sans titre.png

A vos déploiements !

Bouton précédent (Back Button) avec le System Navigation Manager dans les applications (UWP) Windows 10

Dans les nouvelles applications Windows 10, nous avons la possibilité dans la version desktop d’une application d’utiliser le bouton de navigation (bouton précédent) du système qui est placé à coté du nom de l’application dans la barre de titre (comme celui qu’on trouve dans l’application de paramètres:

back

Ce bouton est nativement géré par WinJSContrib, il suffit pour cela d’activer l’option « enableSystemBackButton » :

 WinJSContrib.UI.enableSystemBackButton = true;

Un sample est disponible ici 🙂

Vous pouvez aussi l’implémenter manuellement. Il suffit d’utiliser la classe SystemNavigationManager et de modifier la propriété appViewBackButtonVisibility pour afficher ou cacher le bouton.

// il faut vérifier la disponibilité de l'api (disponible que sur desktop)
if (window.Windows && window.Windows.UI && Windows.UI.Core && Windows.UI.Core.SystemNavigationManager) {
    Windows.UI.Core.SystemNavigationManager.getForCurrentView().appViewBackButtonVisibility = Windows.UI.Core.AppViewBackButtonVisibility.visible;
    // ou pour cacher le bouton
    Windows.UI.Core.SystemNavigationManager.getForCurrentView().appViewBackButtonVisibility = Windows.UI.Core.AppViewBackButtonVisibility.collapsed;
}

Et pour gérer le clique il suffit de se brancher sur l’événement onbackrequested:

if (window.Windows && window.Windows.UI && Windows.UI.Core && Windows.UI.Core.SystemNavigationManager) {
    var systemNavigationManager = Windows.UI.Core.SystemNavigationManager.getForCurrentView();
    systemNavigationManager.onbackrequested = function (args) {
        if (WinJS.Navigation.canGoBack) {
            WinJS.Navigation.back();
            args.handled = true;
        } else {
            systemNavigationManager.appViewBackButtonVisibility = Windows.UI.Core.AppViewBackButtonVisibility.visible;
        }
    }
}

Pour la version en C# (et ma source) c’est par ici

Débuguer une Universal Windows app et d’une application Cordova dans Visual Studio

Petit rappel : Vu l’architecture du projet (Applications natives en Universal App + application cordova) il faut bien penser à ajouter les fichiers copiés depuis le shared project en as link dans l’application cordova. C’est le seule moyen de débuguer/modifier le bon fichier.

 

Dans les applications universelles aucune configuration spéciale n’est nécessaire, Visual Studio gère parfaitement le debug. Il suffit de lancer l’application en mode debug et avoir accès dès le lancement au dom ainsi qu’aux points d’arrêts. Les choses se compliquent légèrement avec les applications cordova. En effet Visual studio met plusieurs secondes (voir minutes) avant d’attacher correctement et d’une façon stable le debugger. Et si on souhaite débuguer les premières lignes qui s’exécutent lors du lancement de l’application, cela devient rapidement compliqué, voir impossible.

Deux solutions existent :

  • La première (mais qui ne marche que si l’application s’initialise correctement = pas de crash au démarrage) c’est simplement via la console JavaScript de Visual Studio d’appeler la méthode window.location.reload() la page index.html est rechargée = les scripts se ré exécutent et on a donc accès aux debug des premières lignes de code.
  • La deuxième c’est de créer un mode debug avec une étape intermédiaire qui consistera à avoir un fichier index.html qui charge le stricte minimum de fichiers qui contient un lien (button) qui va nous permettre de naviguer vers une deuxième page qui va charger le reste des fichiers. Avec cette formule on s’assure du démarrage de l’application, on peut attendre que le debugger Visual Studio soit correctement attaché pour pourvoir charger les autres fichiers.

 

Le débogage/déploiement sous iOS requière un mac et l’installation des outils de build:

  • (Mac) Depuis le terminal lancer la commande désinstallation: sudo npm install -g vs-mda-remote –user=$USER ($user = l’utilisateur courant)
  • (Mac) Après l’installation lancer la commande: vs-mda-remote –secure false
  • Configurer Visual Sutdio: Tools => Options => Tools for Apache Cordova => remote Agent configuration:
    • Enable remote iOS processing = True
    • Host : adresse ip du mac de build.

Le déploiement peut se faire avec le simulateur ou un device.

Avec ces quelques notes vous avez maintenant les outils pour pourvoir débugger facilement votre nouvelle application.

WinJS dans une application cross plateforme Cordova

Depuis avril 2014, WinJS le Framework SPA de Microsoft est devenu libre (disponible sur github) et  surtout cross-platform.
C’est avec ces nouvelles possibilités qu’on peut maintenant l’utiliser sur d’autres plateformes que Windows 8/Phone 8.1.

Dans cette série de posts, je vais donc aborder le développement multiplateforme d’application avec WinJS.

Comment lier une application Windows 8.1 à une application Windows Phone 8.1 (Universal app)

Les applications Universelles ou Universal Apps offrent la possibilité aux développeurs de créer une application qui cible différentes plateformes, du Windows Phone 8.1 au Windows 8.1 en passant par Windows RT.

En réalité lors de la création d’un projet Universal App, Visual Studio va créer deux projets (l’app Windows 8.1 + l’app Windows Phone 8.1) et un espace de partage.

On peut intégrer dans cet espace de partage tous les fichiers susceptibles d’être communs : les vues, le code business… Dans beaucoup d’applications, l’ergonomie va être spécifique pour chaque plateforme et le partage du code d’IHM risque donc d’être très compliqué, voir impossible.

Il est maintenant possible avec Windows Phone 8.1 de développer des applications en HTML5/JS, mais il faut obligatoirement passer par le modèle Universal App.

Et voici les étapes pour lier une application W8.1 à une app WP8.1 :

Étape 1 : Le store

  1. Se connecter au backoffice du store Windows Phone : http://dev.windowsphone.com avec le même compte que celui utilisé pour le backoffice du store Windows http://dev.windows.com.
  2. Aller sur le Dashboard.
  3. Cliquer sur le bouton Submit App.
  4. Cliquer sur le chiffre 1 pour renseigner les informations de l’application.
  5. Il suffit maintenant de choisir le nom de l’application dans la liste (NB: il ne faut pas le renseigner manuellement) et de cliquer sur le bouton « Associate app ».store1store2
  6. Choisir une catégorie et enregistrer les modifications.

 

 

Étape 2 : Visual Studio

Pour transformer une application Windows 8 .1 en Universal App, il suffit de faire un clic droit sur le projet et choisir « Add Windows Phone 8.1 »

vs1

Deux nouveaux projets sont alors créés : l’application Windows Phone et le projet Shared.

vs2

Clic droit sur le nouveau projet Windows Phone, aller dans « Store » et cliquer sur « Associate App with the Store ».

vs3

 

Une fenêtre s’affiche, il faut se connecter avec le compte du store pour obtenir la liste des applications créées dans le dashboard.

vs4

On sélectionne la bonne application et on valide.

Il ne reste plus qu’à faire les développements et soumettre l’application pour la certification 🙂 !