Utilisation du langage de script CSCS pour le développement multiplateforme

Publié: 2022-03-10
Résumé rapide ↬ Dans cet article, Vassili Kaplan explique comment utiliser un langage de script pour développer des applications mobiles multiplateformes. Vous trouverez des exemples dans iOS et Android qui incluent le placement de widgets à l'écran, SQLite, les requêtes Web et l'analyse JSON.
Notre objectif n'est pas de construire une plate-forme ; c'est pour les traverser tous.

—Marc Zuckerberg

CSCS (Customized Scripting in C#) est un langage de script open source implémenté en C#. Syntactiquement, il est très similaire à JavaScript, mais il présente également quelques similitudes avec Python. Certaines de ces similitudes sont les mots clés de la construction bien connue if…elif…else , et ont également la même définition de portée de variable qu'en Python (par exemple, une variable définie à l'intérieur d'un bloc if ou à l'intérieur d'une boucle sera également visible à l'extérieur) .

Contrairement à JavaScript et Python, les variables et les fonctions dans CSCS ne sont pas sensibles à la casse. L'objectif principal de CSCS est de permettre au développeur d'écrire le moins de code possible . De plus, le même code est utilisé pour le développement iOS et Android. De plus, CSCS peut être utilisé pour le développement Windows, Mac et Unity.

Remarque : Vous pouvez en savoir plus sur la façon dont Microsoft utilise CSCS dans son produit Maquette (basé sur Unity) ici.

CSCS peut être ajouté à votre projet en incorporant son code source C# dans un projet Visual Studio Xamarin. Contrairement à la plupart des autres langages, vous avez la pleine propriété du code source CSCS et pouvez facilement ajouter ou modifier ses fonctionnalités. Je partagerai un exemple de cela plus tard dans l'article.

De plus, nous allons apprendre à démarrer avec CSCS et à utiliser certaines fonctionnalités plus avancées qui ont été couvertes dans d'autres articles. Parmi ces fonctionnalités, nous allons accéder à un service Web via des requêtes Web avec analyse de chaînes JSON, et nous utiliserons également SQLite sur iOS et Android.

Le moyen le plus simple de commencer est de télécharger un échantillon d'un projet à l'aide de CSCS et de commencer à jouer avec le fichier start.cscs . C'est ce que nous allons faire dans la section suivante : créer une application iOS/Android avec une interface graphique et des événements de base.

Plus après saut! Continuez à lire ci-dessous ↓

"Bonjour le monde!" Au CSCS

Commençons par un exemple relativement simple de code CSCS qui construit un écran avec quelques widgets :

 AutoScale(); SetBackgroundColor("light_green"); locLabelText = GetLocation("ROOT", "CENTER", "ROOT", "TOP"); AddLabel(locLabelText, "labelText", "Welcome " + _DEVICE_INFO_ + " " + _VERSION_INFO_ + " User!", 600, 100); locTextEdit = GetLocation("ROOT", "LEFT", labelText, "BOTTOM"); AddTextEdit(locTextEdit, "textEdit", "Your name", 320, 80); locButton = GetLocation(textEdit,"RIGHT",textEdit, "CENTER"); AddButton(locButton, "buttonHi", "Hello", 160, 80); function buttonHi_click(sender, arg) { name = getText(textEdit); msg = name != "" ? "Hello, "+ name + "!" : "Hello, World!"; AlertDialog("My Great App", msg); }

L'image ci-dessous montre l'interface utilisateur résultante sur un iPhone ainsi que sur un appareil Android après avoir cliqué sur le bouton "Bonjour" et n'avoir rien tapé dans le champ "Modifier le texte":

Bonjour le monde!" sur iPhone (à gauche) et Android (à droite)
"Bonjour le monde!" sur iPhone (à gauche) et Android (à droite) ( Grand aperçu )

Passons brièvement en revue le code ci-dessus. Cela commence par l'appel de la fonction AutoScale() , et ce que cela fait est de dire à l'analyseur que les tailles du widget sont relatives à la taille de l'écran, c'est-à-dire qu'elles seront automatiquement redimensionnées (le widget paraîtra plus grand sur les écrans plus grands et plus petit sur les plus petits écrans). Ce paramètre peut également être remplacé par widget.

Notez qu'il n'est pas nécessaire de créer un gestionnaire spécial lors d'un clic sur un bouton. Si vous définissez une fonction avec le nom widgetName_click() , elle sera utilisée comme gestionnaire lorsque l'utilisateur clique sur un widget appelé widgetName (il n'est pas nécessaire que ce soit un bouton, cela peut en fait être n'importe quel widget). C'est pourquoi la fonction buttonHi_click() sera déclenchée dès que l'utilisateur cliquera sur le bouton.

Vous avez peut-être remarqué que l'interface graphique est entièrement construite en code. Cela se fait en fournissant un emplacement de widget relatif lors de son ajout. Le format général d'une commande de localisation est le suivant :

 location = GetLocation(WidgetX, HorizontalPlacement, WidgetY, VerticalPlacement, deltaX=0, deltaY=0, autoResize=true);

Ainsi, vous pouvez placer un widget par rapport à d'autres widgets sur l'écran. Un cas particulier de widget est un widget "ROOT", c'est-à-dire l'écran principal.

Après avoir créé un emplacement, vous devez le fournir comme argument à l'une des fonctions suivantes :

  • AddLabel ,
  • AddButton ,
  • AddCombobox ,
  • AddStepper ,
  • AddListView ,
  • AddTextView ,
  • AddStepper ,
  • AddImageView ,
  • AddSlider ,
  • AddPickerView ,
  • etc.

Tous les éléments ci-dessus ont la même structure :

 AddButton(location, newWidgetname, initialValue, width, height);

La largeur et la hauteur du widget seront relatives à la taille de l'écran si la commande AutoScale() CSCS a été exécutée précédemment. De plus, la valeur initiale (dans le cas d'un bouton) est le texte affiché dessus. Cela peut être modifié à tout moment en SetText(widgetName, newText) .

