Si tratta quindi di una sicurezza aggiuntiva per lo sviluppatore (l'utente è già al sicuro: firma del codice e registro degli acquisti sullo Store permettono di identificare lo sviluppatore - che quindi non inserirà codice cattivo - e di essere sicuri che l'app sul proprio Mac è proprio la stessa rilasciata sul MAS).
Allora, meglio procedere!
Ma come si fa a controllare un documento che esisterà solo sul Mac dell'utente dopo l'acquisto e non su quello dello sviluppatore?
Tempo fa Apple forniva un certificato campione per i test, ma ora la procedura è cambiata: una volta terminato lo sviluppo, si aggiunge il codice di controllo, si lancia l'applicazione, il codice nota l'assenza di ricevuta e ne richiede una apposita al MAS. Una volta ottenuta, la usiamo per testare a fondo il codice di controllo.
Bene, lineare, facile a dirsi... non a farsi!
Dopo vari tentativi, tripla lettura della documentazione ufficiale, ricerche su Google... sono arrivato al procedimento corretto, o per lo meno, quello che funziona! Occorre seguire passo passo le seguenti istruzioni.
- Per prima cosa, andiamo su iTunes Connect, entriamo con le credenziali di sviluppatore e andiamo nella sezione di gestione degli utenti. Vedremo due grandi pulsanti: uno è per gli utenti normali, l'altro per gli utenti di test; entriamo su quest'ultimo e generiamo un utente di test. La creazione è simile a quella di un utente normale: dovremo fornire una mail apposita (deve essere vera, perchè ci verrà richiesto di validarla) con password e tutti i vari dati. Per sicurezza li ho messi uguali al mio utente standard (a parte la mail, ovviamente!). Naturalmente, questo utente è collegato quello di sviluppatore: in questo modo, saranno disponibili tutte le nostre applicazioni registrate.
- Mentre siamo su iTunes Connect, registriamo la nostra applicazione come se stessimo per farne l'upload; cioé, il sistema deve avere in pancia il bundleID e la versione dell'app su cui stiamo lavorando. Questo è indispensabile se la nostra app è nuova e non è mai stata registrata; se invece abbiamo già una versione precedente registrata, non c'è bisogno di fare nulla: basta usare per le nostre prove la versione registrata
- Usciamo da iTunes Connect, apriamo l'applicazione App Store e, dal menu Store, usciamo dal nostro utente; così, quando necessario, il sistema ci chiederà utente e password per accedere. Questo è un punto critico: occorre infatti che Mac OS cerchi di chiamare l'AppStore e si trovi disconnesso: solo così potremo usare l'utente di test.
- Chiudiamo l'App Store e torniamo a Xcode. Inseriamo una bozza del codice che controllerà la presenza della ricevuta. Come, non serve che sia completo: è sufficiente che controlli la presenza della ricevuta e, in caso manchi, effettui un'uscita immediata con il codice
exit(173)
, come dice la documentazione Apple. Anzi, meglio che si tratti di un codice molto semplice: sarà così più facile evitare gli errori e quindi di dover ripetere la procedura. - Apriamo i Build Settings del progetto, cerchiamo la voce Code Signing Identity e impostiamola a Mac Developer. Controlliamo che l'impostazione sia sul target della nostra applicazione e non sul progetto. Questa selezione è un punto critico: se ne scegliamo un'altra, non funzionerà! Notiamo che quando faremo l'upload definitivo su iTunesConnect dovremo riportarla a 3rd Party Developer, altrimenti ci verrà segnalato un errore e l'upload non sarà accettato.
- Verifichiamo nel Info.plist della nostra applicazione che il BundleID (
CFBundleIdentifier
) e la versione (CFBundleShortVersionString
) siano proprio quelli registrati ufficialmente sull'App Store; se non lo sono, non otterremo alcun risultato. - Ora dobbiamo esportare l'applicazione, poiché il tutto funziona solo se l'applicazione è lanciata dal Finder, non da Xcode. Quindi eseguiamo un Archive (menu Product) e verremo portati nella schermata Archive dell'Organizer.
- Selezioniamo l'archivio e clicchiamo sul pulsante Distribute. Selezioniamo Export As - Application, click Ok.
- Nella scheda successiva (Code Signing Identity) dobbiamo scegliere la firma indicata con Mac Development (di solito è quella selezionata di default). Dimentichiamoci del triangolino giallo che ci comunica che stiamo sbagliando... e clicchiamo Next. Anche questo è un punto critico: un altro tipo di firma non scatenerebbe l'evento corretto, ma ci ritroveremmo solo con un triste messaggio in Console (App not signed), per di più fuorviante.
- Ci verrà chiesto dove salvare l'app così esportata e su Xcode abbiamo finito.
- Ora passiamo al Finder: troviamo la nostra app appena esportata e lanciamola! Se abbiamo fatto tutto correttamente, entro qualche secondo ci comparirà il dialogo standard di accesso all'App Store con richiesta di utente (mail) e password. Inseriamo la coppia, usando le credenziali dell'utente di test. Senza che ce ne accorgiamo, il Finder avrà scaricato una ricevuta, l'avrà inserita nel percorso
<miaApp.app/Content/_MAS/>
e avrà rilanciato l'applicazione. Questa volta, il codice verificherà che la ricevuta esiste, quindi la nostra app partirà normalmente! Infatti, usciamo dall'app, entriamo dal Finder nel pacchetto e vedremo che nel Content è comparsa la cartella _MAS, contenente la ricevuta! - Da Finder, prendiamo questa cartella e mettiamola in salvo da qualche parte (anche se poi potremmo scaricarla quante volte vogliamo).
- Da Xcode, facciamo una compilazione normale (basta dare un Run oppure Build for Running).
- Da Finder troviamo l'app appena costruita (è al percorso
DerivedData/<nomeApp>/Build/Products/Debug
), apriamo il bundle e copiamo dentroContent
la cartella_MAS
: da ora in poi potremo costruire il nostro codice finale per il controllo della ricevuta e ad ogni Run la ricevuta sarà trovata al suo posto, per cui possiamo inserire tutti i controlli che vogliamo!
Un consiglio: Apple fornisce codice solo per il controllo del GUID del Mac (e conviene utilizzarlo così come è, senza modifiche), ma nulla sugli altri controlli. Non è pigrizia da parte di Apple: il controllo della ricevuta è proprio dove un eventuale pirata va a fare reverse engineering per introdurre del codice che salti i controlli. Se tutte le applicazioni usassero lo stesso codice, trovata una soluzione, sarebbero craccabili tutte le applicazioni con quel codice; per cui è consigliabile che ciascuno trovi la propria soluzione.
Per lo stesso motivo, conviene usare codice di Foundation in C puro e non metodi Objective-C, che si portano nel codice compilato una messe di informazioni in chiaro. Volendo si può procedere a offuscare il codice, in modo che anche un reverse engineering diventi difficile. In ogni caso, un pirata motivato e con tempo a disposizione prima o poi riuscirà nel proprio intento...
Informazioni: la documentazione Apple si trova a questo link; come esempi, consiglio Roddi, AlanQuatermain e Sazameki. Colgo l'occasione per ringraziarli tutti e tre: lo studio del loro codice mi ha fatto imparare molto!