Un tutorial despre iOS 8 App Extensions

Puțini au mai încercat înainte (aruncați o privire la asta), dar Apple, cu primul iPhone, a fost cea care a definit cum trebuie să arate un smartphone și un sistem de operare mobil. Apple a făcut o descoperire incredibilă în ceea ce privește hardware-ul și experiența utilizatorului. Cu toate acestea, uităm adesea că ei au stabilit, de asemenea, standarde în ceea ce privește modul în care ar trebui să funcționeze un sistem de operare mobil și modul în care ar trebui să fie realizate aplicațiile unui Smartphone.

Construirea unor ziduri de beton între aplicații, făcându-le complet izolate și fără să știe una de cealaltă, a fost cea mai bună metodă de a le menține în siguranță și de a le proteja datele. Toate activitățile erau monitorizate îndeaproape de iOS și existau doar o mână de acțiuni pe care o aplicație le-ar fi putut face în afara domeniului său de aplicare.

„Abstinența este cea mai bună protecție!” – dar unde este distracția în asta?

Le-a luat ceva timp; prea mult, dacă mă întrebați pe mine, dar cu iOS 8 Apple a decis să se distreze. iOS 8 a introdus un nou concept numit Extensii de aplicații. Această nouă caracteristică nu a dărâmat zidurile dintre aplicații, dar a deschis câteva uși oferind un contact blând, dar tangibil, între unele aplicații. Cea mai recentă actualizare a oferit dezvoltatorilor iOS opțiunea de a personaliza ecosistemul iOS și suntem nerăbdători să vedem cum se deschide și această cale.

Ce sunt extensiile de aplicații din iOS 8 și cum funcționează?

În termeni simpli, extensiile de aplicații din iOS 8 oferă o nouă metodă de a interacționa cu aplicația, fără a o porni sau a o afișa pe ecran.

Așa cum era de așteptat, Apple s-a asigurat că este la curent cu totul, așa că există doar câteva puncte de intrare noi pe care aplicația dvs. le poate oferi:

  • Today (numit și widget) – o extensie afișată în vizualizarea Today din Centrul de notificări arată informații scurte și permite efectuarea de sarcini rapide.
  • Share (Partajare) – o extensie care permite aplicației dumneavoastră să partajeze conținut cu utilizatorii pe rețelele de socializare și alte servicii de partajare.
  • Action (Acțiune) – o extensie care permite crearea de butoane de acțiune personalizate în foaia Action (Acțiune) pentru a permite utilizatorilor să vizualizeze sau să transforme conținutul care provine dintr-o aplicație gazdă.
  • Photo Editing – o extensie care permite utilizatorilor să editeze o fotografie sau un videoclip în cadrul aplicației Photos.
  • Document Provider – o extensie utilizată pentru a permite altor aplicații să acceseze documentele gestionate de aplicația dumneavoastră.
  • Custom Keyboard – o extensie care înlocuiește tastatura de sistem.

Extensiile aplicațiilor nu sunt aplicații de sine stătătoare. Ele oferă o funcționalitate extinsă a aplicației (care poate fi accesată din alte aplicații, numite aplicații gazdă) care este menită să fie eficientă și concentrată spre o singură sarcină. Ele au propriul binar, propria semnătură de cod și propriul set de elemente, dar sunt livrate prin intermediul App Store ca parte a binarului aplicației care le conține. O aplicație (care conține) poate avea mai mult de o extensie. Odată ce utilizatorul instalează o aplicație care are extensii, acestea vor fi disponibile în tot iOS.

Să ne uităm la un exemplu: Un utilizator găsește o imagine folosind Safari, apasă pe butonul de partajare și alege extensia aplicației dvs. pentru partajare. Safari „vorbește” cu cadrul iOS Social, care încarcă și prezintă extensia. Codul extensiei rulează, transmite date folosind canalele de comunicare instanțiate de sistem și, odată ce sarcina este îndeplinită – Safari desființează vizualizarea extensiei. La scurt timp după aceasta, sistemul încheie procesul, iar aplicația dvs. nu a fost niciodată afișată pe ecran. Cu toate acestea, ea a finalizat o funcție de partajare a imaginilor.

