Uso del lenguaje de secuencias de comandos CSCS para el desarrollo multiplataforma

Publicado: 2022-03-10
Resumen rápido ↬ En este artículo, Vassili Kaplan explica cómo puede usar un lenguaje de secuencias de comandos para desarrollar aplicaciones móviles multiplataforma. Encontrará ejemplos tanto en iOS como en Android que incluyen la colocación de widgets en la pantalla, SQLite, solicitudes web y análisis JSON.
Nuestro objetivo no es construir una plataforma; es cruzarlos a todos.

- Mark Zuckerberg

CSCS (Secuencias de comandos personalizadas en C#) es un lenguaje de secuencias de comandos de código abierto implementado en C#. Sintácticamente es muy similar a JavaScript, pero también tiene algunas similitudes con Python. Algunas de estas similitudes son las palabras clave en la conocida construcción if…elif…else , y también tienen la misma definición de ámbito de variable que en Python (por ejemplo, una variable definida dentro de un bloque if o dentro de un bucle también será visible fuera) .

A diferencia de JavaScript y Python, las variables y funciones en CSCS no distinguen entre mayúsculas y minúsculas. El objetivo principal de CSCS es permitir que el desarrollador escriba la menor cantidad de código posible . Además, se utiliza el mismo código para el desarrollo de iOS y Android. Además, CSCS se puede usar para el desarrollo de Windows, Mac y Unity.

Nota : Puede leer más sobre cómo Microsoft usa CSCS en su producto Maquette (basado en Unity) aquí.

CSCS se puede agregar a su proyecto al incorporar su código fuente de C# en un proyecto de Visual Studio Xamarin. A diferencia de la mayoría de los otros idiomas, usted tiene la propiedad total del código fuente de CSCS y puede agregar o modificar fácilmente su funcionalidad. Compartiré un ejemplo de esto más adelante en el artículo.

Además, aprenderemos cómo comenzar con CSCS y usar algunas funciones más avanzadas que se han tratado en otros artículos. Entre estas funciones, accederemos a un servicio web a través de solicitudes web con análisis de cadenas JSON, y también usaremos SQLite en iOS y Android.

La forma más fácil de comenzar es descargar una muestra de un proyecto usando CSCS y comenzar a jugar con el archivo start.cscs . Esto es lo que haremos en la siguiente sección: crear una aplicación para iOS/Android con GUI básica y eventos.

¡Más después del salto! Continúe leyendo a continuación ↓

"¡Hola Mundo!" En CSCS

Comencemos con un ejemplo relativamente simple de código CSCS que construye una pantalla con algunos 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); }

La siguiente imagen muestra la interfaz de usuario resultante en un iPhone y en un dispositivo Android después de hacer clic en el botón "Hola" y no escribir nada en el campo "Editar texto":

¡Hola Mundo!" en iPhone (izquierda) y Android (derecha)
"¡Hola Mundo!" en iPhone (izquierda) y Android (derecha) (vista previa grande)

Repasemos brevemente el código anterior. Comienza con la llamada a la función AutoScale() , y lo que hace es decirle al analizador que los tamaños de los widgets son relativos al tamaño de la pantalla, es decir, cambiarán de tamaño automáticamente (el widget se verá más grande en pantallas más grandes y más pequeño en pantallas más pequeñas). pantallas). Esta configuración también podría anularse por widget.

Tenga en cuenta que no es necesario crear un controlador especial al hacer clic en un botón. Si define una función con el nombre widgetName_click() , se usará como controlador cuando el usuario haga clic en un widget llamado widgetName (no tiene que ser un botón, en realidad puede ser cualquier widget). Es por eso que la función buttonHi_click() se activará tan pronto como el usuario haga clic en el botón.

Es posible que haya notado que la GUI está construida completamente en código. Esto se hace proporcionando una ubicación de widget relativa al agregarlo. El formato general de un comando de ubicación es el siguiente:

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

Por lo tanto, puede colocar un widget en relación con otros widgets en la pantalla. Un caso especial de un widget es un widget "ROOT", es decir, la pantalla principal.

Después de crear una ubicación, debe proporcionarla como argumento para cualquiera de las siguientes funciones:

  • AddLabel ,
  • AddButton ,
  • AddCombobox ,
  • AddStepper ,
  • AddListView ,
  • Añadir Vista de AddTextView ,
  • AddStepper ,
  • AddImageView ,
  • AddSlider ,
  • AddPickerView ,
  • y así.

Todos los anteriores tienen la misma estructura:

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

