Acesso ao sistema de arquivos no Xamarin.iOS

Você pode usar o Xamarin.iOS e as System.IO classes na BCL (Biblioteca de Classes Base) do .NET para acessar o sistema de arquivos do iOS. A classe File permite criar, excluir e ler arquivos e a classe Directory permite criar, excluir ou enumerar o conteúdo de diretórios. Você também pode usar Stream subclasses, que podem fornecer um maior grau de controle sobre as operações de arquivo (como compactação ou pesquisa de posição em um arquivo).

O iOS impõe algumas restrições sobre o que um aplicativo pode fazer com o sistema de arquivos para preservar a segurança dos dados de um aplicativo e proteger os usuários de aplicativos malignos. Essas restrições fazem parte do Application Sandbox – um conjunto de regras que limita o acesso de um aplicativo a arquivos, preferências, recursos de rede, hardware etc. Um aplicativo está limitado a ler e gravar arquivos em seu diretório inicial (local instalado); ele não pode acessar os arquivos de outro aplicativo.

O iOS também possui alguns recursos específicos do sistema de arquivos: certos diretórios exigem tratamento especial em relação a backups e atualizações, e os aplicativos também podem compartilhar arquivos entre si e com o aplicativo Arquivos (desde o iOS 11) e via iTunes.

Este artigo discute os recursos e as restrições do sistema de arquivos do iOS e inclui um aplicativo de exemplo que demonstra como usar o Xamarin.iOS para executar algumas operações simples do sistema de arquivos:

Um exemplo do iOS executando algumas operações simples do sistema de arquivos

Acesso geral a arquivos

O Xamarin.iOS permite que você use as classes .NET System.IO para operações do sistema de arquivos no iOS.

Os snippets de código a seguir ilustram algumas operações comuns de arquivo. Você encontrará todos eles abaixo no arquivo SampleCode.cs , no aplicativo de exemplo deste artigo.

Trabalhar com diretórios

Esse código enumera os subdiretórios no diretório atual (especificado pelo parâmetro "./"), que é o local do executável do aplicativo. Sua saída será uma lista de todos os arquivos e pastas implantados com seu aplicativo (exibidos na janela do console durante a depuração).

var directories = Directory.EnumerateDirectories("./");
foreach (var directory in directories) {
      Console.WriteLine(directory);
}

Lendo arquivos

Para ler um arquivo de texto, você só precisa de uma única linha de código. Este exemplo exibirá o conteúdo de um arquivo de texto na janela Saída do Aplicativo.

var text = File.ReadAllText("TestData/ReadMe.txt");
Console.WriteLine(text);

Serialização XML

Embora trabalhar com o namespace completo System.Xml esteja além do escopo deste artigo, você pode desserializar facilmente um documento XML do sistema de arquivos usando um StreamReader como este snippet de código:

using (TextReader reader = new StreamReader("./TestData/test.xml")) {
      XmlSerializer serializer = new XmlSerializer(typeof(MyObject));
      var xml = (MyObject)serializer.Deserialize(reader);
}

Para obter mais informações, consulte a documentação de System.Xml e serialização. Consulte a documentação do Xamarin.iOS sobre o vinculador – muitas vezes você precisará adicionar o [Preserve] atributo às classes que pretende serializar.

Criando arquivos e diretórios

Este exemplo mostra como usar a Environment classe para acessar a pasta Documentos, onde podemos criar arquivos e diretórios.

var documents =
 Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments);
var filename = Path.Combine (documents, "Write.txt");
File.WriteAllText(filename, "Write this text into a file");

A criação de um diretório é um processo semelhante:

var documents =
 Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments);
var directoryname = Path.Combine (documents, "NewDirectory");
Directory.CreateDirectory(directoryname);

Para obter mais informações, consulte a referência da API System.IO.

Serialização de JSON

Json.NET é uma estrutura JSON de alto desempenho que funciona com o Xamarin.iOS e está disponível no NuGet. Adicione o pacote NuGet ao seu projeto de aplicativo, usando Adicionar NuGet no Visual Studio para Mac:

Adicionando o pacote NuGet ao projeto de aplicativos

