Cordova compilare il file build.json correttamente per iOS

È ormai anni che lavoro con Apache Cordova e numerosi sono stati gli aggiornamenti ed i miglioramenti che lo hanno interessato. Per fornire qualche dettaglio aggiuntivo, cordova è un insieme di strumenti che consentono di rendere disponibili delle applicazioni web (sviluppate con HTML5, CSS e JavaScript) su diverse piattaforme. Il principio di funzionamento è molto simile a quello di un sito web ma si ha accesso anche alle caratteristiche hardware del dispositivo sul quale l’app verrà installata, attivando gli opportuni plugin. Le app mobili sviluppate con Apache Cordova sono anche definite App Ibride.

Perchè Apache Cordova e non PhoneGap (tanto per citarne uno)

Ci sono numerosi tool che pur dichiarando di produrre “app native” (cioè scritte in Java, C#, C/C++ o Objective-C, in dipendenza della piattaforma per la quale è necessario compilare l’app) partendo da HTML e JavaScript ma oserei dire che quasi tutte in realtà producono delle “app ibride”.

Spinto dal principio che se voglio andare in un posto e tipicamente dispongo di un autista, potrei avere la necessità di doverci andare anche quando l’autista non può accompagnarmi, quindi devo saper guidare la macchina. Allo stesso tempo sviluppare un’app ibrida (anche se si utilizza Adobe PhoneGap o Ionic) ti porta a dover conoscere necessariamente come funziona Cordova e come si configura nel caso questi servizi non fossero disponibili.

Cordova CLI

Abituatomi quindi ad utilizzare Cordova-cli (la serie di comandi di Cordova che opera da linea di comando), mi ero scritto nel tempo una serie di tool di automazione dei processi tipici. Per esempio se dovevo compilare l’app ed eseguirla su uno specifico dispositivo iOS, mi ero preparato uno script bash al quale fornendo in input l’identificativo del dispositivo mi avviava l’app.

Nel tempo però alcuni di questi script sono diventati obsoleti perchè Apache Cordova (più nello specifico cordova-cli) introduceva una serie di funzionalità che sopperivano alla mancanza o alla complessità con la quale un compito di routine doveva essere svolto.

Compilazione dell’app per iOS

La parte più macchinosa (non complessa, ma macchinosa) dello sviluppo di un’app ibrida è probabilmente il processo di distribuzione poichè differisce per ciascuna piattaforma e richiede un procedimento manuale più o meno lungo.

Nello specifico per iOS è necessario creare un tipo di documento chiamato Mobile Provisioning File. Ma per farlo è necessario segurie una serie di passaggi:

  1. prima si crea un file di tipo .certsigningrequest
  2. poi si genera il file .cer
  3. successivamente si produce il file .mobileprovision
  4. e per non farci mancare nulla si deve generare un altro file .p12 necessario ai fini della firma dell’app.

Questa operazione è da svolgersi sia per la compilazione di debug (tramite la quale è possibile analizzare l’app attraverso i tool di svilupo di Safari) sia per la compilazione finale (il cui file è pubblicabile sullo store di Apple, se rispetta le dovute linee guida).

Fino a qualche tempo fa con per poter eseguire l’app sullo smartphone Apple, era necessario prima eseguire un comando da linea di comando per confezionare un progetto XCode, avviare quindi XCode e da lì utilizzare gli strumenti messi a disposizione. Non vi nascondo che ogni volta che dovevo compilare un’app per iOS mi prendeva quasi l’orticaria.

Il file build.json

Poi finalmente sono arrivati (ormai da un bel po’) una serie di strumenti e facilitazioni dell’ambiente che ci aiutano in questo passaggio. Se non ricordo male a partire dalla versione 6, è stata introdotta una nuova modalità di configurazione dei parametri di firma dell’app (altrimenti selezionabili da XCode ogni volta), basati su un file denominato build.json, presente (o comunque da creare) nella root del progetto. L’esempio riportato anche nella documentazione uficiale è il seguente:

{
    "ios": {
        "debug": {
            "codeSignIdentity": "iPhone Developer",
            "developmentTeam": "FG35JLLMXX4A",
            "packageType": "development",
            "buildFlag": [
                "EMBEDDED_CONTENT_CONTAINS_SWIFT = YES",
                "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES=NO",
                "LD_RUNPATH_SEARCH_PATHS = \"@executable_path/Frameworks\""
            ]
        },
        "release": {
            "codeSignIdentity": "iPhone Developer",
            "developmentTeam": "FG35JLLMXX4A",
            "packageType": "app-store",
            "buildFlag": [
                "EMBEDDED_CONTENT_CONTAINS_SWIFT = YES",
                "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES=NO",
                "LD_RUNPATH_SEARCH_PATHS = \"@executable_path/Frameworks\""
            ]
        }
    }
}

Vediamo dove recuperare le varie informazioni e come usarle:

codeSignIdentity

Deve essere iPhone Developer per la modalità debug e iPhone Distribution per la modalità release. Non chiedetemi il motivo, l’ho scoperto nei vari test ed analizzando innumerevoli log di compilazioni fallite.

developmentTeam

È l’identificativo del Team di sviluppo del quale fate parte. Accedendo a https://developer.apple.com nella sezione Membership troverete il Team ID

packageType

Anche in questo caso, per la modalità debug riporteremo development e per la modalità release riporteremo app-store

Quindi eseguendo il comando che segue l’esito è positivo

 cordova build ios --device --release

provisioningProfile la voce non documentata

Ma capita talvolta di ricevere un messaggio di compilazione negativa

Ricevendo un errore del tipo ** EXPORT FAILED ** la cui causa, leggendo a ritroso i messaggi di output, rimandano alla seguente riga:

[MT] IDEDistribution: Step failed: <IDEDistributionSigningAssetsStep: 0x7fbbadead610>: Error Domain=IDEDistributionSigningAssetStepErrorDomain Code=0 "Locating signing assets failed." UserInfo={NSLocalizedDescription=Locating signing assets failed., IDEDistributionSigningAssetStepUnderlyingErrors=(

"Error Domain=IDEProvisioningErrorDomain Code=9 \"\"ApplicationName.app\" requires a provisioning profile.\" UserInfo={NSLocalizedDescription=\"ApplicationName.app\" requires a provisioning profile., NSLocalizedRecoverySuggestion=Add a profile to the \"provisioningProfiles\" dictionary in your Export Options property list.}"

Facendo un po’ di reverse engineering nella libreria cordova per iOS, ho notato che verificava (se disponibile) anche un’ulteriore chiave provisioningProfile.

Questa chiave è recuperabile dal file con estensione .mobileprovision, da dover aprire con un editor di testi (occhio a non modificarlo altrimenti risulterà successivamente corrotto!!!).

Nella parte iniziale oltre a dei caratteri non stampabili è presente un XML (blocco plist) nel quale è presente un dizionario (dict) con chiavi (key) e valori (talvolta string, talvolta array, talvolta altri dizionari nidificati). Bisonga cercare la chiave UUID e recuperare il contenuto del nodo <string> immediatamente successivo ad essa. Inserendo questa voce nel file build.json il file diventerà quindi come quello che segue:

{
    "ios": {
        "debug": {
            "codeSignIdentity": "iPhone Developer",
            "developmentTeam": "FG35JLLMXX4A",
            "packageType": "development",
            "buildFlag": [
                "EMBEDDED_CONTENT_CONTAINS_SWIFT = YES",
                "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES=NO",
                "LD_RUNPATH_SEARCH_PATHS = \"@executable_path/Frameworks\""
            ]
        },
        "release": {
            "codeSignIdentity": "iPhone Developer",
            "developmentTeam": "FG35JLLMXX4A",
            "packageType": "app-store",
            "provisioningProfile": "e10c3696-084c-4a07-9631-06a54d2b87d0", 
            "buildFlag": [
                "EMBEDDED_CONTENT_CONTAINS_SWIFT = YES",
                "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES=NO",
                "LD_RUNPATH_SEARCH_PATHS = \"@executable_path/Frameworks\""
            ]
        }
    }
}

 

Utilizzando questa informazione, finalmente saremo in grado di compilare l’app senza altri intoppi.