iOS, folosind comunicarea între procese, este cel responsabil pentru a se asigura că aplicația gazdă și o extensie de aplicație pot lucra împreună. Dezvoltatorii folosesc API-uri de nivel înalt furnizate de punctul de extensie și de sistem, astfel încât nu trebuie să-și facă niciodată griji cu privire la mecanismele de comunicare subiacente.

Ciclul de viață

Extensiile de aplicații au un ciclu de viață diferit de cel al aplicațiilor iOS. Aplicația gazdă declanșează ciclul de viață al extensiei ca răspuns la o acțiune a utilizatorului. Apoi, sistemul instanțiază extensia de aplicație și stabilește un canal de comunicare între ele. Vizualizarea extensiei este afișată în contextul aplicației gazdă folosind elementele primite în cererea aplicației gazdă. Odată ce vizualizarea extensiei este afișată, utilizatorul poate interacționa cu ea. Ca răspuns la acțiunea utilizatorului, extensia finalizează cererea aplicației gazdă prin executarea/anularea imediată a sarcinii sau, dacă este necesar, prin inițierea unui proces de fundal pentru a o executa. Imediat după aceea, aplicația gazdă desființează vizualizarea extensiei, iar utilizatorul revine la contextul său anterior în cadrul aplicației gazdă. Rezultatele obținute în urma efectuării acestui proces ar putea fi returnate aplicației gazdă odată ce procesul este finalizat. De obicei, extensia se termină la scurt timp după ce finalizează cererea primită de la aplicația gazdă (sau începe un proces în fundal pentru a o efectua).

Sistemul deschide extensia unei acțiuni a utilizatorului din aplicația gazdă, extensia afișează interfața de utilizare, efectuează o anumită activitate și returnează date către aplicația gazdă (dacă acest lucru este adecvat tipului de extensie). Aplicația care o conține nici măcar nu rulează în timp ce extensia sa rulează.

Crearea unei extensii de aplicație – Exemplu practic de utilizare a extensiei Today

Extensiile Today, numite și widget-uri, sunt situate în vizualizarea Today din centrul de notificări. Ele reprezintă o modalitate excelentă de a prezenta un conținut actualizat pentru utilizator (cum ar fi afișarea condițiilor meteorologice) sau de a efectua sarcini rapide (cum ar fi marcarea lucrurilor realizate în widgetul unei aplicații de tip listă de sarcini). Trebuie să precizez aici că nu este acceptată introducerea de la tastatură.

Să creăm o extensie Today care va afișa cele mai recente informații din aplicația noastră (codul pe GitHub). Pentru a rula acest cod, asigurați-vă că ați (re)configurat App Group pentru proiect (selectați echipa de dezvoltare, rețineți că numele App Group trebuie să fie unic și urmați instrucțiunile Xcode).

Crearea unui nou widget

După cum am mai spus, extensiile de aplicații nu sunt aplicații de sine stătătoare. Avem nevoie de o aplicație care să ne conțină, pe care vom construi extensia de aplicație. Odată ce avem aplicația care ne conține, alegem să adăugăm o nouă țintă navigând în Xcode la File -> New -> Target (Fișier -> Nou -> Țintă). De aici alegem șablonul pentru noua noastră țintă pentru a adăuga o extensie Today Extension.

În pasul următor putem alege numele produsului nostru. Acesta este numele care va apărea în vizualizarea Today din Centrul de notificări. Există o opțiune pentru a alege limbajul între Swift și Objective-C și în acest pas. Odată cu finalizarea acestor pași, Xcode creează un șablon Today, care oferă fișiere predefinite de antet și de implementare pentru clasa principală (numită TodayViewController) cu fișierul Info.plist și un fișier de interfață (un fișier storyboard sau .xib). Fișierul Info.plist, în mod implicit, arată astfel:

