Utilizarea limbajului de scripting CSCS pentru dezvoltarea pe mai multe platforme

Publicat: 2022-03-10
Rezumat rapid ↬ În acest articol, Vassili Kaplan explică cum puteți utiliza un limbaj de scripting pentru a dezvolta aplicații mobile multiplatforme. Veți găsi exemple atât în ​​iOS, cât și în Android, care includ plasarea widget-urilor pe ecran, SQLite, solicitări web și analiza JSON.
Scopul nostru nu este să construim o platformă; este să fie încrucișați cu toți.

— Mark Zuckerberg

CSCS (Customized Scripting in C#) este un limbaj de scripting open-source implementat în C#. Sintactic este foarte asemănător cu JavaScript, dar are și unele asemănări cu Python. Unele dintre aceste asemănări sunt cuvintele cheie din binecunoscutul construct if…elif…else și au, de asemenea, aceeași definiție a domeniului de aplicare a variabilei ca și în Python (de exemplu, o variabilă definită în interiorul unui bloc if sau în interiorul unei bucle va fi vizibilă și în exterior) .

Spre deosebire de JavaScript și Python, variabilele și funcțiile din CSCS nu fac distincție între majuscule și minuscule. Scopul principal al CSCS este de a permite dezvoltatorului să scrie cât mai puțin cod posibil . De asemenea, același cod este folosit atât pentru dezvoltarea iOS, cât și pentru Android. În plus, CSCS poate fi utilizat pentru dezvoltarea Windows, Mac și Unity.

Notă : Puteți citi mai multe despre modul în care Microsoft utilizează CSCS în produsul său Maquette (bazat pe Unity) aici.

CSCS poate fi adăugat la proiectul dvs. prin încorporarea codului sursă C# într-un proiect Visual Studio Xamarin. Spre deosebire de majoritatea celorlalte limbi, aveți proprietatea deplină asupra codului sursă CSCS și puteți adăuga sau modifica cu ușurință funcționalitatea acestuia. Voi împărtăși un exemplu în acest sens mai târziu în articol.

De asemenea, vom învăța cum să începem cu CSCS și să folosim câteva funcții mai avansate care au fost tratate în alte articole. Printre aceste funcții, vom accesa un serviciu web prin solicitări web cu analizarea șirurilor JSON și vom folosi și SQLite pe iOS și Android.

Cel mai simplu mod de a începe este să descărcați un eșantion al unui proiect folosind CSCS și să începeți să jucați cu fișierul start.cscs . Aceasta este ceea ce vom face în următoarea secțiune: crearea unei aplicații iOS/Android cu GUI și evenimente de bază.

Mai multe după săritură! Continuați să citiți mai jos ↓

"Salut Lume!" În CSCS

Să începem cu un exemplu relativ simplu de cod CSCS care construiește un ecran cu câteva widget-uri:

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

Imaginea de mai jos arată interfața de utilizator rezultată pe un iPhone, precum și pe un dispozitiv Android după ce ați făcut clic pe butonul „Bună ziua” și nu ați tastat nimic în câmpul „Editare text”:

Salut Lume!" pe iPhone (stânga) și Android (dreapta)
"Salut Lume!" pe iPhone (stânga) și Android (dreapta) (previzualizare mare)

Să trecem pe scurt peste codul de mai sus. Începe cu apelul funcției AutoScale() și ceea ce face este să îi spună analizorului că dimensiunile widget-ului sunt relativ la dimensiunea ecranului, adică vor fi redimensionate automat (widgetul va arăta mai mare pe ecrane mai mari și mai mic pe mai mici. ecrane). Această setare poate fi, de asemenea, suprascrisă pentru fiecare widget.

Rețineți că nu este nevoie să creați un handler special la un clic pe buton. Dacă definiți o funcție cu numele widgetName_click() , aceasta va fi folosită ca handler atunci când utilizatorul face clic pe un widget numit widgetName (nu trebuie să fie un buton, poate fi de fapt orice widget). De aceea funcția buttonHi_click() va fi declanșată imediat ce utilizatorul face clic pe buton.

Poate ați observat că GUI-ul este construit complet în cod. Acest lucru se face prin furnizarea unei locații relative a widget-ului la adăugarea acesteia. Formatul general al unei comenzi de locație este următorul:

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

Deci, puteți plasa un widget în raport cu alte widget-uri pe ecran. Un caz special al unui widget este un widget „ROOT”, adică ecranul principal.

După crearea unei locații, trebuie să o furnizați ca argument pentru oricare dintre următoarele funcții:

  • AddLabel ,
  • AddButton ,
  • AddCombobox ,
  • AddStepper ,
  • AddListView ,
  • AddTextView ,
  • AddStepper ,
  • AddImageView ,
  • AddSlider ,
  • AddPickerView ,
  • și așa mai departe.

Toate cele de mai sus au aceeași structură:

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

Lățimea și înălțimea widgetului vor fi relativ la dimensiunea ecranului dacă comanda AutoScale() CSCS a fost rulată anterior. De asemenea, valoarea inițială (în cazul unui buton) este textul afișat pe acesta. Acest lucru poate fi schimbat oricând prin invocarea SetText(widgetName, newText) .

Utilizarea codului Visual Studio pentru a depana CSCS

De asemenea, putem folosi Visual Studio Code pentru a depana scripturile CSCS. Dacă doriți să dezvoltați aplicații atât pentru Android, cât și pentru iOS, trebuie să utilizați un Mac. După instalarea Visual Studio Code, instalați extensia CSCS Debugger și REPL.

Pentru a utiliza extensia, adăugați această linie de cod oriunde în scriptul CSCS start.cscs :

 StartDebugger();

Următoarea imagine de mai jos arată cum puteți utiliza Visual Studio Code pentru a depana și a schimba funcționalitatea „Hello, World!” aplicație pe care am dezvoltat-o ​​în secțiunea anterioară. În exemplul următor, vom adăuga o etichetă și un buton din zbor la aspectul existent.

Pentru a face acest lucru, selectăm doar codul care urmează să fie executat de parser și apăsăm Ctrl + 8 . Ca rezultat, o etichetă și un buton vor fi adăugate în centrul ecranului. Adăugăm, de asemenea, un handler de butoane care va actualiza noua etichetă cu ora curentă la fiecare clic pe buton.

Schimbarea aspectului din mers cu Visual Studio Code
Schimbarea aspectului din mers cu Visual Studio Code (previzualizare mare)

Utilizarea SQLite în CSCS

SQLite este un tip ACID (Atomicity, Consistency, Isolation, Durability) al unei baze de date relaționale și a fost dezvoltat de Richard Hipp (prima versiune a fost lansată în 2000). Spre deosebire de alte baze de date relaționale, cum ar fi Microsoft SQL Server sau Oracle Database, este încorporat. (Încorporat nu numai în dispozitiv, ci și în programul final.) Este inclus în program ca o bibliotecă foarte compactă, care are o dimensiune mai mică de 500 KB. Dar două aplicații (lansate de același dezvoltator) pot citi același SQLite DB dacă calea fișierului DB este cunoscută de ambele aplicații.

Avantajul SQLite este că poate fi folosit fără o instalare suplimentară pe un dispozitiv iOS sau Android. Dezavantajul este că, evident, nu poate deține la fel de multe date ca o DB „normală” și, de asemenea, că este slab tipizat (adică puteți introduce un șir în loc de un întreg - va fi apoi convertit într-un număr întreg sau 0 în caz de eșec). Pe de altă parte, acesta din urmă poate fi văzut și ca un avantaj.

SQLite poate fi utilizat cu ușurință din CSCS fără instrucțiuni suplimentare de import. Iată un tabel care vă va ajuta să obțineți o prezentare generală a principalelor funcții SQLite utilizate în CSCS:

Comanda Descriere
SQLInit(DBName) Inițializează o bază de date sau setează o bază de date pentru a fi utilizată cu instrucțiuni DB ulterioare.
SQLDBExists(DBName) Verifică dacă DB-ul a fost inițializat. Setează, de asemenea, baza de date să fie utilizată cu instrucțiuni DB ulterioare.
SQLQuery(query) Execută o interogare SQL (o instrucțiune select). Returnează un tabel cu înregistrări.
SQLNonQuery(nonQuery) Execută o interogare SQL, de exemplu, o instrucțiune de actualizare, creare sau ștergere. Returnează numărul de înregistrări afectate.
SQLInsert(tableName, columnList, data) Inserează tabelul transmis cu datele înregistrărilor în tabelul DB specificat. Argumentul columnList are următoarea structură: colName1,colName2,…,colNameN

Tabelul 1: Comenzi SQLite în CSCS

Iată cum sunt utilizate de obicei funcțiile SQLInit() și 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);