Utilisation du code Visual Studio pour déboguer CSCS

Nous pouvons également utiliser Visual Studio Code pour déboguer les scripts CSCS. Si vous souhaitez développer des applications pour Android et iOS, vous devez utiliser un Mac. Après avoir installé Visual Studio Code, installez le débogueur CSCS et l'extension REPL.

Pour utiliser l'extension, ajoutez cette ligne de code n'importe où dans votre script CSCS start.cscs :

 StartDebugger();

L'image ci-dessous montre comment vous pouvez utiliser Visual Studio Code pour déboguer et modifier la fonctionnalité de "Hello, World!" app que nous avons développé dans la section précédente. Dans l'exemple à venir, nous ajouterons une étiquette et un bouton à la volée à la mise en page existante.

Pour ce faire, il suffit de sélectionner le code à exécuter par le parseur et d'appuyer sur Ctrl + 8 . En conséquence, une étiquette et un bouton seront ajoutés au centre de l'écran. Nous ajoutons également un gestionnaire de bouton qui mettra à jour la nouvelle étiquette avec l'heure actuelle à chaque clic sur le bouton.

Modification de la mise en page à la volée avec Visual Studio Code
Modification de la mise en page à la volée avec Visual Studio Code ( Grand aperçu )

Utilisation de SQLite dans CSCS

SQLite est une base de données relationnelle de type ACID (Atomicité, Cohérence, Isolation, Durabilité) et a été développée par Richard Hipp (la première version est sortie en 2000). Contrairement à d'autres bases de données relationnelles, comme Microsoft SQL Server ou Oracle Database, elle est intégrée. (Intégré non seulement dans l'appareil, mais également dans le programme final.) Il est inclus dans le programme en tant que bibliothèque très compacte, d'une taille inférieure à 500 Ko. Mais deux applications (publiées par le même développeur) peuvent lire la même base de données SQLite si le chemin du fichier de base de données est connu des deux applications.

