Archivi tag: OSX

cocoa_cup

OSX e le risorse dinamiche

Molte applicazioni di piccole dimensioni nascono e muoiono con il solo contributo delle risorse definite nel nib/xib MainMenu contenuto nel bundle dell’applicazione.
Ma la disponibilità di risorse nella programmazione macOS è molto più articolata e al crescere della dimensione e funzionalità di una applicazione questa disponibilità verrà prima o poi a convergere nella nostra applicazione.

Vediamo di dare una guida ragionata all’uso di questa disponibilità di risorse.

Le diverse forme di disponibilità vanno scelte in ragione della tipologia di risorsa che intendiamo dinamizzare ed in ragione di quante istanze di questa abbiamo bisogno.

Una ulteriore scelta potrebbe essere fatta in ragione del budle che contiene le risorse che ci interessa utilizzare: potremmo infatti considerare l’uso di risorse collocate diversamente dal bundle dell’applicazione; ma per ora tralasciamo questa possibilità.

Iniziano con il distinguere in base alla dimensione della astrazione che intendiamo recuperare dinamicamente: una intera finestra o una vista custom.

All’interno di questa distinzione possiamo trovare una ulteriore dicotomia in relazione al numero di controller collegati alle viste: un solo controller o più controller.

Pur potendo il nib/xib contenere istanze di oggetti controller, quello che a noi interessa è l’istanza (o istanze) di controller che richiedano il caricamento dinamico di risorsa, e non quindi controller che subiscano essi stessi il meccanismo di caricamento dinamico stesso.

Dal punto di vista del nib/xib esiste un riferimento speciale ad un oggetto che è inteso sempre disponibile per le viste ivi contenute perché responsabile del caricamento dell’intero nib/xib: questo riferimento è il File’s Owner. Si può considerarlo spesso un oggetto di tipo controller o comunque un proxy di qualche genere a modello o azioni (a cui collegare le viste).

In virtù di questo il framework AppKit provvede a fornirci due implementazioni di controller con cui governare (prima delle altre cose accessorie in grado di fare) il caricamento dinamico di una intera finestra o di una specifica vista; sono: NSWindowController e NSViewController. L’implementazione di base è spesso sufficiente allo scopo del caricamento dinamico, comunque non è infrequente che si possa necessitare di una loro estensione per compiti poi specifici.
Perché le viste all’interno del nib/xib abbiano accesso ad un controller o model validi è sufficiente fare affidamento al File’s Owner, definendolo nel nib/xib come di classe scelta per il controllo del caricamento della risorsa rappresentata nel nib/xib (quindi NSWindowController o NSViewController, oppure loro derivazioni).
A questo punto una istanza di NSWindowController potrà tramite -(id)initWithWindowNibName:(NSString *)windowNibName
consentire il recupero di una intera finestra da un nib/xib (indicato per nome) con tutte le azioni, outlet o binding collegate a File’s Owner (e quindi NSWindowController) o suoi elementi.
La finestra stessa è ottenibile dall’istanza di NSWindowController mediante metodo -window, così come sono ottenibili altri strumenti propri del controller.

Esempio

NSWindowController * aWindowController = [[NSWindowController alloc] initWithWindowNibName:@"myWindow"];

[aWindowController showWindow: self];

Allo stesso modo un NSViewController potrà farsi carico del caricamento di un nib/xib che definisce una vista attraverso il metodo:

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil

L’idea di questo controller (contrariamente al precedente, orientata alla finestra/documento) è maggiormente sviluppata al supporto per le viste contenute nel nib/xib nella definizione di legami con il modello; a tale scopo fornisce una proprietà generica representedObject per consentire un facile collegamento tra il nib e un oggetto (modello) che ancora non è noto in fase di caricamento del nib/xib.

Esempio

NSViewController *viewCtlr = [[[NSViewController alloc] initWithNibName:@"myView" bundle:nil] autorelease];

[[window contentView] addSubview: [viewCtlr view]];

Questi due controller risolvono il problema di una vista più o meno complessa governata da un proprio controller. Ma come poter gestire istanze multiple di viste custom afferenti al medesimo controller?

Una derivazione da NSView può implementare la seguente:

- (BOOL)loadFromNib:(NSString *)nibName bundle:(NSBundle *)bundle controller:(id)controller
{
/*
 * Codice liberamente tratto da "Resource Programming Guide" di Apple
 *
 */

    NSNib *		aNib = [[NSNib alloc] initWithNibNamed:@"ReusableView" bundle:bundle];

    NSArray *	topLevelObjs;

    if (![aNib instantiateNibWithOwner:controller topLevelObjects:&topLevelObjs]) {

     NSLog(@"Warning! Could not load nib file.\n");

     return NO;

    }

    // Ricerca delle viste

   for(id obj in topLevelObjs) {

     if ( [obj isKindOfClass: [NSView class]] )

        [self addSubview: obj];

}

   // Release the raw nib data.

    [aNib release];

    // Release the top-level objects so that they are just owned by the array.

    [topLevelObjs makeObjectsPerformSelector:@selector(release)];

   // Do not autorelease topLevelObjs.

    return YES;

}

Esempio

ReusableView * reusable1  = [[ReusableView alloc] initWithFrame:NSMakeRect(0, 0, 100, 95)];

[reusable1 loadFromNib:@"ReusableView" bundle:nil controller: controller];

[reusable2 loadFromNib:@"ReusableView" bundle:nil controller: controller];

Un limite di questa implementazione è l’assenza di un meccanismo automatico per definire la catena dei risponditori sommando il contributo di ciascuna vista (e poter passare da una a l’altra, che detengono così catene indipendenti).
Per semplificare rapporto con model implementare una proprietà representedObject similmente a NSViewController.

Va da se che applicare questa forma di caricamento quando si collegano poi alle viste istanze di controller differenti è una inutile ripetizione della implementazione già in essere con NSViewController.
Con questa ultima osservazione chiudiamo la nostra guida al caricamento dinamico delle risorse in macOS.

Alla prossima.

osx-vb-cpuid

OSX+VirtualBox+cpuid