Dacă nu doriți să utilizați storyboard-ul furnizat de șablon, eliminați cheia NSExtensionMainStoryboard și adăugați cheia NSExtensionPrincipalClass cu numele controlerului de vizualizare ca valoare.

Un widget Today ar trebui:

  • asigura că conținutul pare întotdeauna actualizat
  • să răspundă în mod corespunzător la interacțiunile utilizatorului
  • să aibă performanțe bune (widgeturile iOS trebuie să folosească memoria cu înțelepciune, altfel vor fi terminate de sistem)

Sharing Data and a Shared Container

Extensia aplicației și aplicația care o conține au amândouă acces la datele partajate în containerul lor partajat definit în mod privat – ceea ce reprezintă o modalitate de comunicare indirectă între aplicația care o conține și extensia.

Nu-i așa că vă place cum Apple face aceste lucruri atât de „simple”? 🙂

Părtășirea datelor prin intermediul NSUserDefaults este simplă și este un caz de utilizare comună. În mod implicit, extensia și aplicația care o conține utilizează seturi de date NSUserDefaults separate și nu pot accesa containerele celuilalt. Pentru a schimba acest comportament, iOS a introdus App Groups. După activarea grupurilor de aplicații pe aplicația care o conține și pe extensie, în loc să folosiți folosiți initWithSuiteName:@"group.yourAppGroupName"] pentru accesarea aceluiași container partajat.

Updating the Widget

Pentru a vă asigura că conținutul este întotdeauna actualizat, extensia Today oferă un API pentru gestionarea stării unui widget și pentru tratarea actualizărilor de conținut. Sistemul capturează ocazional instantanee ale vizualizării widgetului, astfel încât, atunci când widgetul devine vizibil, cea mai recentă instantanee este afișată până când este înlocuită cu o versiune live a vizualizării. O conformare la protocolul NCWidgetProviding este importantă pentru actualizarea stării unui widget înainte de realizarea unui instantaneu. Odată ce widgetul primește apelul widgetPerformUpdateWithCompletionHandler:, vizualizarea widgetului ar trebui actualizată cu cel mai recent conținut, iar gestionarul de finalizare ar trebui să fie apelat cu una dintre următoarele constante pentru descrierea rezultatului actualizării:

  • NCUpdateResultNewData – Noul conținut necesită redesenarea vizualizării
  • NCUpdateResultNoDate – Widgetul nu necesită actualizare
  • NCUpdateResultFailed – A apărut o eroare în timpul procesului de actualizare

Controlul momentului în care widgetul poate fi vizualizat

Pentru a controla momentul în care un widget este afișat, utilizați metoda setHasContent:forWidgetWithBundleIdentifier: din clasa NCWidgetController. Această metodă vă va permite să specificați starea conținutului widget-ului. Ea poate fi apelată din widget sau din aplicația care îl conține (dacă este activă). Puteți trece un steguleț NO sau un steguleț YES la această metodă, definind faptul că conținutul widget-ului este gata sau nu. Dacă conținutul nu este pregătit, iOS nu va afișa widgetul dvs. atunci când se deschide vizualizarea Today.

Deschiderea aplicației care conține widgetul

Widgetul Today este singura extensie care poate solicita deschiderea aplicației care îl conține prin apelarea metodei openURL:completionHandler:. Pentru a vă asigura că aplicația care conține aplicația se deschide într-un mod care are sens în contextul sarcinii curente a utilizatorului, ar trebui definită o schemă URL personalizată (pe care atât widgetul, cât și aplicația care conține aplicația).

 completionHandler:nil];

Considerații UI

