objc.io

przed iOS 7 programiści byli dość ograniczeni w tym, co mogli zrobić, gdy ich aplikacje opuściły pierwszy plan. Oprócz funkcji VOIP i opartych na lokalizacji, jedynym sposobem na wykonanie kodu w tle było użycie zadań w tle, ograniczonych do uruchamiania przez kilka minut. Jeśli chcesz pobrać duży film do oglądania w trybie offline lub wykonać kopię zapasową zdjęć użytkownika na swoim serwerze, możesz wykonać tylko część pracy.

iOS 7 dodaje dwa nowe interfejsy API do aktualizacji interfejsu i zawartości aplikacji w tle. Pierwszy, pobieranie w tle, pozwala pobierać nową zawartość z sieci w regularnych odstępach czasu. Drugi, powiadomienia Zdalne, to nowa funkcja wykorzystująca powiadomienia Push do powiadamiania aplikacji o wystąpieniu zdarzenia. Oba te nowe mechanizmy pomagają aktualizować interfejs aplikacji i mogą zaplanować pracę nad nową usługą transferu w tle, która pozwala na wykonywanie poza procesowych transferów sieciowych (pobieranie i przesyłanie).

pobieranie w tle i zdalne powiadomienia to proste haczyki delegowane aplikacji z 30 sekundami czasu zegara ściennego na wykonanie pracy przed zawieszeniem aplikacji. Nie są one przeznaczone do pracy z dużym obciążeniem procesora lub długotrwałych zadań, są raczej przeznaczone do kolejkowania długotrwałych żądań sieciowych, takich jak pobieranie dużych filmów lub wykonywanie szybkich aktualizacji treści.

z punktu widzenia użytkownika jedyną oczywistą zmianą w wielozadaniowości jest nowy przełącznik aplikacji, który wyświetla migawkę interfejsu użytkownika każdej aplikacji tak, jak to było, gdy opuściła pierwszy plan. Ale jest powód wyświetlania migawek – możesz teraz zaktualizować migawkę aplikacji po zakończeniu pracy w tle, pokazując podgląd nowej zawartości. Aplikacje sieci społecznościowych, wiadomości lub pogody mogą teraz wyświetlać najnowsze treści bez konieczności otwierania aplikacji przez użytkownika. Zobaczymy, jak zaktualizować migawkę później.

pobieranie w tle

pobieranie w tle to rodzaj inteligentnego mechanizmu ankiet, który działa najlepiej w aplikacjach, które mają częste aktualizacje treści, takie jak sieci społecznościowe, wiadomości lub aplikacje pogodowe. System budzi aplikację na podstawie zachowania użytkownika i ma na celu uruchomienie pobierania w tle przed uruchomieniem aplikacji przez użytkownika. Na przykład, jeśli użytkownik zawsze korzysta z aplikacji o 13: 00, system uczy się i dostosowuje, wykonując pobieranie przed okresami użytkowania. Pobieranie w tle jest łączone między aplikacjami przez radio urządzenia w celu zmniejszenia zużycia baterii, a jeśli zgłosisz, że nowe dane nie były dostępne podczas pobierania, system iOS może się dostosować, korzystając z tych informacji, aby uniknąć pobierania w cichych czasach.

pierwszym krokiem w włączaniu pobierania w tle jest określenie, że będziesz używać funkcji w kluczu UIBackgroundModes w pliście informacji. Najprostszym sposobem na to jest użycie nowej zakładki możliwości w edytorze projektu Xcode 5, która zawiera sekcję tryby tła dla łatwej konfiguracji opcji wielozadaniowości.

możesz też edytować klucz ręcznie:

<key>UIBackgroundModes</key><array> <string>fetch</string></array>

następnie powiedz iOS, jak często chcesz pobrać:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ ; return YES;}