Em seguida, adicione uma classe para atuar como o modelo de dados para serialização/desserialização (neste caso Account.cs):

using System;
using System.Collections.Generic;
using Foundation; // for Preserve attribute, which helps serialization with Linking enabled

namespace FileSystem
{
    [Preserve]
    public class Account
    {
        public string Email { get; set; }
        public bool Active { get; set; }
        public DateTime CreatedDate { get; set; }
        public List<string> Roles { get; set; }

        public Account() {
        }
    }
}

Por fim, crie uma instância da classe, serialize-a Account em dados json e grave-a em um arquivo:

// Create a new record
var account = new Account(){
    Email = "monkey@xamarin.com",
    Active = true,
    CreatedDate = new DateTime(2015, 5, 27, 0, 0, 0, DateTimeKind.Utc),
    Roles = new List<string> {"User", "Admin"}
};

// Serialize object
var json = JsonConvert.SerializeObject(account, Newtonsoft.Json.Formatting.Indented);

// Save to file
var documents = Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments);
var filename = Path.Combine (documents, "account.json");
File.WriteAllText(filename, json);

Para obter mais informações sobre como trabalhar com dados json em um aplicativo .NET, consulte a documentação do Json.NET.

Considerações especiais

Apesar das semelhanças entre as operações de arquivo do Xamarin.iOS e do .NET, o iOS e o Xamarin.iOS diferem do .NET de algumas maneiras importantes.

Tornando os arquivos de projeto acessíveis no tempo de execução

Por padrão, se você adicionar um arquivo ao seu projeto, ele não será incluído na montagem final e, portanto, não estará disponível para seu aplicativo. Para incluir um arquivo no assembly, você deve marcá-lo com uma ação de compilação especial, chamada Content.

Para marcar um arquivo para inclusão, clique com o botão direito do mouse no(s) arquivo(s) e escolha Criar Conteúdo de Ação > no Visual Studio para Mac. Você também pode alterar a Ação de Compilação na folha de Propriedades do arquivo.

Diferenciar maiúsculas de minúsculas

É importante entender que o sistema de arquivos do iOS diferencia maiúsculas de minúsculas. A diferenciação de maiúsculas e minúsculas significa que os nomes de arquivo e diretório devem corresponder exatamente – README.txt e readme.txt seriam considerados nomes de arquivo diferentes.

Isso pode ser confuso para desenvolvedores .NET que estão mais familiarizados com o sistema de arquivos do Windows, que não diferencia maiúsculas de minúsculasArquivos, ARQUIVOS e arquivos se referem ao mesmo diretório.

Aviso

O Simulador do iOS NÃO diferencia maiúsculas de minúsculas. Se o nome do arquivo que usa maiúsculas e minúsculas for diferente entre o arquivo em si e as referências a ele no código, o código ainda poderá funcionar no simulador, mas falhará em um dispositivo real. Essa é uma das razões pelas quais é importante implantar e testar em um dispositivo real no início e com frequência durante o desenvolvimento do iOS.

Separador de caminho