Când vă proiectați widgetul, profitați de clasa UIVisualEffectView, ținând cont de faptul că vizualizările care trebuie să fie blurate/vibrante trebuie adăugate la contentView și nu la UIVisualEffectView direct. Widgeturile (conforme cu protocolul NCWidgetProviding) trebuie să încarce stările din memoria cache în viewWillAppear: pentru a se potrivi cu starea vizualizării din ultimul viewWillDisappear: și apoi să treacă fără probleme la noile date atunci când acestea sosesc, ceea ce nu este cazul unui controler de vizualizare normal (UI este configurat în viewDidLoad și gestionează animațiile și încărcarea datelor în viewWillAppear). Widgeturile ar trebui să fie concepute pentru a efectua o sarcină sau pentru a deschide aplicația care le conține cu o singură atingere. Introducerea tastaturii nu este disponibilă în cadrul unui widget. Acest lucru înseamnă că nu ar trebui să se utilizeze orice interfață de utilizator care necesită introducerea de text.

Adăugarea de derulări într-un widget, atât pe verticală, cât și pe orizontală, nu este posibilă. Sau, mai exact, adăugarea unei vizualizări de defilare este posibilă, dar defilarea nu va funcționa. Gestul de defilare orizontală într-o vizualizare de defilare în extensia Today va fi interceptat de centrul de notificări, ceea ce va determina defilarea din Today în centrul de notificări. Defilarea pe verticală a unei vizualizări de defilare în interiorul unei extensii Today va fi întreruptă de defilarea vizualizării Today.

Note tehnice

Aici voi sublinia câteva lucruri importante de care trebuie să țineți cont atunci când creați o extensie de aplicație.

Caracteristici comune tuturor extensiilor

Celelalte elemente sunt adevărate pentru toate extensiile:

  • ObiectulsharedApplication este în afara limitelor: Extensiile de aplicații nu pot accesa un obiect sharedApplication și nici nu pot utiliza oricare dintre metodele legate de acest obiect.

  • Camera și microfonul sunt în afara limitelor: Extensiile de aplicații nu pot accesa camera sau microfonul de pe dispozitiv (dar acest lucru nu este valabil pentru toate elementele hardware). Acesta este un rezultat al indisponibilității unor API-uri. Pentru a accesa unele elemente hardware în extensia de aplicații, va trebui să verificați dacă API-ul său este disponibil sau nu pentru extensiile de aplicații (cu ajutorul verificării disponibilității API descrise mai sus).

  • Majoritatea sarcinilor din fundal sunt în afara limitelor: Extensiile de aplicații nu pot efectua sarcini de fundal de lungă durată, cu excepția inițierii încărcărilor sau descărcărilor, care este discutată mai jos.

  • AirDrop este în afara limitelor: Extensiile de aplicații nu pot primi (dar pot trimite) date utilizând AirDrop.

Încărcare/descărcare în fundal

Singura sarcină care poate fi efectuată în fundal este încărcarea/descărcarea, utilizând NSURLSession object.

După ce sarcina de încărcare/descărcare este inițiată, extensia poate finaliza cererea aplicației gazdă și poate fi terminată fără niciun efect asupra rezultatului sarcinii. Dacă extensia nu rulează în momentul în care se finalizează sarcina de fundal, sistemul lansează aplicația care o conține în fundal și este apelată metoda delegată a aplicației application:handleEventsForBackgroundURLSession:completionHandler:.

Aplicația a cărei extensie inițiază o sarcină de fundal NSURLSession trebuie să aibă configurat un container partajat pe care atât aplicația care o conține, cât și extensia sa îl pot accesa.

Asigurați-vă că creați sesiuni de fundal diferite pentru aplicația care o conține și pentru fiecare dintre extensiile sale (fiecare sesiune de fundal trebuie să aibă un identificator unic). Acest lucru este important deoarece numai un singur proces poate utiliza o sesiune de fundal la un moment dat.

Action vs. Share

Diferențele dintre extensiile Action și Share nu sunt complet clare din perspectiva unui programator, deoarece în practică sunt foarte asemănătoare. Șablonul Xcode pentru ținta extensiei Share utilizează SLComposeServiceViewController, care oferă o interfață standard de vizualizare a compunerii pe care o puteți utiliza pentru partajarea socială, dar nu este necesară. O extensie de partajare poate, de asemenea, să moștenească direct din UIViewController pentru un design complet personalizat, în același mod în care o extensie de acțiune poate moșteni din SLComposeServiceViewController.

