Archivi categoria: Mobile

Weee! Xamarin Forms anche su web “grazie a” Ooui

Chi frequenta il mondo .NET e in particolare quello di Xamarin non può non conoscere Praeclarum, al secolo Frank A. Krueger, sviluppatore al limite del mitologico con un profilo pubblico Github che tra le altre cose annovera sqlite-net (libreria per usare Sqlite con Xamarin) e Continuous (IDE per iOS sviluppato in F#).
Ebbene il buon Frank da giugno dell’anno scorso sta lavorando a Ooui, libreria nuget con cui far girare applicazioni Xamarin Forms su un browser.

Il logo è una puzzola

Detta così potrebbe sembrare quasi una minaccia perché è cosa nota che Xamarin Forms non vada proprio del tutto a genio a molti sviluppatori, e il sottoscritto è tra questi, ma è anche innegabile lo sforzo della Microsoft di far evolvere e stabilizzare questa tecnologia, quindi ben venga l’apertura di nuovi filoni che possano aiutare a farla crescere.

Come è stata accolto Ooui nell’ambiente

La faccio breve: seppur sia ancora un progetto quanto meno acerbo, si è manifestato un certo entusiasmo. L’idea di riutilizzare su più piattaforme la stessa base di codice e di conoscenze fa festa. Un bell’articolo da leggere è quello di Telerik, dove l’autore cerca di spiegare i vari approcci e il perché non bisogna a vederla come una rinascita di Silverlight.

https://www.telerik.com/blogs/xamarin-forms-on-the-web

Molto interessante anche il contributo di Adam Pedley altro soggetto che non si può non conoscere se si lavora con Xamarin, in cui Adam spiega come percorrere l’ultimo miglio per far diventare la nostra applicazione web basata su Xamarin.Forms una Progressive Web App a tutti gli effetti aggiungendo manifest e webworker.

https://xamarinhelp.com/create-a-pwa-in-xamarin-forms-with-ooui-wasm/

Il mio esperimento


https://decimaltime.azurewebsites.net/

Un orologio. Decimale. C’è un motivo, e potete leggerlo nel readme del repository. L’orologio è anche tra gli esempi di Xamarin e tra quelli di Ooui, ma nel nostro ci sono un po’ di casi in più: mix di binding e non, immagine di sfondo, popup, libreria nuget per il calcolo delle date… un esempio abbastanza interessante su come trasformare velocemente un’applicazione mobile scritta con Xamarin Forms in webapp utilizzando Ooui.
Nel repository trovate l’applicazione iOS, quella Android (pubblicata anche su PlayStore), l’applicazione web basata su WebAssembly, e quella web ma con backend in cloud e web-socket. Quella sopra è la versione “non wasm”, avviando quella wasm si nota che a differenza dell’altra vengono scaricate tutte le dll e che non si apre alcuna web-socket perché gira tutto in-process all’interno del browser, ma la trasformazione dei componenti Forms in Html5 funziona allo stesso modo.

La versione basata su web socket…

… e quella che usa wasm

Se per qualche misterioso e irrazionale motivo vi piace l’idea dell’orologio decimale con calendario repubblicano sappiate che su Azure ho pubblicato anche delle API web, basate sulla stessa libreria usata nell’app Xamarin Forms.

À la guerre!

Decompilare applicazioni Android di ogni genere, dal dispositivo ai sorgenti

Qualche tempo fa scrissi due righe su come utilizzare l’adb per andarsi a leggere le cartelle private di un’applicazione Android installata sul proprio dispositivo, cosa molto utile per esempio quando si vuole leggere i dati contenuti in un database sqlite.

Quest’oggi invece mi sono tolto la curiosità di provare a tirarmi giù dal telefono varie applicazioni – scritte nei modi più disparati – e “strucinare” un minimo al loro interno giusto per capire quali sono gli strumenti di sviluppo più sicuri.

File apk partendo da un’app installata su un telefono Android

Come già detto nell’altro articolo l’adb nient’altro è che un bridge tra dispositivo e macchina di sviluppo, e lo si può trovare nel percorso android-sdk/platform-tools/adb, dove “android-sdk” è il percorso di installazione dell’sdk Android (la posizione dipende da cosa si utilizza per sviluppare: Android Studio, Xamarin, Titanium, Ionic, …).

Utilizzando l’adb si può scaricare sulla propria macchina l’apk dell’applicazione che ci interessa con questi comandi:

adb shell pm list packages

adb shell pm path com.boh.nonso

adb pull /data/app/com.boh.nonso-2.apk path

Il primo elenca tutte le applicazioni installate, con il secondo si ottiene il path di quella che ci interessa e con il terzo la si scarica.

Listato con tutte le applicazioni installate sul dispositivo

“Esplorazione” dell’apk

Qui si va sul difficile… è sufficiente rinominare il file sostituendo l’estensione “apk” con “zip” e scompattare lo zip.

Studio dei sorgenti dell’applicazione

La fase più complessa principalmente perché l’applicazione potrebbe essere stata sviluppata nei modi più disparati.
Uno strumento utile è sicuramente il Dex2Jar, che come dice il nome permette di convertire il/i file .dex in dei .jar.
Quest’attrezzo però non va molto d’accordo con l’osx, e per farlo funzionare potrebbe essere necessario lavorare un po’ di martello e Stackoverflow, per esempio togliendo la cartella dove si trova il dex2jar dalla quarantena con xattr -rd com.apple.quarantine path.
Una volta dati i permessi di esecuzione allo script d2j-dex2jar.sh basterà eseguirlo e andare a prendere il file generato all’interno della directory del’applicativo:
sh d2j-dex2jar.sh -f apk_path

I file .dex

Qualunque sia lo strumento utilizzato per sviluppare l’applicazione almeno un file .dex c’è sempre, ma non è detto che il codice contenuto al suo interno sia significativo. Se infatti l’applicazione non è stata scritta nativamente in Java (o c++) il .dex serve solo come container/ponte per l’applicazione vera e propria, che potrebbe essere stata sviluppata in Javascript, C#, …
Un file .dex c’è ma non è detto che sia da solo; questo accade quando l’applicazione è stata compilata utilizzando il MultiDex per superare l’infame limite dei 65536 metodi della piattaforma Android.

Decompilazione dei .jar

Una volta ottenuti i jar, ovvero degli archivi contenenti i file .class (classi Java compilate), decompilarli per ottenere il codice Java originario è un gioco da ragazzi, ed è sufficiente aprirli con un decompilatore Java come Java Decompiler.

Senza aver studiato i vari casi nel dettaglio devo dire che tra tutti il sistema apparentemente più difficile da crackare sembra quello di Titanium, che minimizza e cripta il codice javascript originario inserendolo in delle HashMap contenute all’interno del codice java autogenerato dall’sdk di Titanium.

Applicazione scritta in Xamarin, decompilazione delle DLL

Se l’applicazione è stata sviluppata con Xamarin, ovvero scritta in C#, il codice contenuto nel file jar non è molto significativo; all’interno dell’apk però ci sono le dll che possono essere decompilate facilmente con un decompilatore .NET come ILSpy.

Libreria scritta con Xamarin e decompilata con ILSpy


Il processo di decompilazione potrebbe non essere perfetto, e infatti alcuni dettagli si perdono. Per esempio ILSpy non sembra (ancora) in grado di riconoscere le stringhe ottenute con un nameof sulle proprietà di un oggetto, o le stringhe scritte con sintassi $"{code}" (che vengono tradotte con delle string.format), ma per lo più si tratta di zucchero sintattico introdotto nelle ultime versioni del .NET mentre il grosso viene interpretato esattamente così com’era stato scritto originariamente.

Su reverse engineering ed offuscamento

La reverse engineering non è mai vista di buon occhio e, salvo rari casi come lo studio di sistemi esistenti al fine di garantirne l’interoperabilità con altri, è più o meno proibita.
Di certo quelli descritti sommariamente qui sopra sono esercizi molto utili a fini didattici e che in alcuni casi possono aiutarci a scovare bug in strumenti che utilizziamo tutti i giorni, quindi conoscere un po’ queste tematiche è cosa buona e giusta. Inoltre è bene sapere che il nostro lavoro potrebbe essere studiato con grande facilità da malintenzionati e/o concorrenti al fine di danneggiarci o copiare il nostro lavoro, ed è qui che entrano in gioco tutta una serie di buone pratiche su cosa includere o no all’interno della nostra applicazione.

L’offuscamento del codice è argomento controverso senza risposte precise, e in rete si possono trovare moltissime discussioni sull’argomento e vari strumenti per ciascun linguaggio.
In generale a mio avviso non conviene farci affidamento se è vostra intenzione proteggere delle password o rendere incomprensibile il codice di applicazioni molto famose per evitare che dei cracker trovino eventuali vulnerabilità, perché non esiste offuscamento che regga contro il tempo e la pazienza di pirati informatici degni di questo nome.
Al contrario potreste aver sviluppato un’applicazione che deve funzionare offline e al cui interno ci sono soluzioni avanzate e che vi danno un vantaggio competitivo su eventuali concorrenti; in questo caso può aver senso sviluppare tutte le “logiche di calcolo” in una libreria a parte e offuscare solo quella in modo da renderne più difficile lo studio e l’utilizzo da parte di malintenzionati… che poi magari uno sta a pagare per un offuscatore, a rallentare tutta la catena di deploy, a rischiare di inserire bug dovuti all’offuscamento, e il repository con il codice è accessibile via web da molti utenti con password deboli, o ancora più comune si pagano gli sviluppatori come degli operai di basso livello e li si lascia partire con la conoscenza o peggio con i sorgenti, ma questa è un’altra storia.

Xamarin.UITest: il nome inganna, ci si può testare (quasi) tutto

Al recente Xamarin Day di Roma l’MVP Massimo Bonanni ci ha parlato dello Xamarin UITest, framework basato su Calabash per la scrittura e l’automazione di test di accettazione nel mondo dello sviluppo mobile.
Niente di nuovo sotto il sole, se non che in precedenza mi ero fatto ingannare troppo facilmente dall’apparentemente elevato costo del servizio in cloud e non avevo approfondito.

Detto che su GitHub ci sono molti esempi di utilizzo, e che io stesso in un progetto al lavoro ho preso “fortemente spunto” dall’app sviluppata per Xamarin Evolve 2016, riporto qui un mio piccolo esperimento fatto con un’applicazione sviluppata in Titanium. Ebbene sì, il framework è stato sviluppato dal team di Xamarin, ma può essere usato per automatizzare il processo di test su ogni applicazione mobile per Android e iOS. Per Windows non ancora, e la cosa può far sorridere considerato chi è il nuovo padrone di Xamarin, ma magari un giorno colmeranno la lacuna.
Per intenderci, funziona con tutte le tecnologie, ma ha senso usarlo soltanto con quelle che renderizzano componenti nativi: Android, iOS, Xamarin, Titanium, React Native, Native Script, … con i framework ibridi basati su Cordova/Phonegap questo approccio è sbagliato perché tutta l’applicazione gira all’interno di una webview.

Esperimento con Appcelerator Titanium

L'albero degli elementi in un'applicazione Titanium, visualizzato tramite repl

L’albero degli elementi in un’applicazione Titanium visualizzato tramite repl

Quello sopra è l’albero degli elementi in una delle maschere dell’applicazione, così come Titanium l’ha generato a runtime. Gli elementi ci sono tutti, ma scriverci sopra dei test non è sempre banale perché non sempre ci sono degli id a cui poter fare riferimento e a volte bisogna ripiegare sul contenuto testuale. Mmmmm.

Quando si crea un progetto di tipo Xamarin.UITest Xamarin Studio genera in automatico una coppia di file, l’AppInitializer.cs e il Tests.cs. Naturalmente saremo poi liberi di modificare la struttura del nostro progetto come preferiamo (vedi l’app di Xamarin Evolve), ma in quei due file ci sono comunque tutti gli elementi di cui abbiamo bisogno per partire.

public class AppInitializer
{
    public static IApp StartApp(Platform platform)
    {
        if (platform == Platform.Android)
        {
            return ConfigureApp
                .Android
                .ApkFile ("/Users/m.piccotti/src/ti_eatevolution/build/android/bin/Eat Evolution.apk")
                .EnableLocalScreenshots()
                .StartApp();
        }

        return ConfigureApp
            .iOS
            //.AppBundle ("../../../iOS/bin/iPhoneSimulator/Debug/XamarinForms.iOS.app")
            .StartApp();
    }
}
[TestFixture(Platform.Android)]
//[TestFixture(Platform.iOS)]
public class Tests
{
    IApp app;
    Platform platform;

    public Tests(Platform platform)
    {
        this.platform = platform;
    }

    [SetUp]
    public void BeforeEachTest()
    {
        app = AppInitializer.StartApp(platform);
    }

    [Test]
    public void AppWorks()
    {
        //app.Repl();

        Func<AppQuery, AppQuery> title = (arg) => arg.Marked("Eat Evolution");
        Func<AppQuery, AppQuery> barFofoGarden = (arg) => arg.Marked("Bar Fofo Garden");
        Func<AppQuery, AppQuery> filtersButton = (arg) => arg.Class("ActionMenuItemView").Marked("Filters");

        // 1 - info page
        app.WaitForElement(title);
        app.Screenshot("1 - info page");
        Assert.IsNotNull(app.Query("JOIN US OR RECOMMEND").First().Text);

        app.TapCoordinates(100, 300);

        // 2 - list
        app.WaitForElement(barFofoGarden);
        app.Screenshot("2 - list");

        app.Tap(barFofoGarden);

        // 3 - Fofo Garden detail
        app.WaitForElement(barFofoGarden);
        app.Screenshot("3 - Fofo Garden detail");
        Assert.IsNotNull(app.Query("Bar Fofo Garden").First().Text);

        app.TapCoordinates(100, 100);
        app.WaitForElement(barFofoGarden);
        app.Tap(filtersButton);

        // 4 - filters screen
        app.WaitForElement(x => x.Marked("Confirm"));
        app.Screenshot("4 - filters screen");
        Assert.IsNotNull(app.Query("Confirm").First().Text);
        Assert.IsNotNull(app.Query("Cancel").First().Text);

        app.Back();
    }
}

Ora… chi è un po’ pratico di test automatici di unità potrebbe rimanere inorridito al vedere quell’unico metodo AppWorks; certo sarebbe bello se ogni asserzione potesse essere verificata isolatamente, il problema è che questi test sono più lenti di quanto si possa immaginare, e i servizi cloud si pagano a ore. In sostanza entro certi limiti può aver senso anche una robaccia come quella sopra.

Il log generato dal test

Il log generato dal test

Per la cronaca quelle sotto sono le schermate catturate dal test.

Giusto un giretto tra le altre piattaforme

Che tutto avrebbe funzionato anche con le altre tecnologie concorrenti di Xamarin e Titanium non avevo dubbi, ma un giro di prova ho preferito farlo comunque. Sotto potete vedere i miei esperimenti fatti con le applicazioni demo delle varie piattaforme.

Il tree in un'applicazione Native Script

Il tree in un’applicazione Native Script

Per qualche ragione che non ho avuto voglia di approfondire con React Native l’applicazione è esplosa, come se il bridge tra l’applicazione di test e quella da testare non abbia funzionato. Il comando tree richiamato con il repl (strumento utilizzabile da cli) ha visualizzato comunque l’albero degli elementi della schermata rossa.

Il tree in un'applicazione React Native

Il tree in un’applicazione React Native

Il tentativo con Ionic 2 l’ho fatto solo a scopo didattico, per vedere la famosa WebView. Teoricamente si può interagire con l’applicazione a furia di TapCoordinates, ma il test risultante sarebbe così fragile che non ha proprio senso perderci tempo. Per le applicazioni ibride basate su html tanto vale usare Protractor.

Il tree in un'applicazione Ionic 2

Il tree in un’applicazione Ionic 2

Conclusioni

Xamarin.UITest è una figata.

PRO:

  • È possibile interagire con il dispositivo mentre il test automatico è in atto, sia fisicamente che attraverso il repl
  • Con il comando tree da riga di comando si può visualizzare la struttura reale della propria applicazione così com’è stata renderizzata sul dispositivo, cosa che può tornare molto utile
  • Funziona su Android e iOS.
  • Con Xamarin Test Recorder si può registrare il proprio test andando poi a modificarne il codice
  • Il test in cloud è utile non solo per testare automaticamente su molti dispositivi diversi, ma anche per fare test specifici quando ci vengono segnalati dei bug su un dispositivo particolare che non abbiamo a disposizione
  • L’interfaccia web dei test in cloud è spaziale, ci da un sacco di informazioni anche sulle performance ed è possibile fare degli screenshot mirati in presenza di errori e non solo

CONTRO:

  • Non funziona su Windows
  • Il test recorder funziona solo per Android ed è stabile solo per Visual Studio
  • L’esecuzione dei test è lenta da far schifo
  • Il test in cloud costa un occhio della testa anche nella versione Newbie

Le icone raster su Android sono una noia, viva le shape drawable

Chi sviluppa web, o mobile utilizzando strumenti “simil-web” come Titanium e Ionic, da tempo ha imparato a utilizzare asset vettoriali o font, così da rendere impeccabile la resa sui dispositivi con risoluzioni diverse e facilitare operazioni come la modifica dinamica dei colori.
Purtroppo nel mondo dello sviluppo Android si tende ancora a utilizzare icone raster; per la scalatura nelle varie risoluzioni c’è chi fa tutto a mano, chi utilizza script di varia natura in combinazione con ImageMagick e chi si serve di strumenti online come Android Asset Studio.

Su Android l’approccio migliore al momento sembra quello delle Shape Drawables, ovvero immagini vettoriali definite in xml secondo un determinato formato, funzionalità disponibile solo dalla versione 5 di Android (Lollipop), ma abilitabile anche nelle versioni precedenti abilitando la Support Library.

Segnalo un bell’articolo che spiega benissimo come abilitare l’utilizzo degli asset vettoriali anche sulle versioni pre-lollipop e le modifiche da fare per evitare errori abbastanza str.nzi che possono far perdere qualche ora. Con i dovuti adattamenti tutto ciò vale anche per chi (come me) utilizza Xamarin.

Creazione dell’asset vettoriale da icone material e da svg

Qui sotto ci sono i passi da fare, più o meno, per crearsi con Android Studio un’icona vettoriale utilizzabile in Android. Moltissime sono già presenti tra quelle precaricate (material design), ma è possibile anche generarsele a partire dai propri svg.

Creazione di asset vettoriale da Android Studio

Creazione di asset vettoriale da Android Studio

Selezione di una delle icone material già presenti

Selezione di una delle icone material già presenti

Definizione dei dettagli dell'icona scelta

Definizione dei dettagli dell’icona scelta

Importazione di un'immagine raster in Inkscape

Importazione di un’immagine raster in Inkscape

Ridimensionamento dell'immagine vettoriale

Ridimensionamento dell’immagine vettoriale

Vettorizzazione dell'icona raster

Vettorizzazione dell’icona raster

Modalità di vettorizzazione

Modalità di vettorizzazione

Allineamento del tracciato vettoriale nell'immagine

Allineamento del tracciato vettoriale nell’immagine

Errore in fase di importazione dell'icona vettoriale in Android Studio

Errore in fase di importazione dell’icona vettoriale in Android Studio

Correzione dell'xml

Correzione dell’xml

Anteprima dell'svg importato in Android Studio

Anteprima dell’svg importato in Android Studio

Il risultato: icona vettoriale in formato xml

Il risultato: icona vettoriale in formato xml

Debug e profilazione di un’applicazione Titanium (e non solo) su Android Studio

Di recente mi è capitato di dover fare un po’ di profilazione di un’applicazione mobile scritta con Titanium per Android, principalmente per accertarmi dell’assenza di memory leaks in alcuni punti.
Su iOS è facile, perché Xcode mette a disposizione tutto il necessario (tempo fa scrissi un articoletto al riguardo), su Android non avevo mai provato.

Avevo notato che aprendo Android Studio e usando l’applicazione in oggetto sul logcat (console dei log) di Android Studio veniva sparato l’output, ma sul fronte RAM e CPU era tutto fermo.
È stato sufficiente mettere a true il flag android:debuggable sul file tiapp.xml e come per magia riavviando l’applicazione e selezionandola sull’Android Monitor i diagrammi sull’utilizzo delle risorse hanno preso vita.
Questa cosa funziona con tutte le applicazioni in cui si può abiltare il flag debuggable sul manifest Android, quindi per esempio anche usando Xamarin.

Il flag incriminato

Il flag incriminato

I pannelli sull'utilizzo delle risorse

I pannelli sull’utilizzo delle risorse

Per completezza aggiungo un articolo sulla documentazione di Titanium che spiega come ricercare i memory leaks su iOS e Android, ma senza menzionare l’opzione Android Studio.
https://wiki.appcelerator.org/display/guides2/Managing+Memory+and+Finding+Leaks#ManagingMemoryandFindingLeaks-MonitoringallocationsonAndroid

Niente di trascendentale ma magari qualcun altro non aveva mai provato.

Set di icone monocromatiche? Praticamente un font…

inkscape-icon

Purtroppo è da parecchio che non sto più tanto dietro allo sviluppo web, e quindi alcune “novità” tendono ad arrivarmi tardi (leggero eufemismo). La tendenza di usare icone monocromatiche su applicazioni web non è cosa recente, e la somiglianza tra la lettera di un font e un’icona monocromatica è stata notata da tempo, tanto che almeno dal 2010 c’è chi ha avuto l’idea di realizzare “font di icone” per utilizzarle come “icone vettoriali” sul web.
A me questa cosa è arrivata soltanto cinque o sei mesi fa quando mi è stato mostrato Font-Awesome. Mea culpa.

Set di font come se piovesse

Non è mai facile stabilire se sia nato prima l’uovo o la gallina, di certo c’è che le icone sono sempre più monocromatiche, e di set di font con icone di ogni genere ce ne sono ormai una marea. Purtroppo regala’ è morto, e l’utilizzo della maggior parte di questi pacchetti impone il pagamento di licenze o l’inserimento degli autori nei credits/ringraziamenti, a meno di non utilizzare quei set rilasciati con licenza Creative commons che sono originali come l’acqua calda.

Font custom, Inkscape e Icomoon

Tra gli strumenti per realizzare immagini vettoriali uno dei più usati è Inkscape (gratuito e open-source), ma non tutti sanno che in Inkscape è presente anche un editor di font con cui è possibile “raggruppare” dei glifi assegnando a ciascuno un “carattere”, realizzando di fatto un font.
Qui una bella guida sul tema, mentre qui si può trovare un progetto Github con risorse utili per crearsi i propri font.
inkscape-font-editor

Ricapitolando: con Inkscape – partendo da immagini vettoriali – si può arrivare a creare un “font svg” convertibile poi in TrueType (ttf) tramite strumenti esterni. E se non si ha voglia di fare tutti questi passaggi? Come è logico aspettarsi ci sono delle alternative.
Una strada è quella di installarsi un editor di font come FontForge, importarsi i propri glifi svg (opportunamente modificati), e crearsi il font a mano.
Strada più semplice è quella di rivolgersi a un servizio online. Ne ho provati alcuni (tra cui Fontastic), e alla fine l’unico che mi ha sempre importato gli svg e esportato i font correttamente senza distorsioni o ridimensionamenti strani è stato IcoMoon.
icomoon
IcoMoon è di una semplicità disarmante: si selezionano delle icone prese da librerie gratuite/a pagamento messe a disposizione dal sito o tra quelle importate da noi stessi e le si esportano in un pacchetto contenente il font in vari formati più tutte le risorse utili, con possibilità di configurarne anche i codici/nomi delle classi css e adattarne le dimensioni.

“Icone vettoriali” in Titanium

Veniamo alle origini di quest’articolo, visto che come dicevo all’inizio lo sviluppo web purtroppo l’ho abbandonato da un po’ di tempo.
Nel mondo dello sviluppo mobile un problema piuttosto sentito è quello della preparazione di icone e immagini alle varie risoluzioni per una buona resa su tutti i dispositivi che si ha intenzione di gestire. Il problema è ancora più marcato nello sviluppo multipiattaforma perché android e ios (per dirne due) seguono convenzioni dei nomi che non c’entrano niente l’una con l’altra, oltre a girare su dispositivi che gestiscono risoluzioni diverse.
Da un’esigenza nasce (quasi) sempre una soluzione, e in Titanium una parziale soluzione l’ha proposta uno degli sviluppatori di riferimento per questa piattaforma – Fokke Zandbergen – che ha dato alla luce TiCons.
Con TiCons si possono generare le icone dell’app e gli splash screens per tutti i dispositivi, ma la gestione delle immagini usate “all’interno” dell’app resta responsabilità nostra. Per far questo bisogna solitamente ingegnarsi con ImageMagick, che permette di fare velocemente elaborazioni anche complesse a grosse quantità di immagini tramite scripts. Questa cosa però è noiosa e così spesso si finisce per usare immagini con risoluzioni non adatte.

In una delle applicazioni di esempio corporate-directory si vede come sia possibile utilizzare “icone vettoriali” al posto di quelle raster semplicemente aggiungendo il nostro “font contenente le icone” all’interno della directory app/assets/fonts, impostando la proprietà font.fontFamily uguale al nome del font che abbiamo aggiunto e settando la proprietà testuale (es: “text” per le Label o “value” per i TextField) uguale al codice del carattere che rappresenta la nostra icona… più difficile a dirsi che a farsi, basta guardare l’esempio.

Un "esempio riepilogativo" con tutti i pezzi

Un “esempio riepilogativo” con tutti i pezzi

Gli effetti positivi di questa soluzione sono evidenti:

  • miglior renderizzazione delle immagini
  • minor dimensione delle applicazioni
  • possibilità di modificare dimensioni e colori delle icone velocemente e anche a runtime
  • nessun problema in caso di installazione su dispositivi non previsti inizialmente
  • possibilità di cambiare tutto il set di icone anche a runtime semplicemente modificando il nome del font in una classe tss
  • probabilmente ce ne saranno altri che non mi vengono

Un limite è che questa cosa si può fare solo su quel tipo di componenti dove il font (e quindi il font-family) è modificabile, questo significa che per i Tab e forse anche altrove purtroppo bisogna continuare a usare delle immagini raster.

Imparata questa tecnica mi è venuta voglia di usarla dappertutto… non appena sbatterò il muso da qualche parte aggiornerò quest’articolo, per il momento mi godo tutti i punti di cui sopra.

Da Appcelerator Studio a Atom: passaggio abbastanza indolore

Chi sviluppa con Titanium sa che in questo mondo – che poi è solo un sottoinsieme del mondo Javascript – c’è chi utilizza la command line e l’editor/ide di preferenza, e chi preferisce rimanere al calduccio utilizzando tutti e solo gli strumenti messi a disposizione da dalla casa-madre Appcelerator, ovvero Appcelerator Studio.
Appcelerator Studio in se non sarebbe troppo male, il problema è che una versione modificata (e neanche troppo bene) di Eclipse si porta quindi dietro la sua estensibilità ma anche la sua pesantezza. In più a volte ti si blocca mentre stai facendo operazioni banali come aggiungendo stringhe al file strings.xml costringendoti a buttare giù tutto e facendoti perdere del lavoro (le bestemmie che ho tirato giù a causa di questo problema sono veramente tante).
A volte succede anche che l’integrazione con gli stessi servizi di Appcelerator smettano di funzionare, ed è noto come la recente impossibilità di fare build su iOS abbia seminato il caos e costretto molti a un utilizzo più consapevole della CLI.

Sublime? No, Atom

Buona parte dei migliori sviluppatori che conosco, e che hanno a che fare con Titanium, si affidano a Sublime Text, un editor (termine riduttivo) veramente veloce e ben fatto. Per Sublime ci sono plugin di ogni genere, e Titanium non fa eccezione:
https://github.com/AoDev/ti-alloy-in-sublime-text-2
https://github.com/MattTuttle/sublime-ti-build
A me però non va di pagare per utilizzare un editor non open-source, e il simpatico messaggio che compare sempre più spesso con cui invitano ad acquistare una licenza mi da abbastanza fastidio.
sublime-purchase-alert

Chi non usa Sublime ha due grosse alternative: Atom e Visual Studio Code. L’editor di Microsoft (ancora in preview) è molto bello e funziona bene specialmente con linguaggi della galassia Microsoft (vedi Typescript), ma per il resto forse gli è preferibile Atom, giunto da poco alla versione 1.0 e con una miriade di plugins che aumentano e migliorano giorno dopo giorno.

Configurare Atom con il minimo necessario per sviluppare con Titanium

atom
Scelta fatta. Per programmare in Javascript potenzialmente basterebbe un blocco note, e per fare le build con Titanium c’è la CLI, però magari utilizzare qualche aiuto potrebbe non essere una cattiva idea.
Per avere un minimo di auto-completamento ho installato questo plugin che funziona bene:
https://atom.io/packages/titanium-alloy
che ha anche un’integrazione con hyperclick:
https://atom.io/packages/hyperclick

Auto-completamenti e auto-formattazioni non mi hanno mai fatto impazzire, ma un check statico del codice in tempo reale che segnali eventuali imprecisioni ma anche errori e pratiche di cattiva programmazione mi è sempre piaciuto averlo, e su Appcelerator Studio i linter latitano o funzionano male. Un linter è prezioso nel mondo javascript specialmente quando si lavora in squadra, perché sebbene in questi casi si tenda a scrivere in modo simile spesso non lo si fa abbastanza.

In Atom non solo si può installare ESLint, ma si può creare un file di configurazione del linter per ogni progetto.
https://atom.io/packages/linter
https://atom.io/packages/linter-eslint

{
	"globals": {
		"Ti": false,
		"Titanium": false,
		"Alloy": false,
		"$": false,
		"_": false,
		"L": false,
		"arguments": false,
		"require": false,
		"module": false,
		"exports": true,
		"OS_ANDROID": false,
		"OS_IOS": false,
		"ENV_PRODUCTION": false,
		"ENV_DEV": false,
		"setInterval": false,
		"clearInterval": false,
		"setTimeout": false,
		"clearTimeout": false,
		"alert": false,
		"describe": false,
		"it": false,
		"beforeEach": false,
		"afterEach": false
	},
	"rules": {
		"strict": [2, "never"],
		"new-cap": [2, {"capIsNewExceptions": ["L"]}],
		"no-trailing-spaces": [1, { "skipBlankLines": true }],
		"space-infix-ops": [1, {"int32Hint": false}],
		"comma-spacing": [1, {"before": false, "after": true}],
		"key-spacing": [1, {"beforeColon": false, "afterColon": true}],
		"semi-spacing": [1, {"before": false, "after": true}],
		"dot-notation": 1,
		"no-underscore-dangle": 1,
		"no-unused-vars": 1,
		"no-multi-spaces": 1,
		"quotes": [1, "double"],
		"eol-last": 0,
		"no-alert": 0
	}
}

In “globals” si elencano le variabili globali, specificando anche se possono essere assegnate oppure no.
In “rules” si specificano le regole di validazione, indicando se eventuali infrazioni vanno segnalate in rosso come errori (con il 2) o in giallo come warnings (con l’1). Lo 0 le disabilita.
Qui sono elencate tutte le regole di validazione di ESLint.

Altri plugins utili per Atom

Di estensioni ce ne sono una marea, e ognuno ha le sue esigenze, ma finora che mi sono piaciute ce ne sono due in particolare.
Un terminale integrato:
https://atom.io/packages/Termrk
E un project-manager che facilita il passaggio da un progetto all’altro:
https://atom.io/packages/project-manager

Titanium CLI

Come tutti sanno dalla CLI si può fare tutto quello che si fa con Appcelerator Studio. In teoria.
Una cosa che da quanto ne so funziona su Studio ma non sulla CLI è il LiveView, o almeno a me funziona solo su Studio, quando funziona.

Per fare le build io nella mia quasi totale incapacità di scrivere per bash mi sono creato questo script, semplice ma abbastanza efficace. Non è gestito il –liveview perché tanto non funziona.

# The platform: ios or android
PLATFORM=$1
if [ "$PLATFORM" == "" ]; then
	PLATFORM="android"
fi

# The target: device, simulator (ios) or emulator (android)
TARGET=$2
if [ "$TARGET" == "" ]; then
	TARGET="device"
fi

# Y for choosing destination, N for the default
CHOICE=$3
if [ "$CHOICE" == "" ]; then
	CHOICE="N"
fi

if [ "$CHOICE" == "Y" ]; then
	appc ti build --platform $PLATFORM --log-level debug --target $TARGET --skip-js-minify --device-id
else
	appc ti build --platform $PLATFORM --log-level debug --target $TARGET --skip-js-minify
fi

Per fare i deploy (ipa e apk) invece mi sono fatto questo script, forse anche più brutto del precedente:

SRC_PROJECT_NAME="My Project"
DEST_FILE_NAME="MyProject"
OUTPUT_DIR=~/Documents/$DEST_FILE_NAME
ANDROID_OUTPUT_DIR=build/android/bin
IOS_DIST_NAME="My company"
IOS_DIST_UUID="................"

mkdir -p $OUTPUT_DIR

appc ti build --platform ios --build-only --force --log-level info --device-family ipad --target dist-adhoc --distribution-name $IOS_DIST_NAME --pp-uuid $IOS_DIST_UUID --output-dir $OUTPUT_DIR
mv $OUTPUT_DIR"/$SRC_PROJECT_NAME.ipa" $OUTPUT_DIR"/$DEST_FILE_NAME.ipa"

appc ti build --platform android --build-only --force --log-level info
cp $ANDROID_OUTPUT_DIR"/$SRC_PROJECT_NAME.apk" $OUTPUT_DIR"/$DEST_FILE_NAME.apk"

Conclusioni

Appcelerator sembra sulla via del tramonto, opinione diffusa anche nella community. L’aumento di stabilità su Android negli ultimi mesi/anni è evidente e le prestazioni non sono malaccio, ma l’esperienza di sviluppo è pessima rispetto ad altre piattaforme, e il quasi totale abbandono della “gratuità” di sei mesi fa le hanno fatto fare dei passi indietro tra le preferenze degli sviluppatori.
Forse si riprenderanno o forse no, resta il fatto che iniziare a utilizzare con profitto strumenti più generici e utilizzabili anche in altri ambiti può non essere una cattiva idea.
Magari dobbiamo continuare a usare l’SDK di Titanium sui nostri progetti, ma fortunatamente (ancora) nessuno ci obbliga a utilizzare tutti gli strumenti di sviluppo di Appcelerator.

Snellire la libreria google-play-services.jar con Android ProGuard

L’altro giorno stavo cercando di gestire i Google Analytics in una semplice applicazione mobile che sto sviluppando per conto mio con Titanium, ma quando sono andato ad aggiungere il modulo ti.ga non sono più riuscito a compilare niente a causa di un errore dovuto evidentemente a dei conflitti tra librerie.

L’errore di compilazione era questo:
[ERROR] : UNEXPECTED TOP-LEVEL EXCEPTION:
[ERROR] : java.lang.IllegalArgumentException: already added:Lcom/google/android/gms/maps/LocationSource;

Cercando in giro ho avuto la conferma che il problema era dovuto all’inclusione di una libreria google-play-services.jar all’interno del modulo, che faceva a cazzotti con quelle usate in un altro modulo importato nel mio progetto: ti.map.
Per completezza è necessario dire che la libreria ti.map – insieme alle altre native di Titanium – è accessibile in /Utenti/m.piccotti/Library/Application Support/Titanium/modules/android.

Classi duplicate, che fare

Il problema è comune e in molti sul web se ne lamentano, non solo parlando di Titanium e di questi moduli. Ovviamente non si può pensare di eliminare la libreria contenente le implementazioni delle api necessarie al modulo, ma lì per lì non sapevo che fare, finché non ho guardato meglio un altro modulo che mi serviva: ti.admob.
A differenza di ti.map il modulo ti.admob va tirato giù da GitHub e importato nel progetto, quindi mi è balzata all’occhio la presenza delle librerie google-play-services-base.jar e google-play-services-ads.jar; guardando il log ho notato il messaggio che mi interessava:
[DEBUG] : Skipping duplicate jar file: project_directory/modules/android/ti.admob/2.1.6/lib/google-play-services-base.jar

In sostanza Titanium evita l’importazione di librerie duplicate, ma la google-play-services.jar presente all’interno del modulo ti.ga non risultava duplicata ai controlli di Titanium, e il risultato era una duplicazione delle classi Android in fase di compilazione in presenza del modulo all’interno del progetto.

La soluzione

Quando non si sa dove sbattere la testa conviene sempre fare come gli altri, ho quindi preso la libreria google-play-services_base.jar dal modulo ti.admob (sviluppato da quelli di Appcelerator), e mi sono andato a cercare un jar contenente solo le librerie di Google Analytics. Naturalmente non ho trovato niente di simile, ma ero sulla strada giusta.

Pulizie manuali del jar

Mi serviva un jar contenente solo le classi necessarie al funzionamento di Google Analytics. Da ignorante nello sviluppo nativo Android ho fatto quello che avrebbe fatto qualunque altro bimbominkia, scompattando il jar e ricompattandolo solo dopo aver eliminato tutti i package fuori da “analytics”.

Forse un po' troppo drastico

Forse un po’ troppo drastico


Non ha funzionato, per la cronaca comunque un jar si può decomprimere con qualunque utility di decompressione file o con il comando:
jar xf google-play-services-analytics.jar
e si può ricreare con:
jar cf google-play-services-analytics.jar ./com.
Qui si spiega come funziona il comando per la creazione dei jar, questa cosa mi è comunque servita alla fine.

Android ProGuard

Effettivamente ripulire a mano le classi di una libreria sconosciuta aveva probabilità di successo pari a 0.00001, e io non sono così fortunato. Ho trovato però nel giro di poco la soluzione: ProGuard.
ProGuard è un tool che serve per ottimizzare e offuscare il codice Android. A me interessava eliminare dalla libreria google-play-services.jar inclusa nel modulo non funzionante tutto il superfluo lasciando solo la parte di analytics, e questa cosa si fa abbastanza agevolmente.

-injars google-play-services.jar
-outjars google-play-services-analytics.jar

-libraryjars /usr/local/Android/sdk/extras/android/support/v4/android-support-v4.jar
-libraryjars /usr/local/Android/sdk/platforms/android-21/android.jar

-dontoptimize
-dontobfuscate
-dontwarn com.google.**.R
-dontwarn com.google.**.R$*
-dontnote

-keep public class com.google.android.gms.analytics.**  {
    public protected *;
}

Vittoria in (quasi) quattro mosse:

  • includere questi comandi in un file di configurazione (chiamato ad esempio proguard.cfg)
  • copiare il file di configurazione appena creato e la libreria “onnicomprensiva” google-play-services.jar all’interno di /usr/local/Android/sdk/tools/proguard/lib (o comunque all’interno della directory dove si trova proguard.jar)
  • installare android-support-v4.jar se mancante, con l’Android SDK manager
  • eseguire il comando java -jar proguard.jar @proguard.cfg

ga-android-tools-proguard
… e il file salvifico compare magicamente all’interno della stessa directory.

Conclusioni

Ovviamente non è vero che il file risultante funzionava, infatti ho dovuto eliminare a mano tre o quattro classi da quest’ultimo come spiegato nella sezione “Pulizie manuali del jar”.
Il risultato è stato che finalmente quello che doveva funzionare ha funzionato, ma anche che le due librerie “base” e “analytics” pesano insieme meno della metà di quella unica, cosa non di poco conto nel mondo Android.

Ionic e plugin di Cordova

In questi giorni mi sono trovato a dover mettere le mani su un plugin di Cordova, resosi necessario su un’app scritta in Ionic (e quindi in AngularJS).
Ora, “tutti” sanno che i plugin di Cordova così come sono scritti mal si adattano ad applicazioni scritte in Angular, e in questo contesto si tende a utilizzare ngCordova.
Il problema è che, sebbene ngCordova incorpori ormai varie decine di plugin, proprio quello che serviva a me mancava. Io avevo bisogno di catturare l’immagine dello schermo in un certo momento – ovvero fare uno screenshot – e cercando sul web l’unico plugin abbastanza maturo e funzionante su Android e iOS mi è sembrato questo.

Rendere un plugin di Cordova più “AngularJS friendly”

Da sviluppatore (molto) poco esperto tanto in AngularJS quanto in Cordova, ho subito incontrato problemi nel collegare i due mondi, e cercando su Google mi è sembrato di non essere da solo. Al contrario.
Il modo più pulito mi è sembrato il seguente, e il primo “problema” (se così vogliamo chiamarlo) era risolto.

.service('$cordovaScreenshot', ['$q', function ($q){
    return {
        capture: function (filename, extension, quality){
            extension = extension || 'jpg';
            quality = quality || '100';

            var defer = $q.defer();

            navigator.screenshot.save(function (error, res){
                if (error) {
                    console.error(error);
                    defer.reject(error);
                } else {
                    console.log('screenshot saved in: ', res.filePath);
                    defer.resolve(res.filePath);
                }
            }, extension, quality, filename);

            return defer.promise;
        }
    };
}])

$cordovaScreenshot.capture('screenshot', 'jpg', 80);

In sintesi prima si registra il servizio Angular contenente la funzione di cattura (opportunamente corredata di libreria Q e promise), poi la si chiama, eventualmente passando delle funzioni da richiamare al termine o in caso di errore.

Modifica e test di un plugin su applicazione Ionic

Avvio l’applicazione – faccio fare lo screenshot – e subito mi rendo conto che dovrò sporcarmi le mani con l’Objective-C, perché sull’iPad il plugin non funzionava come doveva.
Inizio a modificare il file Screenshot.m che sta in project-directory/plugins/com.darktalker.cordova.screenshot/src/ios, avvio di nuovo il progetto ma delle modifiche neanche l’ombra. Inizio a cercare su internet (non oso immaginare cosa sarebbe il nostro lavoro senza un motore di ricerca web…), lancio vari comandi di Cordova consigliati dagli esperti di turno ma ancora niente.
Ancora un po’ di ricerche e capisco che i plugin vanno rimossi e riaggiunti, in questo modo si è certi che il tutto viene ricompilato ad ogni test. I comandi sono cordova plugin remove plugin-url-or-directory-or-id e cordova plugin add plugin-url-or-directory.

Per rendere tutto il processo un po’ più snello ho registrato un task su Grunt, e ve/me lo allego qui per comodità:

uninstallPluginScreenshot: {
    command: 'cordova plugin remove com.darktalker.cordova.screenshot'
},
installPluginScreenshot: {
    command: 'cordova plugin add sources-directory/cordova-screenshot'
    // la registrazione viene fatta a partire dalla directory dei sorgenti
}
// ...
grunt.registerTask('updatePlugin', [
    'shell:uninstallPluginScreenshot',
    'shell:installPluginScreenshot'
]);

Conclusioni

Chiaramente quanto ho scritto non è niente di trascendentale, ma ho notato che c’è molta gente inesperta “la fuori”, e magari qualcuno troverà utili queste poche righe. Sicuramente faranno comodo a me quando me le dimenticherò.

Appcelerator Titanium, licenza indie e sviluppo enterprise

È passato poco più di un mese da quando Jeff Haynie ha annunciato in pompa magna le grandi novità in casa Appcelerator.
Una di queste – la “monetizzazione” dell’sdk Titanium da parte dell’azienda – non è stata accolta con grande entusiasmo, e qualche giorno dopo andando a leggere sul loro sito il dettaglio delle licenze mi sono allarmato leggendo queste parole: “You may choose to purchase multiple Indie plans, but the users of these seats cannot see or share work with one another”.

Dicitura alquanto preoccupante che spiega le limitazioni della licenza Indie

Dicitura alquanto preoccupante che spiega le limitazioni della licenza Indie


Quel “share work” mi ha fatto preoccupare un po’, perché è vero che come utilizzatore precedente ho diritto a una licenza indie gratuita e permanente, ma è altrettanto vero che lavoro in un’azienda insieme ad altra gente, e lì per lì ci ho visto nero. Un mio tweet al veleno ha attirato l’attenzione di Ricardo Alcocer (Director of Developer Relations and Training), il quale preoccupato si è affrettato a chiarire i miei dubbi spiegandomi che “the Indie account will not allow you to share app’s backend data with other Indie developers”. In pratica a detta sua l’unico limite sarebbe stato nella condivisione di backend e analytics tra utenti non registrati come parte di un’unica organizzazione.

Il test con il doppio account

L’altro giorno finalmente mi è arrivato l’invito per utilizzare in anteprima la versione 4.0 (ancora alla RC2) con l’account registrato a nome dell’azienda, e dal momento che avevo già un invito sul mio account personale sono riuscito a fare i test di lavoro con più account sullo stesso progetto. Ho quindi importato uno dei progetti sviluppati in azienda con l’account aziendale, ho fatto qualche test e poi sono uscito da Appcelerator Studio eseguendo il login seguente con il mio account personale, per vedere cosa sarebbe successo.

…e quando invece si esegue il login con un account diverso da quello con cui si è registrata l’app

…e quando invece si esegue il login con un account diverso da quello con cui si è registrata l’app

…e quando invece si esegue il login con un account diverso da quello con cui si è registrata l’app

…e quando invece si esegue il login con un account diverso da quello con cui si è registrata l’app


Aprendo il tiapp.xml si nota il messaggio di warning in cui si spiega che l’applicazione è associate a un’organizzazione di cui non si fa parte e in cui si consiglia di farsi aggiungere in tale organizzazione… facendo pagare a quell’organizzazione delle (costose) licenze Team o Enterprise.

In Appcelerator controllano e registrano tutto

Il problema vero non è tanto il non aver accesso ai servizi web di Appcelerator, quanto che lanciando l’applicazione l’sdk fallisce il processo di build: Application not registered. In pratica a ogni creazione di una nuova applicazione (o importazione di un’applicazione esistente) viene generato un guid univoco che viene associato all’appid, e sulla console web dell’utente (o meglio dell’azienda correntemente selezionata dallo sviluppatore) compare una coppia di applicazioni: quella mobile (Titanium) e l’Arrow DB.
Se si prova ad avviare l’applicazione e sui server Appcelerator all’organizzazione di cui fa parte lo sviluppatore non è associata un’applicazione con quel guid, il processo di build fallisce.

"Application not registered"

“Application not registered”


Il punto è che a ciascuna licenza Indie sono associate due entità: lo sviluppatore e l’organizzazione di cui questo fa parte. La creazione/importazione di un’app da parte di uno sviluppatore fa sì che all’organizzazione correntemente selezionata venga associata questa nuova applicazione, identificata da un guid univoco. Ciascuno sviluppatore può far parte di più organizzazioni, ma un’organizzazione Indie non può aver al suo interno più di uno sviluppatore.
Fregati?

Come fare se in azienda si hanno più licenze Indie e non si vuole pagare

Detto che Titanium è ancora su GitHub e lo si può compilare come spiegato sulla documentazione, ma anche che su questo repository non ci saranno più informazioni relative a tags e release varie, è chiaro che da San Francisco per rimpinguare le casse non puntano tanto agli sviluppatori indipendenti sprovvisti di un account (che probabilmente punteranno ad altre tecnologie più economiche), quanto a quella galassia di piccole software house che hanno scelto per le loro applicazioni quella che era la più promettente tecnologia gratuita in grado di non far sentire troppo la mancanza del nativo. E che potrebbero farsi ingolosire anche dai migliori servizi cloud e assistenza delle versioni Team e Enterprise rispetto alla Indie.

Il controllo messo in piedi da quelli di Appcelerator sulle applicazioni sviluppate con il loro sdk è abbastanza lasco e non limita più di tanto. Probabilmente hanno preferito non infierire troppo, fatto sta che una soluzione c’è: basta organizzarsi in modo da poter cambiare velocemente il guid associato all’applicazione (nel tiapp.xml) al cambiare dello sviluppatore.

Il nodo è nel guid (ultima riga)

Il nodo è nel guid (ultima riga)

Se in azienda non ci sono altri sviluppatori destinati alla piattaforma Titanium è sufficiente utilizzare sempre lo stesso account, per lo sviluppo e per rilasciare l’applicazione. Qualora si debba lavorare in parallelo sullo stesso progetto è invece meglio fare in modo che chi ha un account personale registri le applicazioni sul quale lavorerà sulla propria console Appcelerator, e implementi un meccanismo di sostituzione veloce dei guid. Lasciando l’account “aziendale” libero per rilasciare l’applicazione o per eventuali sviluppatori sprovvisti di un account Indie.

Registrazione di un applicazione già esistente su un diverso account

Il modo migliore che mi è venuto in mente è quello di creare una nuova applicazione – vuota – utilizzando la CLI, e assegnandole appid e nome uguale a quella che si sta cercando di “clonare”.
Apriamo la console, portiamoci su una cartella vuota creata per l’occasione e digitamo appc new --id APP_ID --name APP_NAME, con APP_ID uguale all’appid dell’app, e APP_NAME uguale al suo nome.
appcelerator-new-app
Questo è il modo più veloce di creare un’applicazione vuota, e oltre al riempimento della directory con i file della nuova applicazioni ci ritroveremo anche con la coppia di applicazioni Titanium/Arrow sulla console web. Per qualche motivo sconosciuto dalla console non si possono (ancora) eliminare le applicazioni Arrow, ma a leggere in rete sembra che presto colmeranno questa lacuna.
appcelerator-online-console

Sostituzione “on-demand” del guid

Una volta creata la nuova applicazione “copia” dell’originale possiamo anche buttarla via, perché l’unica cosa che ci interessa è il guid generato.
Il mio consiglio è quello di creare – all’interno dell’applicazione – una cartella con un file .sh per ogni account Indie che avete, e all’interno di questi mettere un comando che sostituisca il guid originale (quello registrato sull’account “aziendale”) con quello associato all’account Indie personale, e disponibile per la copia sulla console web di quest’ultimo.
In bash un modo per sostituire una stringa all’interno di un file è questo:
sed -i "" 's/ORIGINAL_GUID/REPLACED_GUID/' ../tiapp.xml

Ogniqualvolta si avrà bisogno di usare il proprio account Indie personale basterà eseguire il login con questo e poi lanciare il file .sh che metterà le cose in regola con quelli di Appcelerator. Naturalmente il nostro progetto è sotto git, quindi far ritornare il guid corretto dentro il tiapp.xml della nostra app è questione di un git reset.