El ancho y la altura del widget serán relativos al tamaño de la pantalla si se ejecutó previamente el comando AutoScale() CSCS. Además, el valor inicial (en el caso de un botón) es el texto que se muestra en él. Esto se puede cambiar en cualquier momento invocando SetText(widgetName, newText) .

Uso de Visual Studio Code para depurar CSCS

También podemos usar Visual Studio Code para depurar scripts CSCS. Si desea desarrollar aplicaciones para Android e iOS, debe usar una Mac. Después de instalar Visual Studio Code, instale el depurador CSCS y la extensión REPL.

Para usar la extensión, agregue esta línea de código en cualquier lugar de su secuencia de comandos start.cscs CSCS:

 StartDebugger();

La siguiente imagen a continuación muestra cómo puede usar Visual Studio Code para depurar y cambiar la funcionalidad de "¡Hola, mundo!" aplicación que desarrollamos en la sección anterior. En el próximo ejemplo, agregaremos una etiqueta y un botón sobre la marcha al diseño existente.

Para hacer esto, simplemente seleccionamos el código que ejecutará el analizador y presionamos Ctrl + 8 . Como resultado, se agregará una etiqueta y un botón en el centro de la pantalla. También agregamos un controlador de botón que actualizará la nueva etiqueta con la hora actual en cada clic de botón.

Cambio de diseño sobre la marcha con Visual Studio Code
Cambio de diseño sobre la marcha con Visual Studio Code (vista previa grande)

Usando SQLite en CSCS

SQLite es un tipo ACID (Atomicidad, Consistencia, Aislamiento, Durabilidad) de una base de datos relacional, y fue desarrollado por Richard Hipp (la primera versión se lanzó en 2000). A diferencia de otras bases de datos relacionales, como Microsoft SQL Server u Oracle Database, está integrada. (Incrustado no solo en el dispositivo, sino también en el programa final). Está incluido en el programa como una biblioteca muy compacta, que tiene menos de 500 KB de tamaño. Pero dos aplicaciones (lanzadas por el mismo desarrollador) pueden leer la misma base de datos SQLite si ambas aplicaciones conocen la ruta del archivo de la base de datos.

La ventaja de SQLite es que se puede usar sin una instalación adicional en un dispositivo iOS o Android. La desventaja es que, obviamente, no puede contener tantos datos como una base de datos "normal" y también que tiene un tipo débil (es decir, puede insertar una cadena en lugar de un número entero; luego se convertirá en un número entero o 0 en caso de falla). Por otro lado, esto último también puede verse como una ventaja.

SQLite se puede usar fácilmente desde CSCS sin declaraciones de importación adicionales. Aquí hay una tabla que lo ayudará a obtener una descripción general de las principales funciones de SQLite utilizadas en CSCS:

Mando Descripción
SQLInit(DBName) Inicializa una base de datos o configura una base de datos para que se utilice con las declaraciones DB consiguientes.
SQLDBExists(DBName) Comprueba si la base de datos se ha inicializado. También establece la base de datos que se usará con las declaraciones DB consecuentes.
SQLQuery(query) Ejecuta una consulta SQL (una declaración de selección). Devuelve una tabla con registros.
SQLNonQuery(nonQuery) Ejecuta una no consulta de SQL, por ejemplo, una declaración de actualización, creación o eliminación. Devuelve el número de registros afectados.
SQLInsert(tableName, columnList, data) Inserta la tabla pasada de datos de registros a la tabla DB especificada. El argumento columnList tiene la siguiente estructura: colName1,colName2,…,colNameN

Tabla 1: Comandos de SQLite en CSCS

Así es como se usan normalmente las funciones SQLInit() y SQLDBExists() :

 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);

Veremos más ejemplos de cómo puede seleccionar e insertar datos en una base de datos SQLite más adelante. Le mostraré un ejemplo de cómo escribir datos bursátiles extraídos de un servicio web en una base de datos SQLite local.

Agregar funcionalidad personalizada a CSCS

En esta sección, veremos cómo puede ampliar la funcionalidad CSCS. Como ejemplo, vamos a ver a continuación la implementación existente de la función Suspensión de CSCS.

Para agregar una funcionalidad personalizada, todo lo que necesita hacer es crear una nueva clase derivando de la clase ParserFunction , anulando su método Evaluate() y registrando esta clase con el analizador. Aquí hay una versión corta (sin verificación de errores):

 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; } }

El registro de una clase con el analizador se puede realizar en cualquier parte de la etapa de inicialización mediante el siguiente comando:

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

¡Eso es todo! Ahora, el método Evaluate() de la clase SleepFunction se invocará tan pronto como el analizador extraiga un token "Sleep".

