Un tutorial despre extensiile aplicației iOS 8
Publicat: 2022-03-11Puțini încercaseră înainte (aruncă o privire la asta), dar Apple a fost cel care a definit primul smartphone cum ar trebui 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ă acestea stabilesc și standarde în ceea ce privește modul în care ar trebui să funcționeze un sistem de operare mobil și cum ar trebui să fie realizate aplicațiile pentru un smartphone.
Construirea de pereți de beton între aplicații, făcându-le complet izolate și inconștiente unele de altele, a fost cea mai bună metodă de a le menține în siguranță și de a le proteja datele. Toate activitățile au fost monitorizate îndeaproape de iOS și au existat doar câteva 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 puțin. iOS 8 a introdus un nou concept numit App Extensions. Această nouă caracteristică nu a spart pereții 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 o opțiune de a personaliza ecosistemul iOS și suntem nerăbdători să vedem că se deschide și această cale.
Ce sunt extensiile de aplicație iOS 8 și cum funcționează?
În termeni simpli, iOS 8 App Extensions oferă o nouă metodă de a interacționa cu aplicația dvs., fără a o porni sau a o afișa pe ecran.
Așa cum era de așteptat, Apple s-a asigurat că rămâne în fruntea tuturor, 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 a Centrului de notificare arată informații scurte și permite efectuarea de sarcini rapide.
- Partajare - o extensie care permite aplicației dvs. să partajeze conținut cu utilizatorii de pe rețelele sociale și alte servicii de partajare.
- Acțiune - o extensie care permite crearea de butoane de acțiune personalizate în foaia de acțiuni pentru a permite utilizatorilor să vadă sau să transforme conținutul originar dintr-o aplicație gazdă.
- Editare foto - o extensie care le permite utilizatorilor să editeze o fotografie sau un videoclip în aplicația Fotografii.
- Furnizor de documente - o extensie folosită pentru a permite altor aplicații să acceseze documentele gestionate de aplicația dvs.
- Tastatură personalizată - o extensie care înlocuiește tastatura sistemului.
Extensiile de aplicație nu sunt aplicații independente. 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ă către o singură sarcină. Au propriul lor binar, propria semnătură de cod și propriul set de elemente, dar sunt livrate prin App Store ca parte a binarului aplicației care le conține. O aplicație (care conține) poate avea mai multe extensii. Odată ce utilizatorul instalează o aplicație care are extensii, acestea vor fi disponibile pe iOS.
Să ne uităm la un exemplu: un utilizator găsește o imagine folosind Safari, apasă butonul de partajare și alege extensia de aplicație 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 ale sistemului și, odată ce sarcina este finalizată, Safari distruge vizualizarea extensiei. La scurt timp după aceasta, sistemul încheie procesul și aplicația dvs. nu a fost niciodată afișată pe ecran. Cu toate acestea, a completat 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 funcționa împreună. Dezvoltatorii folosesc API-uri de nivel înalt furnizate de punctul de extensie și de sistem, astfel încât nu trebuie să-și facă griji niciodată cu privire la mecanismele de comunicare subiacente.
Ciclu de viață
Extensiile de aplicație au un ciclu de viață diferit față de aplicațiile iOS. Aplicația gazdă începe ciclul de viață al extensiei ca răspuns la acțiunea unui utilizator. Apoi sistemul instanțiază extensia aplicației și stabilește un canal de comunicare între ei. Vizualizarea extensiei este afișată în contextul aplicației gazdă folosind elementele primite în solicitarea aplicației gazdă. Odată ce vizualizarea extensiei este afișată, utilizatorul poate interacționa cu aceasta. Ca răspuns la acțiunea utilizatorului, extensia completează solicitarea aplicației gazdă efectuând/anulând imediat sarcina sau, dacă este necesar, inițiind un proces de fundal pentru a o efectua. Imediat după aceea, aplicația gazdă dărâmă vizualizarea extensiei și utilizatorul revine la contextul anterior în aplicația gazdă. Rezultatele de la efectuarea acestui proces ar putea fi returnate aplicației gazdă odată ce procesul este finalizat. De obicei, extensia se termină la scurt timp după ce completează solicitarea primită de la aplicația gazdă (sau începe un proces de fundal pentru a o efectua).
Sistemul deschide extensia acțiunii unui utilizator din aplicația gazdă, extensia afișează interfața de utilizare, efectuează unele lucrări și returnează date aplicației gazdă (dacă 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 folosind extensia Today
Extensiile Today, numite și widget -uri , se află în vizualizarea Azi a Centrului de notificare. Sunt 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 făcute în widgetul unei aplicații cu liste de activități). Trebuie să subliniez aici că intrarea de la tastatură nu este acceptată .
Să creăm o extensie Today care va afișa cele mai actualizate informații din aplicația noastră (cod pe GitHub). Pentru a rula acest cod, asigurați-vă că ați (re)configurat grupul de aplicații pentru proiect (selectați echipa de dezvoltare, rețineți că numele grupului de aplicații trebuie să fie unic și urmați instrucțiunile Xcode).
Crearea unui nou widget
După cum am spus anterior, extensiile de aplicație nu sunt aplicații independente. Avem nevoie de o aplicație care conține pe care vom construi extensia aplicației. Odată ce avem aplicația noastră care conține, alegem să adăugăm o nouă țintă navigând la Fișier -> Nou -> Țintă în Xcode. De aici alegem șablonul pentru noua noastră țintă pentru a adăuga o extensie Today.
În pasul următor putem alege numele produsului nostru. Acesta este numele care va apărea în vizualizarea Azi a Centrului de notificări. Există o opțiune pentru a alege limba între Swift și Objective-C și în acest pas. După terminarea acestor pași, Xcode creează un șablon Today, care oferă antet implicit și fișiere de implementare pentru clasa principală (numită TodayViewController
) cu fișierul Info.plist
și un fișier de interfață (un storyboard sau un fișier .xib). Fișierul Info.plist
, implicit, arată astfel:
<key>NSExtension</key> <dict> <key>NSExtensionMainStoryboard</key> <string>MainInterface</string> <key>NSExtensionPointIdentifier</key> <string>com.apple.widget-extension</string> </dict>
Dacă nu doriți să utilizați scenariul oferit 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ți-vă că conținutul arată întotdeauna la zi
- răspunde în mod corespunzător la interacțiunile utilizatorului
- să funcționeze bine (widgeturile iOS trebuie să folosească memoria cu înțelepciune, altfel vor fi terminate de sistem)
Partajarea datelor și a unui container partajat
Extensia de aplicație și aplicația care o conține au ambele acces la datele partajate în containerul lor partajat definit privat - care este o modalitate de comunicare indirectă între aplicația care o conține și extensie.
Nu îți place cum Apple face aceste lucruri atât de „simple”? :)
Partajarea datelor prin NSUserDefaults
este simplă și un caz de utilizare comun. În mod implicit, extensia și aplicația care o conține folosesc seturi de date NSUserDefaults
separate și nu pot accesa containerele celuilalt. Pentru a schimba acest comportament, iOS a introdus Grupuri de aplicații . După activarea grupurilor de aplicații pe aplicația care o conține și pe extensie, în loc să utilizați [NSUserDefaults standardUserDefaults]
utilizați [[NSUserDefaults alloc] initWithSuiteName:@"group.yourAppGroupName"]
pentru a accesa același container partajat.
Actualizarea widget-ului
Pentru a vă asigura că conținutul este întotdeauna actualizat, extensia Today oferă un API pentru gestionarea stării unui widget și gestionarea actualizărilor de conținut. Sistemul captează ocazional instantanee ale vizualizării widget-ului, astfel încât atunci când widget-ul devine vizibil, cel mai recent instantaneu este afișat până când este înlocuit cu o versiune live a vizualizării. O conformare cu protocolul NCWidgetProviding
este importantă pentru actualizarea stării unui widget înainte de a fi luată un instantaneu. Odată ce widget-ul primește apelul widgetPerformUpdateWithCompletionHandler:
vizualizarea widget-ului ar trebui să fie actualizată cu cel mai recent conținut, iar handlerul de finalizare ar trebui apelat cu una dintre următoarele constante pentru a descrie rezultatul 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
- (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler { // Perform any setup necessary in order to update the view. // If an error is encountered, use NCUpdateResultFailed // If there's no update required, use NCUpdateResultNoData // If there's an update, use NCUpdateResultNewData [self updateTableView]; completionHandler(NCUpdateResultNewData); }
Controlul când widgetul este vizibil
Pentru a controla când este afișat un widget, utilizați metoda setHasContent:forWidgetWithBundleIdentifier:
din clasa NCWidgetController
. Această metodă vă va permite să specificați starea conținutului widget-ului. Poate fi apelat din widget sau din aplicația care o conține (dacă este activă). Puteți transmite un NO
sau YES
acestei metode, definind că conținutul widgetului este gata sau nu. Dacă conținutul nu este pregătit, iOS nu va afișa widget-ul dvs. când este deschisă vizualizarea Azi.
NCWidgetController *widgetController = [[NCWidgetController alloc] init]; [widgetController setHasContent:YES forWidgetWithBundleIdentifier:@"com.your-company.your-app.your-widget"];
Deschiderea aplicației care conține din widget
Widgetul Today este singura extensie care poate solicita deschiderea aplicației care o conține apelând metoda openURL:completionHandler:
Pentru a vă asigura că aplicația care o conține se deschide într-un mod care are sens în contextul sarcinii curente a utilizatorului, ar trebui definită o schemă URL personalizată (pe care să o poată utiliza atât widgetul, cât și aplicația care o conține).
[self.extensionContext openURL:[NSURL URLWithString:@"customURLsheme://URLpath"] completionHandler:nil];
Considerații privind UI
Când vă proiectați widget-ul, profitați de clasa UIVisualEffectView
, ținând cont că vizualizările care ar trebui să fie neclare/vibrante trebuie adăugate la contentView
și nu direct la UIVisualEffectView
. Widgeturile (conforme cu protocolul NCWidgetProviding
) ar trebui să încarce stările memorate în cache în viewWillAppear:
pentru a se potrivi cu starea vizualizării din ultima viewWillDisappear:
și apoi tranziția fără probleme la noile date când sosesc, ceea ce nu este un caz cu o vizualizare normală controler (UI este configurat în viewDidLoad
și gestionează animațiile și încărcarea datelor în viewWillAppear
). Widgeturile ar trebui să fie concepute pentru a îndeplini o sarcină sau pentru a deschide aplicația care o conține cu o singură atingere. Intrarea de la tastatură nu este disponibilă într-un widget. Aceasta înseamnă că orice interfață de utilizare care necesită introducerea textului nu trebuie utilizată.
Adăugarea de scroll într-un widget, atât vertical, cât și orizontal, nu este posibilă. Sau mai precis, 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 notificare, ceea ce va determina derularea de la Today la Centrul de notificare. Derularea verticală a unei vizualizări de defilare în interiorul unei extensii Today va fi întreruptă prin defilarea în vizualizarea de astăzi.
Note tehnice
Aici voi evidenția 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
Următoarele elemente sunt valabile pentru toate extensiile:
Obiectul sharedApplication este interzis : extensiile de aplicație nu pot accesa un obiect sharedApplication sau nu pot folosi oricare dintre metodele legate de acel obiect.
Camera și microfonul sunt interzise: extensiile de aplicație nu pot accesa camera sau microfonul de pe dispozitiv (dar acesta nu este cazul pentru toate elementele hardware). Acesta este rezultatul indisponibilității unor API-uri. Pentru a accesa unele elemente hardware din extensia de aplicație, va trebui să verificați dacă API-ul său este disponibil pentru extensiile de aplicație sau nu (cu verificarea disponibilității API descrisă mai sus).
Majoritatea sarcinilor de fundal sunt interzise : extensiile de aplicație 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 interzis: extensiile de aplicație nu pot primi (dar pot trimite) date folosind AirDrop.
Încărcarea/Descărcarea în fundal
Singura sarcină care poate fi efectuată în fundal este încărcarea/descărcarea, folosind NSURLSession object
.
După ce sarcina de încărcare/descărcare este inițiată, extensia poate finaliza solicitarea aplicației gazdă și poate fi încheiată fără niciun efect asupra rezultatului sarcinii. Dacă extensia nu rulează în momentul finalizării sarcinii de fundal, sistemul lansează aplicația care o conține în fundal și este apelată metoda delegată a application:handleEventsForBackgroundURLSession:completionHandler:
Aplicația pentru care extensia inițiază o sarcină NSURLSession
în fundal trebuie să aibă un container partajat configurat pe care să îl poată accesa atât aplicația care o conține, cât și extensia acesteia.
Asigurați-vă că creați diferite sesiuni de fundal pentru aplicația care o conține și pentru fiecare dintre extensiile acesteia (fiecare sesiune de fundal ar trebui să aibă un identificator unic). Acest lucru este important deoarece doar un proces poate folosi o sesiune de fundal la un moment dat.
Acțiune vs. Partajare
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 de partajare folosește SLComposeServiceViewController
, care oferă o interfață de utilizare standard pentru vizualizarea compoziției pe care o puteți utiliza pentru partajarea socială, dar nu este necesar. O extensie de partajare poate moșteni, de asemenea, direct de la UIViewController pentru un design complet personalizat, în același mod în care o extensie Action poate moșteni de la SLComposeServiceViewController
.
Diferențele dintre aceste două tipuri de extensii constă în modul în care sunt menite să fie utilizate. Cu extensia Action, puteți crea o extensie fără interfață de utilizare proprie (de exemplu, o extensie utilizată pentru traducerea textului selectat și returnarea traducerii în aplicația gazdă). Extensia de distribuire vă permite să partajați comentarii, fotografii, videoclipuri, sunet, link-uri și multe altele chiar din aplicația gazdă. UIActivityViewController
conduce atât extensiile Action, cât și Share, unde extensiile Share sunt prezentate ca pictograme colorate în rândul de sus, iar extensiile de acțiune sunt prezentate ca pictograme monocrome în rândul de jos (Imaginea 2.1).
API-uri interzise
API-urile marcate în fișierele antet cu macrocomanda NS_EXTENSION_UNAVAILABLE
sau macrocomandă similară pentru indisponibilitate nu pot fi utilizate (de exemplu: cadrele de interfață HealthKit și EventKit din iOS 8 nu sunt disponibile pentru utilizare în nicio extensie de aplicație).
Dacă partajați cod între o aplicație și o extensie, trebuie să rețineți că chiar ș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 acest lucru prin refactorizarea claselor partajate în ierarhii, cu un părinte comun și subclase diferite pentru ținte diferite. O altă modalitate este să utilizați pre-procesorul prin verificări #ifdef
. Deoarece încă nu există condițional țintă încorporat, trebuie să creați propriul dvs.
Un alt mod frumos de a face acest lucru este prin crearea propriului cadru încorporat. Doar asigurați-vă că nu va conține niciun API indisponibil pentru extensii. Pentru a configura o extensie de aplicație pentru utilizarea unui cadru încorporat, navigați la setările de compilare ale țintei și setați setarea „Require Only App-Extension-Safe API” la Da. La configurarea proiectului Xcode, în faza de compilare a fișierelor de copiere, „Frameworks” trebuie să fie alese ca destinație pentru cadrul încorporat. Dacă alegeți destinația „SharedFrameworks”, trimiterea dvs. va fi respinsă de App Store.
O notă despre compatibilitatea inversă
Deși extensiile de aplicație au fost disponibile numai începând cu iOS 8, puteți face aplicația care vă conține disponibilă pentru versiunile anterioare de iOS.
Conformitatea interfeței umane Apple
Țineți minte Ghidurile Apple pentru interfața umană iOS atunci când proiectați o extensie de aplicație. Trebuie să vă asigurați că extensia dvs. de aplicație este universală, indiferent de dispozitivul pe care îl acceptă aplicația care o 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ție au cu siguranță cel mai vizibil impact în iOS 8. Deoarece 79% dintre dispozitive folosesc deja iOS 8 (măsurat de App Store pe 13 aprilie 2015), extensiile de aplicație 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 despre platformă fără a-și compromite modelul de securitate. Încă nu există nicio modalitate ca aplicațiile terță parte să-și partajeze direct datele între ele. Deși acesta este un concept foarte nou, pare foarte promițător.