Mai târziu vom vedea mai multe exemple despre cum puteți selecta și insera date într-o bază de date SQLite. Vă voi arăta un exemplu despre cum să scrieți datele stocurilor care au fost extrase dintr-un serviciu web într-o bază de date locală SQLite.

Adăugarea de funcționalități personalizate la CSCS

În această secțiune, vom vedea cum puteți extinde funcționalitatea CSCS. Ca exemplu, vom vedea implementarea existentă a funcției CSCS Sleep mai jos.

Pentru a adăuga funcționalitate personalizată, tot ce trebuie să faceți este să creați o nouă clasă derivând din clasa ParserFunction , suprascriind metoda sa Evaluate() și înregistrând această clasă la parser. Iată o versiune scurtă (fără verificarea erorilor):

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

Înregistrarea unei clase cu analizatorul se poate face oriunde în etapa de inițializare prin următoarea comandă:

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

Asta e! Acum metoda Evaluate() a clasei SleepFunction va fi invocată de îndată ce un token „Sleep” este extras de parser.

Rețineți că CSCS nu ține seama de majuscule și minuscule (cu excepția instrucțiunilor de flux de control de bază: if , elif , else , for , while , function , include , new , class , return , try , throw , catch , break , continue ). Aceasta înseamnă că puteți tasta fie „sleep(100)” fie „Sleep(100)” - ambele apeluri vor suspenda firul de execuție timp de 100 de milisecunde.

