antes do iOS 7, os desenvolvedores eram bastante limitados no que podiam fazer quando seus aplicativos deixavam o primeiro plano. Além de VOIP e recursos baseados em localização, a única maneira de executar o código em segundo plano era usar tarefas em segundo plano, restritas à execução por alguns minutos. Se você quiser baixar um vídeo grande para visualização offline ou fazer backup das fotos de um usuário no seu servidor, poderá concluir apenas parte do trabalho.O iOS 7 adiciona duas novas APIs para atualizar a interface do Usuário e o conteúdo do seu aplicativo em segundo plano. O primeiro, Background Fetch, permite que você busque novos conteúdos da rede em intervalos regulares. O segundo, notificações remotas, é um novo recurso que aproveita as notificações Push para notificar um aplicativo quando um evento ocorre. Ambos os novos mecanismos ajudam você a manter a interface do seu aplicativo atualizada e podem agendar o trabalho no novo serviço de transferência em segundo plano, que permite realizar transferências de rede fora do processo (downloads e uploads).
busca de fundo e notificações remotas são simples ganchos delegados de aplicativos com 30 segundos de tempo de relógio de parede para executar o trabalho antes que seu aplicativo seja suspenso. Eles não são destinados a trabalho intensivo de CPU ou tarefas de longa execução, em vez disso, eles são para enfileirar solicitações de rede de longa duração, como um grande download de filme ou executar atualizações rápidas de conteúdo.
do ponto de vista de um usuário, a única mudança óbvia para multitarefa é o novo App switcher, que exibe um instantâneo da interface do usuário de cada aplicativo como era quando ele deixou o primeiro plano. Mas há um motivo para exibir os instantâneos – agora você pode atualizar o instantâneo do seu aplicativo depois de concluir o trabalho em segundo plano, mostrando uma visualização do novo conteúdo. Redes sociais, notícias ou aplicativos meteorológicos agora podem exibir o conteúdo mais recente sem que o usuário precise abrir o aplicativo. Veremos como atualizar o instantâneo mais tarde.
Background Fetch
background Fetch é um tipo de mecanismo de pesquisa inteligente que funciona melhor para aplicativos que têm atualizações de conteúdo frequentes, como redes sociais, notícias ou aplicativos meteorológicos. O sistema acorda o aplicativo com base no comportamento de um usuário e Visa acionar buscas em segundo plano antes do usuário iniciar o aplicativo. Por exemplo, se o usuário sempre usa um aplicativo às 13h, o sistema aprende e se adapta, realizando buscas antes dos períodos de uso. As buscas em segundo plano são agrupadas entre aplicativos pelo rádio do dispositivo para reduzir o uso da bateria e, se você relatar que novos dados não estavam disponíveis durante uma busca, o iOS pode se adaptar, usando essas informações para evitar buscas em momentos de silêncio.
o primeiro passo para ativar a busca em segundo plano é especificar que você usará o recurso na tecla UIBackgroundModes
em sua lista de informações. A maneira mais fácil de fazer isso é usar a nova guia de recursos no editor de projetos do Xcode 5, que inclui uma seção de modos em segundo plano para facilitar a configuração de opções multitarefa.
como Alternativa, você pode editar manualmente a chave:
<key>UIBackgroundModes</key><array> <string>fetch</string></array>
em seguida, informe o iOS quantas vezes você gostaria de buscar:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ ; return YES;}
O padrão de busca intervalo nunca é, por isso, você precisará definir um intervalo de tempo ou o aplicativo não vai ser chamado no plano de fundo. O valor de UIApplicationBackgroundFetchIntervalMinimum
pede ao sistema para gerenciar quando seu aplicativo é acordado, sempre que possível, mas você deve especificar seu próprio intervalo de tempo se isso for desnecessário. Por exemplo, um aplicativo meteorológico só pode atualizar as condições de hora em hora. o iOS aguardará pelo menos o intervalo de tempo especificado entre as buscas em segundo plano.
Se o seu aplicativo permite que o usuário sair, e você sabe que não haverá novos dados, você pode querer definir o minimumBackgroundFetchInterval
para UIApplicationBackgroundFetchIntervalNever
para ser um bom cidadão e conservação dos recursos.
a etapa final é implementar o seguinte método em seu delegado de aplicativo:
- (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 ;}
é aqui que você pode executar o trabalho quando é acordado pelo sistema. Lembre-se de que você só tem 30 segundos para determinar se o novo conteúdo está disponível, para processar o novo conteúdo e para atualizar sua IU. Isso deve ser tempo suficiente para buscar dados da rede e buscar algumas miniaturas para sua IU, mas não muito mais. Quando suas solicitações de rede estiverem concluídas e sua interface do usuário tiver sido atualizada, você deve ligar para o manipulador de conclusão.
o manipulador de conclusão serve a dois propósitos. Primeiro, o sistema mede a potência usada pelo seu processo e registra se novos dados estavam disponíveis com base no argumento UIBackgroundFetchResult
que você passou. Em segundo lugar, quando você chama o manipulador de conclusão, um instantâneo de sua interface do Usuário é obtido e o alternador de aplicativos é atualizado. O usuário verá o novo conteúdo quando estiver trocando de aplicativos. Esse comportamento de snapshotting do manipulador de conclusão é comum a todos os manipuladores de conclusão nas novas APIs multitarefa.
em um aplicativo do mundo real, você deve passar o completionHandler
para sub-componentes do seu aplicativo e chamá-lo quando você processou dados e atualizou sua interface do Usuário.
neste ponto, você pode estar se perguntando como o iOS pode capturar a interface do usuário do seu aplicativo quando ele está sendo executado em segundo plano e como o ciclo de vida do aplicativo funciona com a busca em segundo plano. Se o seu aplicativo estiver suspenso no momento, o sistema o ativará antes de ligar para application: performFetchWithCompletionHandler:
. Se o seu aplicativo não estiver em execução, o sistema o iniciará, chamando os métodos delegados usuais, incluindo application: didFinishLaunchingWithOptions:
. Você pode pensar nisso como o aplicativo rodando exatamente da mesma maneira que se o usuário o tivesse iniciado a partir do Springboard, exceto que a interface do Usuário é invisível, renderizada fora da tela.
Na maioria dos casos, você vai realizar o mesmo trabalho quando o aplicativo é iniciado em segundo plano, como você faria em primeiro plano, mas você pode detectar fundo lança observando o applicationState
propriedade de UIApplication:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ NSLog(@"Launched in background %d", UIApplicationStateBackground == application.applicationState); return YES;}
Teste de Obtenção de Fundo
Há duas maneiras que você pode simular um fundo de busca. O método mais fácil é executar seu aplicativo a partir do Xcode e clicar em simular busca em segundo plano no menu de depuração do Xcode enquanto seu aplicativo está em execução.Como alternativa, você pode usar um esquema para alterar a forma como o Xcode executa seu aplicativo. No item de menu Xcode produto, escolha esquema e, em seguida, Gerenciar esquemas. A partir daqui, edite ou adicione um novo esquema e marque a caixa de seleção Iniciar devido a um evento de busca em segundo plano, conforme mostrado abaixo.
notificações remotas
as notificações remotas permitem que você notifique seu aplicativo quando eventos importantes ocorrerem. Você pode ter novas mensagens instantâneas para entregar, alertas de notícias de última hora para enviar, ou o último episódio do programa de TV favorito do seu usuário pronto para ele ou ela baixar para visualização offline. As notificações remotas são ótimas para conteúdo esporádico, mas imediatamente importante, onde o atraso entre as buscas em segundo plano pode não ser aceitável. As notificações remotas também podem ser muito mais eficientes do que a busca em segundo plano, pois seu aplicativo só é iniciado quando necessário.
uma notificação remota é realmente apenas uma notificação Push normal com o conjunto de sinalizadores content-available
. Você pode enviar um push com uma mensagem de Alerta informando ao usuário que algo aconteceu, enquanto você atualiza a interface do usuário em segundo plano. Mas as notificações remotas também podem ficar em silêncio, sem mensagem de alerta ou som, usadas apenas para atualizar a interface do seu aplicativo ou acionar o trabalho em segundo plano. Você pode postar uma notificação local quando terminar de baixar ou processar o novo conteúdo.
as notificações push silenciosas são limitadas em taxas, portanto, não tenha medo de enviar quantas suas necessidades de aplicativos. o iOS e os servidores APNS controlarão a frequência com que são entregues e você não terá problemas para enviar muitos. Se suas notificações push estiverem limitadas, elas podem ser atrasadas até a próxima vez que o dispositivo enviar um pacote keep-alive ou receber outra notificação.
envio de notificações remotas
para enviar uma notificação remota, defina o sinalizador conteúdo disponível em uma carga útil de notificação por push. O sinalizador de conteúdo disponível é a mesma chave usada para notificar aplicativos de banca de jornal, portanto, a maioria dos scripts e bibliotecas push já oferece suporte a notificações remotas. Ao enviar uma notificação remota, Você também pode incluir alguns dados na carga útil da notificação, para que seu aplicativo possa fazer referência ao evento. Isso pode salvar algumas solicitações de rede e aumentar a capacidade de resposta do seu aplicativo.
eu recomendo usar o Utilitário Houston do Nomad CLI para enviar mensagens push durante o desenvolvimento, mas você pode usar sua biblioteca ou script favorito.
você pode instalar Houston como parte do Nomad-CLI ruby gem:
gem install nomad-cli
e, em seguida, envie uma notificação com o utilitário apn incluído no Nomad
# Send a Push Notification to your Deviceapn push <device token> -c /path/to/key-cert.pem -n -d content-id=42
aqui, o sinalizador -n
especifica que a chave disponível para conteúdo deve ser incluída e -d
nos permite adicionar nossas próprias chaves de dados à carga útil.
a carga útil de notificação resultante é assim:
{ "aps" : { "content-available" : 1 }, "content-id" : 42}
o iOS 7 adiciona um novo método de Delegado de aplicativo, que é chamado quando uma notificação push com a chave de conteúdo disponível é recebida:
- (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);}
novamente, o aplicativo é iniciado em segundo plano e tem 30 segundos para buscar novo conteúdo e atualizar sua interface do Usuário, antes de chamar o manipulador de conclusão. Poderíamos executar uma solicitação de rede rápida como fizemos no exemplo de busca em segundo plano, mas vamos usar o poderoso novo serviço de transferência em segundo plano para enfileirar uma grande tarefa de download e ver como podemos atualizar nossa interface do usuário quando ela for concluída.
NSURLSession e serviço de transferência em segundo plano
embora NSURLSession
seja uma nova classe no iOS 7, também se refere à nova tecnologia em rede de Fundação. Destinados a substituir NSURLConnection
, conceitos e classes familiares como NSURL
, NSURLRequest
e NSURLResponse
são preservados. Você trabalhará com a substituição de NSURLConnection
, NSURLSessionTask
, para fazer solicitações de rede e lidar com suas respostas. Existem três tipos de tarefas de sessão – dados, download e upload – cada uma das quais adiciona açúcar sintático a NSURLSessionTask
, portanto, você deve usar o apropriado para o seu caso de uso.
an NSURLSession
coordena um ou mais desses NSURLSessionTask
s e se comporta de acordo com o NSURLSessionConfiguration
com o qual foi criado. Você pode criar vários NSURLSession
s para agrupar tarefas relacionadas com a mesma configuração. Para interagir com o serviço de transferência em segundo plano, você criará uma configuração de sessão usando . As tarefas adicionadas a uma sessão em segundo plano são executadas em um processo externo e continuam mesmo que seu aplicativo seja suspenso, trave ou seja morto.
NSURLSessionConfiguration
permite definir cabeçalhos HTTP padrão, configurar políticas de cache, restringir o uso da rede celular e muito mais. Uma opção é o sinalizador discretionary
, que permite ao sistema agendar tarefas para um desempenho ideal. O que isso significa é que suas transferências só passarão por Wifi quando o dispositivo tiver energia suficiente. Se a bateria estiver fraca ou apenas uma conexão de celular estiver disponível, sua tarefa não será executada. O sinalizador discretionary
só tem efeito se o objeto de configuração da sessão tiver sido construído chamando o método backgroundSessionConfiguration:
e se a transferência em segundo plano for iniciada enquanto seu aplicativo estiver em primeiro plano. Se a transferência for iniciada em segundo plano, a transferência será sempre executada no modo discricionário.
agora sabemos um pouco sobre NSURLSession
e como funciona uma sessão em segundo plano, vamos retornar ao nosso exemplo de notificação remota e adicionar algum código para fazer um download no serviço de transferência em segundo plano. Quando o download for concluído, notificaremos o usuário de que o arquivo está disponível para uso.
NSURLSessionDownloadTask
em primeiro lugar, vamos lidar com uma notificação remota e enfileirar um NSURLSessionDownloadTask
no serviço de transferência em segundo plano. Em backgroundURLSession
, criamos um NURLSession
com uma configuração de sessão em segundo plano e adicionamos nosso Delegado de aplicativo como o delegado de sessão. A documentação desaconselha a instanciação de várias sessões com o mesmo identificador, portanto, usamos dispatch_once
para evitar possíveis problemas:
- (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);}
criamos uma tarefa de download usando o método de classe NSURLSession
e configuramos sua solicitação e fornecemos uma descrição para uso posterior. Você deve se lembrar de chamar para realmente iniciar a tarefa, pois todas as tarefas da sessão começam no estado suspenso.
agora precisamos implementar os métodos NSURLSessionDownloadDelegate
para receber retornos de chamada quando o download for concluído. Você também pode precisar implementar métodos NSURLSessionDelegate
ou NSURLSessionTaskDelegate
se precisar lidar com autenticação ou outros eventos no ciclo de vida da sessão. Você deve consultar o ciclo de vida do documento da Apple de uma sessão de URL com delegados personalizados, o que explica o ciclo de vida completo em todos os tipos de tarefas de sessão.
nenhum dos métodos delegados NSURLSessionDownloadDelegate
é opcional, embora o único em que precisamos agir neste exemplo seja . Quando a tarefa terminar de baixar, você receberá um URL temporário para o arquivo no disco. Você deve mover ou copiar o arquivo para o armazenamento do seu aplicativo, pois ele será removido do armazenamento temporário quando você retornar deste método delegado.
#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{}
se o seu aplicativo ainda estiver em execução em primeiro plano quando a tarefa de sessão em segundo plano for concluída, o código acima será suficiente. Na maioria dos casos, no entanto, seu aplicativo não estará em execução ou será suspenso em segundo plano. Nesses casos, você deve implementar dois métodos de delegados de aplicativos para que o sistema possa ativar seu aplicativo. Ao contrário dos retornos de chamada do delegado anterior, o delegado do aplicativo é chamado duas vezes, pois seus delegados de sessão e tarefa podem receber várias mensagens. O método delegado do aplicativo application: handleEventsForBackgroundURLSession:
é chamado antes que essas mensagens delegadas NSURLSession
sejam enviadas e URLSessionDidFinishEventsForBackgroundURLSession
seja chamado posteriormente. No método anterior, você armazena um fundo completionHandler
e, no último, você o chama para atualizar sua IU:
- (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(); }}
este processo de dois estágios é necessário para atualizar a interface do usuário do aplicativo se você ainda não estiver em primeiro plano quando a transferência em segundo plano for concluída. Além disso, se o aplicativo não estiver sendo executado quando a transferência em segundo plano terminar, o iOS o iniciará em segundo plano e os métodos delegados de aplicativo e sessão anteriores serão chamados após application:didFinishLaunchingWithOptions:
.
configuração e Limitação
abordamos brevemente o poder das transferências em segundo plano, mas você deve explorar a documentação e observar as opções NSURLSessionConfiguration
que melhor suportam seu caso de uso. Por exemplo, NSURLSessionTasks
tempos limite de recursos de suporte por meio da propriedade NSURLSessionConfiguration
‘s timeoutIntervalForResource
. Você pode usar isso para especificar quanto tempo deseja permitir que uma transferência seja concluída antes de desistir inteiramente. Você pode usar isso se o seu conteúdo estiver disponível apenas por um tempo limitado, ou se a falha em Baixar ou fazer upload do recurso dentro do timeInterval fornecido indicar que o Usuário não tem largura de banda Wifi suficiente.
além de baixar tarefas, NSURLSession
suporta totalmente tarefas de upload, para que você possa fazer upload de um vídeo para o seu servidor em segundo plano e garantir ao usuário que ele ou ela não precisa mais deixar o aplicativo em execução, como pode ter sido feito no iOS 6. Um bom toque seria definir a propriedade sessionSendsLaunchEvents
do seu NSURLSessionConfiguration
para NO
, se o seu aplicativo não precisar ser iniciado em segundo plano quando a transferência for concluída. O uso eficiente dos recursos do sistema mantém o iOS e o usuário felizes.
finalmente, existem algumas limitações no uso de sessões em segundo plano. Como um delegado é necessário, você não pode usar os métodos de retorno de chamada simples baseados em blocos em NSURLSession
. Lançar seu aplicativo em segundo plano é relativamente caro, então redirecionamentos HTTP são sempre tomados. O serviço de transferência em segundo plano suporta apenas HTTP e HTTPS e você não pode usar protocolos personalizados. O sistema otimiza as transferências com base nos recursos disponíveis e você não pode forçar sua transferência a progredir em segundo plano o tempo todo.
observe também que NSURLSessionDataTasks
não são suportados em sessões em segundo plano, e você deve usar essas tarefas apenas para solicitações pequenas de curta duração, não para downloads ou uploads.
resumo
as poderosas novas APIs de multitarefa e rede no iOS 7 abrem toda uma gama de possibilidades para aplicativos novos e existentes. Considere os casos de uso em seu aplicativo que podem se beneficiar de transferências de rede fora do processo e novos dados, e aproveite ao máximo essas novas APIs fantásticas. Em geral, implemente transferências em segundo plano como se seu aplicativo estivesse sendo executado em primeiro plano, fazendo atualizações apropriadas da interface do Usuário e a maior parte do trabalho já foi feito para você.
-
Use a nova API apropriada para o conteúdo do seu aplicativo.
-
seja eficiente e chame os manipuladores de conclusão o mais cedo possível.
-
os manipuladores de conclusão atualizam o instantâneo da interface do usuário do seu aplicativo.
Ler Mais
-
WWDC 2013 sessão “o Que há de Novo com Multitarefa”
-
WWDC 2013 sessão “o Que há de Novo na Fundação de Rede”
-
URL de Carregamento do Sistema Guia de Programação