Tratamento de arquivos em Xamarin.Forms

A manipulação de arquivos com Xamarin.Forms pode ser obtida usando código em uma biblioteca do .NET Standard ou usando recursos incorporados.

Visão geral

Xamarin.Forms O código é executado em várias plataformas - cada uma das quais tem seu próprio sistema de arquivos. Anteriormente, isso significava que ler e gravar arquivos era realizado mais facilmente usando as APIs de arquivo nativo em cada plataforma. Como alternativa, os recursos inseridos são uma solução mais simples para distribuir os arquivos de dados com um aplicativo. No entanto, com o .NET Standard 2.0, é possível compartilhar o código de acesso de arquivo nas bibliotecas .NET Standard.

Para saber mais sobre o tratamento de arquivos de imagem, confira a página Trabalhando com imagens.

Salvamento e carregamento de arquivos

As classes System.IO podem ser usadas para acessar o sistema de arquivos em cada plataforma. A classe File permite criar, excluir e ler arquivos e a classe Directory permite criar, excluir ou enumerar o conteúdo de diretórios. Também é possível usar as subclasses Stream, que podem fornecer um maior grau de controle sobre operações de arquivo (como compactação ou pesquisa de posição em um arquivo).

Um arquivo de texto pode ser escrito usando o método File.WriteAllText:

File.WriteAllText(fileName, text);

Um arquivo de texto pode ser lido usando o método File.ReadAllText:

string text = File.ReadAllText(fileName);

Além disso, o método File.Exists determina se o arquivo especificado existe:

bool doesExist = File.Exists(fileName);

O caminho do arquivo em cada plataforma pode ser determinado com base em uma biblioteca .NET Standard usando um valor da enumeração Environment.SpecialFolder como o primeiro argumento para o método Environment.GetFolderPath. Isso pode ser combinado com um nome de arquivo com o método Path.Combine:

string fileName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "temp.txt");

Essas operações são demonstradas no aplicativo de exemplo, que inclui uma página que salva e carrega texto:

Salvando e carregando texto

Carregamento de arquivos inseridos como recursos

Para inserir um arquivo em um assembly do .NET Standard, crie ou adicione um arquivo e certifique-se de que seja Ação de build: EmbeddedResource.

GetManifestResourceStream é usado para acessar o arquivo inserido usando sua ID de recurso. Por padrão, a ID do recurso é o nome do arquivo prefixado com o namespace padrão para o projeto no qual ele está incorporado - nesse caso, o assembly é WorkingWithFiles e o nome do arquivo é LibTextResource.txt, portanto, a ID do recurso é WorkingWithFiles.LibTextResource.txt.

var assembly = IntrospectionExtensions.GetTypeInfo(typeof(LoadResourceText)).Assembly;
Stream stream = assembly.GetManifestResourceStream("WorkingWithFiles.LibTextResource.txt");
string text = "";
using (var reader = new System.IO.StreamReader (stream))
{  
    text = reader.ReadToEnd ();
}

A variável text pode ser usada para exibir o texto ou, caso contrário, usá-lo no código. A captura de tela a seguir mostra o texto renderizado em um Label controle:

Arquivo de texto incorporado na biblioteca padrão do .NET

Carregar e desserializar um XML é igualmente simples. O código a seguir mostra um arquivo XML sendo carregado e desserializado de um recurso, associado a um ListView para exibição. O arquivo XML contém uma matriz de objetos Monkey (a classe é definida no código de exemplo).

var assembly = IntrospectionExtensions.GetTypeInfo(typeof(LoadResourceText)).Assembly;
Stream stream = assembly.GetManifestResourceStream("WorkingWithFiles.LibXmlResource.xml");
List<Monkey> monkeys;
using (var reader = new System.IO.StreamReader (stream)) {
    var serializer = new XmlSerializer(typeof(List<Monkey>));
    monkeys = (List<Monkey>)serializer.Deserialize(reader);
}
var listView = new ListView ();
listView.ItemsSource = monkeys;

Arquivo xml incorporado na biblioteca padrão do .NET, exibido em ListView

Inserção em projetos compartilhados

Projetos compartilhados também podem conter arquivos como recursos inseridos; no entanto, como o conteúdo de um Projeto compartilhado é compilado em projetos de referência, o prefixo usado para IDs de recurso de arquivo inserido pode ser alterado. Isso significa que a ID do recurso para cada arquivo inserido pode ser diferente para cada plataforma.

Há duas soluções para esse problema com Projetos compartilhados:

  • Sincronizar os projetos – edite as propriedades do projeto para cada plataforma para usar o mesmo nome de assembly e o namespace padrão. Esse valor pode ser embutido como o prefixo para IDs de recurso inserido no Projeto compartilhado.
  • Diretivas de compilador #if – use diretivas de compilador para definir o prefixo correto de ID do recurso e use esse valor para construir dinamicamente a ID do recurso correta.

Veja abaixo o código ilustrando a segunda opção. As diretivas de compilador são usadas para selecionar o prefixo de recurso embutido (normalmente o mesmo que o namespace padrão para o projeto de referência). A variável resourcePrefix é usada para criar uma ID de recurso válida, concatenando-a com o nome do arquivo de recurso inserido.

#if __IOS__
var resourcePrefix = "WorkingWithFiles.iOS.";
#endif
#if __ANDROID__
var resourcePrefix = "WorkingWithFiles.Droid.";
#endif

Debug.WriteLine("Using this resource prefix: " + resourcePrefix);
// note that the prefix includes the trailing period '.' that is required
var assembly = IntrospectionExtensions.GetTypeInfo(typeof(SharedPage)).Assembly;
Stream stream = assembly.GetManifestResourceStream
    (resourcePrefix + "SharedTextResource.txt");

Organização de recursos

Os exemplos acima supõem que o arquivo é inserido na raiz do projeto da biblioteca .NET Standard, em cujo caso a ID de recurso é do formato Namespace.Filename.Extension, como WorkingWithFiles.LibTextResource.txt e WorkingWithFiles.iOS.SharedTextResource.txt.

É possível organizar os recursos inseridos em pastas. Quando um recurso inserido é colocado em uma pasta, o nome da pasta se torna parte da ID de recurso (separado por pontos) para que o formato de ID de recurso se torne Namespace.Folder.Filename.Extension. Colocar os arquivos usados no aplicativo de exemplo em uma pasta MyFolder tornaria as IDs de recurso correspondentes WorkingWithFiles.MyFolder.LibTextResource.txt e WorkingWithFiles.iOS.MyFolder.SharedTextResource.txt.

Depuração de recursos inseridos

Como às vezes é difícil entender por que determinado recurso não está sendo carregado, o código de depuração a seguir pode ser adicionado temporariamente a um aplicativo para ajudar a confirmar que os recursos foram configurados corretamente. Ele transmitirá todos os recursos conhecidos inseridos no assembly fornecido para o painel Erros para ajudar a depurar problemas de carregamento de recursos.

using System.Reflection;
// ...
// use for debugging, not in released app code!
var assembly = IntrospectionExtensions.GetTypeInfo(typeof(SharedPage)).Assembly;
foreach (var res in assembly.GetManifestResourceNames()) {
    System.Diagnostics.Debug.WriteLine("found resource: " + res);
}

Resumo

Este artigo mostrou algumas operações de arquivo simples para salvar e carregar texto no dispositivo e para carregar recursos inseridos. Com o .NET Standard 2.0, é possível compartilhar o código de acesso de arquivo nas bibliotecas .NET Standard.