L'avantage de SQLite est qu'il peut être utilisé sans installation supplémentaire sur un appareil iOS ou Android. L'inconvénient est qu'il ne peut évidemment pas contenir autant de données qu'une base de données "normale" et aussi qu'il est faiblement typé (c'est-à-dire que vous pouvez insérer une chaîne au lieu d'un entier — il sera alors converti en entier ou 0 en cas d'échec). D'autre part, ce dernier peut également être considéré comme un avantage.

SQLite peut être facilement utilisé à partir de CSCS sans instructions d'importation supplémentaires. Voici un tableau qui vous aidera à avoir un aperçu des principales fonctions SQLite utilisées dans CSCS :

Commander La description
SQLInit(DBName) Initialise une base de données ou définit une base de données à utiliser avec les instructions DB correspondantes.
SQLDBExists(DBName) Vérifie si le DB a été initialisé. Définit également la base de données à utiliser avec les instructions DB correspondantes.
SQLQuery(query) Exécute une requête SQL (une instruction select). Renvoie une table avec des enregistrements.
SQLNonQuery(nonQuery) Exécute une non-requête SQL, par exemple une instruction de mise à jour, de création ou de suppression. Renvoie le nombre d'enregistrements affectés.
SQLInsert(tableName, columnList, data) Insère une table de données d'enregistrements transmise à la table de base de données spécifiée. L'argument columnList a la structure suivante : colName1,colName2,…,colNameN

Tableau 1 : Commandes SQLite dans CSCS

Voici comment les fonctions SQLInit() et SQLDBExists() sont généralement utilisées :

 DBName = "myDB.db1"; if (!SQLDBExists(DBName)) { create = "CREATE TABLE [Data] (Symbol ntext, Low real, High real, Close real, Volume real, Stamp text DEFAULT CURRENT_TIMESTAMP)"; SQLNonQuery(create); } SQLInit(DBName);

Nous verrons plus tard d'autres exemples de la façon dont vous pouvez sélectionner et insérer des données dans une base de données SQLite. Je vais vous montrer un exemple de la façon d'écrire des données de stock qui ont été extraites d'un service Web dans une base de données SQLite locale.

Ajout de fonctionnalités personnalisées à CSCS

Dans cette section, nous allons voir comment vous pouvez étendre la fonctionnalité CSCS. A titre d'exemple, nous allons voir ci-dessous l'implémentation existante de la fonction CSCS Sleep.

Pour ajouter des fonctionnalités personnalisées, il vous suffit de créer une nouvelle classe en dérivant de la classe ParserFunction , en remplaçant sa méthode Evaluate Evaluate() et en enregistrant cette classe auprès de l'analyseur. Voici une version courte (sans vérification d'erreur):

 class SleepFunction : ParserFunction { protected override Variable Evaluate(ParsingScript script) { List args = script.GetFunctionArgs(); int sleepms = Utils.GetSafeInt(args, 0); Thread.Sleep(sleepms); return Variable.EmptyInstance; } } class SleepFunction : ParserFunction { protected override Variable Evaluate(ParsingScript script) { List args = script.GetFunctionArgs(); int sleepms = Utils.GetSafeInt(args, 0); Thread.Sleep(sleepms); return Variable.EmptyInstance; } }

L'enregistrement d'une classe auprès de l'analyseur peut être effectué n'importe où dans la phase d'initialisation via la commande suivante :

 ParserFunction.RegisterFunction("Sleep", new SleepFunction());

C'est ça! Désormais, la méthode Evaluate Evaluate() de la classe SleepFunction sera appelée dès qu'un jeton "Sleep" sera extrait par l'analyseur.

