diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b92e2a18..670f506b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,7 +39,6 @@ jobs: # - iOS Simulator,name=iPhone 14 Pro # example: # - Examples - # - ProductSample # runs-on: macos-13 # name: Build Example # steps: diff --git a/Examples/Examples.xcodeproj/project.pbxproj b/Examples/Examples.xcodeproj/project.pbxproj index 887c2636..04fc7429 100644 --- a/Examples/Examples.xcodeproj/project.pbxproj +++ b/Examples/Examples.xcodeproj/project.pbxproj @@ -7,14 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - 79018ACF2AE1D6CF006EA669 /* DeleteProductUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79018ACE2AE1D6CF006EA669 /* DeleteProductUseCase.swift */; }; - 79018AD12AE1D775006EA669 /* GetProductsUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79018AD02AE1D775006EA669 /* GetProductsUseCase.swift */; }; - 79018ADB2AE1DAF2006EA669 /* AuthView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79018ADA2AE1DAF2006EA669 /* AuthView.swift */; }; - 79018ADE2AE1DB03006EA669 /* AuthViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79018ADD2AE1DB03006EA669 /* AuthViewModel.swift */; }; - 79018AE02AE309EE006EA669 /* SignInUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79018ADF2AE309EE006EA669 /* SignInUseCase.swift */; }; - 79018AE42AE3F185006EA669 /* Routes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79018AE32AE3F185006EA669 /* Routes.swift */; }; - 79018AE62AE3F1E4006EA669 /* Auth.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79018AE52AE3F1E4006EA669 /* Auth.swift */; }; - 79018AE82AE3F1F3006EA669 /* SignUpUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79018AE72AE3F1F3006EA669 /* SignUpUseCase.swift */; }; 790308E92AEE7B4D003C4A98 /* RealtimeSampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 790308E82AEE7B4D003C4A98 /* RealtimeSampleApp.swift */; }; 790308EB2AEE7B4D003C4A98 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 790308EA2AEE7B4D003C4A98 /* ContentView.swift */; }; 790308ED2AEE7B4E003C4A98 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 790308EC2AEE7B4E003C4A98 /* Assets.xcassets */; }; @@ -37,40 +29,9 @@ 796298992AEBBA77000AA957 /* MFAFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 796298982AEBBA77000AA957 /* MFAFlow.swift */; }; 7962989D2AEBC6F9000AA957 /* SVGView in Frameworks */ = {isa = PBXBuildFile; productRef = 7962989C2AEBC6F9000AA957 /* SVGView */; }; 79719ECE2ADF26C400737804 /* Supabase in Frameworks */ = {isa = PBXBuildFile; productRef = 79719ECD2ADF26C400737804 /* Supabase */; }; - 79C591DC2AE0880F0088A9C8 /* ProductSampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79C591DB2AE0880F0088A9C8 /* ProductSampleApp.swift */; }; - 79C591DE2AE0880F0088A9C8 /* AppView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79C591DD2AE0880F0088A9C8 /* AppView.swift */; }; - 79C591E02AE088110088A9C8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 79C591DF2AE088110088A9C8 /* Assets.xcassets */; }; - 79C591E32AE088110088A9C8 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 79C591E22AE088110088A9C8 /* Preview Assets.xcassets */; }; - 79C591E82AE088250088A9C8 /* Supabase in Frameworks */ = {isa = PBXBuildFile; productRef = 79C591E72AE088250088A9C8 /* Supabase */; }; - 79C591EA2AE089230088A9C8 /* Product.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79C591E92AE089230088A9C8 /* Product.swift */; }; - 79C591EC2AE089510088A9C8 /* ProductRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79C591EB2AE089510088A9C8 /* ProductRepository.swift */; }; - 79C591EE2AE1258B0088A9C8 /* AuthenticationRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79C591ED2AE1258B0088A9C8 /* AuthenticationRepository.swift */; }; - 79C591F02AE126120088A9C8 /* ProductListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79C591EF2AE126120088A9C8 /* ProductListViewModel.swift */; }; - 79C591F22AE127180088A9C8 /* ProductListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79C591F12AE127180088A9C8 /* ProductListView.swift */; }; - 79C591F62AE12AAC0088A9C8 /* ProductDetailsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79C591F52AE12AAC0088A9C8 /* ProductDetailsViewModel.swift */; }; - 79C591F82AE12B850088A9C8 /* ProductDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79C591F72AE12B850088A9C8 /* ProductDetailsView.swift */; }; - 79C591FB2AE12FDE0088A9C8 /* UseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79C591FA2AE12FDE0088A9C8 /* UseCase.swift */; }; - 79C591FD2AE152590088A9C8 /* Config.plist in Resources */ = {isa = PBXBuildFile; fileRef = 79C591FC2AE152590088A9C8 /* Config.plist */; }; - 79C591FF2AE1527C0088A9C8 /* Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79C591FE2AE1527C0088A9C8 /* Config.swift */; }; - 79C592012AE1561D0088A9C8 /* Dependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79C592002AE1561D0088A9C8 /* Dependencies.swift */; }; - 79C592042AE159130088A9C8 /* CreateProductUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79C592032AE159130088A9C8 /* CreateProductUseCase.swift */; }; - 79C592062AE159250088A9C8 /* UpdateProductUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79C592052AE159250088A9C8 /* UpdateProductUseCase.swift */; }; - 79C592082AE159390088A9C8 /* GetProductUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79C592072AE159390088A9C8 /* GetProductUseCase.swift */; }; - 79C5920A2AE159E20088A9C8 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79C592092AE159E20088A9C8 /* Logger.swift */; }; - 79C5920C2AE1B8820088A9C8 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79C5920B2AE1B8820088A9C8 /* Result.swift */; }; - 79C592112AE1CD040088A9C8 /* ProductImageStorageRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79C592102AE1CD040088A9C8 /* ProductImageStorageRepository.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - 79018ACE2AE1D6CF006EA669 /* DeleteProductUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteProductUseCase.swift; sourceTree = ""; }; - 79018AD02AE1D775006EA669 /* GetProductsUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetProductsUseCase.swift; sourceTree = ""; }; - 79018ADA2AE1DAF2006EA669 /* AuthView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthView.swift; sourceTree = ""; }; - 79018ADD2AE1DB03006EA669 /* AuthViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthViewModel.swift; sourceTree = ""; }; - 79018ADF2AE309EE006EA669 /* SignInUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInUseCase.swift; sourceTree = ""; }; - 79018AE12AE3D0E3006EA669 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 79018AE32AE3F185006EA669 /* Routes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Routes.swift; sourceTree = ""; }; - 79018AE52AE3F1E4006EA669 /* Auth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Auth.swift; sourceTree = ""; }; - 79018AE72AE3F1F3006EA669 /* SignUpUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpUseCase.swift; sourceTree = ""; }; 790308E62AEE7B4D003C4A98 /* RealtimeSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RealtimeSample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 790308E82AEE7B4D003C4A98 /* RealtimeSampleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RealtimeSampleApp.swift; sourceTree = ""; }; 790308EA2AEE7B4D003C4A98 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; @@ -93,28 +54,6 @@ 795640692955AFBD0088A06F /* ErrorText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorText.swift; sourceTree = ""; }; 796298982AEBBA77000AA957 /* MFAFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MFAFlow.swift; sourceTree = ""; }; 7962989A2AEBBD9F000AA957 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; - 79C591D92AE0880F0088A9C8 /* ProductSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ProductSample.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 79C591DB2AE0880F0088A9C8 /* ProductSampleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductSampleApp.swift; sourceTree = ""; }; - 79C591DD2AE0880F0088A9C8 /* AppView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppView.swift; sourceTree = ""; }; - 79C591DF2AE088110088A9C8 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 79C591E22AE088110088A9C8 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; - 79C591E92AE089230088A9C8 /* Product.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Product.swift; sourceTree = ""; }; - 79C591EB2AE089510088A9C8 /* ProductRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductRepository.swift; sourceTree = ""; }; - 79C591ED2AE1258B0088A9C8 /* AuthenticationRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationRepository.swift; sourceTree = ""; }; - 79C591EF2AE126120088A9C8 /* ProductListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductListViewModel.swift; sourceTree = ""; }; - 79C591F12AE127180088A9C8 /* ProductListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductListView.swift; sourceTree = ""; }; - 79C591F52AE12AAC0088A9C8 /* ProductDetailsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductDetailsViewModel.swift; sourceTree = ""; }; - 79C591F72AE12B850088A9C8 /* ProductDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductDetailsView.swift; sourceTree = ""; }; - 79C591FA2AE12FDE0088A9C8 /* UseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UseCase.swift; sourceTree = ""; }; - 79C591FC2AE152590088A9C8 /* Config.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Config.plist; sourceTree = ""; }; - 79C591FE2AE1527C0088A9C8 /* Config.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Config.swift; sourceTree = ""; }; - 79C592002AE1561D0088A9C8 /* Dependencies.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dependencies.swift; sourceTree = ""; }; - 79C592032AE159130088A9C8 /* CreateProductUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateProductUseCase.swift; sourceTree = ""; }; - 79C592052AE159250088A9C8 /* UpdateProductUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateProductUseCase.swift; sourceTree = ""; }; - 79C592072AE159390088A9C8 /* GetProductUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetProductUseCase.swift; sourceTree = ""; }; - 79C592092AE159E20088A9C8 /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; }; - 79C5920B2AE1B8820088A9C8 /* Result.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Result.swift; sourceTree = ""; }; - 79C592102AE1CD040088A9C8 /* ProductImageStorageRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductImageStorageRepository.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -137,102 +76,9 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 79C591D62AE0880F0088A9C8 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 79C591E82AE088250088A9C8 /* Supabase in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 79018AD22AE1D7DE006EA669 /* Models */ = { - isa = PBXGroup; - children = ( - 79C591E92AE089230088A9C8 /* Product.swift */, - 79018AE52AE3F1E4006EA669 /* Auth.swift */, - ); - path = Models; - sourceTree = ""; - }; - 79018AD32AE1DA1B006EA669 /* ProductList */ = { - isa = PBXGroup; - children = ( - 79C591EF2AE126120088A9C8 /* ProductListViewModel.swift */, - 79C591F12AE127180088A9C8 /* ProductListView.swift */, - ); - path = ProductList; - sourceTree = ""; - }; - 79018AD42AE1DA25006EA669 /* ProductDetails */ = { - isa = PBXGroup; - children = ( - 79C591F52AE12AAC0088A9C8 /* ProductDetailsViewModel.swift */, - 79C591F72AE12B850088A9C8 /* ProductDetailsView.swift */, - ); - path = ProductDetails; - sourceTree = ""; - }; - 79018AD52AE1DA31006EA669 /* Features */ = { - isa = PBXGroup; - children = ( - 79018ADC2AE1DAF6006EA669 /* Auth */, - 79018AD42AE1DA25006EA669 /* ProductDetails */, - 79018AD32AE1DA1B006EA669 /* ProductList */, - ); - path = Features; - sourceTree = ""; - }; - 79018AD62AE1DA4C006EA669 /* Application */ = { - isa = PBXGroup; - children = ( - 79C591DD2AE0880F0088A9C8 /* AppView.swift */, - 79C592002AE1561D0088A9C8 /* Dependencies.swift */, - 79C591DB2AE0880F0088A9C8 /* ProductSampleApp.swift */, - ); - path = Application; - sourceTree = ""; - }; - 79018AD72AE1DA64006EA669 /* Helpers */ = { - isa = PBXGroup; - children = ( - 79C591FE2AE1527C0088A9C8 /* Config.swift */, - 79C592092AE159E20088A9C8 /* Logger.swift */, - 79C5920B2AE1B8820088A9C8 /* Result.swift */, - ); - path = Helpers; - sourceTree = ""; - }; - 79018AD92AE1DA75006EA669 /* SupportFiles */ = { - isa = PBXGroup; - children = ( - 79018AE12AE3D0E3006EA669 /* Info.plist */, - 79C591DF2AE088110088A9C8 /* Assets.xcassets */, - 79C591E12AE088110088A9C8 /* Preview Content */, - 79C591FC2AE152590088A9C8 /* Config.plist */, - ); - path = SupportFiles; - sourceTree = ""; - }; - 79018ADC2AE1DAF6006EA669 /* Auth */ = { - isa = PBXGroup; - children = ( - 79018ADA2AE1DAF2006EA669 /* AuthView.swift */, - 79018ADD2AE1DB03006EA669 /* AuthViewModel.swift */, - ); - path = Auth; - sourceTree = ""; - }; - 79018AE22AE3F17D006EA669 /* Routes */ = { - isa = PBXGroup; - children = ( - 79018AE32AE3F185006EA669 /* Routes.swift */, - ); - path = Routes; - sourceTree = ""; - }; 790308E72AEE7B4D003C4A98 /* RealtimeSample */ = { isa = PBXGroup; children = ( @@ -257,7 +103,6 @@ isa = PBXGroup; children = ( 793895C82954ABFF0044F2B8 /* Examples */, - 79C591DA2AE0880F0088A9C8 /* ProductSample */, 790308E72AEE7B4D003C4A98 /* RealtimeSample */, 793895C72954ABFF0044F2B8 /* Products */, 7956405A2954AC3E0088A06F /* Frameworks */, @@ -268,7 +113,6 @@ isa = PBXGroup; children = ( 793895C62954ABFF0044F2B8 /* Examples.app */, - 79C591D92AE0880F0088A9C8 /* ProductSample.app */, 790308E62AEE7B4D003C4A98 /* RealtimeSample.app */, ); name = Products; @@ -311,62 +155,6 @@ name = Frameworks; sourceTree = ""; }; - 79C591DA2AE0880F0088A9C8 /* ProductSample */ = { - isa = PBXGroup; - children = ( - 79018AD62AE1DA4C006EA669 /* Application */, - 79C5920F2AE1CCE80088A9C8 /* Data */, - 79C591F92AE12FD10088A9C8 /* Domain */, - 79018AD52AE1DA31006EA669 /* Features */, - 79018AD72AE1DA64006EA669 /* Helpers */, - 79018AE22AE3F17D006EA669 /* Routes */, - 79018AD92AE1DA75006EA669 /* SupportFiles */, - ); - path = ProductSample; - sourceTree = ""; - }; - 79C591E12AE088110088A9C8 /* Preview Content */ = { - isa = PBXGroup; - children = ( - 79C591E22AE088110088A9C8 /* Preview Assets.xcassets */, - ); - path = "Preview Content"; - sourceTree = ""; - }; - 79C591F92AE12FD10088A9C8 /* Domain */ = { - isa = PBXGroup; - children = ( - 79018AD22AE1D7DE006EA669 /* Models */, - 79C592022AE159070088A9C8 /* UseCases */, - ); - path = Domain; - sourceTree = ""; - }; - 79C592022AE159070088A9C8 /* UseCases */ = { - isa = PBXGroup; - children = ( - 79C591FA2AE12FDE0088A9C8 /* UseCase.swift */, - 79C592032AE159130088A9C8 /* CreateProductUseCase.swift */, - 79C592052AE159250088A9C8 /* UpdateProductUseCase.swift */, - 79C592072AE159390088A9C8 /* GetProductUseCase.swift */, - 79018ACE2AE1D6CF006EA669 /* DeleteProductUseCase.swift */, - 79018AD02AE1D775006EA669 /* GetProductsUseCase.swift */, - 79018ADF2AE309EE006EA669 /* SignInUseCase.swift */, - 79018AE72AE3F1F3006EA669 /* SignUpUseCase.swift */, - ); - path = UseCases; - sourceTree = ""; - }; - 79C5920F2AE1CCE80088A9C8 /* Data */ = { - isa = PBXGroup; - children = ( - 79C591EB2AE089510088A9C8 /* ProductRepository.swift */, - 79C591ED2AE1258B0088A9C8 /* AuthenticationRepository.swift */, - 79C592102AE1CD040088A9C8 /* ProductImageStorageRepository.swift */, - ); - path = Data; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -413,26 +201,6 @@ productReference = 793895C62954ABFF0044F2B8 /* Examples.app */; productType = "com.apple.product-type.application"; }; - 79C591D82AE0880F0088A9C8 /* ProductSample */ = { - isa = PBXNativeTarget; - buildConfigurationList = 79C591E62AE088110088A9C8 /* Build configuration list for PBXNativeTarget "ProductSample" */; - buildPhases = ( - 79C591D52AE0880F0088A9C8 /* Sources */, - 79C591D62AE0880F0088A9C8 /* Frameworks */, - 79C591D72AE0880F0088A9C8 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = ProductSample; - packageProductDependencies = ( - 79C591E72AE088250088A9C8 /* Supabase */, - ); - productName = ProductSample; - productReference = 79C591D92AE0880F0088A9C8 /* ProductSample.app */; - productType = "com.apple.product-type.application"; - }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -449,9 +217,6 @@ 793895C52954ABFF0044F2B8 = { CreatedOnToolsVersion = 14.1; }; - 79C591D82AE0880F0088A9C8 = { - CreatedOnToolsVersion = 15.0.1; - }; }; }; buildConfigurationList = 793895C12954ABFF0044F2B8 /* Build configuration list for PBXProject "Examples" */; @@ -473,7 +238,6 @@ projectRoot = ""; targets = ( 793895C52954ABFF0044F2B8 /* Examples */, - 79C591D82AE0880F0088A9C8 /* ProductSample */, 790308E52AEE7B4D003C4A98 /* RealtimeSample */, ); }; @@ -498,16 +262,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 79C591D72AE0880F0088A9C8 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 79C591E32AE088110088A9C8 /* Preview Assets.xcassets in Resources */, - 79C591FD2AE152590088A9C8 /* Config.plist in Resources */, - 79C591E02AE088110088A9C8 /* Assets.xcassets in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -538,39 +292,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 79C591D52AE0880F0088A9C8 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 79C591DE2AE0880F0088A9C8 /* AppView.swift in Sources */, - 79018AE42AE3F185006EA669 /* Routes.swift in Sources */, - 79018AE62AE3F1E4006EA669 /* Auth.swift in Sources */, - 79C591DC2AE0880F0088A9C8 /* ProductSampleApp.swift in Sources */, - 79C591F62AE12AAC0088A9C8 /* ProductDetailsViewModel.swift in Sources */, - 79018AD12AE1D775006EA669 /* GetProductsUseCase.swift in Sources */, - 79C592042AE159130088A9C8 /* CreateProductUseCase.swift in Sources */, - 79C592012AE1561D0088A9C8 /* Dependencies.swift in Sources */, - 79C591F22AE127180088A9C8 /* ProductListView.swift in Sources */, - 79C592112AE1CD040088A9C8 /* ProductImageStorageRepository.swift in Sources */, - 79C591F02AE126120088A9C8 /* ProductListViewModel.swift in Sources */, - 79018AE02AE309EE006EA669 /* SignInUseCase.swift in Sources */, - 79C5920C2AE1B8820088A9C8 /* Result.swift in Sources */, - 79C591FF2AE1527C0088A9C8 /* Config.swift in Sources */, - 79018ACF2AE1D6CF006EA669 /* DeleteProductUseCase.swift in Sources */, - 79C5920A2AE159E20088A9C8 /* Logger.swift in Sources */, - 79C591FB2AE12FDE0088A9C8 /* UseCase.swift in Sources */, - 79018AE82AE3F1F3006EA669 /* SignUpUseCase.swift in Sources */, - 79018ADB2AE1DAF2006EA669 /* AuthView.swift in Sources */, - 79C592062AE159250088A9C8 /* UpdateProductUseCase.swift in Sources */, - 79C591EA2AE089230088A9C8 /* Product.swift in Sources */, - 79C592082AE159390088A9C8 /* GetProductUseCase.swift in Sources */, - 79018ADE2AE1DB03006EA669 /* AuthViewModel.swift in Sources */, - 79C591EE2AE1258B0088A9C8 /* AuthenticationRepository.swift in Sources */, - 79C591EC2AE089510088A9C8 /* ProductRepository.swift in Sources */, - 79C591F82AE12B850088A9C8 /* ProductDetailsView.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ @@ -816,52 +537,6 @@ }; name = Release; }; - 79C591E42AE088110088A9C8 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_ASSET_PATHS = "\"ProductSample/SupportFiles/Preview Content\""; - DEVELOPMENT_TEAM = ELTTE7K8TT; - ENABLE_PREVIEWS = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = ProductSample/SupportFIles/Info.plist; - INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; - INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchScreen_Generation = YES; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - PRODUCT_BUNDLE_IDENTIFIER = dev.grds.ProductSample; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; - SWIFT_EMIT_LOC_STRINGS = YES; - }; - name = Debug; - }; - 79C591E52AE088110088A9C8 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_ASSET_PATHS = "\"ProductSample/SupportFiles/Preview Content\""; - DEVELOPMENT_TEAM = ELTTE7K8TT; - ENABLE_PREVIEWS = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = ProductSample/SupportFIles/Info.plist; - INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; - INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchScreen_Generation = YES; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - PRODUCT_BUNDLE_IDENTIFIER = dev.grds.ProductSample; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_EMIT_LOC_STRINGS = YES; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -892,15 +567,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 79C591E62AE088110088A9C8 /* Build configuration list for PBXNativeTarget "ProductSample" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 79C591E42AE088110088A9C8 /* Debug */, - 79C591E52AE088110088A9C8 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ @@ -954,10 +620,6 @@ isa = XCSwiftPackageProductDependency; productName = Supabase; }; - 79C591E72AE088250088A9C8 /* Supabase */ = { - isa = XCSwiftPackageProductDependency; - productName = Supabase; - }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 793895BE2954ABFF0044F2B8 /* Project object */; diff --git a/Examples/ProductSample/Application/AppView.swift b/Examples/ProductSample/Application/AppView.swift deleted file mode 100644 index 1f7addc4..00000000 --- a/Examples/ProductSample/Application/AppView.swift +++ /dev/null @@ -1,112 +0,0 @@ -// -// AppView.swift -// ProductSample -// -// Created by Guilherme Souza on 18/10/23. -// - -import OSLog -import SwiftUI - -@MainActor -final class AppViewModel: ObservableObject { - private let logger = Logger.make(category: "AppViewModel") - private let authenticationRepository: AuthenticationRepository - - enum AuthState { - case authenticated(ProductListViewModel) - case notAuthenticated(AuthViewModel) - } - - @Published var addProductRoute: AddProductRoute? - @Published var authState: AuthState? - - private var authStateListenerTask: Task? - - init(authenticationRepository: AuthenticationRepository = Dependencies.authenticationRepository) { - self.authenticationRepository = authenticationRepository - - authStateListenerTask = Task { - for await state in await authenticationRepository.authStateListener() { - logger.debug("auth state changed: \(String(describing: state))") - - if Task.isCancelled { - logger.debug("auth state task cancelled, returning.") - return - } - - self.authState = - switch state - { - case .signedIn: .authenticated(.init()) - case .signedOut: .notAuthenticated(.init()) - } - } - } - } - - deinit { - authStateListenerTask?.cancel() - } - - func productDetailViewModel(with productId: String?) -> ProductDetailsViewModel { - ProductDetailsViewModel(productId: productId) { [weak self] _ in - Task { - if case let .authenticated(model) = self?.authState { - await model.loadProducts() - } - } - } - } - - func signOutButtonTapped() async { - await authenticationRepository.signOut() - } -} - -struct AppView: View { - @StateObject var model = AppViewModel() - - var body: some View { - switch model.authState { - case let .authenticated(model): - authenticatedView(model: model) - case let .notAuthenticated(model): - notAuthenticatedView(model: model) - case .none: - ProgressView() - } - } - - func authenticatedView(model: ProductListViewModel) -> some View { - NavigationStack { - ProductListView(model: model) - .toolbar { - ToolbarItem(placement: .cancellationAction) { - Button("Sign out") { - Task { await self.model.signOutButtonTapped() } - } - } - ToolbarItem(placement: .primaryAction) { - Button { - self.model.addProductRoute = .init() - } label: { - Label("Add", systemImage: "plus") - } - } - } - .navigationDestination(for: ProductDetailRoute.self) { route in - ProductDetailsView(model: self.model.productDetailViewModel(with: route.productId)) - } - } - .sheet(item: self.$model.addProductRoute) { _ in - NavigationStack { - ProductDetailsView(model: self.model.productDetailViewModel(with: nil)) - } - } - } - - func notAuthenticatedView(model: AuthViewModel) -> some View { - AuthView(model: model) - } -} diff --git a/Examples/ProductSample/Application/Dependencies.swift b/Examples/ProductSample/Application/Dependencies.swift deleted file mode 100644 index 897bb7a3..00000000 --- a/Examples/ProductSample/Application/Dependencies.swift +++ /dev/null @@ -1,58 +0,0 @@ -// -// Dependencies.swift -// ProductSample -// -// Created by Guilherme Souza on 19/10/23. -// - -import Foundation -import Supabase - -enum Dependencies { - static let supabase = SupabaseClient( - supabaseURL: URL(string: Config.SUPABASE_URL)!, - supabaseKey: Config.SUPABASE_ANON_KEY - ) - - // MARK: Repositories - - static let productRepository: ProductRepository = ProductRepositoryImpl(supabase: supabase) - static let productImageStorageRepository: ProductImageStorageRepository = - ProductImageStorageRepositoryImpl(storage: supabase.storage) - static let authenticationRepository: AuthenticationRepository = AuthenticationRepositoryImpl( - client: supabase.auth - ) - - // MARK: Use Cases - - static let updateProductUseCase: any UpdateProductUseCase = UpdateProductUseCaseImpl( - productRepository: productRepository, - productImageStorageRepository: productImageStorageRepository - ) - - static let createProductUseCase: any CreateProductUseCase = CreateProductUseCaseImpl( - productRepository: productRepository, - productImageStorageRepository: productImageStorageRepository, - authenticationRepository: authenticationRepository - ) - - static let getProductUseCase: any GetProductUseCase = GetProductUseCaseImpl( - productRepository: productRepository - ) - - static let deleteProductUseCase: any DeleteProductUseCase = DeleteProductUseCaseImpl( - repository: productRepository - ) - - static let getProductsUseCase: any GetProductsUseCase = GetProductsUseCaseImpl( - repository: productRepository - ) - - static let signInUseCase: any SignInUseCase = SignInUseCaseImpl( - repository: authenticationRepository - ) - - static let signUpUseCase: any SignUpUseCase = SignUpUseCaseImpl( - repository: authenticationRepository - ) -} diff --git a/Examples/ProductSample/Application/ProductSampleApp.swift b/Examples/ProductSample/Application/ProductSampleApp.swift deleted file mode 100644 index 3b45dab7..00000000 --- a/Examples/ProductSample/Application/ProductSampleApp.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// ProductSampleApp.swift -// ProductSample -// -// Created by Guilherme Souza on 18/10/23. -// - -import Supabase -import SwiftUI - -@main -struct ProductSampleApp: App { - var body: some Scene { - WindowGroup { - AppView() - } - } -} diff --git a/Examples/ProductSample/Data/AuthenticationRepository.swift b/Examples/ProductSample/Data/AuthenticationRepository.swift deleted file mode 100644 index 5dcbd42a..00000000 --- a/Examples/ProductSample/Data/AuthenticationRepository.swift +++ /dev/null @@ -1,84 +0,0 @@ -// -// AuthenticationRepository.swift -// ProductSample -// -// Created by Guilherme Souza on 19/10/23. -// - -import Foundation -import Supabase - -protocol AuthenticationRepository: Sendable { - var currentUserID: UUID { get async throws } - - func authStateListener() async -> AsyncStream - func signIn(email: String, password: String) async throws - func signUp(email: String, password: String) async throws -> SignUpResult - func signInWithApple() async throws - func signOut() async -} - -struct AuthenticationRepositoryImpl: AuthenticationRepository { - let client: GoTrueClient - - var currentUserID: UUID { - get async throws { - try await client.session.user.id - } - } - - func authStateListener() async -> AsyncStream { - await client.onAuthStateChange().compactMap { event, session in - switch event { - case .initialSession: session != nil ? AuthenticationState.signedIn : .signedOut - case .signedIn: AuthenticationState.signedIn - case .signedOut: AuthenticationState.signedOut - case .passwordRecovery, .tokenRefreshed, .userUpdated, .userDeleted, .mfaChallengeVerified: - nil - } - } - .eraseToStream() - } - - func signIn(email: String, password: String) async throws { - try await client.signIn(email: email, password: password) - } - - func signUp(email: String, password: String) async throws -> SignUpResult { - let response = try await client.signUp( - email: email, - password: password, - redirectTo: URL(string: "dev.grds.ProductSample://") - ) - if case .session = response { - return .success - } - return .requiresConfirmation - } - - func signInWithApple() async throws { - fatalError("\(#function) unimplemented") - } - - func signOut() async { - try? await client.signOut() - } -} - -extension AsyncStream { - init(_ sequence: S) where S.Element == Element { - var iterator: S.AsyncIterator? - self.init { - if iterator == nil { - iterator = sequence.makeAsyncIterator() - } - return try? await iterator?.next() - } - } -} - -extension AsyncSequence { - func eraseToStream() -> AsyncStream { - AsyncStream(self) - } -} diff --git a/Examples/ProductSample/Data/ProductImageStorageRepository.swift b/Examples/ProductSample/Data/ProductImageStorageRepository.swift deleted file mode 100644 index 1b1bc93d..00000000 --- a/Examples/ProductSample/Data/ProductImageStorageRepository.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// ProductImageStorageRepository.swift -// ProductSample -// -// Created by Guilherme Souza on 19/10/23. -// - -import Foundation -import Storage - -protocol ProductImageStorageRepository: Sendable { - func uploadImage(_ params: ImageUploadParams) async throws -> String - func downloadImage(_ key: ImageKey) async throws -> Data -} - -struct ProductImageStorageRepositoryImpl: ProductImageStorageRepository { - let storage: SupabaseStorageClient - - func uploadImage(_ params: ImageUploadParams) async throws -> String { - let fileName = "\(params.fileName).\(params.fileExtension ?? "png")" - let contentType = params.mimeType ?? "image/png" - let imagePath = try await storage.from(id: "product-images") - .upload( - path: fileName, - file: File( - name: fileName, data: params.data, fileName: fileName, contentType: contentType - ), - fileOptions: FileOptions(contentType: contentType, upsert: true) - ) - return imagePath - } - - func downloadImage(_ key: ImageKey) async throws -> Data { - // we save product images in the format "bucket-id/image.png", but SupabaseStorage prefixes - // the path with the bucket-id already so we must provide only the file name to the download - // call, this is what lastPathComponent is doing below. - let fileName = (key.rawValue as NSString).lastPathComponent - return try await storage.from(id: "product-images").download(path: fileName) - } -} diff --git a/Examples/ProductSample/Data/ProductRepository.swift b/Examples/ProductSample/Data/ProductRepository.swift deleted file mode 100644 index 6293b0f6..00000000 --- a/Examples/ProductSample/Data/ProductRepository.swift +++ /dev/null @@ -1,79 +0,0 @@ -// -// ProductRepository.swift -// ProductSample -// -// Created by Guilherme Souza on 18/10/23. -// - -import Foundation -import Supabase - -struct InsertProductDto: Encodable { - let name: String - let price: Double - let image: String? - let ownerID: UserID - - enum CodingKeys: String, CodingKey { - case name - case price - case image - case ownerID = "owner_id" - } -} - -protocol ProductRepository: Sendable { - func createProduct(_ product: InsertProductDto) async throws - func getProducts() async throws -> [Product] - func getProduct(id: Product.ID) async throws -> Product - func deleteProduct(id: Product.ID) async throws - func updateProduct(id: String, name: String?, price: Double?, image: String?) async throws -} - -struct ProductRepositoryImpl: ProductRepository { - let supabase: SupabaseClient - - func createProduct(_ product: InsertProductDto) async throws { - try await supabase.database.from("products").insert(values: product).execute() - } - - func getProducts() async throws -> [Product] { - try await supabase.database.from("products").select().execute().value - } - - func getProduct(id: Product.ID) async throws -> Product { - try await supabase.database.from("products").select().eq(column: "id", value: id).single() - .execute().value - } - - func deleteProduct(id: Product.ID) async throws { - try await supabase.database.from("products").delete().eq(column: "id", value: id).execute() - .value - } - - func updateProduct(id: String, name: String?, price: Double?, image: String?) async throws { - var params: [String: AnyJSON] = [:] - - if let name { - params["name"] = .string(name) - } - - if let price { - params["price"] = .number(price) - } - - if let image { - params["image"] = .string(image) - } - - if params.isEmpty { - // nothing to update, just return. - return - } - - try await supabase.database.from("products") - .update(values: params) - .eq(column: "id", value: id) - .execute() - } -} diff --git a/Examples/ProductSample/Domain/Models/Auth.swift b/Examples/ProductSample/Domain/Models/Auth.swift deleted file mode 100644 index ca09def0..00000000 --- a/Examples/ProductSample/Domain/Models/Auth.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// Auth.swift -// ProductSample -// -// Created by Guilherme Souza on 21/10/23. -// - -import Foundation - -struct Credentials { - let email, password: String -} - -enum AuthenticationState { - case signedIn - case signedOut -} diff --git a/Examples/ProductSample/Domain/Models/Product.swift b/Examples/ProductSample/Domain/Models/Product.swift deleted file mode 100644 index 0cbbcd67..00000000 --- a/Examples/ProductSample/Domain/Models/Product.swift +++ /dev/null @@ -1,43 +0,0 @@ -// -// Product.swift -// ProductSample -// -// Created by Guilherme Souza on 18/10/23. -// - -import Foundation - -import struct GoTrue.User - -typealias UserID = User.ID - -struct Product: Identifiable, Decodable { - let id: String - let name: String - let price: Double - let image: ImageKey? -} - -struct ImageKey: RawRepresentable, Decodable { - var rawValue: String -} - -struct CreateProductParams { - let name: String - let price: Double - let image: ImageUploadParams? -} - -struct ImageUploadParams { - let fileName: String - let fileExtension: String? - let mimeType: String? - let data: Data -} - -struct UpdateProductParams { - var id: String - var name: String? - var price: Double? - var image: ImageUploadParams? -} diff --git a/Examples/ProductSample/Domain/UseCases/CreateProductUseCase.swift b/Examples/ProductSample/Domain/UseCases/CreateProductUseCase.swift deleted file mode 100644 index a20f4923..00000000 --- a/Examples/ProductSample/Domain/UseCases/CreateProductUseCase.swift +++ /dev/null @@ -1,35 +0,0 @@ -// -// CreateProductUseCase.swift -// ProductSample -// -// Created by Guilherme Souza on 19/10/23. -// - -import Foundation -import Supabase - -protocol CreateProductUseCase: UseCase> {} - -struct CreateProductUseCaseImpl: CreateProductUseCase { - let productRepository: ProductRepository - let productImageStorageRepository: ProductImageStorageRepository - let authenticationRepository: AuthenticationRepository - - func execute(input: CreateProductParams) -> Task { - Task { - let ownerID = try await authenticationRepository.currentUserID - - var imageFilePath: String? - - if let image = input.image { - imageFilePath = try await productImageStorageRepository.uploadImage(image) - } - - try await productRepository.createProduct( - InsertProductDto( - name: input.name, price: input.price, image: imageFilePath, ownerID: ownerID - ) - ) - } - } -} diff --git a/Examples/ProductSample/Domain/UseCases/DeleteProductUseCase.swift b/Examples/ProductSample/Domain/UseCases/DeleteProductUseCase.swift deleted file mode 100644 index d5cca7bc..00000000 --- a/Examples/ProductSample/Domain/UseCases/DeleteProductUseCase.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// DeleteProductUseCase.swift -// ProductSample -// -// Created by Guilherme Souza on 19/10/23. -// - -import Foundation - -protocol DeleteProductUseCase: UseCase> {} - -struct DeleteProductUseCaseImpl: DeleteProductUseCase { - let repository: ProductRepository - - func execute(input: Product.ID) -> Task { - Task { - try await repository.deleteProduct(id: input) - } - } -} diff --git a/Examples/ProductSample/Domain/UseCases/GetProductUseCase.swift b/Examples/ProductSample/Domain/UseCases/GetProductUseCase.swift deleted file mode 100644 index 039d98cf..00000000 --- a/Examples/ProductSample/Domain/UseCases/GetProductUseCase.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// GetProductUseCase.swift -// ProductSample -// -// Created by Guilherme Souza on 19/10/23. -// - -import Foundation - -protocol GetProductUseCase: UseCase> {} - -struct GetProductUseCaseImpl: GetProductUseCase { - let productRepository: ProductRepository - - func execute(input: Product.ID) -> Task { - Task { - try await productRepository.getProduct(id: input) - } - } -} diff --git a/Examples/ProductSample/Domain/UseCases/GetProductsUseCase.swift b/Examples/ProductSample/Domain/UseCases/GetProductsUseCase.swift deleted file mode 100644 index b4fe6dd7..00000000 --- a/Examples/ProductSample/Domain/UseCases/GetProductsUseCase.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// GetProductsUseCase.swift -// ProductSample -// -// Created by Guilherme Souza on 19/10/23. -// - -import Foundation - -protocol GetProductsUseCase: UseCase> {} - -struct GetProductsUseCaseImpl: GetProductsUseCase { - let repository: any ProductRepository - - func execute(input _: ()) -> Task<[Product], Error> { - Task { - try await repository.getProducts() - } - } -} diff --git a/Examples/ProductSample/Domain/UseCases/SignInUseCase.swift b/Examples/ProductSample/Domain/UseCases/SignInUseCase.swift deleted file mode 100644 index 19628a9c..00000000 --- a/Examples/ProductSample/Domain/UseCases/SignInUseCase.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// SignInUseCase.swift -// ProductSample -// -// Created by Guilherme Souza on 20/10/23. -// - -import Foundation - -protocol SignInUseCase: UseCase> {} - -struct SignInUseCaseImpl: SignInUseCase { - let repository: AuthenticationRepository - - func execute(input: Credentials) -> Task { - Task { - try await repository.signIn(email: input.email, password: input.password) - } - } -} diff --git a/Examples/ProductSample/Domain/UseCases/SignUpUseCase.swift b/Examples/ProductSample/Domain/UseCases/SignUpUseCase.swift deleted file mode 100644 index a8a25e1d..00000000 --- a/Examples/ProductSample/Domain/UseCases/SignUpUseCase.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// SignUpUseCase.swift -// ProductSample -// -// Created by Guilherme Souza on 21/10/23. -// - -import Foundation - -enum SignUpResult { - case success - case requiresConfirmation -} - -protocol SignUpUseCase: UseCase> {} - -struct SignUpUseCaseImpl: SignUpUseCase { - let repository: AuthenticationRepository - - func execute(input: Credentials) -> Task { - Task { - try await repository.signUp(email: input.email, password: input.password) - } - } -} diff --git a/Examples/ProductSample/Domain/UseCases/UpdateProductUseCase.swift b/Examples/ProductSample/Domain/UseCases/UpdateProductUseCase.swift deleted file mode 100644 index 99339784..00000000 --- a/Examples/ProductSample/Domain/UseCases/UpdateProductUseCase.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// UpdateProductUseCase.swift -// ProductSample -// -// Created by Guilherme Souza on 19/10/23. -// - -import Foundation -import Supabase - -protocol UpdateProductUseCase: UseCase> {} - -struct UpdateProductUseCaseImpl: UpdateProductUseCase { - let productRepository: ProductRepository - let productImageStorageRepository: any ProductImageStorageRepository - - func execute(input: UpdateProductParams) -> Task { - Task { - var imageFilePath: String? - - if let image = input.image { - imageFilePath = try await productImageStorageRepository.uploadImage(image) - } - - try await productRepository.updateProduct( - id: input.id, name: input.name, price: input.price, image: imageFilePath - ) - } - } -} diff --git a/Examples/ProductSample/Domain/UseCases/UseCase.swift b/Examples/ProductSample/Domain/UseCases/UseCase.swift deleted file mode 100644 index 530bbb3f..00000000 --- a/Examples/ProductSample/Domain/UseCases/UseCase.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// UseCase.swift -// ProductSample -// -// Created by Guilherme Souza on 19/10/23. -// - -import Foundation -import Storage - -protocol UseCase: Sendable { - associatedtype Input: Sendable - associatedtype Output: Sendable - - func execute(input: Input) -> Output -} - -extension UseCase where Input == Void { - func execute() -> Output { - execute(input: ()) - } -} diff --git a/Examples/ProductSample/Features/Auth/AuthView.swift b/Examples/ProductSample/Features/Auth/AuthView.swift deleted file mode 100644 index d30aaecb..00000000 --- a/Examples/ProductSample/Features/Auth/AuthView.swift +++ /dev/null @@ -1,63 +0,0 @@ -// -// AuthView.swift -// ProductSample -// -// Created by Guilherme Souza on 19/10/23. -// - -import SwiftUI - -struct AuthView: View { - @ObservedObject var model: AuthViewModel - - var body: some View { - Form { - Section { - TextField("Email", text: $model.email) - .keyboardType(.emailAddress) - .textContentType(.emailAddress) - .autocorrectionDisabled() - .textInputAutocapitalization(.never) - SecureField("Password", text: $model.password) - .textContentType(.password) - .autocorrectionDisabled() - .textInputAutocapitalization(.never) - } - - Section { - Button("Sign in") { - Task { - await model.signInButtonTapped() - } - } - Button("Sign up") { - Task { - await model.signUpButtonTapped() - } - } - Button("Sign in with Apple") { - Task { - await model.signInWithAppleButtonTapped() - } - } - } - - if let status = model.status { - switch status { - case let .error(error): - Text(error.localizedDescription).font(.callout).foregroundStyle(.red) - case .requiresConfirmation: - Text( - "Account created, but it requires confirmation, click the verification link sent to the registered email." - ) - .font(.callout) - case .loading: - ProgressView() - } - } - } - .onOpenURL { url in - Task { await model.onOpenURL(url) } - } - } -} diff --git a/Examples/ProductSample/Features/Auth/AuthViewModel.swift b/Examples/ProductSample/Features/Auth/AuthViewModel.swift deleted file mode 100644 index 1e14028b..00000000 --- a/Examples/ProductSample/Features/Auth/AuthViewModel.swift +++ /dev/null @@ -1,79 +0,0 @@ -// -// AuthViewModel.swift -// ProductSample -// -// Created by Guilherme Souza on 19/10/23. -// - -import Foundation -import OSLog - -@MainActor -final class AuthViewModel: ObservableObject { - private let logger = Logger.make(category: "AuthViewModel") - - private let signInUseCase: any SignInUseCase - private let signUpUseCase: any SignUpUseCase - - @Published var email = "" - @Published var password = "" - - enum Status { - case loading - case requiresConfirmation - case error(Error) - } - - @Published var status: Status? - - init( - signInUseCase: any SignInUseCase = Dependencies.signInUseCase, - signUpUseCase: any SignUpUseCase = Dependencies.signUpUseCase - ) { - self.signInUseCase = signInUseCase - self.signUpUseCase = signUpUseCase - } - - func signInButtonTapped() async { - status = .loading - do { - try await signInUseCase.execute(input: .init(email: email, password: password)).value - status = nil - } catch { - status = .error(error) - logger.error("Error signing in: \(error)") - } - } - - func signUpButtonTapped() async { - status = .loading - do { - let result = try await signUpUseCase.execute(input: .init(email: email, password: password)) - .value - if result == .requiresConfirmation { - status = .requiresConfirmation - } else { - status = nil - } - } catch { - status = .error(error) - logger.error("Error signing up: \(error)") - } - } - - func signInWithAppleButtonTapped() async {} - - func onOpenURL(_ url: URL) async { - status = .loading - - do { - logger.debug("Retrieve session from url: \(url)") - try await Dependencies.supabase.auth.session(from: url) - await signInButtonTapped() - status = nil - } catch { - status = .error(error) - logger.error("Error creating session from url: \(error)") - } - } -} diff --git a/Examples/ProductSample/Features/ProductDetails/ProductDetailsView.swift b/Examples/ProductSample/Features/ProductDetails/ProductDetailsView.swift deleted file mode 100644 index 56db0bec..00000000 --- a/Examples/ProductSample/Features/ProductDetails/ProductDetailsView.swift +++ /dev/null @@ -1,60 +0,0 @@ -// -// ProductDetailsView.swift -// ProductSample -// -// Created by Guilherme Souza on 19/10/23. -// - -import PhotosUI -import SwiftUI - -struct ProductDetailsView: View { - @ObservedObject var model: ProductDetailsViewModel - - @Environment(\.dismiss) private var dismiss - - var body: some View { - Form { - Section { - Group { - if let productImage = model.imageSource?.productImage { - productImage.image - .resizable() - } else { - Color.clear - } - } - .scaledToFit() - .frame(width: 80) - .overlay { - PhotosPicker(selection: $model.imageSelection, matching: .images) { - Image(systemName: "pencil.circle.fill") - .symbolRenderingMode(.multicolor) - .font(.system(size: 30)) - .foregroundColor(.accentColor) - } - } - } - Section { - TextField("Product Name", text: $model.name) - TextField("Product Price", value: $model.price, formatter: NumberFormatter()) - } - } - .task { await model.loadProductIfNeeded() } - .toolbar { - ToolbarItem(placement: .primaryAction) { - if model.isSavingProduct { - ProgressView() - } else { - Button("Save") { - Task { - if await model.saveButtonTapped() { - dismiss() - } - } - } - } - } - } - } -} diff --git a/Examples/ProductSample/Features/ProductDetails/ProductDetailsViewModel.swift b/Examples/ProductSample/Features/ProductDetails/ProductDetailsViewModel.swift deleted file mode 100644 index b5ead01b..00000000 --- a/Examples/ProductSample/Features/ProductDetails/ProductDetailsViewModel.swift +++ /dev/null @@ -1,170 +0,0 @@ -// -// ProductDetailsViewModel.swift -// ProductSample -// -// Created by Guilherme Souza on 19/10/23. -// - -import OSLog -import PhotosUI -import SwiftUI - -@MainActor -final class ProductDetailsViewModel: ObservableObject { - private let logger = Logger.make(category: "ProductDetailsViewModel") - - private let productId: Product.ID? - - private let updateProductUseCase: any UpdateProductUseCase - private let createProductUseCase: any CreateProductUseCase - private let getProductUseCase: any GetProductUseCase - private let productImageStorage: ProductImageStorageRepository - - @Published var name: String = "" - @Published var price: Double = 0 - - enum ImageSource { - case remote(ProductImage) - case local(ProductImage) - - var productImage: ProductImage { - switch self { - case let .remote(image), let .local(image): image - } - } - } - - @Published var imageSelection: PhotosPickerItem? { - didSet { - if let imageSelection { - Task { - await loadTransferable(from: imageSelection) - } - } - } - } - - @Published var imageSource: ImageSource? - @Published var isSavingProduct = false - - let onCompletion: (Bool) -> Void - - init( - updateProductUseCase: any UpdateProductUseCase = Dependencies.updateProductUseCase, - createProductUseCase: any CreateProductUseCase = Dependencies.createProductUseCase, - getProductUseCase: any GetProductUseCase = Dependencies.getProductUseCase, - productImageStorage: ProductImageStorageRepository = Dependencies.productImageStorageRepository, - productId: Product.ID?, - onCompletion: @escaping (Bool) -> Void - ) { - self.updateProductUseCase = updateProductUseCase - self.createProductUseCase = createProductUseCase - self.getProductUseCase = getProductUseCase - self.productImageStorage = productImageStorage - self.productId = productId - self.onCompletion = onCompletion - } - - func loadProductIfNeeded() async { - guard let productId else { return } - - do { - let product = try await getProductUseCase.execute(input: productId).value - name = product.name - price = product.price - - if let image = product.image { - let data = try await productImageStorage.downloadImage(image) - imageSource = ProductImage(data: data).map(ImageSource.remote) - } - } catch { - logger.error("Error loading product: \(error)") - } - } - - func saveButtonTapped() async -> Bool { - isSavingProduct = true - defer { isSavingProduct = false } - - let imageUploadParams = - if case let .local(image) = imageSource - { - ImageUploadParams( - fileName: UUID().uuidString, - fileExtension: imageSelection?.supportedContentTypes.first?.preferredFilenameExtension, - mimeType: imageSelection?.supportedContentTypes.first?.preferredMIMEType, - data: image.data - ) - } else { - ImageUploadParams?.none - } - - do { - if let productId { - logger.info("Will update product: \(productId)") - - try await updateProductUseCase.execute( - input: UpdateProductParams( - id: productId, - name: name, - price: price, - image: imageUploadParams - ) - ).value - } else { - logger.info("Will add product") - try await createProductUseCase.execute( - input: CreateProductParams( - name: name, - price: price, - image: imageUploadParams - ) - ).value - } - - logger.error("Save succeeded") - onCompletion(true) - return true - } catch { - logger.error("Save failed: \(error)") - onCompletion(false) - return false - } - } - - private func loadTransferable(from imageSelection: PhotosPickerItem) async { - if let image = try? await imageSelection.loadTransferable(type: ProductImage.self) { - imageSource = .local(image) - } - } -} - -struct ProductImage: Transferable { - let image: Image - let data: Data - - static var transferRepresentation: some TransferRepresentation { - DataRepresentation(importedContentType: .image) { data in - guard let image = ProductImage(data: data) else { - throw TransferError.importFailed - } - - return image - } - } -} - -extension ProductImage { - init?(data: Data) { - guard let uiImage = UIImage(data: data) else { - return nil - } - - let image = Image(uiImage: uiImage) - self.init(image: image, data: data) - } -} - -enum TransferError: Error { - case importFailed -} diff --git a/Examples/ProductSample/Features/ProductList/ProductListView.swift b/Examples/ProductSample/Features/ProductList/ProductListView.swift deleted file mode 100644 index 75440ef0..00000000 --- a/Examples/ProductSample/Features/ProductList/ProductListView.swift +++ /dev/null @@ -1,50 +0,0 @@ -// -// ProductListView.swift -// ProductSample -// -// Created by Guilherme Souza on 19/10/23. -// - -import SwiftUI - -struct ProductListView: View { - @ObservedObject var model: ProductListViewModel - - var body: some View { - List { - if let error = model.error { - Text(error.localizedDescription) - .frame(maxWidth: .infinity, alignment: .leading) - .padding() - .background(Color.red.opacity(0.5)) - .clipShape(RoundedRectangle(cornerRadius: 8)) - .padding() - .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) - .listRowSeparator(.hidden) - } - - ForEach(model.products) { product in - NavigationLink(value: ProductDetailRoute(productId: product.id)) { - LabeledContent(product.name, value: product.price.formatted(.currency(code: "USD"))) - } - } - .onDelete { indexSet in - Task { - await model.didSwipeToDelete(indexSet) - } - } - } - .listStyle(.plain) - .overlay { - if model.products.isEmpty { - Text("Product list empty.") - } - } - .task { - await model.loadProducts() - } - .refreshable { - await model.loadProducts() - } - } -} diff --git a/Examples/ProductSample/Features/ProductList/ProductListViewModel.swift b/Examples/ProductSample/Features/ProductList/ProductListViewModel.swift deleted file mode 100644 index ed71cdb3..00000000 --- a/Examples/ProductSample/Features/ProductList/ProductListViewModel.swift +++ /dev/null @@ -1,64 +0,0 @@ -// -// ProductListViewModel.swift -// ProductSample -// -// Created by Guilherme Souza on 19/10/23. -// - -import OSLog -import SwiftUI - -@MainActor -final class ProductListViewModel: ObservableObject { - private let logger = Logger.make(category: "ProductListViewModel") - - private let deleteProductUseCase: any DeleteProductUseCase - private let getProductsUseCase: any GetProductsUseCase - - @Published var products: [Product] = [] - @Published var isLoading = false - @Published var error: Error? - - init( - deleteProductUseCase: any DeleteProductUseCase = Dependencies.deleteProductUseCase, - getProductsUseCase: any GetProductsUseCase = Dependencies.getProductsUseCase - ) { - self.deleteProductUseCase = deleteProductUseCase - self.getProductsUseCase = getProductsUseCase - } - - func loadProducts() async { - isLoading = true - defer { isLoading = false } - - do { - products = try await getProductsUseCase.execute().value - logger.info("Products loaded.") - error = nil - } catch { - logger.error("Error loading products: \(error)") - self.error = error - } - } - - func didSwipeToDelete(_ indexes: IndexSet) async { - for index in indexes { - let product = products[index] - await removeItem(product: product) - } - } - - private func removeItem(product: Product) async { - products.removeAll { $0.id == product.id } - - do { - try await deleteProductUseCase.execute(input: product.id).value - error = nil - } catch { - logger.error("Failed to remove product: \(product.id) error: \(error)") - self.error = error - } - - await loadProducts() - } -} diff --git a/Examples/ProductSample/Helpers/Config.swift b/Examples/ProductSample/Helpers/Config.swift deleted file mode 100644 index 0322e950..00000000 --- a/Examples/ProductSample/Helpers/Config.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// Config.swift -// ProductSample -// -// Created by Guilherme Souza on 19/10/23. -// - -import Foundation - -enum Config { - static let SUPABASE_URL = load(key: "SUPABASE_URL") ?? "" - static let SUPABASE_ANON_KEY = load(key: "SUPABASE_ANON_KEY") ?? "" - - private static func load(key: String) -> T? { - guard - let configURL = Bundle.main.url(forResource: "Config", withExtension: "plist"), - let config = try? PropertyListSerialization.propertyList( - from: Data(contentsOf: configURL), format: nil - ) as? [String: Any] - else { - return nil - } - - return config[key] as? T - } -} diff --git a/Examples/ProductSample/Helpers/Logger.swift b/Examples/ProductSample/Helpers/Logger.swift deleted file mode 100644 index 1f6cfffa..00000000 --- a/Examples/ProductSample/Helpers/Logger.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// Logger.swift -// ProductSample -// -// Created by Guilherme Souza on 19/10/23. -// - -import Foundation -import OSLog - -extension Logger { - static func make(category: String) -> Logger { - Logger(subsystem: Bundle.main.bundleIdentifier ?? "", category: category) - } -} diff --git a/Examples/ProductSample/Helpers/Result.swift b/Examples/ProductSample/Helpers/Result.swift deleted file mode 100644 index 25ab4b68..00000000 --- a/Examples/ProductSample/Helpers/Result.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// Result.swift -// ProductSample -// -// Created by Guilherme Souza on 19/10/23. -// - -import Foundation - -extension Result { - var value: Success? { - if case let .success(success) = self { - return success - } - return nil - } -} diff --git a/Examples/ProductSample/Routes/Routes.swift b/Examples/ProductSample/Routes/Routes.swift deleted file mode 100644 index 8fb642e5..00000000 --- a/Examples/ProductSample/Routes/Routes.swift +++ /dev/null @@ -1,16 +0,0 @@ -// -// Routes.swift -// ProductSample -// -// Created by Guilherme Souza on 21/10/23. -// - -import Foundation - -struct ProductDetailRoute: Hashable { - let productId: Product.ID -} - -struct AddProductRoute: Identifiable, Hashable { - var id: AnyHashable { self } -} diff --git a/Examples/ProductSample/SupportFiles/Assets.xcassets/AccentColor.colorset/Contents.json b/Examples/ProductSample/SupportFiles/Assets.xcassets/AccentColor.colorset/Contents.json deleted file mode 100644 index eb878970..00000000 --- a/Examples/ProductSample/SupportFiles/Assets.xcassets/AccentColor.colorset/Contents.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "colors" : [ - { - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Examples/ProductSample/SupportFiles/Assets.xcassets/AppIcon.appiconset/Contents.json b/Examples/ProductSample/SupportFiles/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 13613e3e..00000000 --- a/Examples/ProductSample/SupportFiles/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Examples/ProductSample/SupportFiles/Assets.xcassets/Contents.json b/Examples/ProductSample/SupportFiles/Assets.xcassets/Contents.json deleted file mode 100644 index 73c00596..00000000 --- a/Examples/ProductSample/SupportFiles/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Examples/ProductSample/SupportFiles/Info.plist b/Examples/ProductSample/SupportFiles/Info.plist deleted file mode 100644 index a8e3f4cb..00000000 --- a/Examples/ProductSample/SupportFiles/Info.plist +++ /dev/null @@ -1,17 +0,0 @@ - - - - - CFBundleURLTypes - - - CFBundleTypeRole - Editor - CFBundleURLSchemes - - dev.grds.ProductSample - - - - - diff --git a/Examples/ProductSample/SupportFiles/Preview Content/Preview Assets.xcassets/Contents.json b/Examples/ProductSample/SupportFiles/Preview Content/Preview Assets.xcassets/Contents.json deleted file mode 100644 index 73c00596..00000000 --- a/Examples/ProductSample/SupportFiles/Preview Content/Preview Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Makefile b/Makefile index 909ee34a..bb82c6f3 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ PLATFORM ?= iOS Simulator,name=iPhone 15 Pro -EXAMPLE ?= ProductSample +EXAMPLE ?= Examples test-library: xcodebuild test \