O iOS usa a barra '/'como separador de caminho (que é diferente do Windows, que usa a barra invertida '\').

Devido a essa diferença confusa, é uma boa prática usar o System.IO.Path.Combine método, que se ajusta à plataforma atual, em vez de codificar um separador de caminho específico. Esta é uma etapa simples que torna seu código mais portátil para outras plataformas.

Área restrita do aplicativo

O acesso do aplicativo ao sistema de arquivos (e outros recursos, como recursos de rede e hardware) é limitado por motivos de segurança. Essa restrição é conhecida como Área restrita do aplicativo. Em termos de sistema de arquivos, seu aplicativo está limitado a criar e excluir arquivos e diretórios em seu diretório inicial.

O diretório inicial é um local exclusivo no sistema de arquivos em que seu aplicativo e todos os seus dados são armazenados. Você não pode escolher (ou alterar) o local do diretório base do seu aplicativo; no entanto, o iOS e o Xamarin.iOS fornecem propriedades e métodos para gerenciar os arquivos e diretórios internos.

O pacote de aplicativos

O Pacote de Aplicativos é a pasta que contém seu aplicativo. Ele se distingue de outras pastas por ter o sufixo .app adicionado ao nome do diretório. Seu pacote de aplicativos contém seu arquivo executável e todo o conteúdo (arquivos, imagens, etc.) necessário para seu projeto.

Quando você navega até o pacote de aplicativos no Mac OS, ele aparece com um ícone diferente do que você vê em outros diretórios (e o sufixo .app está oculto); no entanto, é apenas um diretório normal que o sistema operacional está exibindo de forma diferente.

Para exibir o pacote de aplicativos para o código de exemplo, clique com o botão direito do mouse no projeto no Visual Studio para Mac e selecione Revelar no Finder. Em seguida, navegue até o diretório bin/, onde você deve encontrar um ícone de aplicativo (semelhante à captura de tela abaixo).

Navegue pelo diretório bin para encontrar um ícone de aplicativo semelhante a esta captura de tela

Clique com o botão direito do mouse nesse ícone e escolha Mostrar Conteúdo do Pacote para procurar o conteúdo do diretório Pacote de Aplicativos. O conteúdo aparece exatamente como o conteúdo de um diretório normal, como mostrado aqui:

O conteúdo do pacote de aplicativos

O pacote de aplicativos é o que é instalado no simulador ou no seu dispositivo durante o teste e, em última análise, é o que é enviado à Apple para inclusão na App Store.

Diretórios de aplicativos

Quando seu aplicativo é instalado em um dispositivo, o sistema operacional cria um diretório inicial para seu aplicativo e cria vários diretórios dentro do diretório raiz do aplicativo que estão disponíveis para uso. Desde o iOS 8, os diretórios acessíveis pelo usuário NÃO estão localizados na raiz do aplicativo, portanto, você não pode derivar os caminhos para o pacote de aplicativos dos diretórios do usuário ou vice-versa.

Esses diretórios, como determinar seu caminho e suas finalidades estão listados abaixo:

 

Diretório Descrição
[Nome do Aplicativo].app/ No iOS 7 e versões anteriores, esse é o diretório em que o ApplicationBundle executável do aplicativo é armazenado. A estrutura de diretório que você cria em seu aplicativo existe nesse diretório (por exemplo, imagens e outros tipos de arquivo que você marcou como Recursos em seu projeto do Visual Studio para Mac).

Se você precisar acessar os arquivos de conteúdo dentro do Pacote de Aplicativos, o caminho para esse diretório estará disponível por meio da NSBundle.MainBundle.BundlePath propriedade.
Documentos/ Use esse diretório para armazenar documentos do usuário e arquivos de dados do aplicativo.

O conteúdo deste diretório pode ser disponibilizado ao usuário por meio do compartilhamento de arquivos do iTunes (embora isso esteja desabilitado por padrão). Adicione uma UIFileSharingEnabled chave booleana ao arquivo Info.plist para permitir que os usuários acessem esses arquivos.

Mesmo que um aplicativo não habilite imediatamente o compartilhamento de arquivos, você deve evitar colocar arquivos que devem ser ocultados de seus usuários nesse diretório (como arquivos de banco de dados, a menos que você pretenda compartilhá-los). Enquanto os arquivos confidenciais permanecerem ocultos, esses arquivos não serão expostos (e potencialmente movidos, modificados ou excluídos pelo iTunes) se o compartilhamento de arquivos estiver ativado em uma versão futura.

Você pode usar o Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments) método para obter o caminho para o diretório Documents do seu aplicativo.

O conteúdo deste diretório é copiado pelo iTunes.
Biblioteca/ O diretório Library é um bom lugar para armazenar arquivos que não são criados diretamente pelo usuário, como bancos de dados ou outros arquivos gerados pelo aplicativo. O conteúdo deste diretório nunca é exposto ao usuário via iTunes.

Você pode criar seus próprios subdiretórios na Biblioteca; no entanto, já existem alguns diretórios criados pelo sistema aqui que você deve conhecer, incluindo Preferências e Caches.

O conteúdo deste diretório (exceto o subdiretório Caches) é copiado pelo iTunes. Os diretórios personalizados que você criar na Biblioteca serão copiados.
Biblioteca/Preferências/ Os arquivos de preferência específicos do aplicativo são armazenados nesse diretório. Não crie esses arquivos diretamente. Em vez disso, use a NSUserDefaults classe.

O conteúdo deste diretório é copiado pelo iTunes.
Biblioteca/Caches/ O diretório Caches é um bom lugar para armazenar arquivos de dados que podem ajudar seu aplicativo a ser executado, mas que podem ser facilmente recriados. O aplicativo deve criar e excluir esses arquivos conforme necessário e ser capaz de recriá-los, se necessário. O iOS 5 também pode excluir esses arquivos (em situações de pouco armazenamento), no entanto, não o fará enquanto o aplicativo estiver em execução.

O conteúdo deste diretório NÃO é copiado pelo iTunes, o que significa que eles não estarão presentes se o usuário restaurar um dispositivo e podem não estar presentes após a instalação de uma versão atualizada do aplicativo.

Por exemplo, caso seu aplicativo não consiga se conectar à rede, você pode usar o diretório Caches para armazenar dados ou arquivos para fornecer uma boa experiência offline. O aplicativo pode salvar e recuperar esses dados rapidamente enquanto aguarda as respostas da rede, mas não precisa ser feito backup e pode ser facilmente recuperado ou recriado após uma restauração ou atualização de versão.
Tmp/ Os aplicativos podem armazenar arquivos temporários que são necessários apenas por um curto período neste diretório. Para economizar espaço, os arquivos devem ser excluídos quando não forem mais necessários. O sistema operacional também pode excluir arquivos desse diretório quando um aplicativo não estiver em execução.

O conteúdo deste diretório NÃO é copiado pelo iTunes.

Por exemplo, o diretório tmp pode ser usado para armazenar arquivos temporários que são baixados para exibição ao usuário (como avatares do Twitter ou anexos de e-mail), mas que podem ser excluídos depois de visualizados (e baixados novamente se forem necessários no futuro).

Esta captura de tela mostra a estrutura de diretórios em uma janela do Finder:

Esta captura de tela mostra a estrutura de diretórios em uma janela do Finder

Acessando outros diretórios programaticamente

Os exemplos anteriores de diretório e arquivo acessaram o Documents diretório. Para escrever em outro diretório, você deve construir um caminho usando a sintaxe ".." como mostrado aqui:

var documents = Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments);
var library = Path.Combine (documents, "..", "Library");
var filename = Path.Combine (library, "WriteToLibrary.txt");
File.WriteAllText(filename, "Write this text into a file in Library");

A criação de um diretório é semelhante:

var documents = Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments);
var library = Path.Combine (documents, "..", "Library");
var directoryname = Path.Combine (library, "NewLibraryDirectory");
Directory.CreateDirectory(directoryname);

Os caminhos para os Caches diretórios and tmp podem ser construídos assim:

var documents = Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments);
var cache = Path.Combine (documents, "..", "Library", "Caches");
var tmp = Path.Combine (documents, "..", "tmp");

Compartilhando com o aplicativo Arquivos

O iOS 11 introduziu o aplicativo Arquivos - um navegador de arquivos para iOS que permite ao usuário ver e interagir com seus arquivos no iCloud e também armazenados por qualquer aplicativo que o suporte. Para permitir que o usuário acesse diretamente os arquivos em seu aplicativo, crie uma nova chave booleana no arquivo LSSupportsOpeningDocumentsInPlace Info.plist e defina-a como true, como aqui:

Definir LSSupportsOpeningDocumentsInPlace em Info.plist

O diretório Documentos do aplicativo agora estará disponível para navegação no aplicativo Arquivos. No aplicativo Arquivos, navegue até No meu iPhone e cada aplicativo com arquivos compartilhados ficará visível. As capturas de tela abaixo mostram a aparência do aplicativo de exemplo:

Aplicativo de arquivos do iOS 11 Procurar arquivos do meu iPhone Arquivos de aplicativo de exemplo

Compartilhando arquivos com o usuário através do iTunes

Os usuários podem acessar os arquivos no diretório Documentos do aplicativo editando Info.plist e criando um aplicativo que ofereça suporte à entrada de compartilhamento do iTunes (UIFileSharingEnabled) na visualização Código-fonte, conforme mostrado aqui:

Adicionar o aplicativo suporta a propriedade de compartilhamento do iTunes

Esses arquivos podem ser acessados no iTunes quando o dispositivo está conectado e o usuário escolhe a Apps guia. Por exemplo, a captura de tela a seguir mostra os arquivos no aplicativo selecionado compartilhados via iTunes:

Esta captura de tela mostra os arquivos no aplicativo selecionado compartilhados via iTunes

Os usuários só podem acessar os itens de nível superior neste diretório via iTunes. Eles não podem ver o conteúdo de nenhum subdiretório (embora possam copiá-los para o computador ou excluí-los). Por exemplo, com o GoodReader, os arquivos PDF e EPUB podem ser compartilhados com o aplicativo para que os usuários possam lê-los em seus dispositivos iOS.

Os usuários que modificam o conteúdo de sua pasta Documentos podem causar problemas se não forem cuidadosos. Seu aplicativo deve levar isso em consideração e ser resiliente a atualizações destrutivas da pasta Documentos.

O código de exemplo para este artigo cria um arquivo e uma pasta na pasta Documentos (em SampleCode.cs) e habilita o compartilhamento de arquivos no arquivo Info.plist . Esta captura de tela mostra como eles aparecem no iTunes:

Esta captura de tela mostra como os arquivos aparecem no iTunes

Consulte o artigo Trabalhando com Imagens para obter informações sobre como definir ícones para o aplicativo e para qualquer tipo de documento personalizado que você criar.

Se a UIFileSharingEnabled chave for falsa ou não estiver presente, o compartilhamento de arquivos será, por padrão, desabilitado e os usuários não poderão interagir com o diretório Documentos.

Backup e restauração

Quando um dispositivo é copiado pelo iTunes, todos os diretórios criados no diretório inicial do seu aplicativo serão salvos, exceto os seguintes diretórios:

  • [ApplicationName].app – Não grave neste diretório, pois ele está assinado e, portanto, deve permanecer inalterado após a instalação. Ele pode conter recursos que você acessa do seu código, mas eles não exigem backup, pois seriam restaurados baixando novamente o aplicativo.
  • Biblioteca/Caches – O diretório de cache destina-se a arquivos de trabalho que não precisam ser copiados.
  • tmp – Este diretório é usado para arquivos temporários que são criados e excluídos quando não são mais necessários ou para arquivos que o iOS exclui quando precisa de espaço.

Fazer backup de uma grande quantidade de dados pode levar muito tempo. Se você decidir que precisa fazer backup de qualquer documento ou dados específicos, seu aplicativo deverá usar as pastas Documentos e Biblioteca. Para dados transitórios ou arquivos que podem ser facilmente recuperados da rede, use o diretório Caches ou tmp.

Observação

O iOS "limpará" o sistema de arquivos quando um dispositivo for executado com pouco espaço em disco. Este processo removerá todos os arquivos da pasta Library/Caches e tmp dos aplicativos que não estão em execução no momento.

Cumprindo as restrições de backup do iCloud do iOS 5

Observação

Embora essa política tenha sido introduzida pela primeira vez com o iOS 5 (que parece ter sido há muito tempo), a orientação ainda é relevante para os aplicativos hoje.

A Apple introduziu a funcionalidade de backup do iCloud com o iOS 5. Quando o Backup do iCloud está ativado, todos os arquivos no diretório inicial do aplicativo (excluindo diretórios que normalmente não são copiados, por exemplo, o pacote de aplicativos, Cachese tmp) são copiados para os servidores do iCloud. Esse recurso fornece ao usuário um backup completo caso seu dispositivo seja perdido, roubado ou danificado.