Notez que CSCS est insensible à la casse (à l'exception des instructions de flux de contrôle de base : if , elif , else , for , while , function , include , new , class , return , try , throw , catch , break , continue ). Cela signifie que vous pouvez taper "sleep(100)" ou "Sleep(100)" - les deux appels suspendront le thread en cours d'exécution pendant 100 millisecondes.

Traitement de JSON dans CSCS

JSON (JavaScript Object Notation) est un format d'échange de données léger, composé de paires attribut-valeur et de paires de type tableau. Il a été développé par Douglas Crockford au début des années 2000 (à peu près au même moment où SQLite est également apparu).

Dans cette section, nous allons apprendre à analyser JSON à l'aide de CSCS.

La fonction CSCS pour analyser une chaîne JSON est GetVariableFromJSON(jsonText) . Cette fonction renvoie une table de hachage dans laquelle les clés sont les attributs de la chaîne JSON.

Prenons l'exemple suivant d'une chaîne JSON :

 jsonString = '{ "eins" : 1, "zwei" : "zweiString", "mehr" : { "uno": "dos" }, "arrayValue" : [ "une", "deux" ] }';

Après avoir invoqué :

 a = GetVariableFromJSON();

La variable a sera une table de hachage avec les valeurs suivantes :

 a["eins"] = 1 a["zwei"] = "zweiString" a["mehr"]["uno"] = "dos" a["arrayValue"][0] = "une" a["arrayValue"][1] = "deux"

Dans la section suivante, nous allons voir un autre exemple d'analyse d'une chaîne JSON à partir d'un service Web.

Un exemple d'application avec SQLite, requêtes Web et JSON

Pour une application utilisant SQLite, un service Web et une analyse JSON, nous allons utiliser Alpha Vantage Web Service. Vous pouvez obtenir une clé API gratuitement, mais la version gratuite permet d'accéder à leur service Web pas plus de 5 fois par minute.

À l'aide d'Alpha Vantage, vous pouvez extraire divers ensembles de données financières, y compris les cours des actions. C'est ce que nous allons faire dans notre exemple d'application.

L'image ci-dessous montre à quoi ressemblent les applications Stocks sur un appareil iOS et sur un appareil Android.

Extraction d'actions à partir du service Web Alpha Vantage sur iOS (à gauche) et Android (à droite)
Extraction d'actions d'Alpha Vantage Web Service sur iOS (à gauche) et Android (à droite) ( Grand aperçu )

Le code CSCS pour construire l'interface graphique est le suivant :

 locLabel = GetLocation("ROOT","CENTER", "ROOT","TOP", 0,30); AddLabel(locLabel, "labelRefresh", "", 480, 60); locSFWidget = GetLocation("ROOT","CENTER", labelRefresh,"BOTTOM"); AddSfDataGrid(locSFWidget, "DataGrid", "", graphWidth, graphHeight); listCols = {"Symbol","string", "Low","number", "High", "number", "Close","number", "Volume","number"}; AddWidgetData(DataGrid, listCols, "columns"); colWidth = {17, 19, 19, 19, 26}; AddWidgetData(DataGrid, colWidth, "columnWidth"); locButton = GetLocation("ROOT","CENTER",DataGrid,"BOTTOM"); AddButton(locButton, "buttonRefresh", "Refresh", 160, 80); locLabelError = GetLocation("ROOT","CENTER","ROOT","BOTTOM"); AddLabel(locLabelError, "labelError", "", 600, 160); SetFontColor(labelError, "red"); AlignText(labelError, "center"); getDataFromDB();

La méthode getDataFromDB() extraira toutes les données de la base de données SQLite. Il utilise la requête SQL définie comme suit :

 query = "SELECT Symbol, Low, High, Close, Volume, DATETIME(Stamp, 'localtime') as Stamp FROM Data ORDER BY Stamp DESC LIMIT 5;";

Jetez un œil au code ci-dessous pour l'implémentation de getDataFromDB() .

 function getDataFromDB() { results = SQLQuery(query); for (i = 1; i < results.Size; i++) { vals = results[i]; stock = vals[0]; low = Round(vals[1], 2); high = Round(vals[2], 2); close = Round(vals[3], 2); volume = Round(vals[4], 2); refresh = vals[5]; stockData = {stock, low, high, close, volume}; AddWidgetData(DataGrid, stockData, "item"); } SetText(labelRefresh, "DB Last Refresh: " + refresh); lockGui(false); }

Voyons maintenant comment nous obtenons les données du service Web Alpha Vantage. Tout d'abord, nous initialisons les données :

 baseURL = "https://www.alphavantage.co/query? " + "function=TIME_SERIES_DAILY&symbol="; apikey = "Y12T0TY5EUS6BC5F"; stocks = {"MSFT", "AAPL", "GOOG", "FB", "AMZN"}; totalStocks = stocks.Size;

Ensuite, nous chargeons les stocks un par un dès que l'utilisateur clique sur le bouton "Actualiser":

 function buttonRefresh_click(object, arg) { lockGui(); SetText(labelRefresh, "Loading ..."); SetText(labelError, ""); ClearWidget(DataGrid); loadedStocks = 0; getData(stocks[loadedStocks]); } function getData(symbol) { stockUrl = baseURL + symbol + "&apikey=" + apikey; WebRequest("GET", stockUrl, "", symbol, "OnSuccess", "OnFailure"); }

Voici la principale fonction CSCS à utiliser pour récupérer les données d'un Web Service :

 WebRequest("GET", stockUrl, "", symbol, "OnSuccess", "OnFailure");

Les deux derniers paramètres sont des fonctions à invoquer à la fin de la requête Web. Par exemple, en cas d'échec, la fonction CSCS suivante sera appelée :

 function OnFailure(object, errorCode, text) { SetText(labelError, text); lockGui(false); }

En conséquence, l'utilisateur recevra un message d'erreur comme indiqué ci-dessous :

Une erreur lors de la demande de données Web
Une erreur lors de la demande de données Web ( Grand aperçu )

Mais, si tout va bien, nous allons analyser la chaîne JSON et insérer son contenu dans la base de données SQLite.

 function OnSuccess(object, errorCode, text) { jsonFromText = GetVariableFromJSON(text); metaData = jsonFromText[0]; result = jsonFromText[1]; symbol = metaData["2. Symbol"]; lastRefreshed = metaData["3. Last Refreshed"]; allDates = result.keys; dateData = result[allDates[0]]; high = Round(dateData["2. high"], 2); low = Round(dateData["3. low"], 2); close = Round(dateData["4. close"], 2); volume = dateData["5. volume"]; stockData = {symbol, low, high, close, volume}; SQLInsert("Data","Symbol,Low,High,Close,Volume",stockData); if (++loadedStocks >= totalStocks) { getDataFromDB(); } else { getData(stocks[loadedStocks]); } }

Afin de comprendre comment nous accédons aux différents champs de la table de hachage ci-dessus, examinons la chaîne réelle reçue de la requête Web Alpha Vantage :

 { "Meta Data": { "1. Information": "Daily Prices (open, high, low, close) and Volumes", "2. Symbol": "MSFT", "3. Last Refreshed": "2019-10-02 14:23:20", "4. Output Size": "Compact", "5. Time Zone": "US/Eastern" }, "Time Series (Daily)": { "2019-10-02": { "1. open": "136.3400", "2. high": "136.3700", "3. low": "133.5799", "4. close": "134.4100", "5. volume": "11213086" }, … } }

Comme vous pouvez le voir, nous obtenons la dernière date comme premier élément du tableau allDates qui comprend toutes les dates extraites.

Conclusion

Ajouter CSCS à votre projet est facile. Tout ce que vous avez à faire est simplement d'intégrer le code source de CSCS en tant que module dans votre projet, comme cela se fait dans un exemple de projet Xamarin.

Utilisez-vous et étendez-vous le langage de script CSCS dans vos projets ? Laissez un commentaire ci-dessous - je serais heureux d'avoir de vos nouvelles!

Lectures complémentaires

Si vous souhaitez explorer un peu plus le langage CSCS, voici quelques-uns des articles que j'ai écrits sur le sujet :

  • "Un analyseur d'expression fractionné et fusionné en C#", MSDN Magazine (octobre 2015)
  • « Scripts personnalisables en C# », MSDN Magazine (février 2016)
  • "Écrire des applications mobiles natives à l'aide d'un langage de script personnalisable", MSDN Magazine (février 2018)
  • « CSCS : scripts personnalisés en C# », GitHub
  • "Développement d'applications natives multiplateformes avec un langage de script fonctionnel", CODE Magazine
  • "Mise en œuvre succincte d'un langage personnalisé" (eBook)
  • « Écrire succinctement des applications mobiles natives dans un langage fonctionnel » (eBook)

En tant que ressource supplémentaire, je recommande également de lire comment vous pouvez améliorer les performances de CSCS en précompilant ses fonctions.