From cb7bfef3562bf344fc05c91c014050e720392701 Mon Sep 17 00:00:00 2001 From: Daniel Arantes Loverde Date: Thu, 19 Mar 2026 16:11:07 -0300 Subject: [PATCH] fix(android): isolate platform-specific shared ui code --- .../PediFoods/Support/PlatformCompat.swift | 72 ++++ .../PediFoods/Views/Auth/LegalViews.swift | 4 +- .../Views/Main/AddAddressFormView.swift | 2 +- .../PediFoods/Views/Main/AddressesView.swift | 2 +- .../PediFoods/Views/Main/CheckoutView.swift | 9 +- .../Views/Main/OrderDetailsView.swift | 2 +- .../Views/Main/OrderTrackingView.swift | 2 +- .../PediFoods/Views/Main/OrdersView.swift | 2 +- .../Views/Main/PizzaFlavorAddonsSheet.swift | 10 +- .../Views/Main/PizzaProductDetailSheet.swift | 10 +- .../Views/Main/ProductDetailSheet.swift | 10 +- .../PediFoods/Views/Main/ProfileView.swift | 2 + .../PediFoods/Views/Main/ReviewsView.swift | 4 +- .../Views/Main/StoreDetailView.swift | 5 +- .../Views/Main/UserProfileView.swift | 2 +- skip_for_android_plan.md | 310 ++++++++++++++++++ 16 files changed, 413 insertions(+), 35 deletions(-) create mode 100644 pedi-foods/Sources/PediFoods/Support/PlatformCompat.swift create mode 100644 skip_for_android_plan.md diff --git a/pedi-foods/Sources/PediFoods/Support/PlatformCompat.swift b/pedi-foods/Sources/PediFoods/Support/PlatformCompat.swift new file mode 100644 index 0000000..64c91f9 --- /dev/null +++ b/pedi-foods/Sources/PediFoods/Support/PlatformCompat.swift @@ -0,0 +1,72 @@ +import Foundation +#if canImport(SwiftUI) +import SwiftUI +#endif + +#if canImport(CoreGraphics) +import CoreGraphics +#endif + +#if canImport(UIKit) +import UIKit +#endif + +#if !canImport(UIKit) +struct UIDevice { + static let topNotch: CGFloat = 0 + static let bottomNotch: CGFloat = 0 + + var modelName: String { + "android" + } +} +#endif + +#if !(canImport(LCEssentials) && os(iOS)) +func printLog(title: String, msg: String) { + print("[\(title)] \(msg)") +} + +func printError(title: String, msg: String) { + print("[\(title)] \(msg)") +} +#endif + +extension View { + @ViewBuilder + func appInlineNavigationTitle() -> some View { +#if os(macOS) + self +#else + self.navigationBarTitleDisplayMode(.inline) +#endif + } + + @ViewBuilder + func appHiddenNavigationBar() -> some View { +#if os(macOS) + self +#else + self + .toolbar(.hidden, for: .navigationBar) + .toolbarBackground(.hidden, for: .navigationBar) +#endif + } + + @ViewBuilder + func appTopBarTrailingToolbar(@ViewBuilder content: () -> Content) -> some View { +#if os(macOS) + self.toolbar { + ToolbarItem { + content() + } + } +#else + self.toolbar { + ToolbarItem(placement: .topBarTrailing) { + content() + } + } +#endif + } +} diff --git a/pedi-foods/Sources/PediFoods/Views/Auth/LegalViews.swift b/pedi-foods/Sources/PediFoods/Views/Auth/LegalViews.swift index d875f39..71e47a3 100644 --- a/pedi-foods/Sources/PediFoods/Views/Auth/LegalViews.swift +++ b/pedi-foods/Sources/PediFoods/Views/Auth/LegalViews.swift @@ -18,7 +18,7 @@ struct TermsOfUseView: View { .background((colorScheme == .dark ? Color.black : AppColors.backgroundLight).ignoresSafeArea()) .navigationTitle("Termos de Uso") #if os(iOS) - .navigationBarTitleDisplayMode(.inline) + .appInlineNavigationTitle() #endif } } @@ -41,7 +41,7 @@ struct PrivacyPolicyView: View { .background((colorScheme == .dark ? Color.black : AppColors.backgroundLight).ignoresSafeArea()) .navigationTitle("Privacidade") #if os(iOS) - .navigationBarTitleDisplayMode(.inline) + .appInlineNavigationTitle() #endif } } diff --git a/pedi-foods/Sources/PediFoods/Views/Main/AddAddressFormView.swift b/pedi-foods/Sources/PediFoods/Views/Main/AddAddressFormView.swift index a077f12..4712a44 100644 --- a/pedi-foods/Sources/PediFoods/Views/Main/AddAddressFormView.swift +++ b/pedi-foods/Sources/PediFoods/Views/Main/AddAddressFormView.swift @@ -128,7 +128,7 @@ struct AddAddressFormView: View { .padding(.top, 0) } .navigationBarBackButtonHidden(true) - .toolbar(.hidden, for: .navigationBar) + .appHiddenNavigationBar() .onAppear { populateFromExistingAddressIfNeeded() } diff --git a/pedi-foods/Sources/PediFoods/Views/Main/AddressesView.swift b/pedi-foods/Sources/PediFoods/Views/Main/AddressesView.swift index f710b00..1e949d3 100644 --- a/pedi-foods/Sources/PediFoods/Views/Main/AddressesView.swift +++ b/pedi-foods/Sources/PediFoods/Views/Main/AddressesView.swift @@ -106,7 +106,7 @@ struct AddressesView: View { } .navigationBarBackButtonHidden(true) - .toolbar(.hidden, for: .navigationBar) + .appHiddenNavigationBar() .sheet(isPresented: $openAddAddressForm) { NavigationStack { AddAddressFormView(existingAddress: editingAddress) { updatedAddresses, wasEditing in diff --git a/pedi-foods/Sources/PediFoods/Views/Main/CheckoutView.swift b/pedi-foods/Sources/PediFoods/Views/Main/CheckoutView.swift index d9f8ec3..d031fbd 100644 --- a/pedi-foods/Sources/PediFoods/Views/Main/CheckoutView.swift +++ b/pedi-foods/Sources/PediFoods/Views/Main/CheckoutView.swift @@ -119,7 +119,7 @@ struct CheckoutView: View { } .background(AppColors.backgroundLight) .navigationTitle("Finalizar Pedido") - .navigationBarTitleDisplayMode(.inline) + .appInlineNavigationTitle() .safeAreaInset(edge: .bottom) { bottomBar } @@ -659,7 +659,7 @@ struct PaymentPixView: View { } .background(AppColors.backgroundLight) .navigationTitle("Pagamento via PIX") - .navigationBarTitleDisplayMode(.inline) + .appInlineNavigationTitle() .task { tracker.onOrderUpdated = { updated in latestOrder = updated @@ -851,7 +851,7 @@ struct PaymentCardView: View { } .background(AppColors.backgroundLight) .navigationTitle("Pagamento") - .navigationBarTitleDisplayMode(.inline) + .appInlineNavigationTitle() .task { tracker.onOrderUpdated = { updated in latestOrder = updated @@ -879,8 +879,7 @@ struct PaymentCardView: View { .font(AppTypography.caption) .foregroundStyle(AppColors.textMuted) TextField(placeholder, text: text) - .textInputAutocapitalization(.never) - .disableAutocorrection(true) + .appNoAutoCap() .padding(.horizontal, 12) .frame(height: 46) .background(AppColors.backgroundLight) diff --git a/pedi-foods/Sources/PediFoods/Views/Main/OrderDetailsView.swift b/pedi-foods/Sources/PediFoods/Views/Main/OrderDetailsView.swift index 1494bab..7bbd472 100644 --- a/pedi-foods/Sources/PediFoods/Views/Main/OrderDetailsView.swift +++ b/pedi-foods/Sources/PediFoods/Views/Main/OrderDetailsView.swift @@ -23,7 +23,7 @@ struct OrderDetailsView: View { } .background(AppColors.backgroundLight) .navigationTitle("Detalhes do Pedido") - .navigationBarTitleDisplayMode(.inline) + .appInlineNavigationTitle() .safeAreaInset(edge: .bottom) { VStack { reorderButton diff --git a/pedi-foods/Sources/PediFoods/Views/Main/OrderTrackingView.swift b/pedi-foods/Sources/PediFoods/Views/Main/OrderTrackingView.swift index d120d1d..f19edee 100644 --- a/pedi-foods/Sources/PediFoods/Views/Main/OrderTrackingView.swift +++ b/pedi-foods/Sources/PediFoods/Views/Main/OrderTrackingView.swift @@ -43,7 +43,7 @@ struct OrderTrackingView: View { } .background(AppColors.backgroundLight) .navigationTitle("Pedido \(displayOrderTitle)") - .navigationBarTitleDisplayMode(.inline) + .appInlineNavigationTitle() .alert("Motivo do cancelamento", isPresented: $showCancellationReason) { Button("Fechar", role: .cancel) {} } message: { diff --git a/pedi-foods/Sources/PediFoods/Views/Main/OrdersView.swift b/pedi-foods/Sources/PediFoods/Views/Main/OrdersView.swift index a5ccade..9e3df0c 100644 --- a/pedi-foods/Sources/PediFoods/Views/Main/OrdersView.swift +++ b/pedi-foods/Sources/PediFoods/Views/Main/OrdersView.swift @@ -39,7 +39,7 @@ struct OrdersView: View { } .background(AppColors.backgroundLight) .navigationTitle("Meus Pedidos") - .navigationBarTitleDisplayMode(.inline) + .appInlineNavigationTitle() .task { await loadOrdersIfNeeded() await refreshStoreRatings() diff --git a/pedi-foods/Sources/PediFoods/Views/Main/PizzaFlavorAddonsSheet.swift b/pedi-foods/Sources/PediFoods/Views/Main/PizzaFlavorAddonsSheet.swift index 2a29537..3170436 100644 --- a/pedi-foods/Sources/PediFoods/Views/Main/PizzaFlavorAddonsSheet.swift +++ b/pedi-foods/Sources/PediFoods/Views/Main/PizzaFlavorAddonsSheet.swift @@ -78,12 +78,10 @@ struct PizzaFlavorAddonsSheet: View { } .background(AppColors.backgroundLight.ignoresSafeArea()) .navigationTitle("Adicionais") - .navigationBarTitleDisplayMode(.inline) - .toolbar { - ToolbarItem(placement: .topBarTrailing) { - Button("Concluir") { dismiss() } - .foregroundStyle(AppColors.primary) - } + .appInlineNavigationTitle() + .appTopBarTrailingToolbar { + Button("Concluir") { dismiss() } + .foregroundStyle(AppColors.primary) } } diff --git a/pedi-foods/Sources/PediFoods/Views/Main/PizzaProductDetailSheet.swift b/pedi-foods/Sources/PediFoods/Views/Main/PizzaProductDetailSheet.swift index 8ff0b03..0b10bc7 100644 --- a/pedi-foods/Sources/PediFoods/Views/Main/PizzaProductDetailSheet.swift +++ b/pedi-foods/Sources/PediFoods/Views/Main/PizzaProductDetailSheet.swift @@ -292,12 +292,10 @@ struct PizzaProductDetailSheet: View { } .background(AppColors.backgroundLight.ignoresSafeArea()) .navigationTitle("Monte sua pizza") - .navigationBarTitleDisplayMode(.inline) - .toolbar { - ToolbarItem(placement: .topBarTrailing) { - Button("Fechar") { dismiss() } - .foregroundStyle(AppColors.primary) - } + .appInlineNavigationTitle() + .appTopBarTrailingToolbar { + Button("Fechar") { dismiss() } + .foregroundStyle(AppColors.primary) } .onAppear { applyAutoSelections() diff --git a/pedi-foods/Sources/PediFoods/Views/Main/ProductDetailSheet.swift b/pedi-foods/Sources/PediFoods/Views/Main/ProductDetailSheet.swift index cf4149d..dde14e6 100644 --- a/pedi-foods/Sources/PediFoods/Views/Main/ProductDetailSheet.swift +++ b/pedi-foods/Sources/PediFoods/Views/Main/ProductDetailSheet.swift @@ -222,12 +222,10 @@ struct ProductDetailSheet: View { } .background(AppColors.backgroundLight.ignoresSafeArea()) .navigationTitle("Detalhes") - .navigationBarTitleDisplayMode(.inline) - .toolbar { - ToolbarItem(placement: .topBarTrailing) { - Button("Fechar") { dismiss() } - .foregroundStyle(AppColors.primary) - } + .appInlineNavigationTitle() + .appTopBarTrailingToolbar { + Button("Fechar") { dismiss() } + .foregroundStyle(AppColors.primary) } .onAppear { let existing = currentQuantityForItemId(cartItemId) diff --git a/pedi-foods/Sources/PediFoods/Views/Main/ProfileView.swift b/pedi-foods/Sources/PediFoods/Views/Main/ProfileView.swift index 5d98c11..98b6007 100644 --- a/pedi-foods/Sources/PediFoods/Views/Main/ProfileView.swift +++ b/pedi-foods/Sources/PediFoods/Views/Main/ProfileView.swift @@ -1,5 +1,7 @@ import SwiftUI +#if canImport(LCEssentials) && os(iOS) import LCEssentials +#endif #if canImport(UIKit) import UIKit #endif diff --git a/pedi-foods/Sources/PediFoods/Views/Main/ReviewsView.swift b/pedi-foods/Sources/PediFoods/Views/Main/ReviewsView.swift index e606d3e..54d9725 100644 --- a/pedi-foods/Sources/PediFoods/Views/Main/ReviewsView.swift +++ b/pedi-foods/Sources/PediFoods/Views/Main/ReviewsView.swift @@ -67,7 +67,7 @@ struct MyReviewsView: View { } .background(AppColors.backgroundLight) .navigationTitle("Minhas Avaliações") - .navigationBarTitleDisplayMode(.inline) + .appInlineNavigationTitle() .navigationDestination(item: $selectedDraft) { draft in OrderReviewView(draft: draft) { Task { await loadReviewsFromBackend(forceRefresh: true) } @@ -451,7 +451,7 @@ struct OrderReviewView: View { } .background(AppColors.backgroundLight) .navigationTitle("Avaliar Pedido") - .navigationBarTitleDisplayMode(.inline) + .appInlineNavigationTitle() .onAppear { if let cachedReview = SessionStateStore.loadOrderReview(orderId: draft.orderId) { existingReview = cachedReview diff --git a/pedi-foods/Sources/PediFoods/Views/Main/StoreDetailView.swift b/pedi-foods/Sources/PediFoods/Views/Main/StoreDetailView.swift index cb9fd61..8731b11 100644 --- a/pedi-foods/Sources/PediFoods/Views/Main/StoreDetailView.swift +++ b/pedi-foods/Sources/PediFoods/Views/Main/StoreDetailView.swift @@ -1,5 +1,7 @@ import SwiftUI +#if canImport(LCEssentials) && os(iOS) import LCEssentials +#endif #if canImport(UIKit) import UIKit #endif @@ -74,8 +76,7 @@ struct StoreDetailView: View { } } .navigationBarBackButtonHidden(true) - .toolbar(.hidden, for: .navigationBar) - .toolbarBackground(.hidden, for: .navigationBar) + .appHiddenNavigationBar() .task { guard didLoad == false else { return } didLoad = true diff --git a/pedi-foods/Sources/PediFoods/Views/Main/UserProfileView.swift b/pedi-foods/Sources/PediFoods/Views/Main/UserProfileView.swift index d7b49bc..3ce4028 100644 --- a/pedi-foods/Sources/PediFoods/Views/Main/UserProfileView.swift +++ b/pedi-foods/Sources/PediFoods/Views/Main/UserProfileView.swift @@ -33,7 +33,7 @@ struct UserProfileView: View { } .background(AppColors.backgroundLight) .navigationTitle("Meu Perfil") - .navigationBarTitleDisplayMode(.inline) + .appInlineNavigationTitle() .onAppear { hydrateFromAppState() } diff --git a/skip_for_android_plan.md b/skip_for_android_plan.md new file mode 100644 index 0000000..d59daf9 --- /dev/null +++ b/skip_for_android_plan.md @@ -0,0 +1,310 @@ +# Plano de Ação - Skip para Android + +## Objetivo + +Preparar o projeto `PediFoods` para o primeiro ciclo real de testes no Android, preservando integralmente o funcionamento atual do iOS. + +Regra principal deste plano: + +1. Todo ajuste necessário para Android deve ser isolado e não pode causar regressão no iOS. +2. Sempre que possível, a separação deve ser feita com condicionais por plataforma, abstrações compatíveis ou implementações específicas para Android. +3. Cada task concluída e validada deve gerar commit próprio. + +## Premissas confirmadas + +1. O projeto já possui base Skip dual-platform. +2. O iOS está funcional e deve ser preservado como referência de comportamento. +3. O Android já possui estrutura inicial pronta: + - `pedi-foods/Android/*` + - `pedi-foods/Android/app/src/main/kotlin/Main.kt` + - `pedi-foods/Android/app/src/main/AndroidManifest.xml` + - `pedi-foods/Sources/PediFoods/Skip/skip.yml` +4. O objetivo imediato não é publicar Android, e sim conseguir compilar, instalar, abrir e validar os fluxos principais. + +## Diagnóstico atual + +### 1. Estrutura Android existente + +O projeto já possui: + +1. Manifest com permissões de internet e localização. +2. `Main.kt` com tratamento inicial de permissão de localização no Android. +3. Script dedicado para exportar e rodar no Android: + - `pedi-foods/scripts/android-run.sh` +4. Recursos Android básicos como ícones launcher. + +### 2. Riscos identificados + +Os principais riscos para o primeiro teste Android são: + +1. Imports e dependências iOS-only ainda expostos em arquivos compartilhados. +2. Build local atualmente bloqueado por problema de artefato/toolchain Skip. +3. Recursos e comportamentos com paridade incompleta no Android. +4. Cobertura de testes ainda insuficiente para confiar só em automação. + +### 3. Bloqueios técnicos já identificados + +#### 3.1. Código compartilhado com dependência iOS-only + +Foram encontrados pontos com alto risco para Android: + +1. `pedi-foods/Sources/PediFoods/Views/Main/StoreDetailView.swift` + - possui `import LCEssentials` sem blindagem de plataforma. +2. `pedi-foods/Sources/PediFoods/Views/Main/ProfileView.swift` + - possui `import LCEssentials` sem blindagem de plataforma. +3. Outros arquivos com dependências iOS-only exigem revisão cuidadosa: + - `pedi-foods/Sources/PediFoods/Views/Main/HomeView.swift` + - `pedi-foods/Sources/PediFoods/Views/Main/ReviewsView.swift` + - `pedi-foods/Sources/PediFoods/Views/Main/UserProfileView.swift` + - `pedi-foods/Sources/PediFoods/Services/ApiClient.swift` + +#### 3.2. Ambiente/build Skip inconsistente + +Na validação local, o comando de build falhou antes da compilação funcional: + +1. `swift build` retornou erro de artefato ausente do Skip. +2. O erro aponta para caminho antigo de projeto, indicando problema de cache, artefato ou configuração local do ambiente. + +Sem resolver isso, não é possível validar corretamente o Android. + +#### 3.3. Paridade Android ainda parcial + +Alguns comportamentos parecem estar deliberadamente simplificados para Android: + +1. Snackbar no Android está atualmente neutro/stubado. +2. Splash nativo Android ainda não aparenta estar finalizado. +3. Recursos visuais e alguns fluxos podem abrir, mas não com equivalência total ao iOS. + +Isso não impede o primeiro boot, mas entra como ajuste após a compilação inicial. + +## Estratégia de execução + +A execução deve seguir uma ordem rígida para evitar retrabalho: + +1. Primeiro remover bloqueios de compilação cross-platform. +2. Depois estabilizar o ambiente Skip/Android local. +3. Em seguida gerar build Android real. +4. Depois validar execução no device/emulador. +5. Só então ajustar bugs e diferenças de comportamento específicas do Android. +6. Ao fim de cada etapa funcional concluída, gerar commit. + +## Plano de ação detalhado + +### Fase 1 - Blindagem do código compartilhado para Android + +Objetivo: +Garantir que o código compartilhado consiga ser compilado para Android sem quebrar o iOS. + +Ações: + +1. Revisar todos os arquivos compartilhados com: + - `import LCEssentials` + - `import UIKit` + - `import PhotosUI` + - qualquer API não portável pelo Skip +2. Encapsular uso iOS-only com: + - `#if os(iOS)` + - `#if canImport(...)` + - abstrações seguras por plataforma +3. Confirmar que nenhum ajuste Android altere o comportamento já funcional do iOS. +4. Validar se os fallbacks Android são suficientes para compilar. + +Critério de conclusão: + +1. O código compartilhado deixa de possuir bloqueios óbvios de compilação Android. +2. As dependências iOS-only ficam totalmente isoladas. + +Commit ao concluir: + +1. Criar commit específico da blindagem cross-platform. + +### Fase 2 - Saneamento do ambiente Skip/Android + +Objetivo: +Restabelecer o pipeline de build para que a validação Android seja real e repetível. + +Ações: + +1. Identificar a origem do erro de artefato do Skip. +2. Verificar caches, artefatos e referências para caminho antigo do projeto. +3. Corrigir ambiente local sem destruir configuração válida do iOS. +4. Validar novamente: + - `swift build` + - fluxo de export Android via Skip + +Critério de conclusão: + +1. O build compartilhado executa sem erro de artefato. +2. O projeto consegue iniciar a etapa de export Android. + +Commit ao concluir: + +1. Criar commit específico do ajuste de ambiente/configuração, se houver mudança versionada no repositório. +2. Se a correção for apenas local e não versionável, registrar isso no andamento e seguir sem commit desta subparte. + +### Fase 3 - Primeiro build Android real + +Objetivo: +Gerar APK/build Android funcional para teste. + +Ações: + +1. Rodar export/build Android pelo fluxo oficial do projeto. +2. Validar geração dos artefatos Android esperados. +3. Confirmar compatibilidade com ABI do emulador/device. +4. Corrigir falhas de build especificamente Android sem alterar o caminho iOS. + +Critério de conclusão: + +1. APK ou build instalável gerado com sucesso. +2. Nenhum erro de compilação Android pendente. + +Commit ao concluir: + +1. Criar commit específico do primeiro build Android estabilizado. + +### Fase 4 - Primeiro teste funcional no Android + +Objetivo: +Instalar, abrir e validar o app no Android pela primeira vez. + +Ações: + +1. Instalar no emulador ou device. +2. Validar abertura do app. +3. Confirmar: + - splash inicial + - fluxo de login + - navegação principal por tabs + - home + - detalhe da loja + - perfil + - endereços +4. Observar crashes, telas em branco, problemas de navegação ou layout quebrado. + +Critério de conclusão: + +1. O app abre no Android. +2. Os fluxos principais navegam sem crash. + +Commit ao concluir: + +1. Criar commit específico do primeiro ciclo funcional de execução Android. + +### Fase 5 - Correções de runtime específicas do Android + +Objetivo: +Ajustar o que só aparece após o app abrir no Android. + +Ações: + +1. Validar permissão de localização. +2. Validar persistência/local cache usados pelo app no Android. +3. Validar imagens e carregamento remoto. +4. Validar teclado, foco e `adjustResize`. +5. Validar sheets, modais, overlays e navegação. +6. Corrigir bugs Android-only com isolamento por plataforma. + +Critério de conclusão: + +1. Os fluxos principais deixam de ter falhas críticas no Android. +2. Nenhuma correção Android impacta o iOS. + +Commit ao concluir: + +1. Criar commits separados por grupo funcional concluído e validado. + +### Fase 6 - Paridade mínima de UX Android + +Objetivo: +Atingir um nível mínimo aceitável de experiência para teste Android sem buscar perfeição visual prematura. + +Ações: + +1. Ajustar snackbar Android para comportamento visível e funcional. +2. Revisar splash Android nativo, se necessário. +3. Corrigir diferenças de layout mais críticas. +4. Revisar componentes com comportamento visual diferente entre plataformas. + +Critério de conclusão: + +1. O Android fica funcional e utilizável para teste contínuo. +2. As diferenças visuais restantes não impedem validação do produto. + +Commit ao concluir: + +1. Criar commit específico das melhorias de UX Android aprovadas. + +### Fase 7 - Regressão rápida no iOS + +Objetivo: +Garantir que os ajustes Android não introduziram regressões no iOS. + +Ações: + +1. Rodar build iOS novamente. +2. Validar navegação principal no iOS. +3. Confirmar que os fluxos já funcionais continuam intactos. +4. Revisar especialmente os pontos alterados com condicionais de plataforma. + +Critério de conclusão: + +1. iOS continua funcional após os ajustes Android. +2. Nenhuma regressão relevante é detectada. + +Commit ao concluir: + +1. Criar commit final de consolidação apenas se houver mudanças adicionais nesta etapa. + +## Regras operacionais durante a execução + +1. Nenhum arquivo pode ultrapassar 500 linhas. +2. Ajustes Android devem ser isolados e não devem reescrever a lógica funcional do iOS. +3. Toda correção deve passar por validação antes de seguir para a próxima task. +4. Cada task funcional concluída deve resultar em commit. +5. Se uma task encontrar erro, ela retorna ao ciclo: + - identificar causa raiz + - corrigir + - testar novamente + - só então marcar como concluída + +## Convenção de commits + +Padrão recomendado: + +1. `fix(android): isolate ios-only dependencies for skip build` +2. `fix(build): stabilize skip android export pipeline` +3. `feat(android): first successful android build` +4. `fix(android): resolve runtime issues on first boot` +5. `feat(android): improve minimum ux parity` + +## Sequência de tasks para acompanhamento + +Legenda: + +- 🔴 Pendente +- 🟢 Concluído + +1. 🟢 Revisar e blindar imports/dependências iOS-only no código compartilhado. +2. 🟢 Corrigir o problema de artefato/caminho do ambiente Skip local. +3. 🟢 Validar `swift build` sem erro de ambiente. +4. 🔴 Validar export/build Android via Skip. +5. 🔴 Gerar APK/build Android instalável. +6. 🔴 Instalar e abrir o app no Android. +7. 🔴 Validar login no Android. +8. 🔴 Validar home e navegação principal no Android. +9. 🔴 Validar detalhe de loja e carrinho no Android. +10. 🔴 Validar perfil e endereços no Android. +11. 🔴 Validar permissão e uso de localização no Android. +12. 🔴 Corrigir bugs críticos de runtime Android. +13. 🔴 Ajustar snackbar Android. +14. 🔴 Revisar splash Android, se necessário. +15. 🔴 Rodar regressão rápida no iOS após os ajustes Android. +16. 🟢 Criar commit após cada task concluída e funcional. + +## Andamento + +1. Task 1 concluída e validada via build compartilhado. +2. Task 2 concluída com ajuste local no cache/estado de build do Skip. + - Correção local não versionada. +3. Task 3 concluída com `swift build` finalizado com sucesso.