Procesarea JSON în CSCS

JSON (JavaScript Object Notation) este un format ușor de schimb de date, constând din perechi atribut-valoare și perechi de tip matrice. A fost dezvoltat de Douglas Crockford la începutul anilor 2000 (aproximativ în aceeași perioadă când a apărut și SQLite).

În această secțiune, vom învăța cum să analizăm JSON folosind CSCS.

Funcția CSCS pentru a analiza un șir JSON este GetVariableFromJSON(jsonText) . Această funcție returnează un tabel hash în care cheile sunt atributele din șirul JSON.

Luați în considerare următorul exemplu de șir JSON:

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

După invocarea:

 a = GetVariableFromJSON();

Variabila a va fi un tabel hash cu următoarele valori:

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

În secțiunea următoare, vom vedea un alt exemplu de analiză a unui șir JSON dintr-un serviciu web.

Un exemplu de aplicație cu SQLite, solicitări web și JSON

Pentru o aplicație care folosește SQLite, un serviciu web și analiza JSON, vom folosi Alpha Vantage Web Service. Puteți obține o cheie API gratuit, dar versiunea gratuită permite accesarea serviciului lor web de cel mult 5 ori pe minut.

Folosind Alpha Vantage, puteți extrage diverse seturi de date financiare — inclusiv prețurile acțiunilor. Acesta este ceea ce vom face în aplicația noastră exemplu.

Imaginea de mai jos arată cum arată aplicațiile Stocks pe un dispozitiv iOS și pe un dispozitiv Android.

Extragerea stocurilor din Alpha Vantage Web Service pe iOS (stânga) și Android (dreapta)
Extragerea stocurilor din Alpha Vantage Web Service pe iOS (stânga) și Android (dreapta) (previzualizare mare)

Codul CSCS pentru a construi GUI este următorul:

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

Metoda getDataFromDB() va extrage toate datele din baza de date SQLite. Utilizează interogarea SQL definită după cum urmează:

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

Aruncă o privire la codul de mai jos pentru implementarea 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); }

Acum să vedem cum obținem date de la Serviciul Web Alpha Vantage. Mai întâi, inițializam datele:

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

În continuare, încărcăm stocurile unul câte unul de îndată ce utilizatorul face clic pe butonul „Actualizează”:

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

Iată principala funcție CSCS de utilizat pentru a obține date de la un serviciu web:

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

Ultimii doi parametri sunt funcții de invocat la finalizarea cererii web. De exemplu, în cazul unei defecțiuni, următoarea funcție CSCS va fi apelată:

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

Ca rezultat, utilizatorul va primi un mesaj de eroare, după cum se arată mai jos:

O eroare la solicitarea datelor web
O eroare la solicitarea datelor web (previzualizare mare)

Dar, dacă totul este bine, vom analiza șirul JSON și vom introduce conținutul acestuia în SQLite DB.

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

Pentru a înțelege cum accesăm diferite câmpuri din tabelul hash de mai sus, să aruncăm o privire la șirul real primit de la solicitarea 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" }, … } }

După cum puteți vedea, obținem cea mai recentă dată ca prim element al matricei allDates care constă în toate datele extrase.

Concluzie

Adăugarea CSCS la proiectul dvs. este ușoară. Tot ce trebuie să faceți este pur și simplu să încorporați codul sursă al CSCS ca modul în proiectul dvs. - la fel cum se face într-un exemplu de proiect Xamarin.

Utilizați și extindeți limbajul de scripting CSCS în proiectele dvs.? Lasă un comentariu mai jos — m-aș bucura să aud de la tine!

Lectură suplimentară

Dacă doriți să explorați mai mult limbajul CSCS, iată câteva dintre articolele despre care am scris pe această temă:

  • „A Split-and-Merge Expression Parser in C#”, Revista MSDN (oct. 2015)
  • „Scripturi personalizabile în C#”, MSDN Magazine (feb. 2016)
  • „Scrierea de aplicații mobile native utilizând un limbaj de scripting personalizabil”, Revista MSDN (feb. 2018)
  • „CSCS: Scripturi personalizate în C#”, GitHub
  • „Dezvoltarea de aplicații native multiplatforme cu un limbaj de scriptare funcțional”, Revista CODE
  • „Implementarea succintă a unei limbi personalizate” (eBook)
  • „Scrierea succint a aplicațiilor mobile native într-un limbaj funcțional” (eBook)

Ca o resursă suplimentară, vă recomand să citiți cum puteți îmbunătăți performanța CSCS prin precompilarea funcțiilor sale.