Tenga en cuenta que CSCS no distingue entre mayúsculas y minúsculas (excepto las declaraciones de flujo de control principal: if , elif , else , for , while , function , include , new , class , return , try , throw , catch , break , continue ). Esto significa que puede escribir "dormir (100)" o "dormir (100)": ambas llamadas suspenderán la ejecución del hilo durante 100 milisegundos.

Procesando JSON en CSCS

JSON (Notación de objetos de JavaScript) es un formato de intercambio de datos liviano, que consta de pares de valor de atributo y pares de tipo de matriz. Fue desarrollado por Douglas Crockford a principios de la década de 2000 (alrededor de la misma época en que apareció SQLite).

En esta sección, vamos a aprender cómo analizar JSON usando CSCS.

La función CSCS para analizar una cadena JSON es GetVariableFromJSON(jsonText) . Esta función devuelve una tabla hash en la que las claves son los atributos de la cadena JSON.

Considere el siguiente ejemplo de una cadena JSON:

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

Después de invocar:

 a = GetVariableFromJSON();

La variable a será una tabla hash con los siguientes valores:

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

En la siguiente sección, veremos otro ejemplo de análisis de una cadena JSON de un servicio web.

Un ejemplo de una aplicación con SQLite, solicitudes web y JSON

Para una aplicación que usa SQLite, un servicio web y análisis JSON, vamos a usar el servicio web Alpha Vantage. Puede obtener una clave API de forma gratuita, pero la versión gratuita permite acceder a su servicio web no más de 5 veces por minuto.

Con Alpha Vantage, puede extraer varios conjuntos de datos financieros, incluidos los precios de las acciones. Esto es lo que vamos a hacer en nuestra aplicación de muestra.

La siguiente imagen muestra cómo se ven las aplicaciones de Bolsa en un dispositivo iOS y Android.

Extracción de acciones del servicio web Alpha Vantage en iOS (izquierda) y Android (derecha)
Extracción de acciones del servicio web Alpha Vantage en iOS (izquierda) y Android (derecha) (vista previa grande)

El código CSCS para construir la GUI es el siguiente:

 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();

El método getDataFromDB() extraerá todos los datos de la base de datos SQLite. Utiliza la consulta SQL definida de la siguiente manera:

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

Eche un vistazo al siguiente código para la implementación 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); }

Ahora veamos cómo obtenemos datos del servicio web Alpha Vantage. Primero, inicializamos los datos:

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

A continuación, cargamos las acciones una por una tan pronto como el usuario haga clic en el botón "Actualizar":

 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"); }

Esta es la función CSCS principal que se debe usar para obtener datos de un servicio web:

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

Los dos últimos parámetros son funciones para invocar al completar la solicitud web. Por ejemplo, en caso de falla, se llamará a la siguiente función CSCS:

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

Como resultado, el usuario recibirá un mensaje de error como se muestra a continuación:

Un error al solicitar datos web
Un error al solicitar datos web (vista previa grande)

Pero, si todo está bien, analizaremos la cadena JSON e insertaremos su contenido en la base de datos 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]); } }

Para comprender cómo accedemos a los diferentes campos en la tabla hash anterior, echemos un vistazo a la cadena real recibida de la solicitud web de 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" }, … } }

Como puede ver, obtenemos la fecha más reciente como el primer elemento de la matriz allDates que consta de todas las fechas extraídas.

Conclusión

Agregar CSCS a su proyecto es fácil. Todo lo que necesita hacer es simplemente insertar el código fuente de CSCS como un módulo en su proyecto, tal como se hace en un proyecto de muestra de Xamarin.

¿Utiliza y amplía el lenguaje de scripting CSCS en sus proyectos? Deja un comentario a continuación: ¡me encantaría saber de ti!

Otras lecturas

Si desea explorar un poco más el lenguaje CSCS, estos son algunos de los artículos que he escrito sobre el tema:

  • "Un analizador de expresiones de división y combinación en C#", MSDN Magazine (octubre de 2015)
  • "Scripts personalizables en C#", MSDN Magazine (febrero de 2016)
  • “Escribir aplicaciones móviles nativas usando un lenguaje de secuencias de comandos personalizable”, MSDN Magazine (febrero de 2018)
  • "CSCS: secuencias de comandos personalizadas en C#", GitHub
  • “Desarrollo de aplicaciones nativas multiplataforma con un lenguaje de secuencias de comandos funcional”, Revista CODE
  • “Implementación sucinta de un lenguaje personalizado” (libro electrónico)
  • “Escribir aplicaciones móviles nativas en un lenguaje funcional de manera sucinta” (libro electrónico)

Como recurso adicional, también recomiendo leer cómo puede mejorar el rendimiento de CSCS precompilando sus funciones.