domyślnym interwałem pobierania jest nigdy, więc musisz ustawić przedział czasu, w przeciwnym razie aplikacja nigdy nie będzie wywoływana w tle. Wartość UIApplicationBackgroundFetchIntervalMinimum prosi system o zarządzanie, gdy aplikacja jest obudzona, tak często, jak to możliwe, ale jeśli jest to niepotrzebne, należy określić własny przedział czasu. Na przykład aplikacja pogodowa może aktualizować Warunki tylko co godzinę. system iOS odczeka co najmniej określony przedział czasu między pobraniem w tle.

jeśli Twoja aplikacja pozwala użytkownikowi wylogować się i wiesz, że nie będzie żadnych nowych danych, możesz ustawić minimumBackgroundFetchInterval z powrotem na UIApplicationBackgroundFetchIntervalNever, aby być dobrym obywatelem i oszczędzać zasoby.

ostatnim krokiem jest wdrożenie następującej metody w delegacie aplikacji:

- (void) application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{ NSURLSessionConfiguration *sessionConfiguration = ; NSURLSession *session = ; NSURL *url = initWithString:@"http://yourserver.com/data.json"]; NSURLSessionDataTask *task = ; // Start the task ;}

to jest miejsce, w którym możesz wykonywać pracę, gdy jesteś obudzony przez system. Pamiętaj, że masz tylko 30 sekund na określenie, czy nowa zawartość jest dostępna, przetworzenie nowej zawartości i aktualizację interfejsu użytkownika. To powinno wystarczyć na pobranie danych z sieci i pobranie kilku miniaturek dla interfejsu użytkownika, ale niewiele więcej. Gdy żądania sieciowe są kompletne, a interfejs użytkownika został zaktualizowany, należy zadzwonić do modułu obsługi zakończenia.

funkcja obsługi zakończenia służy dwóm celom. Po pierwsze, system mierzy moc używaną przez proces i rejestruje, czy nowe dane były dostępne na podstawie przekazanego argumentu UIBackgroundFetchResult. Po drugie, po wywołaniu funkcji wypełniania zostanie wykonana migawka interfejsu użytkownika i zaktualizowany zostanie przełącznik aplikacji. Użytkownik zobaczy nową zawartość podczas przełączania aplikacji. To zachowanie snapshottingu procedury uzupełniania jest wspólne dla wszystkich procedur obsługi uzupełniania w nowych wielozadaniowych interfejsach API.

W aplikacji w świecie rzeczywistym należy przekazać completionHandler do podskładników aplikacji i wywołać ją po przetworzeniu danych i zaktualizowaniu interfejsu użytkownika.

w tym momencie możesz się zastanawiać, w jaki sposób system iOS może migać interfejs aplikacji, gdy działa w tle, i jak działa cykl życia aplikacji przy pobieraniu w tle. Jeśli aplikacja jest obecnie zawieszona, system obudzi ją przed wywołaniem application: performFetchWithCompletionHandler:. Jeśli aplikacja nie jest uruchomiona, system uruchomi ją, wywołując zwykłe metody delegowania, w tym application: didFinishLaunchingWithOptions:. Możesz myśleć o tym jako o aplikacji działającej dokładnie tak samo, jak gdyby użytkownik uruchomił ją z trampoliny, z wyjątkiem tego, że interfejs użytkownika jest niewidoczny, renderowany poza ekranem.

w większości przypadków wykonasz tę samą pracę, gdy aplikacja zostanie uruchomiona w tle, co na pierwszym planie, ale możesz wykryć uruchomienie w tle, patrząc na właściwość applicationState UIApplication:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ NSLog(@"Launched in background %d", UIApplicationStateBackground == application.applicationState); return YES;}

testowanie pobierania w tle

istnieją dwa sposoby symulacji pobierania w tle. Najprostszą metodą jest uruchomienie aplikacji z Xcode i kliknięcie Symuluj pobieranie w tle w menu debugowania Xcode, gdy aplikacja jest uruchomiona.