Il mondo degli smanettoni è a volte omissivo, a volte ignorante. L’omissivo lo ammetto quando serve ad impedire a giovani sprovveduti di replicare tecniche pericolose o illegali; l’ignoranza è altra cosa. Ma nel chiacchiericcio internet, persa la fonte originale (forse solo omissiva), dopo molti “copia e incolla” molti post diventano un crogiolo di ignoranza.

Non so se questo sia il caso, ma non si trova molto in giro che spieghi le relazioni profonde tra le cose oggetto di questo post.

Cpuid è una istruzione assembly Intel che consente di determinare tipo e caratteristiche della cpu (ovviamente solo per architetture Intel/AMD).

L’insieme di informazioni risultanti è usato da sistemi operativi e software applicativi per determinare certi comportamenti.

Nel caso di  Oracle VirtualBox vengono utilizzate per determinare se il sistema host può eseguire il codice del sistema guest; ma VirtualBox usa una versione “edulcorata” di queste informazioni anche per mostrare al guest la CPU virtuale che intende fornire. Tipicamente fa questo clonando le informazioni cpuid della CPU reale modificando alcune caratteristiche (per ragioni di isolamento e protezione).

VirtualBox fornisce uno strumento per interrogare la CPU host rispetto al cpuid:

VBoxManage list hostcpuids

Consente anche un certo grado di intervento nell’immagine virtuale di queste informazioni che fornirà al sistema guest:

VBoxManage modifyvm --cpuidset

ma attenzione: potranno essere arbitrariamente alterate da VirtualBox per le ragioni di isolamento e protezione di cui sopra.

Come si relaziona tutto questo con OSX?

Parliamoci chiaro: su internet la maggior parte dei post che trattano questi argomenti lo fanno spiegando di installazioni OSX su hardware e OS “non Apple“: questo non è consentito dalle licenze d’uso.

Nessuno tratta argomenti più “Apple” come la sperimentazione di nuove versioni di OSX prima in virtuale (tipico approccio conservativo per chi personalizza pesantemente il sistema e deve sapere se tutto funzionerà correttamente, se software particolari e magari costosi funzioneranno ancora, ecc). Nessuno parla mai dello sviluppo “legacy”, quindi della necessità di avere una vecchia versione di OSX su cui sperimentare il software.

VirtualBox per OSX è lo strumento ideale per tutte queste cose.

Grazie alla manipolazione delle informazioni cpuid Potremo testare anche per hardware differente (sempre Apple, ma meno recente).

Ma OSX non ha uno strumento nativo per leggere il cpuid; quindi se non vogliamo installare VirtualBox su tutte le macchine da cui estrarre le informazioni cpuid  ci serve uno strumento alternativo.

È quello che propongo con questa mia semplice implementazione in C:

http://www.github.com/andrea-tassotti/cpuid

Il codice deriva da un sorgente open source di Apple (con licenza non GPL), e produce un output come quello di VBoxManage.

Il vantaggio di questo codice è che potrà essere eseguito all’interno del sistema guest per verificare che le informazioni impostate in configurazione della VM siano come previste (salvo modifiche applicate da VirtualBox).

Spero possa esserVi di qualche aiuto per comprendere certi meccanismi.

Alla prossima

IMG_3426.PNG

Big-G consuma risorse sul vostro Mac? 

Era un po’ di tempo che avevo notato che il mio Mac, quando non online, faticava a completare l’avvio della sessione utente.
Mi sono risposto troppe volte che forse avevo esagerato con software e servizi installati.

Troppe volte.

Cosè ieri sera ho deciso di fare il sistemista (a casa si evita sempre di farlo, chissà perchè) e ho guardato il log di sistema appena avviato ed entrato in sessione utente: sorpresa!

Scoperto il colpevole.

Non tutti sanno (e anche io lo ignoravo) ma dovrebbero sapere che quando si installa una qualche applicazione Google (Chrome, Earth, ecc) viene installato in sordina un altro software: GoogleSoftwareUpdate. E io avevo installato Chrome !!!

Questo software si pone come demone all’avvio di sessione e contatta i server Google per cercare nuove versioni dei software. Nulla di male se non per due cose:

  • non è chiaramente visibile all’utente
  • non contempla il non essere online

Proprio questo ultimo punto causa il maggior fastidio.

Infatti l’aggressività del software in configurazione base è tale per cui il fallimento nel contattare i server Google si riverbera nel sistema con forti rallentamenti dello stesso per continua ed intensa attività su disco. Troverete in rete molte fonti citare strani rallentamenti causati da tale software.

Ora una semplice alea di rete può rallentare temporaneamente, ma la non connessione permanente causa molto secondi di rallentamento ogni volta che il processo di controllo aggiornamenti viene schedulato (e questo avviene alla partenza e ogni 5 ore).
Questo è quanto ho rilevato nel log:

19/07/16 22.07.22	GoogleSoftwareUpdateAgent[240]	2016-07-19 22:07:21.899 GoogleSoftwareUpdateAgent[240/0xa0cce540] [lvl=2] -[KSAgentApp setupLoggerOutput] Agent settings: <KSAgentSettings:0x20d280 bundleID=com.google.Keystone.Agent lastCheck=2016-06-15 23:04:48 +0200 checkInterval=18000.000000 uiDisplayInterval=604800.000000 sleepInterval=1800.000000 jitterInterval=900 maxRunInterval=0.000000 isConsoleUser=1 ticketStorePath=/Users/andrea/Library/Google/GoogleSoftwareUpdate/TicketStore/Keystone.ticketstore runMode=3 daemonUpdateEngineBrokerServiceName=com.google.Keystone.Daemon.UpdateEngine daemonAdministrationServiceName=com.google.Keystone.Daemon.Administration logEverything=0 logBufferSize=2048 alwaysPromptForUpdates=0 productIDToUpdate=(null) lastUIDisplayed=(null) alwaysShowStatusItem=0 updateCheckTag=(null) printResults=NO userInitiated=NO>

...