Diferențele dintre aceste două tipuri de extensii constau în modul în care sunt menite să fie utilizate. Cu extensia Action, puteți construi o extensie fără interfață proprie (de exemplu, o extensie utilizată pentru traducerea textului selectat și returnarea traducerii către aplicația gazdă). Extensia Share vă permite să partajați comentarii, fotografii, videoclipuri, înregistrări audio, linkuri și multe altele direct din aplicația gazdă. UIActivityViewController conduce atât extensiile de acțiune cât și cele de partajare, unde extensiile de partajare sunt prezentate sub formă de pictograme color în rândul de sus, iar extensiile de acțiune sunt prezentate sub formă de pictograme monocrome în rândul de jos (Imaginea 2.1).

Forbidden APIs

API-urile marcate în fișierele de antet cu macroul NS_EXTENSION_UNAVAILABLE, sau macroul similar pentru indisponibilitate, nu pot fi utilizate (de exemplu: HealthKit și EventKit UI frameworks din iOS 8 nu sunt disponibile pentru utilizare în nicio extensie de aplicație).

Dacă partajați codul între o aplicație și o extensie, trebuie să țineți cont de faptul că și referirea la un API care nu este permis pentru extensia de aplicație va duce la respingerea aplicației dvs. din App Store. Puteți alege să rezolvați această problemă prin refactorizarea claselor partajate în ierarhii, cu un părinte comun și subclase diferite pentru diferite ținte.O altă modalitate este de a utiliza verificările preprocesorului prin #ifdef. Deoarece nu există încă un condițional de țintă încorporat, trebuie să vă creați unul propriu.

O altă modalitate frumoasă de a face acest lucru este crearea propriului cadru încorporat. Doar asigurați-vă că acesta nu va conține API-uri indisponibile pentru extensii. Pentru a configura o extensie de aplicație pentru a utiliza un cadru încorporat, navigați în setările de construire ale țintei și setați setarea „Require Only App-Extension-Safe API” la Yes (Da). La configurarea proiectului Xcode, în faza de construire Copy Files (Copiere fișiere), „Frameworks” (Cadre) trebuie să fie aleasă ca destinație pentru cadrul încorporat. Dacă alegeți destinația „SharedFrameworks”, trimiterea dvs. va fi respinsă de App Store.

O notă privind compatibilitatea retroactivă

Deși extensiile de aplicații sunt disponibile doar de la iOS 8, puteți face ca aplicația care conține aplicația dvs. să fie disponibilă pentru versiunile anterioare de iOS.

Conformitatea interfeței umane Apple

Țineți cont de Orientările Apple privind interfața umană iOS atunci când proiectați o extensie de aplicație. Trebuie să vă asigurați că extensia aplicației dvs. este universală, indiferent de dispozitivul pe care îl suportă aplicația care vă conține. Pentru a vă asigura că extensia aplicației este universală, utilizați setarea de construire a familiei de dispozitive vizate în Xcode, specificând valoarea „iPhone/iPad” (uneori numită universală).

Concluzie

Extensiile de aplicații au cu siguranță cel mai vizibil impact în iOS 8. Având în vedere că 79% dintre dispozitive utilizează deja iOS 8 (conform măsurătorilor efectuate de App Store la 13 aprilie 2015), extensiile de aplicații sunt caracteristici incredibile de care aplicațiile ar trebui să profite. Prin combinarea restricțiilor API-ului și a modului de partajare a datelor între extensii și aplicația care le conține, se pare că Apple a reușit să abordeze una dintre cele mai mari plângeri legate de platformă fără a compromite modelul de securitate. În continuare nu există nicio modalitate prin care aplicațiile terțe să își partajeze direct datele între ele. Deși acesta este un concept foarte nou, pare foarte promițător.

Lasă un răspuns

Adresa ta de email nu va fi publicată.