Alternatywnie możesz użyć schematu, aby zmienić sposób działania aplikacji Xcode. W pozycji menu Xcode produkt wybierz schemat, a następnie Zarządzaj schematami. Z tego miejsca Edytuj lub Dodaj nowy schemat i zaznacz pole wyboru Uruchom z powodu zdarzenia pobierania w tle, jak pokazano poniżej.

powiadomienia zdalne

powiadomienia zdalne umożliwiają powiadamianie aplikacji o wystąpieniu ważnych zdarzeń. Możesz mieć nowe wiadomości do dostarczenia, powiadomienia o najnowszych wiadomościach do wysłania lub najnowszy odcinek ulubionego programu telewizyjnego użytkownika gotowy do pobrania w trybie offline. Powiadomienia zdalne są świetne dla sporadycznych, ale natychmiast ważnych treści, w których opóźnienie między pobieraniem w tle może być niedopuszczalne. Powiadomienia zdalne mogą być również znacznie wydajniejsze niż pobieranie w tle, ponieważ aplikacja uruchamia się tylko wtedy, gdy jest to konieczne.

zdalne powiadomienie jest tak naprawdę zwykłym powiadomieniem Push z ustawioną flagą content-available. Możesz wysłać push z komunikatem informującym użytkownika, że coś się stało, podczas aktualizacji interfejsu użytkownika w tle. Ale powiadomienia zdalne mogą być również ciche, nie zawierające komunikatu alarmowego ani dźwięku, używane tylko do aktualizacji interfejsu aplikacji lub wyzwalania pracy w tle. Po zakończeniu pobierania lub przetwarzania nowej zawartości możesz opublikować powiadomienie lokalne.

powiadomienia Silent push są ograniczone, więc nie bój się wysyłać tyle, ile potrzebuje Twoja aplikacja. iOS i serwery APNS będą kontrolować, jak często są dostarczane,i nie będziesz mieć problemów z wysyłaniem zbyt wielu. Jeśli powiadomienia push są „dławione”, mogą zostać opóźnione do momentu następnego wysłania przez urządzenie pakietu keep-alive lub otrzymania kolejnego powiadomienia.

wysyłanie powiadomień zdalnych

aby wysłać powiadomienie zdalne, Ustaw flagę content-available w pakiecie powiadomień push. Flaga content-available jest tym samym kluczem używanym do powiadamiania aplikacji Newsstand, więc większość skryptów push i bibliotek obsługuje już powiadomienia zdalne. Gdy wysyłasz powiadomienie zdalne, możesz również chcieć dołączyć pewne dane do ładunku powiadomienia, aby aplikacja mogła odwoływać się do zdarzenia. Może to zaoszczędzić kilka żądań sieciowych i zwiększyć responsywność aplikacji.

polecam korzystanie z narzędzia Nomad Cli Houston do wysyłania wiadomości push podczas opracowywania, ale możesz użyć ulubionej biblioteki lub skryptu.

możesz zainstalować Houston jako część nomad-CLI ruby gem:

gem install nomad-cli

a następnie Wyślij powiadomienie za pomocą narzędzia apn zawartego w Nomad

# Send a Push Notification to your Deviceapn push <device token> -c /path/to/key-cert.pem -n -d content-id=42

tutaj znacznik -n określa, że klucz content-available powinien być dołączony, a -d pozwala nam dodać własne klucze danych do ładunku.

wynikowe powiadomienie wygląda następująco:

{ "aps" : { "content-available" : 1 }, "content-id" : 42}

system iOS 7 dodaje nową metodę delegowania aplikacji, która jest wywoływana po otrzymaniu powiadomienia push z kluczem content-available:

- (void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{ NSLog(@"Remote Notification userInfo is %@", userInfo); NSNumber *contentID = userInfo; // Do something with the content ID completionHandler(UIBackgroundFetchResultNewData);}

ponownie, aplikacja jest uruchamiana w tle i ma 30 sekund na pobranie nowej zawartości i aktualizację interfejsu użytkownika, przed wywołaniem procedury obsługi zakończenia. Moglibyśmy wykonać szybkie żądanie sieciowe, Jak to zrobiliśmy w przykładzie pobierania w tle, ale użyjmy potężnej nowej usługi transferu w tle, aby zapytać o duże zadanie pobierania i zobaczyć, jak możemy zaktualizować nasz interfejs po jego zakończeniu.

NSURLSession i usługa transferu w tle

podczas gdy NSURLSession jest nową klasą w iOS 7, odnosi się również do nowej technologii w sieci Fundacji. W celu zastąpienia NSURLConnection zachowane są znane pojęcia i klasy, takie jak NSURL, NSURLRequest i NSURLResponse. Będziesz pracować z zamiennikiem NSURLConnection, NSURLSessionTask, aby tworzyć żądania sieciowe i obsługiwać ich odpowiedzi. Istnieją trzy rodzaje zadań sesji-dane, pobieranie i przesyłanie-z których każdy dodaje cukier składniowy do NSURLSessionTask, więc powinieneś użyć odpowiedniego dla swojego przypadku użycia.

NSURLSession koordynuje jeden lub więcej z tych NSURLSessionTasks i zachowuje się zgodnie z NSURLSessionConfiguration, z którym został utworzony. Możesz utworzyć wiele NSURLSessions, aby grupować powiązane zadania z tą samą konfiguracją. Aby wejść w interakcję z usługą transferu w tle, utworzysz konfigurację sesji za pomocą . Zadania dodane do sesji w tle są uruchamiane w zewnętrznym procesie i kontynuowane, nawet jeśli aplikacja jest zawieszona, ulega awarii lub została zabita.

NSURLSessionConfiguration pozwala ustawić domyślne nagłówki HTTP, skonfigurować zasady pamięci podręcznej, ograniczyć użycie sieci komórkowej i wiele innych. Jedną z opcji jest flaga discretionary, która pozwala systemowi planować zadania dla optymalnej wydajności. Oznacza to, że Twoje transfery będą przechodzić przez Wifi tylko wtedy, gdy urządzenie ma wystarczającą moc. Jeśli bateria jest rozładowana lub dostępne jest tylko połączenie komórkowe, zadanie nie zostanie uruchomione. Znacznik discretionary działa tylko wtedy, gdy obiekt konfiguracji sesji został skonstruowany przez wywołanie metody backgroundSessionConfiguration: i gdy transfer w tle jest inicjowany, gdy aplikacja jest na pierwszym planie. Jeśli transfer zostanie zainicjowany z tła, transfer będzie zawsze przebiegał w trybie uznaniowym.

teraz wiemy trochę o NSURLSession i o tym, jak działa sesja w tle, wróćmy do naszego przykładu zdalnego powiadamiania i dodaj trochę kodu, aby poprosić o pobranie usługi transferu w tle. Po zakończeniu pobierania powiadomimy użytkownika, że plik jest dostępny do użycia.

NSURLSessionDownloadTask

przede wszystkim zajmijmy się zdalnym powiadomieniem i zapytajmy NSURLSessionDownloadTask o usługę transferu w tle. W backgroundURLSession tworzymy NURLSession z konfiguracją sesji w tle i dodajemy delegata naszej aplikacji jako delegata sesji. Dokumentacja odradza tworzenie instancji wielu sesji z tym samym identyfikatorem, więc używamy dispatch_once, aby uniknąć potencjalnych problemów:

- (NSURLSession *)backgroundURLSession{ static NSURLSession *session = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSString *identifier = @"io.objc.backgroundTransferExample"; NSURLSessionConfiguration* sessionConfig = ; session = ]; }); return session;}- (void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{ NSLog(@"Received remote notification with userInfo %@", userInfo); NSNumber *contentID = userInfo; NSString *downloadURLString = ]; NSURL* downloadURL = ; NSURLRequest *request = ; NSURLSessionDownloadTask *task = downloadTaskWithRequest:request]; task.taskDescription = ]; ; completionHandler(UIBackgroundFetchResultNewData);}