19/07/16 22.08.06	GoogleSoftwareUpdateAgent[240]	2016-07-19 22:08:06.993 GoogleSoftwareUpdateAgent[240/0xb0207000] [lvl=3] -[KSOutOfProcessFetcher(PrivateMethods) helperDidTerminate:] KSOutOfProcessFetcher helper tool failure. [com.google.UpdateEngine.CommonErrorDomain:1501 - 'KSOutOfProcessFetcher.m:783']

...

19/07/16 22.08.09	GoogleSoftwareUpdateAgent[240]	2016-07-19 22:08:09.938 GoogleSoftwareUpdateAgent[240/0xa0cce540] [lvl=2] -[KSAgentApp checkForUpdates] Finished update check.

47 secondi di attivitˆà per un singolo processo quando l’intero sistema esegue bootstrap in meno tempo?

Intollerabile.

Come risolvere ?
Si può pensare di modificare la configurazione di schedulazione, oppure (e questo ho fatto io) rimuovere dall’avvio in sessione. Ci si può facilmente spingere oltre rimuovendo l’intero software.

Consigurazioni e cache le troviamo in:

  • ~/Library/LaunchAgents/com.google.keystone.agent.plist
  • ~/Library/Preferences/com.google.keystone.Agent.plist
  • ~/Library/Caches/com.google.keystone.Agent

Il vero e proprio software in:

  • ~/Library/Google/GoogleSoftwareUpdate [Directory]


Rimuovere
com.google.keystone.agent.plist
è sufficiente per fermare l’avvio del demone.

Se volete spingervi oltre (rimuovere), liberi di farlo: basta buttare tutto nel cestino; non accade nulla di strano: semplicemente non avrete più aggiornamenti automatici.

Ma attenti!

Se installerete nuovamente qualche altro software Google, la storia potrebbe ripetersi.

Alla prossima

osxbeep

Beep OS X, Beep!

Nell’implementazione dello strato BSD, OSX omette alcuni comandi; poco male se una alternativa è data da un comando nativo: verosimilmente l’astrazione BSD non era in grado di realizzare a pieno il compito. Ma questo non é evidentemente l’unico motivo.

Tra i vari “ports” BSD, Apple ne seleziona solo alcuni, mantenendoli spesso ad una versione “vecchia” (vedi il caso di bash, ancora rimasta alla 3).

Non dovrebbe dunque sorprendere l’assenza del port del comando beep, sebbene sia difficilmente spiegabile in termini tecnologici; al piú si spiega in termini di conformità alle metafore della interfaccia che prevedono altro per la segnalazione di eventi ad utente.

L’ironia del caso vuole che mentre il mondo Linux evolve la sua implementazione del comando beep, anche il port per FreeBSD è stato rimosso in quanto obsoleto, alla buona faccia di chi voglia ancora utilizzarlo.
Ovviamente il nostro interesse per il comando beep nel mondo OS X è indotto dalle cose espresse nei nostri precedenti articoli, ma andiamo per gradi.
Ricercando nella documentazione di sviluppo si trova la disponibilità della funzione beep() nella libreria ncurses (https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/beep.3x.html): questa peró esegue il suono di avviso configurato negli effetti sonori nelle preferenze di sistema.
Quindi, almeno per conformità alle metafore dell’interfaccia, il comando beep poteva essere implementato semplicemente utilizzando questa funzione di ncurses.

Ricercando in Internet (come è nostro solito) una soluzione per implementare un beep su OS X troviamo tante risposte ovvie, ma non quella che cercavamo (ovvio anche questo!)

E’ ovvio che un comando echo (o print) in shell riproduca con un suono il carattere audible alert (\a=ASCII 0x7, carattere di controllo
che deve i suoi natali alla Teletype Model 33 ASR):

echo -e "\a"

ma é anche ovvio che si comporti come prevede l’implementazione della libreria ncurses, come per l’equivalente:

tput bel

con cui si attiva la capability del terminale (che dipendono dalla solita libreria)

Ovvio (benchè meno diretto) la possibilitá di eseguire un beep come istruzione AppleScript:

osascript -e beep

e non ci si può stupire se anche questo metodo (piú che mai OS X) esegue Il suono di sistema previsto per gli avvisi.

Il suggerimento piú esilarante è certamente il seguente:

say beep

benchè abbia un suo fascino.
Dunque è giunto il momento di trovare una risposta in stile Nerdammer e dotare il nostro OS X del comando beep.
Volevamo scegliere come modello la piú evoluta implementazione Linux (di Johnathan Nightingale) invece di quella del port FreeBSD:
questioni pratiche e di tempo ci impongono al momento di implementare la versione FreeBSD: ritorneremo sulla implementazione per evolverla accessivamente.

Questa scelta ci consentirà comunque di utilizzare le nostre “beepsongs” come prodotte dal software descritto nel
precedente articolo (ponendo in modo FreeBSD l’output dello script). Alla fine in realtà è questo che cercavamo!

Per realizzare il nostro progetto ci avvaliamo del framework Core Audio, ed in particolare utilizziamo una Audio Unit di tipo output con definita una funzione callback per il rendering del buffer audio, il che ci consentirà di definire algoritmicamente (dunque dinamicamente) la forma d’onda emessa.

Vogliamo con questo simulare al meglio il circuito analogico del beeper delle schede madri di pc e server, evitando i limiti e la complessità che altri metodi imporrebbero volendo ottenere campo di frequenze e durata di emissione molto ampi.

Come al solito la nostra implementazione é una guida: ci si perdoni quindi l’assenza di ricerca di ottimizzazioni nelle prestazioni (ad esempio l’algoritmo di generazione dell’onda sinusoidale non é il piú efficiente in assoluto).

Potete scaricare il codice sorgente da Git Hub e compilarlo sul vostro OS X come indicato nella documentazione.

Ancora una volta buon ascolto!

IMG_2826.JPG

Cocoa & Perl: come ti faccio diventare uno script una applicazione Cocoa

Torniamo a parlare di programmazione Cocoa.

Questa volta parliamo di come integrare una interfaccia Cocoa ad una preesistente applicazione a linea di comando, e perché no, persino un’applicazione interpretata.

Avete preferenze?

Io una si.

Ho scelto di integrare uno script perl che ho sempre utilizzato in Linux e che volevo anche su Mac, ovviamente in una veste più “consona”, passatemi il termine.