Como o iCloud fornece apenas 5 Gb de espaço livre para cada usuário e para evitar o uso desnecessário de largura de banda, a Apple espera que os aplicativos façam backup apenas dos dados essenciais gerados pelo usuário. Para estar em conformidade com as Diretrizes de armazenamento de dados do iOS, você deve limitar a quantidade de dados que são armazenados em backup aderindo aos seguintes itens:

  • Armazene apenas dados gerados pelo usuário ou dados que não possam ser recriados no diretório Documentos (do qual é feito backup).
  • Armazene quaisquer outros dados que possam ser facilmente recriados ou baixados novamente em Library/Caches ou tmp (que não é backup e pode ser 'limpo').
  • Se você tiver arquivos que podem ser apropriados para a Library/Caches pasta ou tmp , mas não quiser ser 'limpo', armazene-os em outro lugar (como Library/YourData) e aplique o atributo 'não fazer backup' para evitar que os arquivos usem a largura de banda e o espaço de armazenamento do Backup do iCloud. Esses dados ainda ocupam espaço no dispositivo, portanto, você deve gerenciá-los com cuidado e excluí-los quando possível.

O atributo 'não fazer backup' é definido usando a NSFileManager classe. Certifique-se de que sua turma seja using Foundation e chame SetSkipBackupAttribute assim:

var documents = Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments);
var filename = Path.Combine (documents, "LocalOnly.txt");
File.WriteAllText(filename, "This file will never get backed-up. It would need to be re-created after a restore or re-install");
NSFileManager.SetSkipBackupAttribute (filename, true); // backup will be skipped for this file

Quando SetSkipBackupAttribute é true o arquivo não será copiado, independentemente do diretório em que está armazenado (até mesmo o Documents diretório). Você pode consultar o atributo usando o GetSkipBackupAttribute método e redefini-lo chamando o SetSkipBackupAttribute método com false, assim:

NSFileManager.SetSkipBackupAttribute (filename, false); // file will be backed-up

Compartilhamento de dados entre aplicativos iOS e extensões de aplicativo

Como as Extensões de Aplicativo são executadas como parte de um aplicativo host (em vez do aplicativo que as contém), o compartilhamento de dados não é incluído automaticamente, portanto, é necessário trabalho extra. Os grupos de aplicativos são o mecanismo que o iOS usa para permitir que diferentes aplicativos compartilhem dados. Se os aplicativos tiverem sido configurados corretamente com os direitos e o provisionamento corretos, eles poderão acessar um diretório compartilhado fora da sandbox normal do iOS.

Configurar um grupo de aplicativos

O local compartilhado é configurado usando um Grupo de Aplicativos, que é configurado na seção Certificados, Identificadores e Perfis no Centro de Desenvolvimento do iOS. Esse valor também deve ser referenciado no Entitlements.plist de cada projeto.

Para obter informações sobre como criar e configurar um grupo de aplicativos, consulte o guia Recursos do grupo de aplicativos.

Arquivos

O aplicativo iOS e a extensão também podem compartilhar arquivos usando um caminho de arquivo comum (desde que tenham sido configurados corretamente com os direitos e provisionamento corretos):

var FileManager = new NSFileManager ();
var appGroupContainer =FileManager.GetContainerUrl ("group.com.xamarin.WatchSettings");
var appGroupContainerPath = appGroupContainer.Path

Console.WriteLine ("Group Path: " + appGroupContainerPath);

// use the path to create and update files
...

Importante

Se o Caminho do grupo retornado for null, verifique a configuração dos direitos e do perfil de provisionamento e verifique se eles estão corretos.

Atualizações de versão do aplicativo

Quando uma nova versão do seu aplicativo é baixada, o iOS cria um novo diretório inicial e armazena o novo pacote de aplicativos nele. Em seguida, o iOS move as seguintes pastas da versão anterior do Pacote de Aplicativos para o novo diretório inicial:

  • Documentos
  • Biblioteca

Outros diretórios também podem ser copiados e colocados em seu novo diretório base, mas não há garantia de que sejam copiados, portanto, seu aplicativo não deve depender desse comportamento do sistema.

Resumo

Este artigo mostrou que as operações do sistema de arquivos com o Xamarin.iOS são semelhantes a qualquer outro aplicativo .NET. Ele também introduziu o Application Sandbox e examinou as implicações de segurança que ele causa. Em seguida, explorou o conceito de um pacote de aplicativos. Por fim, ele enumerou os diretórios especializados disponíveis para seu aplicativo e explicou suas funções durante atualizações e backups de aplicativos.