tworzymy zadanie pobierania przy użyciu metody klasy NSURLSession i konfigurujemy jego żądanie, a następnie udostępniamy opis do późniejszego wykorzystania. Musisz pamiętać o wywołaniu , aby faktycznie rozpocząć zadanie, ponieważ wszystkie zadania sesji zaczynają się w stanie zawieszonym.

teraz musimy zaimplementować metody NSURLSessionDownloadDelegate, aby odbierać wywołania zwrotne po zakończeniu pobierania. Aby obsługiwać uwierzytelnianie lub inne zdarzenia w cyklu życia sesji, konieczne może być również wdrożenie metod NSURLSessionDelegate lub NSURLSessionTaskDelegate. Należy zapoznać się z cyklem życia dokumentu Apple sesji URL z niestandardowymi delegatami, co wyjaśnia pełny cykl życia we wszystkich typach zadań sesji.

żadna z NSURLSessionDownloadDelegate metod delegowania nie jest opcjonalna, chociaż jedyną, w której musimy podjąć działania w tym przykładzie, jest . Po zakończeniu pobierania zadania otrzymasz Tymczasowy adres URL do pliku na dysku. Musisz przenieść lub skopiować plik do pamięci aplikacji, ponieważ zostanie on usunięty z pamięci tymczasowej po powrocie z tej metody delegowania.

#Pragma Mark - NSURLSessionDownloadDelegate- (void) URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{ NSLog(@"downloadTask:%@ didFinishDownloadingToURL:%@", downloadTask.taskDescription, location); // Copy file to your app's storage with NSFileManager // ... // Notify your UI}- (void) URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes{}- (void) URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{}

Jeśli aplikacja nadal działa na pierwszym planie po zakończeniu zadania sesji w tle, powyższy kod będzie wystarczający. Jednak w większości przypadków aplikacja nie będzie działać lub zostanie zawieszona w tle. W takich przypadkach należy zaimplementować dwie metody delegowania aplikacji, aby system mógł obudzić aplikację. W przeciwieństwie do poprzednich wywołań zwrotnych delegat aplikacji jest wywoływany dwukrotnie, ponieważ delegaci sesji i zadań mogą otrzymywać kilka wiadomości. Metoda delegowania aplikacji application: handleEventsForBackgroundURLSession: jest wywoływana przed wysłaniem tych wiadomości delegowania NSURLSession, a URLSessionDidFinishEventsForBackgroundURLSession jest wywoływana później. W pierwszej metodzie przechowujesz tło completionHandler, a w drugiej wywołujesz je, aby zaktualizować interfejs użytkownika:

- (void) application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler{ // You must re-establish a reference to the background session, // or NSURLSessionDownloadDelegate and NSURLSessionDelegate methods will not be called // as no delegate is attached to the session. See backgroundURLSession above. NSURLSession *backgroundSession = ; NSLog(@"Rejoining session with identifier %@ %@", identifier, backgroundSession); // Store the completion handler to update your UI after processing session events ;}- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session{ NSLog(@"Background URL session %@ finished events.\n", session); if (session.configuration.identifier) { // Call the handler we stored in -application:handleEventsForBackgroundURLSession: ; }}- (void)addCompletionHandler:(CompletionHandlerType)handler forSession:(NSString *)identifier{ if () { NSLog(@"Error: Got multiple handlers for a single session identifier. This should not happen.\n"); } ;}- (void)callCompletionHandlerForSession: (NSString *)identifier{ CompletionHandlerType handler = ; if (handler) { ; NSLog(@"Calling completion handler for session %@", identifier); handler(); }}

ten dwuetapowy proces jest niezbędny do zaktualizowania interfejsu aplikacji, Jeśli nie jesteś jeszcze na pierwszym planie po zakończeniu przesyłania w tle. Ponadto, jeśli aplikacja nie jest w ogóle uruchomiona po zakończeniu transferu w tle, system iOS uruchomi ją w tle, a metody poprzedzające aplikację i delegat sesji zostaną wywołane po application:didFinishLaunchingWithOptions:.

Konfiguracja i ograniczenie

krótko omówiliśmy moc transferów w tle, ale powinieneś zapoznać się z dokumentacją i opcjami NSURLSessionConfiguration, które najlepiej obsługują Twój przypadek użycia. Na przykład NSURLSessionTasks obsługuje timeouty zasobów za pośrednictwem właściwości NSURLSessionConfiguration timeoutIntervalForResource. Możesz użyć tego, aby określić, jak długo chcesz zezwolić na zakończenie transferu, zanim całkowicie zrezygnujesz. Możesz go użyć, jeśli twoja zawartość jest dostępna tylko przez ograniczony czas lub jeśli brak pobrania lub przesłania zasobu w określonym czasieeinterval oznacza, że użytkownik nie ma wystarczającej przepustowości Wi-Fi.

oprócz zadań pobierania, NSURLSession w pełni obsługuje zadania przesyłania, więc możesz przesłać wideo na serwer w tle i zapewnić użytkownika, że nie musi już pozostawiać uruchomionej aplikacji, jak to miało miejsce w systemie iOS 6. Dobrym akcentem byłoby ustawienie właściwości sessionSendsLaunchEvents Twojej NSURLSessionConfiguration na NO, jeśli Twoja aplikacja nie wymaga uruchamiania w tle po zakończeniu transferu. Efektywne wykorzystanie zasobów systemowych sprawia, że zarówno system iOS, jak i użytkownik są zadowoleni.

wreszcie, istnieje kilka ograniczeń w korzystaniu z sesji w tle. Ponieważ delegat jest wymagany, nie można używać prostych metod wywołania zwrotnego opartych na blokach na NSURLSession. Uruchomienie aplikacji w tle jest stosunkowo drogie, więc przekierowania HTTP są zawsze podejmowane. Usługa transferu w tle obsługuje tylko HTTP i HTTPS i nie można używać niestandardowych protokołów. System optymalizuje transfery w oparciu o dostępne zasoby i nie można zmusić transferu do postępu w tle przez cały czas.

zauważ również, że NSURLSessionDataTasks nie są w ogóle obsługiwane w sesjach w tle i powinieneś używać tych zadań tylko do krótkotrwałych, małych żądań, a nie do pobierania lub przesyłania.

podsumowanie

wydajne nowe interfejsy API wielozadaniowości i sieci w systemie iOS 7 otwierają całą gamę możliwości zarówno dla nowych, jak i istniejących aplikacji. Rozważ przypadki użycia w swojej aplikacji, które mogą korzystać z nieprzetworzonych transferów sieciowych i świeżych danych, i wykorzystaj w pełni te fantastyczne nowe interfejsy API. Ogólnie rzecz biorąc, zaimplementuj transfery w tle tak, jakby aplikacja była uruchomiona na pierwszym planie, dokonując odpowiednich aktualizacji interfejsu użytkownika, a większość pracy jest już wykonana za Ciebie.

  • użyj odpowiedniego nowego interfejsu API dla zawartości aplikacji.

  • bądź wydajny i zadzwoń do obsługi zakończenia jak najwcześniej.

  • procedury wypełniania aktualizują migawkę interfejsu aplikacji.

Czytaj dalej

  • sesja WWDC 2013 „Co nowego w wielozadaniowości”

  • sesja WWDC 2013 „Co nowego w sieci Fundacji”

  • przewodnik programowania systemu ładowania adresów URL

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.