Mobile
Publique Apps Solana Mobile: dApp Store e App Store Review

Publique Apps Solana Mobile: dApp Store e App Store Review

Melhores Práticas de Produção

Seu app está publicado, seguro e os usuários estão fazendo onboarding. Agora vem o longo prazo: mantê-lo funcionando bem, lidando com atualizações sem quebrar coisas e escalando suas operações.

Esta última lição aborda as práticas operacionais que separam projetos de fim de semana de aplicações de qualidade de produção.

Monitorando o Que Importa

Você não pode consertar o que não pode ver. Configure monitoramento desde o primeiro dia.

Relatórios de crashes

Crashes destroem a confiança do usuário, especialmente em apps financeiros. Use um serviço de relatório de crashes:

typescript
// Usando Sentry (funciona com React Native)
import * as Sentry from '@sentry/react-native';

Sentry.init({
  dsn: 'https://your-dsn@sentry.io/project',
  tracesSampleRate: 0.2, // 20% das transações para desempenho
  environment: __DEV__ ? 'development' : 'production',
});

// Envolva seu componente raiz
const App = () => {
  return (
    <Sentry.ErrorBoundary fallback={<ErrorScreen />}>
      <YourApp />
    </Sentry.ErrorBoundary>
  );
};

// Adicione contexto para depuração
Sentry.setUser({ id: userId });
Sentry.setTag('wallet_connected', hasWallet.toString());

Monitoramento de transações

Para apps de cripto, você precisa de visibilidade nas interações com a blockchain:

typescript
// Registre tentativas de transação com contexto
const executeTransaction = async (tx: Transaction, type: string) => {
  const startTime = Date.now();
  
  try {
    const signature = await connection.sendTransaction(tx, [wallet]);
    await connection.confirmTransaction(signature);
    
    // Registre o sucesso
    analytics.track('transaction_success', {
      type,
      duration: Date.now() - startTime,
      signature,
    });
    
    return signature;
  } catch (error) {
    // Registre a falha com detalhes
    analytics.track('transaction_failure', {
      type,
      duration: Date.now() - startTime,
      error: error.message,
      errorCode: error.code,
    });
    
    Sentry.captureException(error, {
      tags: { transaction_type: type },
      extra: { tx: tx.serialize().toString('base64') }
    });
    
    throw error;
  }
};

Métricas-chave para acompanhar

MétricaPor Que Importa
Taxa livre de crashesMeta de 99.5%+
Taxa de sucesso de transaçõesIdentificar problemas de RPC ou rede
Latência de transaçõesIndicador de experiência do usuário
Sucesso de conexão com carteiraSaúde da autenticação
Taxa de erros da APISaúde do backend
Duração da sessãoIndicador de engajamento

Limites de alertas

Configure alertas para anomalias:

  • Taxa de crashes > 1% na última hora → Acione o plantonista

  • Taxa de falha de transações > 10% → Investigue imediatamente

  • Erros de API > 5% → Verifique backend/serviços de terceiros

  • Zero transações por 30 minutos (se você espera uso constante) → Possível queda

Gerenciamento de RPC

Endpoints RPC da Solana são sua ligação vital com a blockchain. Gerencie-os com cuidado.

Use múltiplos provedores RPC

Não dependa de um único provedor:

typescript
const RPC_ENDPOINTS = [
  'https://your-primary.rpc.com',
  'https://your-backup.rpc.com',
  'https://api.mainnet-beta.solana.com', // Fallback público
];

let currentEndpointIndex = 0;

const getConnection = () => {
  return new Connection(RPC_ENDPOINTS[currentEndpointIndex], 'confirmed');
};

const rotateEndpoint = () => {
  currentEndpointIndex = (currentEndpointIndex + 1) % RPC_ENDPOINTS.length;
  console.log(`Switched to RPC: ${RPC_ENDPOINTS[currentEndpointIndex]}`);
};

const executeWithRetry = async <T>(
  operation: (connection: Connection) => Promise<T>,
  maxRetries = 3
): Promise<T> => {
  let lastError;
  
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await operation(getConnection());
    } catch (error) {
      lastError = error;
      
      // Rotacione em erros de conexão
      if (error.message.includes('429') || error.message.includes('timeout')) {
        rotateEndpoint();
      }
    }
  }
  
  throw lastError;
};

Consciência sobre rate limiting

Provedores RPC têm limites de taxa. Trate-os elegantemente:

typescript
// Implemente exponential backoff
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));