Si tratta di cdlabelgen (http://www.aczoom.com/tools/cdinsert/), un utilissimo strumento per generare rapidamente copertine e buste per CDROM e DVD.

Scaricare e provare lo script è stata la prima cosa, anche per verificare quali potevano essere i requisiti per il suo funzionamento su OSX. Tutto tranquillo: funziona senza ulteriori installazioni.
La veste più consona è ovviamente la veste grafica: scartati Automator e AppleScript quali potenziali strumenti, la soluzione è ovviamente una applicazione Cocoa. Come in molti porting di software open source l’applicazione essenzialmente avrà il compito di generare la lista argomenti conseguente alle scelte utente (frontend) e attivare il programma di backend (nel nostro caso cdlabelgen): per non essere dipendente da fattori esterni l’applicazione conterrà lo script nel proprio bundle.

Volendo essere precisi, trattandosi di uno script interpretato, l’applicazione attivata sarà l’interprete perl del sistema a cui verrà passato il nome dello script con i suoi argomenti.
Il risultato mi è sembrato soddisfacente, quindi ve lo propongo. 
Vediamo come si realizza.
Per prima cosa creiamo un nuovo progetto Cocoa Application nel nostro XCode e copiamo lo script cdlabelgen nel gruppo Resources del nostro progetto; parimenti facciamo anche della cartella “postscript” fornita assieme allo script (ci sono i modelli grafici di riferimento).
Poi in un metodo di delega (applicationDidFinishLaunching) dell’applicazione Cocoa leggiamo il percorso alle risorse del bundle: ci serviranno per avviare lo script, in particolare per fornire il percorso delle risorse come program working directory a cdlabelgen in cui cercherà la cartella postScript.

NSString * programWorkingDirectory = [[[NSBundle mainBundle] resourcePath] retain];

NSString * commandPath = [[programWorkingDirectory stringByAppendingPathComponent: @"cdlabelgen"] retain];

(Ovviamente creiamole come variabili dell’istanza di NSObject <NSApplicationDelegate> di cui scegliamo un metodo in cui inserire il precedente codice).
Creiamo quindi una interfaccia per raccogliere le opzioni per la linea di comando ed un pulsante “Vai”, “Disegna”, “Go”, “Salva” o qualsiasi cosa gradiate. Colleghiamo il selettore di questo pulsante al metodo – (IBAction)save:(id)sender che avrete creato nel delegato dell’applicazione.

In questo metodo eseguiremo quanto necessario all’esecuzione di cdlabelgen e alla raccolta eventuale dei suoi frutti.

Un piccolo accenno su come costruire rapidamente una interfaccia per gli argomenti.
Create una istanza di oggetto NSUserDefaultController nello XIB; otterrete un istanza “Shared”; collegate i value dei widget al controller key “value” dell’istanza condivisa di User Defaults: non solo avrete un oggetto per rendere “attive” e visibili al programma le scelte effettuate, ma avrete anche una persistenza delle ultime configurazioni scelte, tutto in uno!

Solo per i tipi derivati da NSPopupMenu c’é una piccola eccezione: occorre in questo caso collegare il “Selected Index”: il value contenuto è per noi solo una label.
Per la costruzione della linea di comando, comprensiva delle opzioni, sarà ora sufficiente decodificare tutte le chiavi negli userDefaults elencandole in un array (è quanto vuole l’API di esecuzione di programmi esterni); ad esempio in questo modo:

NSMutableArray * args = [[NSMutableArray alloc] init];

[args addObject: commandPath];

if ( [[[NSUserDefaults standardUserDefaults] valueForKey:@"noTrayPlaque"] boolValue] == YES )

[args addObject:@"--no-tray-plaque"];

Forse non è elegante, ma questo metodo è il più rapido da realizzare senza dover progettare un complesso modello che tenga conto delle varie necessità o scomodare Core Data. Ricorsarsi di applicare prime il metodo synchronize sui defaults prima di iniziare:

[[NSUserDefaults standardUserDefaults] synchronize];

Per la parte operativa utilizziamo una istanza di NSTask per eseguire il comando esterno.
Per configurare l’esecuzione occorre il percorso del programma interprete.
Su Terminale quindi eseguiamo questo comando:

iMac:~ andrea$ which perl

/usr/bin/perl

Portiamo questo a conoscenza di una istanza NSTask:

NSTask * task = [[NSTask alloc] init];
task.launchPath = @"/usr/bin/perl";

task.currentDirectoryPath = programWorkingDirectory;

oppure (variante senza property):

[task setLaunchPath: @"/usr/bin/perl"];

[task setCurrentDirectoryPath: programWorkingDirectory];

così come per gli argomenti:

NSMutableArray * args = [[NSMutableArray alloc] init];

[args addObject: commandPath];
[args addObject: @"-o"];
[args addObject: filename];

Per ovviare i problemi di bufferizzazione di NSPipe, invece di “leggere” lo standard output di cdlabelgen gli passiamo sempre l’opzione “–output-file” con un nome di file precedentemente fornito dall’utente con un NSSavePanel.
Non rimane che lanciare il nostro programma:

task.arguments = args;
[task launch];

avremo il nostro file generato nel percorso e nome dati.
È opportuno dare un riscontro all’utente con una finestra di dialogo.

Per questo è sufficiente porre un risponditore alla notifica NSTaskDidTerminateNotification che possa presentare un semplice NSAlert. La notifica è lanciata da NSTask al termine del processo, e questo completa l’interfaccia utente.
Potrete aggiungere a piacimento tutto quello che ritenete utile ad una applicazione compiuta, ivi compresa una icona accattivante.

Ovviamente , se decidete di realizzare questo progetto e lo distribuite (anche tra amici), ricordatevi di inserire le credenziale degli sviluppatori di cdlabelgen in qualche parte dell’interfaccia, ossia Avinash Chopde <avinash@aczoom.com> www.aczoom.com e B. W. Fitzpatrick <fitz@red-bean.com>

Al prossimo esperimento.

IMG_2710.JPG

Condivisione multi utente per la libreria iTunes e altro ancora (parte 2)

Continuiamo l’analisi di iTunes per raggiungere il nostro obiettivo di condivisione.

Ripartiamo con alcune caratteristiche “fisiche” della libreria iTunes.

La libreria è composta di un file “.itl” e un insieme ordinato di cartelle.
Lo schema della cartella della libreria è il seguente:

  • Album Artwork
  • iTunes Library.xml
  • iTunes Library Extras.itdb
  • iTunes Library Genius.itdb
  • iTunes Library.itl
  • iTunes Media
  • Previous iTunes Libraries

In particolare la cartella iTunes Media contiene le seguenti cartelle (molte create dinamicamente all’aggiunta in libreria dei media per cui sono predisposte, così come verranno rimosse quando dalla libreria verrà eliminata l’ultima istanza di in certo media):

  • Automatically Add to iTunes.localized
  • Books
  • Music
  • Home Videos
  • iTunes U
  • Mobile Applications
  • Podcasts

Se la vostra libreria è vecchia come la mia, ha probabilmente ereditato la struttura gerarchica precedente: potete liberamente trasferire le cartelle nella posizione corretta (troverete probabilmente Books, Podcast e Home Videos in una cartella iTunes Music).

Mentre le finalità di ciascuna cartella sono abbastanza ovvie, meno noto è l’uso della cartella “Automatically Add to iTunes.localized”.

La cartella “Automatically Add to iTunes.localized” serve ad aggiungere alla libreria (all’avvio di iTunes) gli oggetti ivi eventualmente contenuti: ha due difetti: accetta solo file di media del tipo gestito  (no alias, no playlist in formato xml, testo, m3u, ecc) e copia l’oggetto nel ramo di iTunes Media anche se opzione consolidamento non è attiva. Questo impedisce a questa cartella di essere usata come luogo di scambio (o migrazione) tra librerie da sincronizzare.

ITunes consente di esportare la libreria in formato xml: è una operazione da menu, e si chiama appunto “Esporta Libreria”. Come sottolineato dalla documentazione Apple questa esportazione non è completa; aggiungiamo noi quanto non detto: il file non contempla ad esempio le app mobile, ma per noi è sufficiente riguardi la playlist Musica e le playlist utente derivanti.export

L’importazione sarebbe un mistero se non si tenesse in considerazione quanto abbiamo sottolineato in precedenza e quanto è osservabile interrogando iTunes via AppleScript: la Musica nella libreria è una playlist (particolare, ma pur sempre una playlist). Quindi la funzione “Importa Playlist” è la nostra funzione di importazione della libreria di musica. Per quanto accennato la funzione importerà anche tutte le playlist utente esportate col precedente metodo.import

Non considerate questa esportazione/importazione un sistema di backup completo per la vostra libreria: abbiamo già detto che non contiene tutte le informazioni.

Eppure Apple stessa la considera utile per un disaster recovery della vostra libreria: perché ?

La nascita stessa di una libreria in iTunes fonda le sue ragioni nella necessità di preservare quelle informazioni che non possono risiedere nel formato file del media (è ancora vero per alcuni formati). Tutto quanto è parte del formato file è recuperabile aggiungendo nuovamente il file alla libreria, ma il resto delle informazioni aggiunte grazie alla capacità della libreria non lo è: queste informazioni sono oggetto di esportazione nel formato xml della funzione Esporta Libreria, ecco perché è utile ancora oggi a recuperare una libreria corrotta.

Noi sfrutteremo questa funzione con ben altro scopo.


Quindi, prima di iniziare, affidatevi ad una copia aggiornata della vostra libreria in Time Machine, o (se proprio non potete) almeno affidatevi ad una copia su altro percorso del file Library.itl.

Ci leggiamo quando siete pronti !

IMG_2710.JPG

Condivisione multi utente per la libreria iTunes e altro ancora (parte 1)

Torniamo a parlare di iTunes.

Uno degli aspetti più trascurati in molti software è la possibilità di condividere il proprio archivio tra gli utenti del sistema quando questo archivio abbia una naturale vocazione alla condivisione, anche e non solo per la necessità di tenere sotto controllo il peso degli oggetti di archivio che possono ripetersi tra più utenti.
Forse questa tendenza a trascurare la multi-utenza è dovuta alla ridotta necessità di questa in un mercato dominato da computer portatili utilizzati da un solo proprietario (personal computer, dunque). Il predominare poi dei dispositivi mobile (per loro natura “personal”) ha ulteriormente allontanato la multi-utenza tra le priorità degli sviluppatori software.

Peccano di mancanza di strumenti per la multi-utenza iTunes come gli altri software della famiglia i* (tutto il pacchetto iLife, per intenderci).

Ma stiamo parlando di OSX, un sistema che a differenza del MacOS nasce multiutente, e questa caratteristica ha una naturale collocazione nell’uso domestico e familiare di un iMac, ove ciascun membro può avere un profilo proprio ma che (per naturale appartenenza al medesimo gruppo umano) condividerà molto con gli altri utenti (pensiamo solo ai video o le foto delle gite familiari).

Eppure tutti questi programmi ignorano la loro possibile collocazione in ambito multiutente, lasciando a noi l’onere di individuare una soluzione.

Certo una qualche soluzione a volte è fornita dal supporto Apple (vedi https://support.apple.com/it-it/HT204488), ma come si può leggere non tutto è agevole, e nemmeno tutto è esplicitato.
Focalizzandoci su iTunes (dovremmo affrontare analoghe questioni anche per iPhoto o iMovie) vedremo di trovare alcune soluzioni più “complete”.
iTunes nasce in epoca MacOS ed era dedicato alla sola riproduzione audio. Si è evoluto gradualmente: video, podcast e app mobile sono arrivare col tempo e ben dopo la migrazione a OSX. Librerie ereditate da un’epoca pre iOS (come nel caso della mia) contengono i file multimediali in una cartella iTunes Music. Abbastanza significativo, no?
Gli aggiornamenti dell’applicativo hanno modificato la base dati, ma non la struttura di directory della mia libreria.
Ma il tempo è passato, e la struttura prevista dalle nuove versioni prevede ora tutta un’altra gerarchia, come si può osservare in una libreria creata ex-novo con una nuova versione (vedi procedura https://support.apple.com/it-it/HT201596).

In questa serie di articoli affronteremo principalmente il problema della condivisione della libreria iTunes a vari livelli, ma forniremo anche alcune esperienze sul significato di alcune componenti e sulle possibili configurazioni della libreria, nonché qualche suggerimento sparso. Sia ben chiaro: la “Condivisione in famiglia” non ha nulla a che vedere con il problema affrontato: questa condivisione è via rete locale, quindi l’iPod di vostra moglie non potrà essere sincronizzato con i brani che voi avete acquistato sullo store, pertanto non potrà sentirli durante la sua sessione di palestra (a meno che non abbiate la palestra in casa).
Purtroppo quello che vorremmo raggiungere, ossia la a piena automazione di tutte le procedure, non è ottenibile (come evidente dal documento del supporto Apple https://support.apple.com/it-it/HT204488) anche e non solo in quanto iTunes (come altri software) può aprire librerie differenti solo al suo avvio (vedi procedura https://support.apple.com/it-it/HT201596) e non è capace di fare questo nemmeno via Apple Script (esistono delle possibilità alterando un file di configurazione attraverso il comando defaults, ma non è una scelta condivisibile: troppo suscettibile di variazioni con gli aggiornamenti software). Comunque e una possibilità che completerebbe l’automazione cercata.
Cercheremo comunque di avvicinarci offrendo delle soluzioni di automazione che almeno supportino i processi eventualmente necessari a raggiungere lo scopo principale di realizzare una condivisione multiutente.

Chiariamo subito: una totale condivisione della libreria è possibile in un modo semplice ed immediato ma non utile. Si può infatti completamente condividere una libreria ponendola in area condivisa (Shared folder.localized) tra più utenti e dando i giusti permessi (fatto questo abbastanza ovvio).
Questa però non è (dicevamo) una soluzione utile, in quanto la libreria è contenitore di tutti i media gestibili con iTunes e ciascun utente vorrebbe una qualche autonomia di configurazione, lasciando che solo quanto è realmente condivisibile lo sia mentre il resto possa restare autonomo e privato. Se consideriamo che il file “.itl” gestisce il parco musica, così come le app mobile, libri e playlist e altro ancora è di tutta evidenza come ciascun utente voglia poter mantenere una certa autonomia di alcuni di questi media.

Cercheremo di mostrare come consentire una condivisione completa di musica e al contempo una piena indipendenza di app o quanto altro.
Come si evince da documenti Apple, si possono avere due librerie distinte che condividono gli stessi media, ma non si ha un modo per sincronizzarle.

Questo in quanto le due librerie hanno file “.itl” indipendenti: in questi è presente l’insieme delle informazioni che costituiscono la libreria, e non esistono strumenti di esportazione o importazione parziali.

Cercheremo di superare questo ostacolo. Inoltre daremo una soluzione alla individualità di gestione di altre risorse come le app mobile (la frattura tra versioni di iOS e dispositivi di vecchia generazione impone di avere un parco software non aggiornatissimo in una libreria indipendente diciamo “legacy”).

Iniziamo dunque con una panoramica degli strumenti disponibili, ma questo lo faremo in un altro articolo: ci siamo dilungati troppo.

cilindro2

crypt-à-porter

Tutti usano SSL nella vita digitale quotidiana: nell’utilizzo della posta elettronica, nell’accesso ai siti web, nei propri certificati digitali e, perfino a nostra insaputa, in mille altre appliances del nascente campo “internet-of-things”, quindi facilmente recuperabile onboard sui diversi sistemi embedded (O_o).

Se hai bisogno di crittografare velocemente un file on-the-fly in maniera simmetrica, non è necessario ricorrere a complesse architetture o a programmi addizionali, o impazzire con le migliaia di salse in cui è possibile interagire con openssl. Basta digitare da semplice riga di comando:

$ openssl enc -in <fileA> -out <fileB> -e -aes256 -k <mypass>

e, altrettanto semplicemente, decrittografare con:

$ openssl enc -in <fileB> -out <fileA> -d -aes256 -k <mypass>

Ovviamente, funziona anche sul tuo Mac  😉

cocoa_cup

Introduzione a Cocoa – Parte III

KVO, KVC & Binding
Key-Value Observing (KVO) è un meccanismo che consente agli oggetti di ricevere notifiche su cambiamenti di specifiche proprietà di altri oggetti.

E’ basato sul protocollo informale NSKeyValueObserving.

Le proprietà osservate possono essere semplici attributi, relazioni uno-a-uno o uno-a-molti. Nel contesto MVC, questo meccanismo è particolarmente importante in quanto consente agli oggetti della vista di osservare i cambiamenti negli oggetti del modello attraverso il livello controllo.
Essenziale in questo la tecnologia Cocoa Bindings.

Cocoa fornisce molti oggetti della capacità di essere osservati attraverso il protocollo NSKeyValueObserving.

Key-Value Coding
Key-Value Coding (KVC) è un meccanismo per accedere in modo indiretto agli attributi e relazioni degli oggetti usando identificatori stringa.
kvc

Tecnica sottointesa da molti meccanismi e tecnologie Cocoa; particolare rilevanza ha invero nella programmazione di applicazioni Core Data, con uso intensivo di binding, e altro.
Il meccanismo KVC è reso possibile da protocollo informale NSKeyValueCoding.
Due metodi di questo protocollo (valueForKey: e setValue:forKey:) sono particolarmente importanti in quanto consentono di leggere e scrivere valori delle proprietà di oggetti conformi mediante una chiave di accesso nominale (una stringa con il nome della proprietà). NSOject fornisce una implementazione base di questi metodi. Specializzazioni sono possibili.
Implementare questi metodi può significare dotare le classi di proprietà “virtuali”, ossia non corrispondenti a effettivi attributi negli oggetti, ma “calcolati” o comunque gestiti in modo meno diretto (composti da più proprietà, ecc).
Stabilire collegamenti tra oggetti via KVO è una capacità di Interface Builder.

Cocoa e metafore di interfaccia grafica
Cocoa trova soluzioni per le seguenti metafore:

  • gestione finestre
  • menu statici vs dinamici
  • undo e redo
  • drag & drop (vedi protocollo NSTableViewDataSource)

Cocoa, Applicazioni e Interface Builder
Nell’architettura complessiva di un’applicazione Cocoa entra in gioco anche il concetto di “bundle”.
L’applicazione MacOSX è un insieme (bundle, per l’appunto) di risorse: non solo il codice eseguibile, ma anche descrizioni formali dell’interfaccia, immagini, dati, ecc.
La costruzione di questo insieme è demandata a XCode; nello specifico vendono posti insieme il prodotto della compilazione ed in particolare tutto quanto è compreso nella cartella Resource e del progetto corrente.

In particolare in questa cartella è presente un NIB (ad oggi in formato binario, dunque si parla di XIB, ma per quel che segue è del tutto equivalente) un archivio di informazioni prodotto dal programma Interface Builder che, come appare ovvio, riguarderanno la descrizione formale delle componenti di interfaccia grafica.
xib

In realtà la cosa è più generica: nella maggiorparte dei casi Interface Builder gestisce informazioni per l’interfaccia grafica, ma nei casi pratici può qualcosa di più.
Quando si parla di informazioni per l’interfaccia, in realtà si deve parlare propriamente di istanze di oggetti di classi Cocoa (in particolare di ApplicationKit) capaci di astrarre concetti come finestra, menu, casella di testo, ecc.
Interface Builder dunque istanzia (crea) oggetti di interfaccia: la descrizione dell’interfaccia dunque altro non è che un “contenitore di istanze di oggetti”, un concetto che apparentemente sembra una cosa strana ma che è semplicemente spiegabile introducendo il pattern di programmazione della serializzazione di oggetti.
Interface Builder istanzia dunque oggetti Cocoa (ogni icona nella sua finestra che elenca il contenuto di un NIB è una istanza) mentre l’utente programatore “disegna” l’interfaccia; in seguito li “serializza” nel file NIB/XIB.
Il processo di deserializzazione avviene alla partenza della applicazione che si ritroverà dunque già create tutte le istanze degli oggetti introdotti da Interface Builder.
A ciascun oggetto creato dal NIB verrà inviato il messaggio (leggi “verrà eseguito il metodo”) initWithCoder: e awakeFromNib:.
Eventuali specializzazioni di oggetti (classi utente derivare da qualche classe del framework) potranno governare queste fasi implementando una loro versione di questi metodi.
Ogni icona in Interface Builder rappresenta una istanza di una certa classe.

La seguente icona
object
rappresenta ad esempio una istanza di una classe NSObject o di una versione personalizzata (in questo caso il tipo è da indicare esplicitamente nella finestra inspector di Interface Builder).

Questa è una possibilità di generare istanze di oggetti, anche utente, in maniera visuale e con nascita legata all’avvio dell’applicazione, e dunque esistenti “per costruzione”.

Allocazione di oggetti nel codice ObjC
Diversa la sorte di una istanza di oggetto creata da codice utente.
Tipicamente l’allocazione avviene per mezzo del messaggio statico alloc: inviato ad una classe, a cui segue il messaggio init: (semplice o in qualche variante con argomenti) inviato all’istanza ottenuta.

[[NSObject alloc] init];

Qualora le specializzazioni utente delle classi abbiano la necessità di istanziare oggetti a partire da un NIB e a partire da codice devono implementare entrambi i messaggi initWithCoder: che init: ed eventuali varianti.

Oggetti e messaggi
Gli oggetti Cocoa sono progettati per essere immersi in un circuito di invio reciproco di messaggi atti a costituire quella collaborazione tra oggetti che in object-oriented produce l’essenza di un programma.
E’ dunque importante conoscere, specie nell’ottica della specializzazione, significato e ordine di invio di ciascun messaggio.
Raramente sarà il nostro codice specializzato a inviare messaggi; più frequentemente dovremo analizzare e realizzare le specializzazioni in relazione alla catena di messaggi che “l’applicazione” (vista in una ottica di autonomia di funzionamento) invia alle sue componenti, anche in relazione ad interazione con l’utente (eventi).

Continua

cocoa_cup

Introduzione a Cocoa – Parte II

Cocoa e pattern di programmazione
Cocoa è un framework complesso. Nella sua progettazione ed implementazione sono utilizzate molteplici tecniche di progettazione ad oggetti.
Tra queste ricordiamo l’adozione dei seguenti pattern di programmazione:

  • Modello-Vista-Controller (MVC)
  • Delega
  • Delega di data source
  • Observer – KVC, KVO & binding
  • Notifica

L’adozione di tali modelli di programmazione è praticamente obbligatoria: nella adozione più semplice (senza coinvolgere il progetto di nuove classi) si esplicita nell’individuare specifici oggetti (anche realizzati da utente) capaci di essere attori per uno di questi ruoli.
Cerchiamo di specificare brevemente.

Delega
Il processo di delega è un modo che una istanza di oggetto ha per “traslare” l’implementazione di un suo comportamento (metodo) con il metodo omonimo appartenente ad un altro oggetto (anche di tipo diverso).
Il processo di delega è una forma di composizione (composizione dei comportamenti) tra oggetti.
delega

Una classe che prevede processo di delega per i suoi oggetti avrà un attributo indicante l’oggetto delegato (delegate). Per classi gestite con Interface Builder è sufficiente collegare l’attributo delegato di un oggetto ad un altro oggetto per stabilire la possibilità che il processo di delega abbia luogo.
Nel processo di delega, uno stesso oggetto delegato può essere delegato ad implementare più di un compito (metodo).
Vediamo come esempio quali metodi vengono delegati dalla classe NSTableView ad un suo oggetto delegato:

  • tableView:willDisplayCell:forTableColumn:row:
  • tableView:shouldSelectRow:
  • tableView:shouldSelectTableColumn:
  • selectionShouldChangeInTableView:
  • tableView:shouldEditTableColumn:row:

Modello-Vista-Controllo (MVC – Model/View/Controller)
Pattern di programmazione storico introdotto da SmallTalk e comune a tutti gli approcci ad oggetti alle interfacce grafiche (Java compreso).
Si fonda sulla separazione logica ed implementativa della rappresentazione dei dati dell’applicazione (modello), della componente visuale di interfaccia utente (vista) e della componente che coordina i due e costituisca tutto quanto è eventualmente procedurale nella applicazione (controller).
Una applicazione Cocoa è fortemente conforme al pattern MVC.
Non a caso il framework introduce classi e derivazioni da queste per rappresentare una implementazione delle componenti conforme al modello, con forme e comportamenti prestabiliti a governare le più disparate esigenze.
Per Vista e Controllo abbiamo:

  • NSView
  • NSController

Per il modello si possono usare tutte le classi conforme al modello “container” o “collection” quali:

  • NSArray
  • NSSet

oppure la classe NSManagedObject con l’adozione del framework Core Data (>= 10.4).
mvc

Composizione
La composizione è una tecnica di ADT (Abstract Type Definition) per la composizione delle informazioni degli oggetti. In progettazione object-oriented viene indicata come avente maggiori vantaggi dell’ereditarietà (si prefersce in sostanza descrivere i problemi con relazione ha-un invece che è-un, per ridurre la complessità della gerarchia di derivazione o eventuali effetti collaterali).
Questa filosofia è adottata anche da Cocoa.
Alcune istanze di classe dell’ApplicationKit esistono solo come elementi parte di classi più grandi come “aiuto all’implementazione”.
Un esempio classico è la NSTextFieldCell, che incapsulata in unica istanza in una NSTableView costituisce l’elemento che “disegna” tutte le celle della tabella, riutilizzata più volte dall’algoritmo di disegno della classe NSTableView.
In questo caso si ha anche una scelta architetturale particolare per cui non si incapsulano n istanze di celle per quante sono gli elementi della tabella (aumento di complessità), ma una sola istanza che non è un vero oggetto grafico (non deriva da NSView), ma è uno strumento che implementa il comportamento grafico del “disegnatore”.

Protocolli
Un protocollo è un modo alternativo alla derivazione per rendere possibile l’intoperabilità di classi molto differenti che devono rispondere ai medesimi messaggi.
L’insieme dei messaggi in comune, invece di essere una interfaccia, diviene un protocollo.
Un protocollo diviene così una interfaccia che qualsiasi classe può adottare in maniera indipendente dal progetto.
Esistono due tipi di protocolli in objc: formali ed informali.
Un protocollo informale è sostanzialmente una categoria di NSObject. Contrariamente ad una interfaccia, un protocollo informale non è da implementarsi obbligatoriamente. Prima che sia invocato un metodo, l’oggetto chiamante controlla se l’oggetto destinatario del messaggio implementa il metodo. Dato che i metodi opzionali di un protocollo sono stati introdotti solo nel Objective-C 2.0, i protocolli informali sono stati essenziali nel modo in cui le classi di Foundation e AppKit hanno implementato la delega..

NSObject protocols:

  • autorelease
  • class
  • conformsToProtocol:
  • description
  • hash
  • isEqual:
  • isKindOfClass:
  • isMemberOfClass:
  • isProxy
  • performSelector:
  • performSelector:withObject:
  • performSelector:withObject:withObject:
  • release
  • respondsToSelector:
  • retain
  • retainCount
  • self
  • superclass
  • zone

Un protocollo formale dichiara una lista di metodi che ci si aspetta utilizzata da una classe utente. Hanno una propria sintassi per dichiarazione, adozione e controllo di tipo.

protocollo_formale

Esempio particolare di protocollo: NSTableViewDataSource
Il metodo setDataSource: di una NSTableView vuole un oggetto conforme ad protocollo informale NSTableVewDataSource:

- (void)setDataSource:(id<NSTableViewDataSource>)anObject

L’oggetto che viene impostato come “sorgente dati” sarà in realtà un oggetto sottoposto a processo di delega che risponde al protocollo informale richiesto.

In particolare delega i seguenti compiti (che sono dettati dal protocollo):

Delegate Message Significato
numberOfRowsInTableView: Lettura valori
tableView:objectValueForTableColumn:row: Lettura valori
tableView:setObjectValue:forTableColumn:row: Impostazione valori
tableView:acceptDrop:row:dropOperation: Dragging
tableView:namesOfPromisedFilesDroppedAtDestination:forDraggedRowsWithIndexes: Dragging
tableView:validateDrop:proposedRow:proposedDropOperation: Dragging
tableView:writeRowsWithIndexes:toPasteboard: Dragging
tableView:sortDescriptorsDidChange: Ordinamento

Osservatori e Notifiche
L’Observer pattern è un design pattern utilizzato per tenere sotto controllo lo stato di diversi oggetti.

Observer
Un observer definisce una dipendenza uno-a-molti tra oggetti al fine di notificare e aggiornare automaticamente tutti gli oggetti dipendenti al cambiamento dello stato dell’oggetto osservato. Il pattern Observer è essenzialmente un modello “pubblica e sottoscrivi” in cui un soggetto e i suoi osservatori sono non strettamente accoppiati. La comunicazione può avvenire tra gli osservatori e gli osservati senza che l’uno sappia molto dell’altro.

Notifications
Il meccanismo di notifica in Cocoa implementa un sistema diffusione messaggi uno-a-molti in accoro con il Observer pattern. Gli oggetti di un programma aggiungono se stessi o altri oggetti ad una lista di osservatori per una o più notifiche, ciascuna delle quali viene identificata da una stringa (nome della notifica).
notification
Gli oggetti delegati vengono automaticamente registrati per ricevere messaggi corrispondenti alle notifiche.

Esempio: delegato di una NSTableView. Questi messaggi informano il delegato quando la selezione è stata modificata o quando una colonna viene mossa o ridimensinoata:

Delegate Message Notification
tableViewColumnDidMove: NSTableViewColumnDidMoveNotification
tableViewColumnDidResize: NSTableViewColumnDidResizeNotification
tableViewSelectionDidChange: NSTableViewSelectionDidChangeNotification
tableViewSelectionIsChanging: NSTableViewSelectionIsChangingNotification

 

Continua