Inițiativa unei aplicații TSM pentru Windows Phone a luat naștere acum mai bine de un an, în cadrul centrului de competență 3Pillar Global, ca mod de explorare și a dobândirii de cunoștințe în ce privește platforma Microsoft pentru mobile. Proiectul a continuat ulterior cu multă dedicare din inițiativa proprie a celor implicați: Cosmin Jeler, autorul prezentului articol, responsabil de partea de dezvoltare, Robin Molnar axat asupra părții de asigurare a calității, Dan Hădărău pentru suportul acordat pe partea de design și server-side iar Ovidiu Mățan project manager / product owner.
Windows Phone este la acest moment în partea de jos a clasamentului în ceea ce privește cota de piață a dispozitivelor mobile. Microsoft a intrat mult prea târziu în acest joc, iar odată ajuns aici, metodologiile sale clasice de marketing și dezvoltare de produs nu au făcut față competitivității. Motiv pentru care gigantul din Redmond s-a văzut nevoit să înceapă un proces masiv de reinventare proprie: schimbarea CEO-ului, deschiderea către software-ul open-source, dezvoltarea de aplicații pentru alte ecosisteme. Acestea sunt doar câteva puncte de menționat în această direcție.
Datele pentru primul trimestru din 2015 arată creșteri pe piața vest-europeană, telefoanele cu Windows Phone ajungând să depășească pragul de 10% din piața de mobile în Franța, Spania, Germania, Italia și Marea Britanie.În ultimii ani, Microsoft a investit în reducerea efortului depus de către dezvoltatorii de aplicații pentru această platformă, aplicând o serie de măsuri precum scăderea prețului pentru obținerea contului de dezvoltator, unificarea platformelor de dezvoltare pentru telefon și tabletă, iar curând, odată cu lansarea Windows 10, unificarea Store-urilor pentru toate dispozitivele din ecosistemul Microsoft incluzând Xbox One. Pe de altă parte, Microsoft a început în ultima perioadă și numeroase colaborări cu producătorii aplicațiilor populare din AppStore și Google Play, pentru a aduce aceste aplicații pe platforma Windows și a diminua din frustrarea utilizatorilor legată de absența aplicațiilor. Toate acestea indică o mare miză pusă de Microsoft pe lansarea Windows 10.
Ca platformă hardware, gigantul software s-a concentrat în ultima vreme pe dispozitivele din zona cu buget redus, lansând numeroase modele noi sau variante îmbunătățite ale modelelor existente. Însă în zona dispozitivelor de top dinamica e mult mai mică. Probabil că achiziția Nokia și procesele de tranziție aferente sunt două dintre motive, dar într-o piață așa dinamică precum cea a dispozitivelor mobile, lansări la intervale mai mari de un an nu sunt permise. Ar fi totuși de apreciat parteneriatul cu HTC pentru modelul M8, doar că e nevoie de continuitate și prezență de spirit pentru a ține cumpărătorii în priză - nimeni nu ar fi încântat să plătească prețul unui telefon de top cu Windows când modelul mai nou cu Android e deja lansat. Să sperăm totuși că o dată cu Windows 10, Microsoft va aduce un suflu nou și la partea de hardware.
Prima provocare cu care m-am confruntat în dezvoltarea pentru mobile a fost dinamicitatea mult mai mare a platformei: la momentul în care am demarat proiectul, Windows Phone 7 era încă un cetățean cu drepturi depline, iar WP 8 devenea noua modă. Pe parcursul dezvoltării a apărut WP 8.1, care deși pare un update minor, e de fapt o cu totul altă platformă față de 8.0. Trecerea la WP 8.1 ar fi însemnat un efort nesustenabil având în vedere că aveam deja de făcut față diferențelor între 7 și 8. Iar mai târziu, la momentul lansării aplicației, avem deja o avanpremieră pentru Windows 10, iar aplicația pentru WP 7 a devenit din păcate prea veche pentru a mai putea fi publicată în Store.
Tendința inițială la demararea aplicației a fost preluarea designului din versiunea de iOS și transpunerea lui în Windows Phone. Dar nu a trecut mult până să realizez că unele lucruri nu păreau deloc la locul lor. Dacă cu Android se mai pot păstra unele similarități între aplicații, în Windows Phone experiența utilizator e complet diferită. Câteva exemple în acest sens ar fi modul de deschidere a meniului (partea stângă în iOS, fără corespondență în WP), modul de navigare (cu butoane soft față de hard), meniul contextual specific doar WP și multe altele.
M-am văzut foarte curând în fața misiunii de a concepe un design nou, adaptat la experiența și capabilitățile dispozitivelor Windows Phone, care să respecte recomandările de experiență utilizator a platformei, și poate cel mai greu pentru un programator, să arate bine. Am luat în considerare sugestiile și feedbackul tuturor persoanelor din jur pe care le-am putut implica: colegi, prieteni, familie, astfel încât versiunile pe care le prezentam beneficiarului final (Ovidiu - TSM) pentru încă o rundă de feedback să fie cât mai atractive.
Chiar și cel mai reușit design însă nu este suficient pentru a face aplicația captivantă, motiv pentru care am apelat și la acele mici artificii numite animații. Mult mai experimentații colegi de pe platformele iOS și-au adus aportul prin sugestii și uneori chiar detalii de dinamică a animațiilor, motiv pentru care țin să le mulțumesc pe această cale.
Pentru cei cu experiență în tehnologiile bazate pe limbajul XAML, pattern-ul MVVM folosit este un model prea bine împământenit în mediile profesionale pentru a fi considerat o "alegere" în adevăratul sens al cuvântului. Nu voi intra în detalii legate de acest pattern, însă pentru a vă face o idee despre el, se poate spune pe scurt că reprezintă o variantă specializată pentru tehnologiile XAML a pattern-ului Presentation Model (PM), cu scopul de a simplifica modelul de programare bazat pe evenimente a interfețelor utilizator. Pattern-ul PM la fel ca și Model-View-Presenter, separă interfața utilizator de comportament și stare și creează o abstracție a interfeței.
Aria de tehnologii din care am avut de ales a fost destul de limitată fiind vorba de o platformă de dezvoltare destul de tânără.
Având nevoie de o bază de date pentru stocarea revistelor și articolelor, am avut norocul de a începe implementarea într-un moment foarte prielnic din acest punct de vedere, având la dispoziție baza de date LINQ to SQL - o variantă simplistă a tehnologiei Entity Framework cunoscută oricărui dezvoltator .NET. Singurele probleme ridicate au fost legate de sincronizarea accesului - scrierea de date într-o tabelă din care alt fir de execuție încearcă să citească, s-a dovedit problematică, motiv pentru care a fost nevoie să sincronizez accesul la baza de date.
Din păcate, în versiunea RT a Windows Phone 8.1 ,suportul pentru baza de date a fost omis, aplicațiile mai noi fiind nevoite să folosească SQLite. Acest lucru nu ar fi neapărat o problemă dacă Microsoft ar fi pus la dispoziție și un furnizor de LINQ pentru el. Din păcate în acest moment există o singură librărie externă decentă, și anume sqlite-net - rămâne să sperăm că Microsoft nu va amâna prea mult rezolvarea acestei situații și va veni cu o soluție oficială.
Exemplu cod de acces sincronizat la DB:
public T Find(Expression> selector, params Expression>[] includes)
where T : class
{
OperationOnDatabase.WaitOne();
using (DataContext dataContext = IoC.
Get())
{
if (includes != null && includes.Length != 0)
{
DataLoadOptions loadOptions =
new DataLoadOptions();
foreach (Expression>
include in includes)
{
loadOptions.LoadWith(include);
}
dataContext.LoadOptions = loadOptions;
}
IQueryable query = dataContext.GetTable();
T result = query.FirstOrDefault(selector);
OperationOnDatabase.Set();
return result;
}
}
O surpriză plăcută a fost să descopăr disponibilitatea pentru WP a unor librării deja consacrate în celelalte tehnologii .NET, referindu-mă aici la arhicunoscutul Google Analytics și la Json.NET - util la transformarea rezultatelor în format JSON de la server în modele interne ale aplicației. Un alt avantaj a fost disponibilitatea operațiilor de share pe e-mail și rețelele sociale oferită de sistemul de operare.
O altă librărie externă pe care am folosit-o este o extensie a celor de la Caliburn.Micro numită BindableAppBar, ce permite tratarea evenimentelor din bara de aplicații (meniul de jos din aplicațiile WP) într-o manieră specifică MVVM - în contrast cu bara oferită de sistemul de operare ce suportă doar evenimente în code-behind.
Poate cel mai interesant aspect legat de tehnologii ar fi modul de alegere a librăriei MVVM ajutătoare. Deși există o sumedenie de opțiuni pe piață, doar câteva din ele au ajuns la o relativă maturitate și sunt disponibile pe WP. Aș menționa aici deja consacratele MVVM Light Toolkit și Prism. Cu Prism avusesem deja experiență și știam că are limitările lui - probabil a evoluat între timp dar am preferat să nu risc, ci mai degrabă să învăț ceva nou. Așa încât următoarea opțiune a fost arhicunoscutul MVVM Light. Din păcate am ajuns foarte repede să pun la îndoială alegerea făcută, însă probabil multora dintre voi vă pare cunoscut scenariul de a nu avea certitudinea dacă problema rezidă în alegerea făcută sau în competența utilizatorului… În astfel de situații caut semne obiective care să mă scoată din impas, iar în cazul de față factorul de decizie a fost lipsa crasă a documentației. Probabil autorii MVVM Light mizează pe faptul că unealta lor e așa ușor de folosit încât nu are nevoie de documentație - ceea ce nu a fost cazul meu. Chiar și astăzi, aruncând o privire la pagina lor web, se poate constata nonșalanța cu care au tratat acest subiect, așa încât am ajuns să testez o librărie mai puțin populară la acea vreme și anume Caliburn.Micro. Acesta m-a atras de la început prin simplitate și puterea oferită. Doar că simplitatea care nu e și intuitivă poate deveni frustrantă. Dar ajungând la pagina lor de documentație am realizat că e de un profesionalism desăvârșit - se observă din doar două rânduri că autorii lui au gândit și răzgândit fiecare lucru pe care l-au scris și implementat.
Un alt subiect interesant este ciclul de viață al aplicației - complet diferit față de experiența din programarea desktop și web de până atunci. Aplicația are mai multe stări decât clasicele pornită și oprită, introducând multe scenarii interesante legate de modul în care o pagină din aplicație ajunge să fie vizibilă utilizatorului. Intrarea în orice pagină se poate face de la o pagină anterioară urmând fluxul standard de navigare. Se mai poate face prin întoarcerea de la pagina următoare (caz în care pagina se află deja în memorie, nu mai trebuie inițializată, devenind doar vizibilă pe ecran), sau prin pornirea aplicației din starea suspendată direct la ultima pagină vizitată. Pe lângă aceste moduri, experiența din alte proiecte mi-a arătat că mai există un caz hibrid, în care prin apelul din aplicații externe sau din sistem, aplicația poate fi pornită direct într-o pagină intermediară fără a trece prin fluxul normal de navigare între pagini și poate accepta și parametri de intrare. Toate aceste cazuri plus natura asincronă a comunicării pe mobil, pot genera stări foarte interesante ale aplicației.
Având multă experiență la activ în WPF și mai ales în Silverlight, m-aș fi așteptat ca dezvoltarea de aplicații Windows Phone să nu îmi ridice prea multe probleme. Realitatea însă a fost puțin diferită: la nivel de interfață utilizator și limbaj lucrurile sunt relativ asemănătoare, deși am constatat o mare diferență la navigarea între pagini și ciclul de viață al aplicației. Experiența poate fi comparată cu trecerea de la programarea desktop la web pentru cei care au trecut prin așa ceva.
Foarte simplu, în WP nu se păstrează starea între paginile aplicației. Pasarea de parametri între pagini se face prin query string-uri, iar pentru tipuri mai complexe situația se complică pentru că nu există o metodă standard de a rezolva problema. Calibun.Micro s-a dovedit de un real ajutor în acest caz, oferind o soluție simplă și intuitivă:
navigationService.UriFor()
.WithParam(articleVM => articleVM.IssueId
,selectedIssue.Id).Navigate();
Exemplu cod navigare folosind Caliburn.Micro
Partea de animații a fost una din cele mai interesante părți din dezvoltare, ele contribuind la identitatea și atractivitatea unei aplicații. Nu avem cum compara aplicația de față cu cele din topul magazinelor de aplicații, dar aceasta nu înseamnă nici că trebuie sa ne mulțumim cu o variantă de duzină cum sunt multe din coada magazinelor de aplicații mobile. Pe de altă parte, pe cât sunt de interesante animațiile, pe atât sunt de costisitoare ca timp de implicare.
Există un nivel de animații oferite implicit de sistemul de operare, referindu-mă aici la stilurile prestabilite ale controalelor folosite în aplicație (teme și tranziții între stări).
Un alt nivel de animații sunt cele puse la dispoziție în librării ce trebuie doar importate manual și folosite adecvat. Dintre acestea aș menționa animațiile de intrare și ieșire din pagină și animațiile de rotație la schimbarea orientării ecranului.
Următorul nivel ar fi animațiile personalizate, care deși sunt destul de discrete, nu au cum să fie trecute cu vederea. Mă refer aici la butoanele de alegere a limbii, la animarea ultimelor ediții ale revistei la pornirea paginii de start, la oscilațiile imaginilor edițiilor din lista completă de reviste (tot în pagina de start), la intensitatea progresivă cu care apar imaginile articolelor din revistă, la antetul care apare și se ascunde în funcție de modul de derulare a conținutului unui articol. Cei care au avut de-a face cu ecosistemele iOS vor remarca probabil similaritatea cu bara de adresă din Safari, a cărei funcționalitate am încercat să o reproduc.
De foarte departe, cele mai multe probleme întâlnite au fost legate de pagina de conținut a articolelor.
Sfatul cel mai important pe care vi-l ofer gratis este legat de controalele de web: nu folosiți controale de web în aplicații mobile. Dacă totuși sunteți nevoit să folosiți unul, încercați să vă limitați la a interacționa cu el într-un mod cât mai minimalist posibil, în genul trimiterii adresei paginii HTML ce trebuie vizualizată, pentru că restul e cancan.
Problemele controlului web sunt nenumărate și nu toate țin de faptul că vorbim despre Internet Explorer - sunt multe ce țin de natura foarte diferită a controlului. Vă prezint mai jos o listă cu câteva din aceste probleme pe care le-am întâlnit:
Un alt eșec de proporții legat de controlul web a fost încercarea de a implementa un scroll infinit pe orizontală între articolele din aceeași revistă. Un gest de swipe din partea dreaptă a ecranului ar fi dus la trecerea printr-o animație la următorul articol și analog la swipe din partea stângă. Deși ajunsesem la final cu implementarea unei variante funcționale, am fost nevoit să renunț complet la această idee pentru că web control-ul nu raporta fidel gesturile utilizatorului în termeni de distanță delta, frecvența raportării și accelerație.
Alte aspecte interesante din punct de vedere tehnic au ținut de natura asincronă a apelurilor în dezvoltarea pe mobil. Ce se întâmplă spre exemplu atunci când rezultatul unui apel asincron (fie el la server sau la baza de date) se întoarce după ce utilizatorul a navigat deja la altă pagină? În primă instanță nimic, dar la o testare mai atentă aplicația ridică o excepție care uneori e ignorată, alteori termină aplicația. Acele apeluri pot fi anulate, dar când acest lucru nu este posibil, rezultatele acelor apeluri pot fi și trebuie ignorate. Limbajul C# oferă suport pentru anularea de apeluri pornite asincron, punctul de intrare în acest subiect fiind clasa TaskCancellation pentru cine vrea să afle mai multe detalii. Ce se întâmplă în cazul în care o pagină modifică anumite date care apar deja și pe o pagină anterioară? La navigarea în pagina anterioară vor fi vizibile modificările? Răspunsul implicit este nu, pentru că nu există stări între pagini și nu pot fi trimise nici notificări. Programatorul poate însă avea grijă să trateze astfel de situații verificând manual starea obiectelor susceptibile modificărilor. Un exemplu elocvent este pagina principală, unde numărul de articole dintr-o revistă nu este vizibil decât după explorarea acelei ediții, moment în care se încarcă lista de articole de la server. La întoarcerea în pagina principală, numărul de articole din ediția vizitată trebuie actualizat manual.
O altă secțiune destul de costisitoare a fost schimbarea de limbă, care deși pare o operație destul de facilă în sine, varianta finală a implementării a ajuns departe de a fi simplă. Apelurile la server în curs trebuie anulate, baza de date trebuie golită, unele setări ale aplicației trebuie resetate, iar aplicația trebuie repornită fără posibilitatea de navigare înapoi.
Robin Molnar, colegul din partea 3Pillar Global, a fost responsabil cu asigurarea calității aplicației, dând dovadă de mult profesionalism în ce privește procesul de testare. S-a implicat atât în partea de aplicație mobilă cât și în partea de server, iar rigurozitatea și sugestiile lui vor avea cu siguranță un impact consistent în percepția utilizatorilor asupra aplicației. Diferitele scenarii de test aplicate pe dispozitive și platforme diferite au necesitat multă atenție și efort.
Fără intenția unui clișeu de premiu Oscar, țin să le aduc mulțumiri 3Pillar Global pentru susținerea oferită, directorului Dan Suciu pentru demararea și susținerea acestui proiect, lui Robin Molnar pentru profesionalismul exemplar și lui Ovidiu Mățan, Dan Hădărău pentru partea de design și echipei Gemini Solutions pentru suportul oferit în toate stagiile de dezvoltare a aplicației.
Ca o concluzie, aș putea spune că am învățat foarte mult din acest proiect, atât tehnologic cât mai ales la părțile de comunicare și coordonare. Am învățat că este nevoie de foarte multă muncă la un produs pentru a putea fi considerat complet, și că poți spune aceasta doar în momentul în care produsul se află în mâinile clienților finali. Deși pare simplu din punct de vedere al programatorului care scrie codul și închide ultimul task, efortul depus până la adevăratul "gata" nu este deloc de neglijat. Nu este cazul aici, însă uneori acest efort poate ajunge la fel sau chiar mai mare decât efortul de implementare. Există o sumedenie de alți factori care contribuie la succesul unui produs, iar modul de implementare de către programator are de prea puține ori ponderea majoritară.
Închei cu invitația de instala aplicația din WP Store și de a contribui cu sugestii și feedback, pe care le vom aprecia și care evident vor constitui baza de plecare pentru următoarele versiuni ale aplicației.
de Ovidiu Mățan
de Vlad Ciurca
de Ștefan Bălan
de Ioana Varga , Ioana Costea