const fetchWithBackoff = async (fetchFn: () => Promise<any>, maxRetries = 5) => {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await fetchFn();
    } catch (error) {
      if (error.message.includes('429') && attempt < maxRetries - 1) {
        const delay = Math.min(1000 * Math.pow(2, attempt), 30000);
        console.log(`Rate limited, waiting ${delay}ms`);
        await sleep(delay);
      } else {
        throw error;
      }
    }
  }
};

Saúde do WebSocket

Para atualizações em tempo real, conexões WebSocket podem falhar silenciosamente:

typescript
const setupAccountSubscription = (publicKey: PublicKey) => {
  let ws: number;
  let heartbeatInterval: NodeJS.Timeout;
  
  const connect = () => {
    ws = connection.onAccountChange(
      publicKey,
      (accountInfo) => {
        handleAccountUpdate(accountInfo);
      }
    );
    
    // Heartbeat para detectar desconexões silenciosas
    heartbeatInterval = setInterval(async () => {
      try {
        await connection.getSlot();
      } catch {
        console.log('WebSocket health check failed, reconnecting...');
        cleanup();
        connect();
      }
    }, 30000);
  };
  
  const cleanup = () => {
    if (ws) connection.removeAccountChangeListener(ws);
    if (heartbeatInterval) clearInterval(heartbeatInterval);
  };
  
  connect();
  
  return cleanup;
};

Estratégia de Atualização

Atualizações de apps mobile exigem mais planejamento do que deploy web.

Compatibilidade de versões

Seu app terá múltiplas versões em uso simultaneamente. Projete para isso:

typescript
// Versionamento de API
const API_VERSION = '2024.1';

const apiRequest = async (endpoint: string, options: RequestInit) => {
  return fetch(`https://api.yourapp.com/v1/${endpoint}`, {
    ...options,
    headers: {
      ...options.headers,
      'X-App-Version': APP_VERSION,
      'X-API-Version': API_VERSION,
    }
  });
};

// O backend pode rotear ou transformar com base na versão

Mecanismo de atualização forçada

Às vezes você precisa que os usuários estejam na versão mais recente (correção de segurança, mudança que quebra a API):

typescript
const checkForceUpdate = async () => {
  const response = await fetch('https://api.yourapp.com/app-config');
  const config = await response.json();
  
  const currentVersion = DeviceInfo.getVersion();
  
  if (compareVersions(currentVersion, config.minimumVersion) < 0) {
    // Atualização forçada necessária
    Alert.alert(
      'Atualização Necessária',
      'Por favor, atualize para a versão mais recente para continuar.',
      [
        {
          text: 'Atualizar',
          onPress: () => Linking.openURL(config.storeUrl)
        }
      ],
      { cancelable: false }
    );
    return false;
  }
  
  if (compareVersions(currentVersion, config.latestVersion) < 0) {
    // Aviso suave de atualização
    Alert.alert(
      'Atualização Disponível',
      'Uma nova versão está disponível com melhorias.',
      [
        { text: 'Depois', style: 'cancel' },
        { text: 'Atualizar', onPress: () => Linking.openURL(config.storeUrl) }
      ]
    );
  }
  
  return true;
};

Feature flags

Controle a liberação de funcionalidades sem atualizações do app:

typescript
interface FeatureFlags {
  newSwapInterface: boolean;
  enableNftGallery: boolean;
  showPromotionalBanner: boolean;
  maxTransactionRetries: number;
}

const defaultFlags: FeatureFlags = {
  newSwapInterface: false,
  enableNftGallery: true,
  showPromotionalBanner: false,
  maxTransactionRetries: 3,
};

const fetchFeatureFlags = async (): Promise<FeatureFlags> => {
  try {
    const response = await fetch('https://api.yourapp.com/feature-flags');
    const serverFlags = await response.json();
    return { ...defaultFlags, ...serverFlags };
  } catch {
    return defaultFlags; // Fallback para padrões se a requisição falhar
  }
};

// Uso
const FeatureFlagProvider: React.FC = ({ children }) => {
  const [flags, setFlags] = useState<FeatureFlags>(defaultFlags);
  
  useEffect(() => {
    fetchFeatureFlags().then(setFlags);
  }, []);
  
  return (
    <FeatureFlagContext.Provider value={flags}>
      {children}
    </FeatureFlagContext.Provider>
  );
};

Atualizações over-the-air

Para React Native, ferramentas como CodePush permitem atualizações de JavaScript sem revisão da App Store:

typescript
import CodePush from 'react-native-code-push';

// Verifique atualizações ao iniciar o app
const codePushOptions = {
  checkFrequency: CodePush.CheckFrequency.ON_APP_START,
  installMode: CodePush.InstallMode.ON_NEXT_RESTART,
};

const App = () => {
  // ... seu app
};

export default CodePush(codePushOptions)(App);

Limitações:

  • Não é possível atualizar código nativo (apenas o bundle JavaScript)

  • Apple e Google têm políticas sobre atualizações OTA

  • Não use para burlar a revisão com conteúdo que viole políticas

Suporte ao Usuário e Feedback

Apps de cripto têm desafios únicos de suporte. Os usuários perdem dinheiro se algo der errado.

Suporte no app

Facilite o relato de problemas com contexto:

typescript
const reportIssue = async (userDescription: string) => {
  const diagnostics = {
    appVersion: DeviceInfo.getVersion(),
    buildNumber: DeviceInfo.getBuildNumber(),
    os: Platform.OS,
    osVersion: DeviceInfo.getSystemVersion(),
    deviceModel: DeviceInfo.getModel(),
    walletConnected: !!currentWallet,
    walletType: currentWallet?.type,
    recentTransactions: await getRecentTransactionLogs(),
    timestamp: new Date().toISOString(),
  };
  
  await fetch('https://api.yourapp.com/support/issue', {
    method: 'POST',
    body: JSON.stringify({
      description: userDescription,
      diagnostics,
      userId: currentUser?.id,
    })
  });
};

Solução de problemas de transações

Quando os usuários relatam transações travadas ou falhas, você precisa da assinatura:

typescript
// Armazene o histórico de transações localmente
const logTransaction = async (tx: {
  signature: string;
  type: string;
  status: 'pending' | 'confirmed' | 'failed';
  timestamp: number;
  error?: string;
}) => {
  const history = await getTransactionHistory();
  history.unshift(tx);
  
  // Mantenha as últimas 100 transações
  if (history.length > 100) history.pop();
  
  await AsyncStorage.setItem('txHistory', JSON.stringify(history));
};

// Exponha na tela de suporte/depuração
const TransactionHistory = () => {
  const [history, setHistory] = useState([]);
  
  useEffect(() => {
    getTransactionHistory().then(setHistory);
  }, []);
  
  return (
    <FlatList
      data={history}
      renderItem={({ item }) => (
        <TouchableOpacity
          onPress={() => {
            Clipboard.setString(item.signature);
            Alert.alert('Copiado', 'Assinatura da transação copiada');
          }}
        >
          <Text>{item.type} - {item.status}</Text>
          <Text style={styles.signature}>{item.signature.slice(0, 20)}...</Text>
        </TouchableOpacity>
      )}
    />
  );
};

FAQ e autoatendimento

A maioria das solicitações de suporte é previsível. Construa autoatendimento:

  1. Transação pendente por muito tempo? → Mostre como verificar no Solscan

  2. Carteira não conecta? → Passos de solução de problemas para carteiras comuns

  3. Saldo não atualiza? → Pull-to-refresh, explique tempos de confirmação

  4. Perdeu acesso à carteira? → Explique opções de recuperação (ou a falta delas para carteiras externas)

Considerações de Escala

À medida que sua base de usuários cresce, diferentes partes do seu sistema sofrerão pressão.

Custos de RPC

Endpoints RPC públicos da Solana têm limites de taxa baixos. Provedores pagos cobram por requisição:

typescript
// Otimize o uso de RPC

// Ruim: Polling a cada segundo
setInterval(() => connection.getBalance(publicKey), 1000);

// Melhor: Use subscrições WebSocket
connection.onAccountChange(publicKey, (accountInfo) => {
  updateBalance(accountInfo.lamports);
});

// Ainda melhor: Agrupe requisições quando possível
const [balance, tokenAccounts, recentTx] = await Promise.all([
  connection.getBalance(publicKey),
  connection.getParsedTokenAccountsByOwner(publicKey, { programId: TOKEN_PROGRAM_ID }),
  connection.getSignaturesForAddress(publicKey, { limit: 5 })
]);

Custos de backend

Se você faz proxy através de um backend:

  • Faça cache de respostas quando apropriado (metadados de token, imagens NFT)

  • Limite a taxa por usuário para prevenir abuso

  • Use edge functions (Cloudflare Workers, Vercel Edge) para distribuição global de baixa latência

