Merge pull request 'fix/auth/payment-login-lockout' (#44) from fix/auth/payment-login-lockout into main

Reviewed-on: Loverde-Company-LTDA/Pedi-Foods-Skip#44
This commit is contained in:
2026-09-10 18:22:39 -03:00
20 changed files with 419 additions and 279 deletions

View File

@@ -3,7 +3,7 @@
archiveVersion = 1; archiveVersion = 1;
classes = { classes = {
}; };
objectVersion = 54; objectVersion = 56;
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
@@ -36,6 +36,7 @@
2B202626C34FC9F5C87C0388 /* ApiOrderModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4625B70E3E8BEEB9D0B2042 /* ApiOrderModels.swift */; }; 2B202626C34FC9F5C87C0388 /* ApiOrderModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4625B70E3E8BEEB9D0B2042 /* ApiOrderModels.swift */; };
2F56755488E951C453AC2126 /* LCEssentials in Frameworks */ = {isa = PBXBuildFile; productRef = B8D5E8821C95A331C3A19DA1 /* LCEssentials */; }; 2F56755488E951C453AC2126 /* LCEssentials in Frameworks */ = {isa = PBXBuildFile; productRef = B8D5E8821C95A331C3A19DA1 /* LCEssentials */; };
2FCB3403A764AD74AC3BD47C /* LegalDocumentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AA914FB8B32B106911FD67B /* LegalDocumentTests.swift */; }; 2FCB3403A764AD74AC3BD47C /* LegalDocumentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AA914FB8B32B106911FD67B /* LegalDocumentTests.swift */; };
901A6A5A00C18FC59F764B85 /* PublicStoreDistanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E55893DD92CBFB24F212454 /* PublicStoreDistanceTests.swift */; };
33AB0B87867400F3D0C65FA0 /* ApiClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DBC4B9B43C015C4DD411359 /* ApiClient.swift */; }; 33AB0B87867400F3D0C65FA0 /* ApiClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DBC4B9B43C015C4DD411359 /* ApiClient.swift */; };
3689239282AADF06783CBBEA /* AuthFormatters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28B3C375FC18002A884A44F1 /* AuthFormatters.swift */; }; 3689239282AADF06783CBBEA /* AuthFormatters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28B3C375FC18002A884A44F1 /* AuthFormatters.swift */; };
3BD69E880EBC797B87EE547B /* StoreDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71DB74B676A58B367F026090 /* StoreDetailView.swift */; }; 3BD69E880EBC797B87EE547B /* StoreDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71DB74B676A58B367F026090 /* StoreDetailView.swift */; };
@@ -79,7 +80,7 @@
7CC171B83BC4CDEC38B25F8C /* SnackbarCenterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2CE116C585A7529CF309B3D /* SnackbarCenterTests.swift */; }; 7CC171B83BC4CDEC38B25F8C /* SnackbarCenterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2CE116C585A7529CF309B3D /* SnackbarCenterTests.swift */; };
7D7BF04765D6CA6AC4F11DDD /* StoreCatalogNormalizerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18EC38EEE1E7105BC21E354C /* StoreCatalogNormalizerTests.swift */; }; 7D7BF04765D6CA6AC4F11DDD /* StoreCatalogNormalizerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18EC38EEE1E7105BC21E354C /* StoreCatalogNormalizerTests.swift */; };
80B476CCE2B90BEE2FA4CB37 /* PediFoodsUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56E3E6C1834786CA3621B7BB /* PediFoodsUITestsLaunchTests.swift */; }; 80B476CCE2B90BEE2FA4CB37 /* PediFoodsUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56E3E6C1834786CA3621B7BB /* PediFoodsUITestsLaunchTests.swift */; };
811217F88AA350A70CC22479 /* NotificationServiceExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = C724E02EC34BB1D4352C2FF0 /* NotificationServiceExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 811217F88AA350A70CC22479 /* NotificationService.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = C724E02EC34BB1D4352C2FF0 /* NotificationService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
85EA52253BABA327EC07CFF0 /* AuthFormattersTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 561E00C486D60EC2DF720EAF /* AuthFormattersTests.swift */; }; 85EA52253BABA327EC07CFF0 /* AuthFormattersTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 561E00C486D60EC2DF720EAF /* AuthFormattersTests.swift */; };
8D6622AE9325D02B830BD115 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6CAA9B253B1A6C8815A0E69D /* LaunchScreen.storyboard */; }; 8D6622AE9325D02B830BD115 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6CAA9B253B1A6C8815A0E69D /* LaunchScreen.storyboard */; };
90ADA376585F5438F3A1A1D6 /* HomeView+Favorites.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1517B141263438E466452037 /* HomeView+Favorites.swift */; }; 90ADA376585F5438F3A1A1D6 /* HomeView+Favorites.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1517B141263438E466452037 /* HomeView+Favorites.swift */; };
@@ -114,7 +115,6 @@
CCE23FD7706B34BDE544E05A /* Buttons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EC381D02A37312BE360024F /* Buttons.swift */; }; CCE23FD7706B34BDE544E05A /* Buttons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EC381D02A37312BE360024F /* Buttons.swift */; };
CD96980F3D7B603126FDE6AC /* URLProtocolStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51798A414469C911D44F0F6 /* URLProtocolStub.swift */; }; CD96980F3D7B603126FDE6AC /* URLProtocolStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51798A414469C911D44F0F6 /* URLProtocolStub.swift */; };
CEA08090E08B5E39FD0DD5D6 /* CachedRemoteImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = EECF1BBC608B65EC1F1B6114 /* CachedRemoteImage.swift */; }; CEA08090E08B5E39FD0DD5D6 /* CachedRemoteImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = EECF1BBC608B65EC1F1B6114 /* CachedRemoteImage.swift */; };
38452FD60CBDE248531DA484 /* AppBackButtonIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52EEF6262A813BB8274266C8 /* AppBackButtonIcon.swift */; };
D2FB3C624B3C27E59BCA997A /* MainTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EA34EE194C71755B3C82F30 /* MainTabView.swift */; }; D2FB3C624B3C27E59BCA997A /* MainTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EA34EE194C71755B3C82F30 /* MainTabView.swift */; };
D3EC6E0A68B809F7A3370B1B /* ProductDetailSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553A05B9EAD0B9D0D9F1B369 /* ProductDetailSheet.swift */; }; D3EC6E0A68B809F7A3370B1B /* ProductDetailSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553A05B9EAD0B9D0D9F1B369 /* ProductDetailSheet.swift */; };
D48148B155BA2F76C41C0787 /* HomeFiltersStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FBD7D809ACE1840165A9DCC /* HomeFiltersStateTests.swift */; }; D48148B155BA2F76C41C0787 /* HomeFiltersStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FBD7D809ACE1840165A9DCC /* HomeFiltersStateTests.swift */; };
@@ -128,6 +128,7 @@
E4D492E4B651A8FCC5016B43 /* PizzaFlavorAddonsSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5ECD3AA9543133BE623668D /* PizzaFlavorAddonsSheet.swift */; }; E4D492E4B651A8FCC5016B43 /* PizzaFlavorAddonsSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5ECD3AA9543133BE623668D /* PizzaFlavorAddonsSheet.swift */; };
E7817C56D8F083A231E11E4E /* LocationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAB9F980426D39E93E091051 /* LocationService.swift */; }; E7817C56D8F083A231E11E4E /* LocationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAB9F980426D39E93E091051 /* LocationService.swift */; };
EBBF7EFFDC9286D4F64544F4 /* PublicLocationModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = F85F176EA2B204CC95072D28 /* PublicLocationModels.swift */; }; EBBF7EFFDC9286D4F64544F4 /* PublicLocationModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = F85F176EA2B204CC95072D28 /* PublicLocationModels.swift */; };
EC9F35551075A78EF7869784 /* AppBackButtonIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3056B162576522D94702AE6E /* AppBackButtonIcon.swift */; };
ECFEB3B3779C4F267EC148A4 /* SnackbarCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 226D1B7C049B405436DD36B4 /* SnackbarCenter.swift */; }; ECFEB3B3779C4F267EC148A4 /* SnackbarCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 226D1B7C049B405436DD36B4 /* SnackbarCenter.swift */; };
F03F08F65DFAD9166884EAD4 /* StoreCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31916C1A4005396ADA5FF3BC /* StoreCard.swift */; }; F03F08F65DFAD9166884EAD4 /* StoreCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31916C1A4005396ADA5FF3BC /* StoreCard.swift */; };
F280C0AC9902B13E2224C790 /* OrdersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF413C3924669E761BF455B8 /* OrdersView.swift */; }; F280C0AC9902B13E2224C790 /* OrdersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF413C3924669E761BF455B8 /* OrdersView.swift */; };
@@ -168,7 +169,7 @@
dstPath = ""; dstPath = "";
dstSubfolderSpec = 13; dstSubfolderSpec = 13;
files = ( files = (
811217F88AA350A70CC22479 /* NotificationServiceExtension.appex in Embed Foundation Extensions */, 811217F88AA350A70CC22479 /* NotificationService.appex in Embed Foundation Extensions */,
); );
name = "Embed Foundation Extensions"; name = "Embed Foundation Extensions";
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@@ -199,6 +200,7 @@
2EA34EE194C71755B3C82F30 /* MainTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabView.swift; sourceTree = "<group>"; }; 2EA34EE194C71755B3C82F30 /* MainTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabView.swift; sourceTree = "<group>"; };
2F4ED1B7B71C905115380DC0 /* FeatureControlService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureControlService.swift; sourceTree = "<group>"; }; 2F4ED1B7B71C905115380DC0 /* FeatureControlService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureControlService.swift; sourceTree = "<group>"; };
30024AC97E79912E1358DDFB /* FiltersModalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FiltersModalView.swift; sourceTree = "<group>"; }; 30024AC97E79912E1358DDFB /* FiltersModalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FiltersModalView.swift; sourceTree = "<group>"; };
3056B162576522D94702AE6E /* AppBackButtonIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppBackButtonIcon.swift; sourceTree = "<group>"; };
30B3B6E69309F3EDF28E81CC /* LegalViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegalViews.swift; sourceTree = "<group>"; }; 30B3B6E69309F3EDF28E81CC /* LegalViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegalViews.swift; sourceTree = "<group>"; };
31916C1A4005396ADA5FF3BC /* StoreCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreCard.swift; sourceTree = "<group>"; }; 31916C1A4005396ADA5FF3BC /* StoreCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreCard.swift; sourceTree = "<group>"; };
35400BEE3D09C61B2058579F /* AppContentCacheTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppContentCacheTests.swift; sourceTree = "<group>"; }; 35400BEE3D09C61B2058579F /* AppContentCacheTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppContentCacheTests.swift; sourceTree = "<group>"; };
@@ -227,7 +229,7 @@
69A9FB3DE679C3D4C60E7E42 /* ApiConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiConfig.swift; sourceTree = "<group>"; }; 69A9FB3DE679C3D4C60E7E42 /* ApiConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiConfig.swift; sourceTree = "<group>"; };
6BF467DF1447525F04B242B0 /* ReviewsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReviewsView.swift; sourceTree = "<group>"; }; 6BF467DF1447525F04B242B0 /* ReviewsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReviewsView.swift; sourceTree = "<group>"; };
6CAA9B253B1A6C8815A0E69D /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; }; 6CAA9B253B1A6C8815A0E69D /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
6DD1D8390E57439A347F9E07 /* PediFoodsTests.xctest */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = PediFoodsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 6DD1D8390E57439A347F9E07 /* PediFoodsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PediFoodsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
6EC381D02A37312BE360024F /* Buttons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Buttons.swift; sourceTree = "<group>"; }; 6EC381D02A37312BE360024F /* Buttons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Buttons.swift; sourceTree = "<group>"; };
6FC454261845C1EDF970552A /* CheckoutTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckoutTypes.swift; sourceTree = "<group>"; }; 6FC454261845C1EDF970552A /* CheckoutTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckoutTypes.swift; sourceTree = "<group>"; };
7034E89267D9F5D86BF1E424 /* CartCheckoutFlowTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CartCheckoutFlowTests.swift; sourceTree = "<group>"; }; 7034E89267D9F5D86BF1E424 /* CartCheckoutFlowTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CartCheckoutFlowTests.swift; sourceTree = "<group>"; };
@@ -240,7 +242,7 @@
76B24E69A3F6A3EB86449870 /* AddAddressFormView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddAddressFormView.swift; sourceTree = "<group>"; }; 76B24E69A3F6A3EB86449870 /* AddAddressFormView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddAddressFormView.swift; sourceTree = "<group>"; };
794249487F718BCFBF176E8D /* ApiModelsDecodingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiModelsDecodingTests.swift; sourceTree = "<group>"; }; 794249487F718BCFBF176E8D /* ApiModelsDecodingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiModelsDecodingTests.swift; sourceTree = "<group>"; };
79D2854B8062D96742CFDD99 /* HomeViewComponents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewComponents.swift; sourceTree = "<group>"; }; 79D2854B8062D96742CFDD99 /* HomeViewComponents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewComponents.swift; sourceTree = "<group>"; };
7A6D74FD2D2F2F79349EA04E /* PediFoods.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = PediFoods.app; sourceTree = BUILT_PRODUCTS_DIR; }; 7A6D74FD2D2F2F79349EA04E /* PediFoods.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PediFoods.app; sourceTree = BUILT_PRODUCTS_DIR; };
7A9F14B1BDD7747C546DFAF0 /* GuestSessionService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuestSessionService.swift; sourceTree = "<group>"; }; 7A9F14B1BDD7747C546DFAF0 /* GuestSessionService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuestSessionService.swift; sourceTree = "<group>"; };
7C13751F450A42A2BDB0783D /* ProfileLoggedOutFlowTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileLoggedOutFlowTests.swift; sourceTree = "<group>"; }; 7C13751F450A42A2BDB0783D /* ProfileLoggedOutFlowTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileLoggedOutFlowTests.swift; sourceTree = "<group>"; };
7CD06F9A7AF9365D1FBE35D0 /* AddressComponents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressComponents.swift; sourceTree = "<group>"; }; 7CD06F9A7AF9365D1FBE35D0 /* AddressComponents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressComponents.swift; sourceTree = "<group>"; };
@@ -249,7 +251,8 @@
89B662E8B7E1B8F1CF7E657E /* KeychainStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainStore.swift; sourceTree = "<group>"; }; 89B662E8B7E1B8F1CF7E657E /* KeychainStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainStore.swift; sourceTree = "<group>"; };
8A3FB85A2A2E2952B9A2BAB0 /* DeepLinkDestination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeepLinkDestination.swift; sourceTree = "<group>"; }; 8A3FB85A2A2E2952B9A2BAB0 /* DeepLinkDestination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeepLinkDestination.swift; sourceTree = "<group>"; };
8AA914FB8B32B106911FD67B /* LegalDocumentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegalDocumentTests.swift; sourceTree = "<group>"; }; 8AA914FB8B32B106911FD67B /* LegalDocumentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegalDocumentTests.swift; sourceTree = "<group>"; };
8AF987AB319512C123DFD558 /* PediFoodsUITests.xctest */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = PediFoodsUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 4E55893DD92CBFB24F212454 /* PublicStoreDistanceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublicStoreDistanceTests.swift; sourceTree = "<group>"; };
8AF987AB319512C123DFD558 /* PediFoodsUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PediFoodsUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
8D2C6A0E85E133F947293181 /* OrderDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderDetailsView.swift; sourceTree = "<group>"; }; 8D2C6A0E85E133F947293181 /* OrderDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderDetailsView.swift; sourceTree = "<group>"; };
8DD2B73D89E6D33643EF58F5 /* DeepLinkRouteEffectTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeepLinkRouteEffectTests.swift; sourceTree = "<group>"; }; 8DD2B73D89E6D33643EF58F5 /* DeepLinkRouteEffectTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeepLinkRouteEffectTests.swift; sourceTree = "<group>"; };
8E3A0A2E253ABFCA35D02F28 /* TextFieldCompat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldCompat.swift; sourceTree = "<group>"; }; 8E3A0A2E253ABFCA35D02F28 /* TextFieldCompat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldCompat.swift; sourceTree = "<group>"; };
@@ -284,7 +287,7 @@
C44662BAA20AD862BE1DBC40 /* PublicLocationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublicLocationService.swift; sourceTree = "<group>"; }; C44662BAA20AD862BE1DBC40 /* PublicLocationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublicLocationService.swift; sourceTree = "<group>"; };
C4745C12F14B695C77DFE178 /* SnackbarOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnackbarOverlay.swift; sourceTree = "<group>"; }; C4745C12F14B695C77DFE178 /* SnackbarOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnackbarOverlay.swift; sourceTree = "<group>"; };
C6B08901A67C8D90A3B50882 /* ApiCustomerPayloadModelsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiCustomerPayloadModelsTests.swift; sourceTree = "<group>"; }; C6B08901A67C8D90A3B50882 /* ApiCustomerPayloadModelsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiCustomerPayloadModelsTests.swift; sourceTree = "<group>"; };
C724E02EC34BB1D4352C2FF0 /* NotificationServiceExtension.appex */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = "wrapper.app-extension"; path = NotificationServiceExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; C724E02EC34BB1D4352C2FF0 /* NotificationService.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NotificationService.appex; sourceTree = BUILT_PRODUCTS_DIR; };
CA44F5384D21B7F718F95522 /* StoreDetailSupport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreDetailSupport.swift; sourceTree = "<group>"; }; CA44F5384D21B7F718F95522 /* StoreDetailSupport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreDetailSupport.swift; sourceTree = "<group>"; };
CAB9F980426D39E93E091051 /* LocationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationService.swift; sourceTree = "<group>"; }; CAB9F980426D39E93E091051 /* LocationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationService.swift; sourceTree = "<group>"; };
CD5EE81AE4347B4086684F8B /* CartView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CartView.swift; sourceTree = "<group>"; }; CD5EE81AE4347B4086684F8B /* CartView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CartView.swift; sourceTree = "<group>"; };
@@ -300,7 +303,6 @@
EB95D6804877D2ED244811D0 /* Inputs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Inputs.swift; sourceTree = "<group>"; }; EB95D6804877D2ED244811D0 /* Inputs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Inputs.swift; sourceTree = "<group>"; };
ECBFCE20C54A79DEA8F4512B /* AppCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCoordinator.swift; sourceTree = "<group>"; }; ECBFCE20C54A79DEA8F4512B /* AppCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCoordinator.swift; sourceTree = "<group>"; };
EECF1BBC608B65EC1F1B6114 /* CachedRemoteImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CachedRemoteImage.swift; sourceTree = "<group>"; }; EECF1BBC608B65EC1F1B6114 /* CachedRemoteImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CachedRemoteImage.swift; sourceTree = "<group>"; };
52EEF6262A813BB8274266C8 /* AppBackButtonIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppBackButtonIcon.swift; sourceTree = "<group>"; };
F0B079DFAB606DCA87A536C9 /* LaunchSplashView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchSplashView.swift; sourceTree = "<group>"; }; F0B079DFAB606DCA87A536C9 /* LaunchSplashView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchSplashView.swift; sourceTree = "<group>"; };
F18F768F7A2A47111A85FE88 /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = "<group>"; }; F18F768F7A2A47111A85FE88 /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = "<group>"; };
F2B2C5BBDFF4473C472F45B4 /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = "<group>"; }; F2B2C5BBDFF4473C472F45B4 /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = "<group>"; };
@@ -344,7 +346,7 @@
126A579C448C784867DC3AB1 /* Products */ = { 126A579C448C784867DC3AB1 /* Products */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
C724E02EC34BB1D4352C2FF0 /* NotificationServiceExtension.appex */, C724E02EC34BB1D4352C2FF0 /* NotificationService.appex */,
7A6D74FD2D2F2F79349EA04E /* PediFoods.app */, 7A6D74FD2D2F2F79349EA04E /* PediFoods.app */,
6DD1D8390E57439A347F9E07 /* PediFoodsTests.xctest */, 6DD1D8390E57439A347F9E07 /* PediFoodsTests.xctest */,
8AF987AB319512C123DFD558 /* PediFoodsUITests.xctest */, 8AF987AB319512C123DFD558 /* PediFoodsUITests.xctest */,
@@ -446,6 +448,7 @@
23B6B814027518969EB04341 /* HomeViewFilteringTests.swift */, 23B6B814027518969EB04341 /* HomeViewFilteringTests.swift */,
5A82497C63D507E82FE02693 /* ImageSourceResolverTests.swift */, 5A82497C63D507E82FE02693 /* ImageSourceResolverTests.swift */,
8AA914FB8B32B106911FD67B /* LegalDocumentTests.swift */, 8AA914FB8B32B106911FD67B /* LegalDocumentTests.swift */,
4E55893DD92CBFB24F212454 /* PublicStoreDistanceTests.swift */,
747AD255B6D881E011FDF907 /* SessionStateStoreTests.swift */, 747AD255B6D881E011FDF907 /* SessionStateStoreTests.swift */,
160571AB2F8D4DF98C0BE16F /* SmokeTests.swift */, 160571AB2F8D4DF98C0BE16F /* SmokeTests.swift */,
B2CE116C585A7529CF309B3D /* SnackbarCenterTests.swift */, B2CE116C585A7529CF309B3D /* SnackbarCenterTests.swift */,
@@ -558,9 +561,9 @@
D7552D3AF029ADAC7DE5A76B /* Components */ = { D7552D3AF029ADAC7DE5A76B /* Components */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
3056B162576522D94702AE6E /* AppBackButtonIcon.swift */,
6EC381D02A37312BE360024F /* Buttons.swift */, 6EC381D02A37312BE360024F /* Buttons.swift */,
EECF1BBC608B65EC1F1B6114 /* CachedRemoteImage.swift */, EECF1BBC608B65EC1F1B6114 /* CachedRemoteImage.swift */,
52EEF6262A813BB8274266C8 /* AppBackButtonIcon.swift */,
EB95D6804877D2ED244811D0 /* Inputs.swift */, EB95D6804877D2ED244811D0 /* Inputs.swift */,
C4745C12F14B695C77DFE178 /* SnackbarOverlay.swift */, C4745C12F14B695C77DFE178 /* SnackbarOverlay.swift */,
31916C1A4005396ADA5FF3BC /* StoreCard.swift */, 31916C1A4005396ADA5FF3BC /* StoreCard.swift */,
@@ -621,7 +624,7 @@
); );
name = NotificationServiceExtension; name = NotificationServiceExtension;
productName = NotificationServiceExtension; productName = NotificationServiceExtension;
productReference = C724E02EC34BB1D4352C2FF0 /* NotificationServiceExtension.appex */; productReference = C724E02EC34BB1D4352C2FF0 /* NotificationService.appex */;
productType = "com.apple.product-type.app-extension"; productType = "com.apple.product-type.app-extension";
}; };
66486D25E3A8A75EA8FACE44 /* PediFoodsUITests */ = { 66486D25E3A8A75EA8FACE44 /* PediFoodsUITests */ = {
@@ -671,15 +674,10 @@
BuildIndependentTargetsInParallel = YES; BuildIndependentTargetsInParallel = YES;
LastUpgradeCheck = 1430; LastUpgradeCheck = 1430;
TargetAttributes = { TargetAttributes = {
33EB2407F0F758EB98A769D3 = {
DevelopmentTeam = K4E5BZMM4V;
ProvisioningStyle = Automatic;
};
66486D25E3A8A75EA8FACE44 = { 66486D25E3A8A75EA8FACE44 = {
TestTargetID = B3B5CFF77A8ED019D1299ABB; TestTargetID = B3B5CFF77A8ED019D1299ABB;
}; };
B3B5CFF77A8ED019D1299ABB = { B3B5CFF77A8ED019D1299ABB = {
ProvisioningStyle = Manual;
SystemCapabilities = { SystemCapabilities = {
com.apple.Push = { com.apple.Push = {
enabled = YES; enabled = YES;
@@ -775,6 +773,7 @@
42FD703A96353119FAB2F711 /* ApiPizzaModels.swift in Sources */, 42FD703A96353119FAB2F711 /* ApiPizzaModels.swift in Sources */,
691351CFE207B9F34361AEB6 /* ApiReviewModels.swift in Sources */, 691351CFE207B9F34361AEB6 /* ApiReviewModels.swift in Sources */,
279A489F01434E0523EC1BAA /* ApiService.swift in Sources */, 279A489F01434E0523EC1BAA /* ApiService.swift in Sources */,
EC9F35551075A78EF7869784 /* AppBackButtonIcon.swift in Sources */,
B239BDCEB9F8934F1C71258C /* AppCache.swift in Sources */, B239BDCEB9F8934F1C71258C /* AppCache.swift in Sources */,
9EDDCECCAEEB8FA4909DA9D4 /* AppCoordinator.swift in Sources */, 9EDDCECCAEEB8FA4909DA9D4 /* AppCoordinator.swift in Sources */,
9F09502CF9331F0C94BFA5F0 /* AppState.swift in Sources */, 9F09502CF9331F0C94BFA5F0 /* AppState.swift in Sources */,
@@ -782,7 +781,6 @@
3689239282AADF06783CBBEA /* AuthFormatters.swift in Sources */, 3689239282AADF06783CBBEA /* AuthFormatters.swift in Sources */,
CCE23FD7706B34BDE544E05A /* Buttons.swift in Sources */, CCE23FD7706B34BDE544E05A /* Buttons.swift in Sources */,
CEA08090E08B5E39FD0DD5D6 /* CachedRemoteImage.swift in Sources */, CEA08090E08B5E39FD0DD5D6 /* CachedRemoteImage.swift in Sources */,
38452FD60CBDE248531DA484 /* AppBackButtonIcon.swift in Sources */,
DE499D6150C15EB046AF7963 /* CartView.swift in Sources */, DE499D6150C15EB046AF7963 /* CartView.swift in Sources */,
70EA850C067AB6C6B87539E2 /* CheckoutTypes.swift in Sources */, 70EA850C067AB6C6B87539E2 /* CheckoutTypes.swift in Sources */,
41BE548D929D3814A736FA12 /* CheckoutView+Logic.swift in Sources */, 41BE548D929D3814A736FA12 /* CheckoutView+Logic.swift in Sources */,
@@ -872,6 +870,7 @@
63AB40542AABCC3CB2390B87 /* HomeViewFilteringTests.swift in Sources */, 63AB40542AABCC3CB2390B87 /* HomeViewFilteringTests.swift in Sources */,
22E3BF78B8C2B22D14EABE8B /* ImageSourceResolverTests.swift in Sources */, 22E3BF78B8C2B22D14EABE8B /* ImageSourceResolverTests.swift in Sources */,
2FCB3403A764AD74AC3BD47C /* LegalDocumentTests.swift in Sources */, 2FCB3403A764AD74AC3BD47C /* LegalDocumentTests.swift in Sources */,
901A6A5A00C18FC59F764B85 /* PublicStoreDistanceTests.swift in Sources */,
F760557D906B860D0EA277DE /* SessionStateStoreTests.swift in Sources */, F760557D906B860D0EA277DE /* SessionStateStoreTests.swift in Sources */,
B24B898508C8A8EF4191EC89 /* SmokeTests.swift in Sources */, B24B898508C8A8EF4191EC89 /* SmokeTests.swift in Sources */,
7CC171B83BC4CDEC38B25F8C /* SnackbarCenterTests.swift in Sources */, 7CC171B83BC4CDEC38B25F8C /* SnackbarCenterTests.swift in Sources */,
@@ -968,6 +967,11 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Distribution";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
CODE_SIGN_STYLE = Manual;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = K4E5BZMM4V;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
@@ -975,6 +979,7 @@
"@loader_path/Frameworks", "@loader_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.br.pedifoods.PediFoodsTests; PRODUCT_BUNDLE_IDENTIFIER = com.br.pedifoods.PediFoodsTests;
PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_VERSION = 6; SWIFT_VERSION = 6;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
@@ -990,54 +995,10 @@
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = PediFoods/PediFoods.entitlements; CODE_SIGN_ENTITLEMENTS = PediFoods/PediFoods.entitlements;
CODE_SIGN_IDENTITY = "Apple Distribution"; CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual; CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 2026.0.25.0.0.1;
DEVELOPMENT_TEAM = K4E5BZMM4V;
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = K4E5BZMM4V;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = PediFoods/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Pedi Foods";
INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO;
INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "PediFoods usa sua localização para mostrar lojas perto de você";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = NO;
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
INFOPLIST_KEY_UIStatusBarStyle = UIStatusBarStyleDefault;
INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 0.0.1;
PRODUCT_BUNDLE_IDENTIFIER = com.br.pedifoods.app;
PRODUCT_NAME = PediFoods;
PROVISIONING_PROFILE_SPECIFIER = "com.br.pedifoods.app AppStore";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "PediFoods Dist XC Prov Prof";
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 6;
TARGETED_DEVICE_FAMILY = 1;
};
name = Release;
};
4424276379B7A6A98892CFBC /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
APP_ATTEST_ENVIRONMENT = development;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = PediFoods/PediFoods.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = K4E5BZMM4V; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = K4E5BZMM4V;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
@@ -1061,7 +1022,52 @@
PRODUCT_BUNDLE_IDENTIFIER = com.br.pedifoods.app; PRODUCT_BUNDLE_IDENTIFIER = com.br.pedifoods.app;
PRODUCT_NAME = PediFoods; PRODUCT_NAME = PediFoods;
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "PediFoods Dev Prov Prof"; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "LC Prov PediFoods Dist Profile";
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 6;
TARGETED_DEVICE_FAMILY = 1;
};
name = Release;
};
4424276379B7A6A98892CFBC /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
APP_ATTEST_ENVIRONMENT = development;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = PediFoods/PediFoods.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 2026.0.25.0.0.1;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = K4E5BZMM4V;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = PediFoods/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Pedi Foods";
INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO;
INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "PediFoods usa sua localização para mostrar lojas perto de você";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = NO;
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
INFOPLIST_KEY_UIStatusBarStyle = UIStatusBarStyleDefault;
INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 0.0.1;
PRODUCT_BUNDLE_IDENTIFIER = com.br.pedifoods.app;
PRODUCT_NAME = PediFoods;
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "LC Prov PediFoods Dev Profile";
SDKROOT = iphoneos; SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO; SUPPORTS_MACCATALYST = NO;
@@ -1075,6 +1081,12 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Manual;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = K4E5BZMM4V;
"DEVELOPMENT_TEAM[sdk=macosx*]" = K4E5BZMM4V;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
@@ -1082,6 +1094,7 @@
"@loader_path/Frameworks", "@loader_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.br.pedifoods.PediFoodsTests; PRODUCT_BUNDLE_IDENTIFIER = com.br.pedifoods.PediFoodsTests;
PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_VERSION = 6; SWIFT_VERSION = 6;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
@@ -1093,9 +1106,11 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CLANG_ENABLE_OBJC_WEAK = NO; CLANG_ENABLE_OBJC_WEAK = NO;
CODE_SIGN_STYLE = Automatic; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = K4E5BZMM4V; DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = K4E5BZMM4V;
INFOPLIST_FILE = NotificationServiceExtension/Info.plist; INFOPLIST_FILE = NotificationServiceExtension/Info.plist;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
@@ -1105,6 +1120,8 @@
MARKETING_VERSION = 0.0.1; MARKETING_VERSION = 0.0.1;
PRODUCT_BUNDLE_IDENTIFIER = com.br.pedifoods.app.NotificationService; PRODUCT_BUNDLE_IDENTIFIER = com.br.pedifoods.app.NotificationService;
PRODUCT_NAME = NotificationService; PRODUCT_NAME = NotificationService;
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "LC Prov PediFoods Dev Push Profile";
SDKROOT = iphoneos; SDKROOT = iphoneos;
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SWIFT_VERSION = 6; SWIFT_VERSION = 6;
@@ -1116,6 +1133,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
DEVELOPMENT_TEAM = K4E5BZMM4V;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
@@ -1190,6 +1208,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
DEVELOPMENT_TEAM = K4E5BZMM4V;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
@@ -1208,9 +1227,12 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CLANG_ENABLE_OBJC_WEAK = NO; CLANG_ENABLE_OBJC_WEAK = NO;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = K4E5BZMM4V; DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = K4E5BZMM4V;
INFOPLIST_FILE = NotificationServiceExtension/Info.plist; INFOPLIST_FILE = NotificationServiceExtension/Info.plist;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
@@ -1220,6 +1242,8 @@
MARKETING_VERSION = 0.0.1; MARKETING_VERSION = 0.0.1;
PRODUCT_BUNDLE_IDENTIFIER = com.br.pedifoods.app.NotificationService; PRODUCT_BUNDLE_IDENTIFIER = com.br.pedifoods.app.NotificationService;
PRODUCT_NAME = NotificationService; PRODUCT_NAME = NotificationService;
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "LC Prov PediFoods Dist Push Profile";
SDKROOT = iphoneos; SDKROOT = iphoneos;
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SWIFT_VERSION = 6; SWIFT_VERSION = 6;

View File

@@ -53,12 +53,17 @@ struct FeaturedStoreCard: View {
Text("(\(store.reviews))") Text("(\(store.reviews))")
.font(.caption) .font(.caption)
.foregroundStyle(AppColors.textMuted) .foregroundStyle(AppColors.textMuted)
Text("·") // Empty distance means "unavailable" - drop the separator
.font(.caption) // with it, otherwise the row ends in a dangling "·".
.foregroundStyle(AppColors.textMuted) // See docs/plans/public-store-distance-consumer.md.
Text(store.distance) if store.distance.isEmpty == false {
.font(.caption) Text("·")
.foregroundStyle(AppColors.textMuted) .font(.caption)
.foregroundStyle(AppColors.textMuted)
Text(store.distance)
.font(.caption)
.foregroundStyle(AppColors.textMuted)
}
} }
} else { } else {
Text(store.statusLabel?.isEmpty == false ? (store.statusLabel ?? "Fechado") : "Fechado") Text(store.statusLabel?.isEmpty == false ? (store.statusLabel ?? "Fechado") : "Fechado")

View File

@@ -15,8 +15,6 @@ struct ContentView: View {
@State var hasPerformedInitialLaunchBootstrap = false @State var hasPerformedInitialLaunchBootstrap = false
@State var showLaunchSplash = true @State var showLaunchSplash = true
@State var shouldPulseLaunchSplash = true @State var shouldPulseLaunchSplash = true
@State var shouldPrepareAuthEntryAnimation = DefaultTokenStore().jwt == nil
@State var authEntryAnimationToken = 0
@State private var sessionExpiredObserver: NSObjectProtocol? @State private var sessionExpiredObserver: NSObjectProtocol?
@State var cartResetObserver: Any? @State var cartResetObserver: Any?
@State var appResumeObserver: Any? @State var appResumeObserver: Any?
@@ -32,15 +30,15 @@ struct ContentView: View {
root: $root, root: $root,
selectedTab: $selectedTab, selectedTab: $selectedTab,
tokenStore: tokenStore, tokenStore: tokenStore,
appState: $appState, appState: $appState
shouldPrepareLoginEntry: shouldPrepareAuthEntryAnimation,
authEntryAnimationToken: authEntryAnimationToken
) )
.transition(.move(edge: .trailing))
case .main: case .main:
if isBootstrappingSession { if isBootstrappingSession {
sessionBootstrapLoadingView sessionBootstrapLoadingView
} else { } else {
MainTabView(selectedTab: $selectedTab, tokenStore: tokenStore, appState: $appState, enterAuth: enterAuthFlow) MainTabView(selectedTab: $selectedTab, tokenStore: tokenStore, appState: $appState, enterAuth: enterAuthFlow)
.transition(.move(edge: .leading))
} }
} }
} }
@@ -136,15 +134,11 @@ struct ContentView: View {
} }
if root == .auth { if root == .auth {
shouldPrepareAuthEntryAnimation = true
shouldPulseLaunchSplash = false shouldPulseLaunchSplash = false
try? await Task.sleep(nanoseconds: 180_000_000) try? await Task.sleep(nanoseconds: 180_000_000)
withAnimation(.easeInOut(duration: 0.34)) { withAnimation(.easeInOut(duration: 0.34)) {
showLaunchSplash = false showLaunchSplash = false
} }
try? await Task.sleep(nanoseconds: 360_000_000)
authEntryAnimationToken += 1
scheduleDisableAuthEntryPreparation()
return return
} }
@@ -410,17 +404,15 @@ struct ContentView: View {
enterAuthFlow() enterAuthFlow()
} }
/// The only correct way to transition `root` to `.auth`: pairs the /// The single funnel for entering the auth flow. `LoginView` is a
/// switch with the token bump `LoginView` needs to run its entry /// static screen with its own `LCENavigationView` back button (sets
/// reveal animation. A bare `root = .auth` leaves the login screen's /// `root = .main`), so no animation priming is needed here - see
/// hero/text/button stuck hidden - see /// decisions/2026-08-27-payment-auth-lockout-clean-intro-screen.md.
/// decisions/2026-08-06-login-entry-animation-bug.md.
@MainActor @MainActor
private func enterAuthFlow() { private func enterAuthFlow() {
shouldPrepareAuthEntryAnimation = true withAnimation(.easeInOut(duration: 0.3)) {
root = .auth root = .auth
authEntryAnimationToken += 1 }
scheduleDisableAuthEntryPreparation()
} }
@MainActor @MainActor
@@ -463,13 +455,6 @@ struct ContentView: View {
) )
} }
private func scheduleDisableAuthEntryPreparation() {
Task { @MainActor in
try? await Task.sleep(nanoseconds: 900_000_000)
shouldPrepareAuthEntryAnimation = false
}
}
private func hasConfiguredAddress() -> Bool { private func hasConfiguredAddress() -> Bool {
if appState.address.selectedId != nil { if appState.address.selectedId != nil {
return true return true

View File

@@ -88,6 +88,9 @@
}, },
"Abrir Ajustes" : { "Abrir Ajustes" : {
},
"Acesse sua conta" : {
}, },
"Acompanhamento em tempo real" : { "Acompanhamento em tempo real" : {
@@ -346,10 +349,6 @@
"comment" : "A prompt asking users to share their opinion about the delivery.", "comment" : "A prompt asking users to share their opinion about the delivery.",
"isCommentAutoGenerated" : true "isCommentAutoGenerated" : true
}, },
"Criar conta" : {
"comment" : "A link that navigates to the registration screen.",
"isCommentAutoGenerated" : true
},
"Crie sua conta" : { "Crie sua conta" : {
"comment" : "A link that directs users to create an account.", "comment" : "A link that directs users to create an account.",
"isCommentAutoGenerated" : true "isCommentAutoGenerated" : true
@@ -748,9 +747,6 @@
"Não recebeu o código?" : { "Não recebeu o código?" : {
"comment" : "A question displayed below the button to re-send the OTP.", "comment" : "A question displayed below the button to re-send the OTP.",
"isCommentAutoGenerated" : true "isCommentAutoGenerated" : true
},
"Não tem conta ainda?" : {
}, },
"Nenhum cartão cadastrado" : { "Nenhum cartão cadastrado" : {

View File

@@ -28,6 +28,12 @@ struct PublicStoreListItem: Decodable, Sendable, Identifiable {
let deliveryTime: String? let deliveryTime: String?
let deliveryFee: Double? let deliveryFee: Double?
let minOrder: Double? let minOrder: Double?
/// Kilometres from the picked city's centroid to the store - always
/// approximate, since the origin is the city rather than the visitor.
/// The server sends `0` for "unavailable" and never null; optional here
/// only so the app keeps decoding responses from before the field
/// shipped. See docs/plans/public-store-distance-consumer.md.
let distance: Double?
} }
struct PublicStoreDetail: Decodable, Sendable { struct PublicStoreDetail: Decodable, Sendable {
@@ -56,6 +62,9 @@ struct PublicStoreDetail: Decodable, Sendable {
let deliveryPrice: Double? let deliveryPrice: Double?
let minOrder: Double? let minOrder: Double?
let acceptPix: Bool? let acceptPix: Bool?
/// Same contract as `PublicStoreListItem.distance`, but only populated
/// when the detail request carries `state`/`city`.
let distance: Double?
} }
// Maps the public (anonymous) store-detail projection onto the same models // Maps the public (anonymous) store-detail projection onto the same models

View File

@@ -34,13 +34,31 @@ final class PublicLocationService: @unchecked Sendable {
} }
/// No guest token this route is fully public/unauthenticated per the doc. /// No guest token this route is fully public/unauthenticated per the doc.
func fetchStoreDetail(identifier: String) async throws -> PublicStoreDetail { ///
/// `state`/`city` are optional on the server and are what let it resolve
/// the city centroid the store `distance` is measured from; without them
/// the response carries `distance: 0` (see
/// docs/plans/public-store-distance-consumer.md). Defaults to whatever the
/// visitor picked in "ENTREGAR EM:".
func fetchStoreDetail(
identifier: String,
state: String? = GuestLocationStore.shared.selectedState,
city: String? = GuestLocationStore.shared.selectedCity
) async throws -> PublicStoreDetail {
let encodedIdentifier = identifier.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? identifier let encodedIdentifier = identifier.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? identifier
var query: [URLQueryItem] = []
if let state, state.isEmpty == false {
query.append(URLQueryItem(name: "state", value: state))
}
if let city, city.isEmpty == false {
query.append(URLQueryItem(name: "city", value: city))
}
let req = ApiRequest( let req = ApiRequest(
path: "/api/public/store/\(encodedIdentifier)", path: "/api/public/store/\(encodedIdentifier)",
method: "GET", method: "GET",
module: .none, module: .none,
requiresAuth: false, requiresAuth: false,
queryItems: query,
baseURLOverride: ApiConfig.pediFoodsBFFURL baseURLOverride: ApiConfig.pediFoodsBFFURL
) )
let envelope: ApiEnvelope<PublicStoreDetail> = try await client.send(req) let envelope: ApiEnvelope<PublicStoreDetail> = try await client.send(req)

View File

@@ -11,21 +11,12 @@ struct AuthFlowView: View {
@Binding var selectedTab: MainTab @Binding var selectedTab: MainTab
let tokenStore: TokenStore let tokenStore: TokenStore
@Binding var appState: AppState @Binding var appState: AppState
var shouldPrepareLoginEntry: Bool = false
var authEntryAnimationToken: Int = 0
@State var path: [Route] = [] @State var path: [Route] = []
var body: some View { var body: some View {
NavigationStack(path: $path) { NavigationStack(path: $path) {
LoginView( LoginView(root: $root) { route in
root: $root,
selectedTab: $selectedTab,
tokenStore: tokenStore,
appState: $appState,
shouldPrepareEntryAnimation: shouldPrepareLoginEntry,
authEntryAnimationToken: authEntryAnimationToken
) { route in
path.append(route) path.append(route)
} }
.navigationDestination(for: Route.self) { route in .navigationDestination(for: Route.self) { route in

View File

@@ -1,4 +1,7 @@
import SwiftUI import SwiftUI
#if canImport(LCEssentials)
import LCEssentials
#endif
struct LoginEmailView: View { struct LoginEmailView: View {
@Binding var root: RootFlow @Binding var root: RootFlow
@@ -11,7 +14,6 @@ struct LoginEmailView: View {
@State var isLoading = false @State var isLoading = false
@State var errorMessage: String? @State var errorMessage: String?
@Environment(\.dismiss) var dismiss @Environment(\.dismiss) var dismiss
@Environment(\.colorScheme) var colorScheme
@ViewBuilder private var logoImage: some View { @ViewBuilder private var logoImage: some View {
SwiftUI.Image("pedifoods") SwiftUI.Image("pedifoods")
@@ -19,8 +21,18 @@ struct LoginEmailView: View {
} }
var body: some View { var body: some View {
LCENavigationView {
content
}
.setLeftButton(image: AnyView(AppBackButtonIcon())) { dismiss() }
.setNavigationBarBackgroundColor(AppColors.backgroundLight)
.buttonStyle(.plain)
.preferredColorScheme(.light)
}
private var content: some View {
ZStack { ZStack {
(colorScheme == .dark ? Color.black : AppColors.backgroundLight).ignoresSafeArea() AppColors.backgroundLight.ignoresSafeArea()
VStack(spacing: 0) { VStack(spacing: 0) {
logoImage logoImage
@@ -29,7 +41,7 @@ struct LoginEmailView: View {
Text("Boas-vindas!") Text("Boas-vindas!")
.font(AppTypography.heading1) .font(AppTypography.heading1)
.foregroundStyle(colorScheme == .dark ? Color.white : AppColors.textPrimary) .foregroundStyle(AppColors.textPrimary)
.padding(Edge.Set.top, 8) .padding(Edge.Set.top, 8)
.padding(.bottom, 16) .padding(.bottom, 16)
@@ -66,7 +78,7 @@ struct LoginEmailView: View {
HStack(spacing: 6) { HStack(spacing: 6) {
Text("Novo por aqui?") Text("Novo por aqui?")
.foregroundStyle(colorScheme == .dark ? Color.white.opacity(0.8) : Color.gray) .foregroundStyle(Color.gray)
Text("Crie sua conta") Text("Crie sua conta")
.foregroundStyle(AppColors.primary) .foregroundStyle(AppColors.primary)
.onTapGesture { .onTapGesture {

View File

@@ -1,142 +1,81 @@
import SwiftUI import SwiftUI
#if canImport(LCEssentials)
import LCEssentials
#endif
struct LoginView: View { struct LoginView: View {
@Binding var root: RootFlow @Binding var root: RootFlow
@Binding var selectedTab: MainTab
let tokenStore: TokenStore
@Binding var appState: AppState
@Environment(\.colorScheme) var colorScheme
var shouldPrepareEntryAnimation: Bool = false
var authEntryAnimationToken: Int = 0
let navigate: (Route) -> Void let navigate: (Route) -> Void
@State var heroVisible = true
@State var textVisible = true
@State var buttonVisible = true
@State var lastAnimatedToken = 0
@ViewBuilder private var logoImage: some View { @ViewBuilder private var logoImage: some View {
SwiftUI.Image("pedifoods") SwiftUI.Image("pedifoods")
.resizable() .resizable()
} }
@ViewBuilder private var pinHeroImage: some View {
SwiftUI.Image("pin_image_app")
.resizable()
}
var body: some View { var body: some View {
GeometryReader { geo in LCENavigationView {
let heroHeight = max(360, geo.size.height * 0.44) content
let logoTopInset = max(0, (geo.size.height - 180) / 2) }
.setLeftButton(image: AnyView(AppBackButtonIcon())) {
withAnimation(.easeInOut(duration: 0.3)) { root = .main }
}
.setTitle(text: Text("Acesse sua conta").font(AppTypography.heading2).foregroundStyle(AppColors.textPrimary))
.setNavigationBarBackgroundColor(AppColors.backgroundLight)
.buttonStyle(.plain)
// The app ships no dark theme; force light so system dark mode
// can't invert the nav bar / title against the fixed DS colors.
.preferredColorScheme(.light)
}
ZStack(alignment: .top) { private var content: some View {
(colorScheme == .dark ? Color.black : AppColors.backgroundLight) VStack(spacing: 20) {
.ignoresSafeArea() Spacer()
pinHeroImage logoImage
.scaledToFill() .scaledToFit()
.frame(height: heroHeight + 80) .frame(height: 160)
.offset(y: heroVisible ? -60 : -(heroHeight + 220))
.mask(
LinearGradient(
colors: [.black, .black, .black.opacity(0.0)],
startPoint: .top,
endPoint: .bottom
)
)
VStack(spacing: 18) { Text("Sua vontade, no seu tempo.\nTudo o que você precisa, em um toque.")
Spacer().frame(height: logoTopInset) .font(AppTypography.heading25)
.foregroundStyle(AppColors.textPrimary)
.multilineTextAlignment(.center)
.padding(.horizontal, 28)
logoImage Spacer()
.scaledToFit()
.frame(height: 180)
Text("Sua vontade, no seu tempo.\nTudo o que você precisa, em um toque.") VStack(spacing: 12) {
.font(AppTypography.heading25) PrimaryButton(title: "Criar conta", image: Image(systemName: "arrow.right")) {
.foregroundStyle(colorScheme == .dark ? AppColors.textInverse : AppColors.textPrimary) navigate(.registration)
.multilineTextAlignment(.leading) }
.frame(maxWidth: .infinity, alignment: .leading) .tint(AppColors.tertiary)
.padding(.horizontal, 28)
.opacity(textVisible ? 1.0 : 0.0)
.offset(y: textVisible ? 0 : 24)
Spacer().frame(height: 40) SecondaryButton(title: "Entrar") {
navigate(.loginEmail)
Button {
navigate(.loginEmail)
} label: {
PrimaryButtonLabel(title: "ENTRAR")
}
.padding(.horizontal, 28)
.tint(AppColors.tertiary)
.offset(y: buttonVisible ? 0 : 140)
.opacity(buttonVisible ? 1.0 : 0.0)
.buttonStyle(.plain)
HStack(spacing: 6) {
Text("Não tem conta ainda?")
.foregroundStyle(colorScheme == .dark ? AppColors.textInverse.opacity(0.9) : AppColors.textPrimary)
Button("Criar conta") {
navigate(.registration)
}
.buttonStyle(.plain)
.foregroundStyle(AppColors.primary)
}
.font(AppTypography.body)
.opacity(textVisible ? 1.0 : 0.0)
.offset(y: textVisible ? 0 : 24)
Spacer().frame(height: 12)
} }
} }
.ignoresSafeArea() .padding(.horizontal, 28)
.onAppear {
if shouldPrepareEntryAnimation { legalLinks
applyHiddenStateWithoutAnimation() .padding(.top, 4)
} else { .padding(.bottom, 24)
showFinalStateWithoutAnimation()
}
}
.task(id: authEntryAnimationToken) {
await runEntryAnimationIfNeeded(for: authEntryAnimationToken)
}
} }
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(AppColors.backgroundLight.ignoresSafeArea())
} }
@MainActor private var legalLinks: some View {
private func applyHiddenStateWithoutAnimation() { HStack(spacing: 6) {
heroVisible = false Button("Termos de Uso") { navigate(.terms) }
textVisible = false .buttonStyle(.plain)
buttonVisible = false Text("·")
} Button("Política de Privacidade") { navigate(.policy) }
.buttonStyle(.plain)
@MainActor
private func showFinalStateWithoutAnimation() {
heroVisible = true
textVisible = true
buttonVisible = true
}
@MainActor
private func runEntryAnimationIfNeeded(for token: Int) async {
guard token > 0 else { return }
guard token != lastAnimatedToken else { return }
lastAnimatedToken = token
applyHiddenStateWithoutAnimation()
try? await Task.sleep(nanoseconds: 40_000_000)
withAnimation(.spring(response: 0.64, dampingFraction: 0.9)) {
heroVisible = true
}
try? await Task.sleep(nanoseconds: 160_000_000)
withAnimation(.easeOut(duration: 0.42)) {
textVisible = true
}
try? await Task.sleep(nanoseconds: 150_000_000)
withAnimation(.spring(response: 0.52, dampingFraction: 0.86)) {
buttonVisible = true
} }
.font(.system(size: 14, weight: .medium))
.foregroundStyle(AppColors.textMuted)
} }
} }
#Preview {
LoginView(root: .constant(.auth), navigate: { _ in })
}

View File

@@ -1,5 +1,8 @@
import SwiftUI import SwiftUI
import UIKit import UIKit
#if canImport(LCEssentials)
import LCEssentials
#endif
struct OtpView: View { struct OtpView: View {
private let resendDelaySeconds = 45 private let resendDelaySeconds = 45
@@ -21,13 +24,23 @@ struct OtpView: View {
@Environment(\.dismiss) var dismiss @Environment(\.dismiss) var dismiss
var body: some View { var body: some View {
LCENavigationView {
content
}
.setLeftButton(image: AnyView(AppBackButtonIcon())) { dismiss() }
.setNavigationBarBackgroundColor(AppColors.backgroundLight)
.buttonStyle(.plain)
.preferredColorScheme(.light)
}
private var content: some View {
ScrollView { ScrollView {
VStack(spacing: 0) { VStack(spacing: 0) {
SwiftUI.Image("pedifoods") SwiftUI.Image("pedifoods")
.resizable() .resizable()
.scaledToFit() .scaledToFit()
.frame(width: 74, height: 74) .frame(width: 74, height: 74)
.padding(.top, 140) .padding(.top, 48)
Text("Verificação") Text("Verificação")
.font(AppTypography.heading1) .font(AppTypography.heading1)
@@ -122,7 +135,6 @@ struct OtpView: View {
} }
.scrollDismissesKeyboard(.interactively) .scrollDismissesKeyboard(.interactively)
.background(AppColors.backgroundLight) .background(AppColors.backgroundLight)
.ignoresSafeArea()
.onAppear { .onAppear {
isOtpFocused = true isOtpFocused = true
startResendCooldown() startResendCooldown()

View File

@@ -1,4 +1,7 @@
import SwiftUI import SwiftUI
#if canImport(LCEssentials)
import LCEssentials
#endif
struct RegistrationView: View { struct RegistrationView: View {
@Binding var root: RootFlow @Binding var root: RootFlow
@@ -12,7 +15,6 @@ struct RegistrationView: View {
@State var isLoading = false @State var isLoading = false
@State var errorMessage: String? @State var errorMessage: String?
@Environment(\.dismiss) var dismiss @Environment(\.dismiss) var dismiss
@Environment(\.colorScheme) var colorScheme
let navigate: (Route) -> Void let navigate: (Route) -> Void
private var isFormValid: Bool { !name.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && !email.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && !phone.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && acceptedTerms } private var isFormValid: Bool { !name.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && !email.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && !phone.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && acceptedTerms }
@@ -23,8 +25,18 @@ struct RegistrationView: View {
} }
var body: some View { var body: some View {
LCENavigationView {
content
}
.setLeftButton(image: AnyView(AppBackButtonIcon())) { dismiss() }
.setNavigationBarBackgroundColor(AppColors.backgroundLight)
.buttonStyle(.plain)
.preferredColorScheme(.light)
}
private var content: some View {
ZStack { ZStack {
(colorScheme == .dark ? Color.black : AppColors.backgroundLight).ignoresSafeArea() AppColors.backgroundLight.ignoresSafeArea()
ScrollView { ScrollView {
VStack(spacing: 0) { VStack(spacing: 0) {
@@ -34,11 +46,11 @@ struct RegistrationView: View {
Text("Crie sua conta") Text("Crie sua conta")
.font(AppTypography.heading1) .font(AppTypography.heading1)
.foregroundStyle(colorScheme == .dark ? Color.white : AppColors.textPrimary) .foregroundStyle(AppColors.textPrimary)
Text("Preencha os dados abaixo para começar.") Text("Preencha os dados abaixo para começar.")
.font(AppTypography.body) .font(AppTypography.body)
.foregroundStyle(colorScheme == .dark ? Color.white : AppColors.textPrimary) .foregroundStyle(AppColors.textPrimary)
.padding(.top, 8) .padding(.top, 8)
.padding(.bottom, 20) .padding(.bottom, 20)
@@ -64,7 +76,7 @@ struct RegistrationView: View {
Group { Group {
Text("Ao se cadastrar, você concorda com nossos [Termos de Uso](app://terms) e [Política de Privacidade](app://policy)") Text("Ao se cadastrar, você concorda com nossos [Termos de Uso](app://terms) e [Política de Privacidade](app://policy)")
.foregroundStyle(colorScheme == .dark ? Color.white : AppColors.textPrimary) .foregroundStyle(AppColors.textPrimary)
.tint(AppColors.primary) .tint(AppColors.primary)
.environment(\.openURL, OpenURLAction { url in .environment(\.openURL, OpenURLAction { url in
guard url.scheme == "app" else { return .handled } guard url.scheme == "app" else { return .handled }
@@ -98,7 +110,7 @@ struct RegistrationView: View {
HStack(spacing: 6) { HStack(spacing: 6) {
Text("Já tem uma conta?") Text("Já tem uma conta?")
.foregroundStyle(colorScheme == .dark ? Color.white.opacity(0.8) : AppColors.textPrimary) .foregroundStyle(AppColors.textPrimary)
NavigationLink("Entrar") { NavigationLink("Entrar") {
LoginEmailView(root: $root, selectedTab: $selectedTab, tokenStore: tokenStore, appState: $appState, navigate: navigate) LoginEmailView(root: $root, selectedTab: $selectedTab, tokenStore: tokenStore, appState: $appState, navigate: navigate)
} }
@@ -110,7 +122,6 @@ struct RegistrationView: View {
Spacer().frame(height: 12) Spacer().frame(height: 12)
} }
} }
.padding(.top, -40)
} }
} }

View File

@@ -177,7 +177,7 @@ extension HomeView {
extension StoreSummary { extension StoreSummary {
/// Maps the public-locator DTO onto the same model HomeView already /// Maps the public-locator DTO onto the same model HomeView already
/// renders distance/positiveReviews don't exist in that response. /// renders positiveReviews doesn't exist in that response.
init(publicItem: PublicStoreListItem) { init(publicItem: PublicStoreListItem) {
self.id = publicItem.id self.id = publicItem.id
self.name = publicItem.name ?? "Loja" self.name = publicItem.name ?? "Loja"
@@ -189,7 +189,10 @@ extension StoreSummary {
self.positiveReviews = nil self.positiveReviews = nil
self.deliveryTime = publicItem.deliveryTime self.deliveryTime = publicItem.deliveryTime
self.deliveryFee = publicItem.deliveryFee self.deliveryFee = publicItem.deliveryFee
self.distance = nil // The contract uses `0` for "no distance available"; normalize it to
// nil here so every downstream consumer (card label, max-distance
// filter) sees one representation of "unknown".
self.distance = (publicItem.distance ?? 0) > 0 ? publicItem.distance : nil
self.isOpen = publicItem.isOpen self.isOpen = publicItem.isOpen
self.statusLabel = publicItem.statusLabel self.statusLabel = publicItem.statusLabel
} }

View File

@@ -82,8 +82,13 @@ extension HomeView {
resolveStoreMediaURL(appState.profile.profilePicture) resolveStoreMediaURL(appState.profile.profilePicture)
} }
/// Empty string means "no distance to show" - the caller omits the whole
/// segment rather than labelling it. Per the API contract
/// (docs/plans/public-store-distance-consumer.md) the server never sends
/// null and uses `0` for "unavailable", so both that and a missing value
/// collapse to the same empty result.
func formatDistance(_ distance: Double?) -> String { func formatDistance(_ distance: Double?) -> String {
guard let distance else { return "Distância indisponível" } guard let distance, distance > 0 else { return "" }
if distance >= 1 { if distance >= 1 {
return String(format: "%.1f km", distance) return String(format: "%.1f km", distance)
} }

View File

@@ -109,8 +109,13 @@ extension StoreDetailView {
} }
HStack(spacing: 0) { HStack(spacing: 0) {
statItem(title: "DISTÂNCIA", value: distanceValueLabel) // No distance available: drop the tile and its divider
Divider().frame(height: 34) // instead of showing a placeholder - see
// docs/plans/public-store-distance-consumer.md.
if hasStoreDistance {
statItem(title: "DISTÂNCIA", value: distanceValueLabel)
Divider().frame(height: 34)
}
if let deliveryTime = info?.deliveryTime { if let deliveryTime = info?.deliveryTime {
statItem(title: "TEMPO MIN.", value: deliveryTime+" min.") statItem(title: "TEMPO MIN.", value: deliveryTime+" min.")
} else { } else {

View File

@@ -57,12 +57,14 @@ extension StoreDetailView {
return "R$ --" return "R$ --"
} }
/// Already formatted upstream by `HomeView.formatDistance`, which returns
/// an empty string when there's no distance to show.
var distanceValueLabel: String { var distanceValueLabel: String {
let raw = (storeDistance ?? "").trimmingCharacters(in: .whitespacesAndNewlines) (storeDistance ?? "").trimmingCharacters(in: .whitespacesAndNewlines)
if raw.isEmpty { }
return "--"
} var hasStoreDistance: Bool {
return raw distanceValueLabel.isEmpty == false
} }
var isStoreOpen: Bool { var isStoreOpen: Bool {

View File

@@ -63,5 +63,17 @@ func formatDistanceSwitchesUnitsAtOneKm() {
#expect(view.formatDistance(0.5) == "500 m") #expect(view.formatDistance(0.5) == "500 m")
#expect(view.formatDistance(1.0) == "1.0 km") #expect(view.formatDistance(1.0) == "1.0 km")
#expect(view.formatDistance(2.3) == "2.3 km") #expect(view.formatDistance(2.3) == "2.3 km")
#expect(view.formatDistance(nil) == "Distância indisponível") }
/// Contract (docs/plans/public-store-distance-consumer.md): the API never
/// sends null - `0` means "no distance available". Both that and a missing
/// value render as an empty string so the caller omits the segment
/// entirely, instead of showing "Distância indisponível" or "0 m".
@Test("formatDistance renders nothing when the distance is missing or zero")
@MainActor
func formatDistanceIsEmptyWhenUnavailable() {
let view = makeHomeView()
#expect(view.formatDistance(nil) == "")
#expect(view.formatDistance(0) == "")
#expect(view.formatDistance(-1) == "")
} }

View File

@@ -0,0 +1,49 @@
import Foundation
import Testing
@testable import PediFoods
/// Covers the public-locator distance contract documented in
/// docs/plans/public-store-distance-consumer.md: the server sends
/// `distance` as a number (never null) where `0` means "unavailable", and
/// the app collapses that to `nil` so a single representation of "unknown"
/// reaches the card label and the max-distance filter.
private func decode<T: Decodable>(_ type: T.Type, _ json: String) throws -> T {
try JSONDecoder().decode(T.self, from: Data(json.utf8))
}
@Test("PublicStoreListItem decodes the distance field")
func publicStoreListItemDecodesDistance() throws {
let item = try decode(PublicStoreListItem.self, #"{"id":"1","distance":3.4}"#)
#expect(item.distance == 3.4)
}
/// The field ships on the backend after the app - a response without it
/// must still decode rather than throwing.
@Test("PublicStoreListItem still decodes a response with no distance field")
func publicStoreListItemDecodesWithoutDistance() throws {
let item = try decode(PublicStoreListItem.self, #"{"id":"1"}"#)
#expect(item.distance == nil)
}
@Test("PublicStoreDetail decodes the distance field")
func publicStoreDetailDecodesDistance() throws {
let detail = try decode(PublicStoreDetail.self, #"{"id":"1","distance":1.2}"#)
#expect(detail.distance == 1.2)
let without = try decode(PublicStoreDetail.self, #"{"id":"1"}"#)
#expect(without.distance == nil)
}
@Test("StoreSummary carries a real public distance through")
func storeSummaryKeepsPublicDistance() throws {
let item = try decode(PublicStoreListItem.self, #"{"id":"1","name":"A","distance":2.5}"#)
#expect(StoreSummary(publicItem: item).distance == 2.5)
}
@Test("StoreSummary maps the unavailable sentinel and a missing distance to nil")
func storeSummaryNormalizesUnavailableDistance() throws {
let zero = try decode(PublicStoreListItem.self, #"{"id":"1","name":"A","distance":0}"#)
#expect(StoreSummary(publicItem: zero).distance == nil)
let absent = try decode(PublicStoreListItem.self, #"{"id":"1","name":"A"}"#)
#expect(StoreSummary(publicItem: absent).distance == nil)
}

View File

@@ -1,10 +1,7 @@
import XCTest import XCTest
/// Covers the Profile tab's logged-out state (`ProfileLoggedOutView`) - /// Covers the Profile tab's logged-out state (`ProfileLoggedOutView`) and
/// confirmed reachable. Actually logging in is blocked by a separate /// the auth intro screen (`LoginView`) it opens.
/// navigation issue in LoginView's entry-reveal animation, documented in
/// decisions/2026-08-06-ui-test-account-and-app-attest-bypass.md - not
/// covered here.
final class ProfileLoggedOutFlowTests: XCTestCase { final class ProfileLoggedOutFlowTests: XCTestCase {
override func setUpWithError() throws { override func setUpWithError() throws {
continueAfterFailure = false continueAfterFailure = false
@@ -20,18 +17,52 @@ final class ProfileLoggedOutFlowTests: XCTestCase {
XCTAssertTrue(app.buttons["Entrar ou Cadastrar"].isHittable) XCTAssertTrue(app.buttons["Entrar ou Cadastrar"].isHittable)
} }
func testTappingLoginButtonDoesNotCrash() throws { /// The auth intro screen must offer both entry points plus the legal
/// links, and the reason App Review rejected the build in 2026-08
/// a working way back out (its `LCENavigationView` back button, which
/// sets `root = .main`).
func testAuthIntroScreenShowsChoicesAndCanGoBack() throws {
let app = XCUIApplication() let app = XCUIApplication()
app.launch() app.launch()
XCTAssertTrue(app.reachLoggedOutProfile()) XCTAssertTrue(app.reachLoggedOutProfile())
app.buttons["Entrar ou Cadastrar"].tap() app.buttons["Entrar ou Cadastrar"].tap()
sleep(2)
// Known limitation (see decisions doc): the login screen's entry XCTAssertTrue(app.buttons["Criar conta"].waitForExistence(timeout: 5), "Auth intro missing 'Criar conta'")
// animation doesn't reliably reveal when navigating here from an XCTAssertTrue(app.buttons["Entrar"].exists, "Auth intro missing 'Entrar'")
// already-running guest session, so this only asserts the app is XCTAssertTrue(app.buttons["Termos de Uso"].exists, "Auth intro missing 'Termos de Uso' link")
// still alive and responsive, not that login itself completes. XCTAssertTrue(app.buttons["Política de Privacidade"].exists, "Auth intro missing 'Política de Privacidade' link")
XCTAssertEqual(app.state, .runningForeground)
let backButton = app.buttons.matching(NSPredicate(format: "label == %@", "Back")).firstMatch
XCTAssertTrue(backButton.exists, "Auth intro has no back button — user is trapped")
backButton.tap()
// Back out of `.auth` lands on the main tab bar again.
XCTAssertTrue(app.buttons["Home"].waitForExistence(timeout: 5), "Back button did not return to the main flow")
}
/// The pushed auth screens (Registration, LoginEmail) must carry the
/// app's standard `AppBackButtonIcon` via `LCENavigationView`, not the
/// oversized iOS 26 system glass back button.
func testPushedAuthScreensUseAppBackButton() throws {
let app = XCUIApplication()
app.launch()
XCTAssertTrue(app.reachLoggedOutProfile())
app.buttons["Entrar ou Cadastrar"].tap()
XCTAssertTrue(app.buttons["Criar conta"].waitForExistence(timeout: 5))
app.buttons["Criar conta"].tap()
XCTAssertTrue(app.staticTexts["Crie sua conta"].waitForExistence(timeout: 5))
let regBack = app.buttons.matching(NSPredicate(format: "label == %@", "Back")).firstMatch
XCTAssertTrue(regBack.exists, "Registration screen has no LCENavigationView back button")
regBack.tap()
XCTAssertTrue(app.buttons["Entrar"].waitForExistence(timeout: 5))
app.buttons["Entrar"].tap()
XCTAssertTrue(app.staticTexts["Boas-vindas!"].waitForExistence(timeout: 5))
let loginBack = app.buttons.matching(NSPredicate(format: "label == %@", "Back")).firstMatch
XCTAssertTrue(loginBack.exists, "Login e-mail screen has no LCENavigationView back button")
loginBack.tap()
XCTAssertTrue(app.buttons["Criar conta"].waitForExistence(timeout: 5), "Back did not return to the auth intro")
} }
} }

View File

@@ -147,7 +147,7 @@ extension XCUIApplication {
buttons["Entrar ou Cadastrar"].tap() buttons["Entrar ou Cadastrar"].tap()
let entrarButton = buttons["ENTRAR"] let entrarButton = buttons["Entrar"]
guard entrarButton.waitForExistence(timeout: 5) else { return false } guard entrarButton.waitForExistence(timeout: 5) else { return false }
entrarButton.tap() entrarButton.tap()

View File

@@ -52,17 +52,19 @@ targets:
INFOPLIST_KEY_UIStatusBarStyle: UIStatusBarStyleDefault INFOPLIST_KEY_UIStatusBarStyle: UIStatusBarStyleDefault
configs: configs:
Debug: Debug:
CODE_SIGN_IDENTITY: "Apple Development"
"CODE_SIGN_IDENTITY[sdk=iphoneos*]": "iPhone Developer"
DEVELOPMENT_TEAM: "" DEVELOPMENT_TEAM: ""
"DEVELOPMENT_TEAM[sdk=iphoneos*]": K4E5BZMM4V "DEVELOPMENT_TEAM[sdk=iphoneos*]": K4E5BZMM4V
PROVISIONING_PROFILE_SPECIFIER: "" PROVISIONING_PROFILE_SPECIFIER: ""
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]": "PediFoods Dev Prov Prof" "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]": "LC Prov PediFoods Dev Profile"
Release: Release:
DEVELOPMENT_TEAM: K4E5BZMM4V CODE_SIGN_IDENTITY: "Apple Development"
"DEVELOPMENT_TEAM[sdk=iphoneos*]": K4E5BZMM4V
CODE_SIGN_IDENTITY: "Apple Distribution"
"CODE_SIGN_IDENTITY[sdk=iphoneos*]": "iPhone Distribution" "CODE_SIGN_IDENTITY[sdk=iphoneos*]": "iPhone Distribution"
PROVISIONING_PROFILE_SPECIFIER: "com.br.pedifoods.app AppStore" DEVELOPMENT_TEAM: ""
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]": "PediFoods Dist XC Prov Prof" "DEVELOPMENT_TEAM[sdk=iphoneos*]": K4E5BZMM4V
PROVISIONING_PROFILE_SPECIFIER: ""
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]": "LC Prov PediFoods Dist Profile"
attributes: attributes:
SystemCapabilities: SystemCapabilities:
com.apple.Push: com.apple.Push:
@@ -80,12 +82,25 @@ targets:
MARKETING_VERSION: 0.0.1 MARKETING_VERSION: 0.0.1
CURRENT_PROJECT_VERSION: 1 CURRENT_PROJECT_VERSION: 1
INFOPLIST_FILE: NotificationServiceExtension/Info.plist INFOPLIST_FILE: NotificationServiceExtension/Info.plist
CODE_SIGN_STYLE: Automatic CODE_SIGN_STYLE: Manual
DEVELOPMENT_TEAM: K4E5BZMM4V
SWIFT_VERSION: "6" SWIFT_VERSION: "6"
TARGETED_DEVICE_FAMILY: "1" TARGETED_DEVICE_FAMILY: "1"
SKIP_INSTALL: YES SKIP_INSTALL: YES
CLANG_ENABLE_OBJC_WEAK: NO CLANG_ENABLE_OBJC_WEAK: NO
configs:
Debug:
"CODE_SIGN_IDENTITY[sdk=iphoneos*]": "iPhone Developer"
DEVELOPMENT_TEAM: ""
"DEVELOPMENT_TEAM[sdk=iphoneos*]": K4E5BZMM4V
PROVISIONING_PROFILE_SPECIFIER: ""
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]": "LC Prov PediFoods Dev Push Profile"
Release:
CODE_SIGN_IDENTITY: "Apple Development"
"CODE_SIGN_IDENTITY[sdk=iphoneos*]": "iPhone Distribution"
DEVELOPMENT_TEAM: ""
"DEVELOPMENT_TEAM[sdk=iphoneos*]": K4E5BZMM4V
PROVISIONING_PROFILE_SPECIFIER: ""
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]": "LC Prov PediFoods Dist Push Profile"
PediFoodsTests: PediFoodsTests:
type: bundle.unit-test type: bundle.unit-test
@@ -98,6 +113,21 @@ targets:
base: base:
SWIFT_VERSION: "6" SWIFT_VERSION: "6"
GENERATE_INFOPLIST_FILE: YES GENERATE_INFOPLIST_FILE: YES
CODE_SIGN_STYLE: Manual
configs:
Debug:
"CODE_SIGN_IDENTITY[sdk=iphoneos*]": "iPhone Developer"
"CODE_SIGN_IDENTITY[sdk=macosx*]": "Apple Development"
DEVELOPMENT_TEAM: ""
"DEVELOPMENT_TEAM[sdk=iphoneos*]": K4E5BZMM4V
"DEVELOPMENT_TEAM[sdk=macosx*]": K4E5BZMM4V
PROVISIONING_PROFILE_SPECIFIER: ""
Release:
"CODE_SIGN_IDENTITY[sdk=iphoneos*]": "Apple Distribution"
"CODE_SIGN_IDENTITY[sdk=macosx*]": "-"
DEVELOPMENT_TEAM: ""
"DEVELOPMENT_TEAM[sdk=iphoneos*]": K4E5BZMM4V
PROVISIONING_PROFILE_SPECIFIER: ""
PediFoodsUITests: PediFoodsUITests:
type: bundle.ui-testing type: bundle.ui-testing
@@ -110,6 +140,7 @@ targets:
base: base:
SWIFT_VERSION: "6" SWIFT_VERSION: "6"
GENERATE_INFOPLIST_FILE: YES GENERATE_INFOPLIST_FILE: YES
DEVELOPMENT_TEAM: K4E5BZMM4V
schemes: schemes:
PediFoods: PediFoods: