Skip to content

Storage pt BR

Edgar Mesquita edited this page Aug 14, 2026 · 4 revisions

Storage

🌐 Esta página em: English · Português

Uma assinatura sobre o armazenamento nativo de cada plataforma. Um app escreve o mesmo C# em todo lugar, e o valor aterrissa onde aquela plataforma já guarda esse tipo de coisa — então o backup dela, a migração dela e a desinstalação dela se comportam como o usuário espera, sem o framework inventar ou empacotar nada.

Desde 0.2.0-preview.10

Existem duas interfaces, e qual delas você toma é uma decisão, não um parâmetro.

IAppStorage ISecretStore
Web localStorage nenhum, resolve nulo
macOS / iOS NSUserDefaults Keychain
Android SharedPreferences AES/GCM respaldado por KeyStore

As duas são tomadas por construtor, como toda outra capacidade:

public sealed class SettingsShell(IAppStorage storage) : StatefulComponent
{
    private void Remember(string language) => storage.Set("language", language);
}

Todos os alvos. No nativo o container do .NET resolve; no browser o construtor transpilado resolve pelo nome da interface; durante o SSR resolve para um store que não guarda nada e diz isso: leituras respondem null, escritas não vão a lugar nenhum, porque o armazenamento que isso representa vive na máquina do visitante. Assim uma página lê o valor guardado no primeiro render do cliente, e o markup renderizado no servidor mostra a mesma coisa que uma primeira visita mostra. Veja Capabilities para o quadro inteiro.

Leia no OnMount, não no construtor: o reconciliador constrói instâncias novas a cada passada e mantém a retida, então um construtor que lê o armazenamento lê de novo para uma instância que é jogada fora. (Componentes write-once)

Por que segredos são um tipo diferente

Colocar um token no localStorage é o erro de segurança mais cometido no web, e uma API onde a segurança é um argumento secure: true é uma API onde a segurança está a um argumento esquecido de distância. Aqui a escolha não pode ser feita por omissão: um segredo precisa de um serviço diferente, e pedir por ele é algo que alguém fez de propósito.

O browser não tem cofre, então no web o ISecretStore resolve para null, do mesmo jeito que o framework reporta toda capacidade que um host não tem, em vez de fingir com um store que qualquer script na origem consegue ler. Um app que estende a mão para ele no web precisa responder à pergunta que estava evitando: manter o segredo no servidor e entregar ao browser uma sessão que ele só pode gastar, ou aceitar que aquilo que guardava nunca foi realmente um segredo.

No nativo é o cofre real da plataforma. Apple: um item kSecClassGenericPassword por chave sob o bundle identifier do app, com AfterFirstUnlockThisDeviceOnly para que um refresh em background consiga ler enquanto um aparelho não desbloqueado desde o boot não consiga, e nunca sincronizado com o iCloud. Android: uma chave AES respaldada por hardware no AndroidKeyStore (material de chave ilegível até para este processo) com ciphertext AES/GCM num arquivo de preferências. É o que o EncryptedSharedPreferences do AndroidX faz, escrito diretamente porque a plataforma entrega cada peça e o framework não pega dependência para algo que o SO já oferece.

Só strings

Todo store aqui é um mapa de strings por baixo. Uma superfície tipada seriam três serializadores a manter idênticos, e a primeira discordância entre eles é um valor que escreve numa plataforma e não pode ser lido em outra. Serialize no seu app, onde o formato é seu.

Armazenamento é uma capacidade do CLIENTE

Não existe realização no servidor. Durante o SSR o serviço resolve para null e as leituras não respondem nada.

Isso importa mais do que parece. Uma página que ramifica a árvore com base em estado guardado renderiza uma coisa no servidor e outra no browser, e o reconciliador vai fielmente reconciliar a diferença na frente do leitor. Leia o armazenamento num handler de evento ou depois do mount, nunca enquanto constrói.

É também por isso que armazenamento sozinho não consegue lembrar um tema sem piscar: o servidor pinta primeiro e o localStorage não é algo que ele possa ver. Esse problema específico quer um cookie, porque o requisito não é "lembrar" e sim "contar ao servidor" — e o framework já cuida disso, para que um app não precise. Veja ServerIntegration.

Falhas respondem, nunca lançam

  • O localStorage é a API do DOM mais propensa a lançar em vez de falhar em silêncio. O modo privado do Safari já lançou na escrita, um browser na cota lança, e lê-lo lança quando a origem está com armazenamento bloqueado. Uma preferência que não conseguiu salvar não pode derrubar a página: uma escrita que não pode acontecer simplesmente não acontece, e uma leitura que não pode acontecer responde null.
  • Um item do Keychain não sobrevive a todo restore, e uma chave do KeyStore é invalidada por uma nova tela de bloqueio. Os dois tornam permanentemente ilegível todo valor sob eles.

Então trate uma leitura null como "pergunte de novo" (entre novamente, caia no padrão) e nunca como um erro a reportar. É a mesma resposta que uma chave nunca escrita dá, e quem chama já sabe lidar com essa.

Trazendo o seu

Toda capacidade é registrada com TryAdd, então registrar o seu vence:

builder.Services.AddSingleton<IAppStorage, MyOwnStorage>();

Essa é a válvula de escape para um store com durabilidade diferente, um arquivo criptografado ou um dublê de teste.

Clone this wiki locally