Considerações de banco de dados

Histórico de transações e preferências de usuário crescem ao longo do tempo:

  • Arquive logs de transações antigos

  • Indexe campos frequentemente consultados

  • Considere bancos de dados de séries temporais para dados de analytics

Isso não é aconselhamento jurídico, mas conscientização ajuda você a fazer as perguntas certas.

Termos de Serviço

Todo app precisa de um ToS. Para apps de cripto, cubra:

  • Aviso de autocustódia (você não guarda as chaves deles)

  • Aviso de que não oferece aconselhamento financeiro

  • Restrições geográficas, se houver

  • Política de uso aceitável

Política de Privacidade

Exigida pela App Store e Play Store. Para apps de cripto:

  • Quais dados você coleta (endereços de carteira, histórico de transações, info do dispositivo)

  • Como você os usa

  • Serviços de terceiros com quem compartilha (analytics, provedores RPC)

  • Conformidade com GDPR se atender usuários da UE

Restrições geográficas

Algumas funcionalidades exigem licenciamento em jurisdições específicas:

  • Funcionalidades de trading/exchange: Licenças de transmissão de dinheiro em estados dos EUA

  • On/off ramps fiduciários: Requisitos de processador de pagamentos

  • Securities: Se seus tokens podem ser títulos, proceda com cautela

Muitos apps simplesmente bloqueiam certas jurisdições para evitar exposição regulatória.

Checklist de Manutenção

Tarefas contínuas para manter seu app saudável:

Semanalmente

  • Revise relatórios de crashes e priorize correções

  • Verifique as taxas de sucesso de transações

  • Monitore a saúde e os custos do RPC

  • Revise feedback de usuários/tickets de suporte

Mensalmente

  • Atualize dependências (verifique patches de segurança)

  • Revise analytics para padrões de uso

  • Teste fluxos críticos nas versões mais recentes do SO

  • Revise atualizações de políticas da App Store/Play Store

Trimestralmente

  • Audite dependências de terceiros por vulnerabilidades

  • Revise e rotacione chaves de API/credenciais

  • Teste recuperação de desastres (consegue restaurar a partir de backups?)

  • Atualize documentação (API, guias do usuário)

Anualmente

  • Renove certificados (notificações push, SSL pins)

  • Revise a arquitetura geral para necessidades de escala

  • Audite práticas de segurança

  • Planeje o roadmap da próxima versão principal

Resumo do Curso

Você completou o curso final do currículo de desenvolvimento mobile na Solana. Vamos recapitular o que cobrimos:

Lição 1: Cenário de distribuição

  • Três canais: Play Store, App Store, Solana dApp Store

  • Cada um tem políticas e trade-offs diferentes

  • Apps de cripto enfrentam desafios únicos nas lojas tradicionais

Lição 2: Publicação na Solana dApp Store

  • Registro baseado em NFT (Publisher → App → Release)

  • Ferramentas CLI para minting e submissão

  • Requisitos de assets e processo de revisão

Lição 3: Estratégias de revisão da App Store

  • Diretrizes 3.1.5 de criptomoedas da Apple

  • Política de conteúdo blockchain do Google Play

  • Contas de teste e modos de demonstração para revisão

  • Tratamento de rejeições e recursos

Lição 4: Segurança mobile

  • Realidade da engenharia reversa (tudo pode ser extraído)

  • Armazenamento seguro com Keychain/Keystore

  • Padrões de segurança de API (proxy, tokens de curta duração)

  • Proteção de chaves privadas

Lição 5: Operações de produção

  • Monitoramento e alertas

  • Gerenciamento de RPC e fallbacks

  • Estratégias de atualização e feature flags

  • Rotinas de suporte e manutenção

O Que Vem a Seguir

Agora você tem o conhecimento para construir, proteger, publicar e manter uma aplicação mobile Solana em todos os canais de distribuição.

O melhor próximo passo é construir algo. Escolha um problema que você se importa, aplique o que aprendeu e publique.

Algumas ideias para explorar:

  • Uma UI de carteira simples com carteiras embutidas Privy

  • Uma experiência mobile com token-gating

  • Um app de galeria NFT

  • Um browser de dApps otimizado para mobile

O ecossistema mobile da Solana ainda é jovem. Há espaço para construir ferramentas e apps que milhões vão usar. Agora você sabe como levá-los até lá.

Parabéns, você concluiu este curso!
Blueshift © 2026Commit: 1b88646