diff --git a/App/Course App.xcodeproj/project.pbxproj b/App/Course App.xcodeproj/project.pbxproj index 0281dfd..07889f3 100644 --- a/App/Course App.xcodeproj/project.pbxproj +++ b/App/Course App.xcodeproj/project.pbxproj @@ -26,13 +26,6 @@ A8830C4F2BEF8565005CAFEA /* FirebaseAuth in Frameworks */ = {isa = PBXBuildFile; productRef = A8830C4E2BEF8565005CAFEA /* FirebaseAuth */; }; A8830C512BEF8565005CAFEA /* FirebaseFirestore in Frameworks */ = {isa = PBXBuildFile; productRef = A8830C502BEF8565005CAFEA /* FirebaseFirestore */; }; F1076B532C00FE1000653A6E /* TextStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1076B522C00FE1000653A6E /* TextStyle.swift */; }; - F1076B562C00FE4A00653A6E /* TextType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1076B552C00FE4A00653A6E /* TextType.swift */; }; - F1076B582C00FF2400653A6E /* FontSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1076B572C00FF2400653A6E /* FontSize.swift */; }; - F1076B5A2C00FF2F00653A6E /* FontType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1076B592C00FF2F00653A6E /* FontType.swift */; }; - F1076B5C2C00FF4100653A6E /* PaddingSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1076B5B2C00FF4100653A6E /* PaddingSize.swift */; }; - F1076B5E2C00FF5200653A6E /* CornerRadiusSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1076B5D2C00FF5200653A6E /* CornerRadiusSize.swift */; }; - F1076B612C00FF7500653A6E /* Font+AppFonts.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1076B602C00FF7500653A6E /* Font+AppFonts.swift */; }; - F1076B632C00FF8500653A6E /* UIFont+AppFonts.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1076B622C00FF8500653A6E /* UIFont+AppFonts.swift */; }; F1076B652C01016B00653A6E /* UINavigationController+NavBarUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1076B642C01016B00653A6E /* UINavigationController+NavBarUI.swift */; }; F1076B672C0101A700653A6E /* CustomNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1076B662C0101A700653A6E /* CustomNavigationController.swift */; }; F1076BB02C08FB5500653A6E /* Coordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1076BAF2C08FB5500653A6E /* Coordinator.swift */; }; @@ -49,7 +42,6 @@ F1076BCF2C0B552B00653A6E /* NavigationButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1076BCE2C0B552B00653A6E /* NavigationButtonStyle.swift */; }; F1076BD12C0B560E00653A6E /* ButtonStyle+CustomButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1076BD02C0B560E00653A6E /* ButtonStyle+CustomButtonStyle.swift */; }; F1076BD32C0B5CAA00653A6E /* OnboardingPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1076BD22C0B5CAA00653A6E /* OnboardingPage.swift */; }; - F1076BD52C0D9A7800653A6E /* OpacityVisibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1076BD42C0D9A7800653A6E /* OpacityVisibility.swift */; }; F1076BD72C0D9F2200653A6E /* DeeplinkHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1076BD62C0D9F2200653A6E /* DeeplinkHandling.swift */; }; F1076BD92C0D9F5400653A6E /* Deeplink.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1076BD82C0D9F5400653A6E /* Deeplink.swift */; }; F1076BDB2C0DA2EA00653A6E /* EventEmitting.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1076BDA2C0DA2EA00653A6E /* EventEmitting.swift */; }; @@ -81,8 +73,21 @@ F13089022C17146600FA83EC /* StorageManaging.swift in Sources */ = {isa = PBXBuildFile; fileRef = F13089012C17146600FA83EC /* StorageManaging.swift */; }; F13089042C1714AF00FA83EC /* StorageManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F13089032C1714AF00FA83EC /* StorageManager.swift */; }; F130890C2C19B69E00FA83EC /* HomeViewEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = F130890B2C19B69E00FA83EC /* HomeViewEvent.swift */; }; - F130890E2C19B78700FA83EC /* SwipingViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = F130890D2C19B78700FA83EC /* SwipingViewFactory.swift */; }; + F130890E2C19B78700FA83EC /* SwipingViewCoordinating.swift in Sources */ = {isa = PBXBuildFile; fileRef = F130890D2C19B78700FA83EC /* SwipingViewCoordinating.swift */; }; F13089102C19B81F00FA83EC /* HomeNavigationCoordinatorEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = F130890F2C19B81F00FA83EC /* HomeNavigationCoordinatorEvent.swift */; }; + F13089132C1C55AF00FA83EC /* DependencyInjection in Frameworks */ = {isa = PBXBuildFile; productRef = F13089122C1C55AF00FA83EC /* DependencyInjection */; }; + F13089162C1C5A1300FA83EC /* ManagerRegistration.swift in Sources */ = {isa = PBXBuildFile; fileRef = F13089152C1C5A1300FA83EC /* ManagerRegistration.swift */; }; + F13089182C1C5E0300FA83EC /* ServiceRegistration.swift in Sources */ = {isa = PBXBuildFile; fileRef = F13089172C1C5E0300FA83EC /* ServiceRegistration.swift */; }; + F130891A2C1C64FF00FA83EC /* Store.swift in Sources */ = {isa = PBXBuildFile; fileRef = F13089192C1C64FF00FA83EC /* Store.swift */; }; + F130891D2C1C659100FA83EC /* SwipingViewStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F130891C2C1C659100FA83EC /* SwipingViewStore.swift */; }; + F130891F2C1DA84A00FA83EC /* SwipingViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = F130891E2C1DA84A00FA83EC /* SwipingViewState.swift */; }; + F13089212C1DA85900FA83EC /* SwipingViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = F13089202C1DA85900FA83EC /* SwipingViewAction.swift */; }; + F13089232C1DCB0C00FA83EC /* SwipingViewEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = F13089222C1DCB0C00FA83EC /* SwipingViewEvent.swift */; }; + F13089252C1DE6E000FA83EC /* StoreRegistration.swift in Sources */ = {isa = PBXBuildFile; fileRef = F13089242C1DE6E000FA83EC /* StoreRegistration.swift */; }; + F13089282C202D2B00FA83EC /* SignInViewStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F13089272C202D2B00FA83EC /* SignInViewStore.swift */; }; + F130892B2C202D3E00FA83EC /* SignInViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = F130892A2C202D3E00FA83EC /* SignInViewAction.swift */; }; + F130892D2C202D4500FA83EC /* SignInViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = F130892C2C202D4500FA83EC /* SignInViewState.swift */; }; + F13089342C205C3300FA83EC /* DesignSystem in Frameworks */ = {isa = PBXBuildFile; productRef = F13089332C205C3300FA83EC /* DesignSystem */; }; F18F1A792C107591005364B7 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = F18F1A782C107591005364B7 /* KeychainAccess */; }; F18F1A7B2C1075C7005364B7 /* FirebaseFirestoreCombine-Community in Frameworks */ = {isa = PBXBuildFile; productRef = F18F1A7A2C1075C7005364B7 /* FirebaseFirestoreCombine-Community */; }; F18F1A7D2C1075C7005364B7 /* FirebaseFirestoreSwift in Frameworks */ = {isa = PBXBuildFile; productRef = F18F1A7C2C1075C7005364B7 /* FirebaseFirestoreSwift */; }; @@ -148,13 +153,6 @@ A8830C472BEF80FA005CAFEA /* Poppins-MediumItalic.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Poppins-MediumItalic.ttf"; sourceTree = ""; }; A8830C4B2BEF84BC005CAFEA /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; F1076B522C00FE1000653A6E /* TextStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextStyle.swift; sourceTree = ""; }; - F1076B552C00FE4A00653A6E /* TextType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextType.swift; sourceTree = ""; }; - F1076B572C00FF2400653A6E /* FontSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontSize.swift; sourceTree = ""; }; - F1076B592C00FF2F00653A6E /* FontType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontType.swift; sourceTree = ""; }; - F1076B5B2C00FF4100653A6E /* PaddingSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaddingSize.swift; sourceTree = ""; }; - F1076B5D2C00FF5200653A6E /* CornerRadiusSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CornerRadiusSize.swift; sourceTree = ""; }; - F1076B602C00FF7500653A6E /* Font+AppFonts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Font+AppFonts.swift"; sourceTree = ""; }; - F1076B622C00FF8500653A6E /* UIFont+AppFonts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIFont+AppFonts.swift"; sourceTree = ""; }; F1076B642C01016B00653A6E /* UINavigationController+NavBarUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UINavigationController+NavBarUI.swift"; sourceTree = ""; }; F1076B662C0101A700653A6E /* CustomNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomNavigationController.swift; sourceTree = ""; }; F1076BAF2C08FB5500653A6E /* Coordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coordinator.swift; sourceTree = ""; }; @@ -171,7 +169,6 @@ F1076BCE2C0B552B00653A6E /* NavigationButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationButtonStyle.swift; sourceTree = ""; }; F1076BD02C0B560E00653A6E /* ButtonStyle+CustomButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ButtonStyle+CustomButtonStyle.swift"; sourceTree = ""; }; F1076BD22C0B5CAA00653A6E /* OnboardingPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingPage.swift; sourceTree = ""; }; - F1076BD42C0D9A7800653A6E /* OpacityVisibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpacityVisibility.swift; sourceTree = ""; }; F1076BD62C0D9F2200653A6E /* DeeplinkHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeeplinkHandling.swift; sourceTree = ""; }; F1076BD82C0D9F5400653A6E /* Deeplink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Deeplink.swift; sourceTree = ""; }; F1076BDA2C0DA2EA00653A6E /* EventEmitting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventEmitting.swift; sourceTree = ""; }; @@ -203,8 +200,20 @@ F13089012C17146600FA83EC /* StorageManaging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageManaging.swift; sourceTree = ""; }; F13089032C1714AF00FA83EC /* StorageManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageManager.swift; sourceTree = ""; }; F130890B2C19B69E00FA83EC /* HomeViewEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewEvent.swift; sourceTree = ""; }; - F130890D2C19B78700FA83EC /* SwipingViewFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipingViewFactory.swift; sourceTree = ""; }; + F130890D2C19B78700FA83EC /* SwipingViewCoordinating.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipingViewCoordinating.swift; sourceTree = ""; }; F130890F2C19B81F00FA83EC /* HomeNavigationCoordinatorEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeNavigationCoordinatorEvent.swift; sourceTree = ""; }; + F13089152C1C5A1300FA83EC /* ManagerRegistration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManagerRegistration.swift; sourceTree = ""; }; + F13089172C1C5E0300FA83EC /* ServiceRegistration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceRegistration.swift; sourceTree = ""; }; + F13089192C1C64FF00FA83EC /* Store.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Store.swift; sourceTree = ""; }; + F130891C2C1C659100FA83EC /* SwipingViewStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipingViewStore.swift; sourceTree = ""; }; + F130891E2C1DA84A00FA83EC /* SwipingViewState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipingViewState.swift; sourceTree = ""; }; + F13089202C1DA85900FA83EC /* SwipingViewAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipingViewAction.swift; sourceTree = ""; }; + F13089222C1DCB0C00FA83EC /* SwipingViewEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipingViewEvent.swift; sourceTree = ""; }; + F13089242C1DE6E000FA83EC /* StoreRegistration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreRegistration.swift; sourceTree = ""; }; + F13089272C202D2B00FA83EC /* SignInViewStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInViewStore.swift; sourceTree = ""; }; + F130892A2C202D3E00FA83EC /* SignInViewAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInViewAction.swift; sourceTree = ""; }; + F130892C2C202D4500FA83EC /* SignInViewState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInViewState.swift; sourceTree = ""; }; + F13089312C204FB900FA83EC /* DesignSystem */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = DesignSystem; sourceTree = ""; }; F18F1A802C107656005364B7 /* KeychainManaging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainManaging.swift; sourceTree = ""; }; F18F1A842C1076A8005364B7 /* KeychainManagerError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainManagerError.swift; sourceTree = ""; }; F18F1A862C1076EA005364B7 /* KeychainManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainManager.swift; sourceTree = ""; }; @@ -233,7 +242,9 @@ files = ( F18F1A7D2C1075C7005364B7 /* FirebaseFirestoreSwift in Frameworks */, A8830C4F2BEF8565005CAFEA /* FirebaseAuth in Frameworks */, + F13089342C205C3300FA83EC /* DesignSystem in Frameworks */, F18F1A7B2C1075C7005364B7 /* FirebaseFirestoreCombine-Community in Frameworks */, + F13089132C1C55AF00FA83EC /* DependencyInjection in Frameworks */, A8830C512BEF8565005CAFEA /* FirebaseFirestore in Frameworks */, F18F1A792C107591005364B7 /* KeychainAccess in Frameworks */, ); @@ -259,11 +270,13 @@ A81F21E72BDC1DB700A8A301 = { isa = PBXGroup; children = ( + F13089312C204FB900FA83EC /* DesignSystem */, A8830C342BED05FC005CAFEA /* Configurations */, A81F21F22BDC1DB700A8A301 /* Course App */, A8830C1C2BED0315005CAFEA /* Course App UnitTests */, A8830C292BED03BB005CAFEA /* Course App UITests */, A81F21F12BDC1DB700A8A301 /* Products */, + F13089322C205C3300FA83EC /* Frameworks */, ); sourceTree = ""; }; @@ -281,6 +294,7 @@ isa = PBXGroup; children = ( F1076B682C0101C300653A6E /* App */, + F13089142C1C59EB00FA83EC /* DIRegistration */, A845D9502BF376860039AA1D /* Common */, A8830C3E2BED0E80005CAFEA /* Course-App-Info.plist */, A845D9482BF36F070039AA1D /* DataAPI */, @@ -394,24 +408,9 @@ path = Fonts; sourceTree = ""; }; - F1076B542C00FE4000653A6E /* DesignSystem */ = { - isa = PBXGroup; - children = ( - F1076B5D2C00FF5200653A6E /* CornerRadiusSize.swift */, - F1076B572C00FF2400653A6E /* FontSize.swift */, - F1076B592C00FF2F00653A6E /* FontType.swift */, - F1076BD42C0D9A7800653A6E /* OpacityVisibility.swift */, - F1076B5B2C00FF4100653A6E /* PaddingSize.swift */, - F1076B552C00FE4A00653A6E /* TextType.swift */, - ); - path = DesignSystem; - sourceTree = ""; - }; F1076B5F2C00FF6600653A6E /* Extensions */ = { isa = PBXGroup; children = ( - F1076B602C00FF7500653A6E /* Font+AppFonts.swift */, - F1076B622C00FF8500653A6E /* UIFont+AppFonts.swift */, F1076B642C01016B00653A6E /* UINavigationController+NavBarUI.swift */, F1076BD02C0B560E00653A6E /* ButtonStyle+CustomButtonStyle.swift */, ); @@ -457,7 +456,9 @@ isa = PBXGroup; children = ( F1076BC02C09088900653A6E /* Navigation */, + F130891B2C1C657800FA83EC /* Store */, F190AB332BFDE2D0001AF32D /* SwipingView.swift */, + F13089222C1DCB0C00FA83EC /* SwipingViewEvent.swift */, ); path = SwipingView; sourceTree = ""; @@ -468,6 +469,7 @@ A845D9522BF377250039AA1D /* ReusableIdentifier.swift */, F1076BDA2C0DA2EA00653A6E /* EventEmitting.swift */, F18F1A8E2C107C70005364B7 /* CancellablesContaining.swift */, + F13089192C1C64FF00FA83EC /* Store.swift */, ); path = Protocols; sourceTree = ""; @@ -516,7 +518,7 @@ isa = PBXGroup; children = ( F1076BC12C0908A400653A6E /* SwipingViewNavigationCoordinator.swift */, - F130890D2C19B78700FA83EC /* SwipingViewFactory.swift */, + F130890D2C19B78700FA83EC /* SwipingViewCoordinating.swift */, ); path = Navigation; sourceTree = ""; @@ -576,6 +578,7 @@ F1076BEB2C0DBF8600653A6E /* Navigation */, F1076BEE2C0DBF9A00653A6E /* SignInView.swift */, F1076BF02C0DC1DA00653A6E /* SignInViewEvent.swift */, + F13089262C202D1800FA83EC /* Store */, ); path = SignIn; sourceTree = ""; @@ -634,6 +637,43 @@ path = Storage; sourceTree = ""; }; + F13089142C1C59EB00FA83EC /* DIRegistration */ = { + isa = PBXGroup; + children = ( + F13089152C1C5A1300FA83EC /* ManagerRegistration.swift */, + F13089172C1C5E0300FA83EC /* ServiceRegistration.swift */, + F13089242C1DE6E000FA83EC /* StoreRegistration.swift */, + ); + path = DIRegistration; + sourceTree = ""; + }; + F130891B2C1C657800FA83EC /* Store */ = { + isa = PBXGroup; + children = ( + F13089202C1DA85900FA83EC /* SwipingViewAction.swift */, + F130891E2C1DA84A00FA83EC /* SwipingViewState.swift */, + F130891C2C1C659100FA83EC /* SwipingViewStore.swift */, + ); + path = Store; + sourceTree = ""; + }; + F13089262C202D1800FA83EC /* Store */ = { + isa = PBXGroup; + children = ( + F130892A2C202D3E00FA83EC /* SignInViewAction.swift */, + F130892C2C202D4500FA83EC /* SignInViewState.swift */, + F13089272C202D2B00FA83EC /* SignInViewStore.swift */, + ); + path = Store; + sourceTree = ""; + }; + F13089322C205C3300FA83EC /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; F18F1A7E2C107639005364B7 /* Managers */ = { isa = PBXGroup; children = ( @@ -692,7 +732,6 @@ F190AB262BFDDB68001AF32D /* SharedUI */ = { isa = PBXGroup; children = ( - F1076B542C00FE4000653A6E /* DesignSystem */, F1076B5F2C00FF6600653A6E /* Extensions */, F190AB272BFDDB89001AF32D /* Styles */, F190AB2C2BFDDD87001AF32D /* ViewModifiers */, @@ -744,6 +783,8 @@ F18F1A782C107591005364B7 /* KeychainAccess */, F18F1A7A2C1075C7005364B7 /* FirebaseFirestoreCombine-Community */, F18F1A7C2C1075C7005364B7 /* FirebaseFirestoreSwift */, + F13089122C1C55AF00FA83EC /* DependencyInjection */, + F13089332C205C3300FA83EC /* DesignSystem */, ); productName = "Course App"; productReference = A81F21F02BDC1DB700A8A301 /* App Course Dev.app */; @@ -821,6 +862,7 @@ A8830C422BEF7B17005CAFEA /* XCRemoteSwiftPackageReference "SwiftLint" */, A8830C4D2BEF8565005CAFEA /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, F18F1A772C107591005364B7 /* XCRemoteSwiftPackageReference "KeychainAccess" */, + F13089112C1C55AF00FA83EC /* XCRemoteSwiftPackageReference "ios-dependency-injection" */, ); productRefGroup = A81F21F12BDC1DB700A8A301 /* Products */; projectDirPath = ""; @@ -876,50 +918,49 @@ F1076BC22C0908A400653A6E /* SwipingViewNavigationCoordinator.swift in Sources */, F1076BED2C0DBF9000653A6E /* SignInNavigationCoordinator.swift in Sources */, F190AB362BFDE300001AF32D /* SwipingCard.swift in Sources */, + F13089212C1DA85900FA83EC /* SwipingViewAction.swift in Sources */, + F13089282C202D2B00FA83EC /* SignInViewStore.swift in Sources */, F1076BDD2C0DA40000653A6E /* OnboardingViewEvent.swift in Sources */, F1076BF92C0DDB2800653A6E /* ProfileNavigationCoordinatorEvent.swift in Sources */, F13088F22C14A79600FA83EC /* JokeService.swift in Sources */, - F1076B582C00FF2400653A6E /* FontSize.swift in Sources */, F1076B532C00FE1000653A6E /* TextStyle.swift in Sources */, F13088EB2C130A1500FA83EC /* HTTPResponseStatusCode.swift in Sources */, - F1076B632C00FF8500653A6E /* UIFont+AppFonts.swift in Sources */, F18F1A922C108CCB005364B7 /* Endpoint.swift in Sources */, F13088E42C11F08000FA83EC /* APIManager.swift in Sources */, F18F1A992C11B400005364B7 /* HTTPMethod.swift in Sources */, A845D9532BF377250039AA1D /* ReusableIdentifier.swift in Sources */, + F13089232C1DCB0C00FA83EC /* SwipingViewEvent.swift in Sources */, F18F1A892C107725005364B7 /* KeychainServicing.swift in Sources */, F1076BCF2C0B552B00653A6E /* NavigationButtonStyle.swift in Sources */, - F1076BD52C0D9A7800653A6E /* OpacityVisibility.swift in Sources */, F1076BB22C08FC4700653A6E /* ViewControllerCoordinator.swift in Sources */, - F1076B562C00FE4A00653A6E /* TextType.swift in Sources */, - F1076B5C2C00FF4100653A6E /* PaddingSize.swift in Sources */, F1076BE12C0DAB1C00653A6E /* CustomNavigationControllerEvent.swift in Sources */, F13089022C17146600FA83EC /* StorageManaging.swift in Sources */, F1076BD32C0B5CAA00653A6E /* OnboardingPage.swift in Sources */, F1076BB02C08FB5500653A6E /* Coordinator.swift in Sources */, F1076BF32C0DC55200653A6E /* SignInNavigationCoordinatorEvent.swift in Sources */, F13088E22C11F05300FA83EC /* APIManaging.swift in Sources */, - F1076B5E2C00FF5200653A6E /* CornerRadiusSize.swift in Sources */, F18F1A9B2C11B413005364B7 /* Endpoint+Convenience.swift in Sources */, F1076BCD2C0B475A00653A6E /* OnboardingNavigationCoordinator.swift in Sources */, F18F1A8D2C107AFE005364B7 /* OnboardingCoordinatorPresenting.swift in Sources */, F18F1A872C1076EA005364B7 /* KeychainManager.swift in Sources */, F1076BBF2C09052900653A6E /* AppCoordinator.swift in Sources */, F18F1A9E2C11B502005364B7 /* ImagesRouter.swift in Sources */, + F130891A2C1C64FF00FA83EC /* Store.swift in Sources */, F13088FF2C1708FC00FA83EC /* FirebaseAuthManager.swift in Sources */, F1076BCA2C0B474200653A6E /* OnboardingView.swift in Sources */, A8830C402BED0EE0005CAFEA /* BuildConfiguration.swift in Sources */, A845D9552BF377A70039AA1D /* UICollectionView+ReusableIdentifier.swift in Sources */, F18F1A8F2C107C70005364B7 /* CancellablesContaining.swift in Sources */, F1076BE52C0DB83300653A6E /* ProfileNavigationCoordinator.swift in Sources */, + F13089252C1DE6E000FA83EC /* StoreRegistration.swift in Sources */, F13089102C19B81F00FA83EC /* HomeNavigationCoordinatorEvent.swift in Sources */, F13088E72C11F32900FA83EC /* JokeResponse.swift in Sources */, F190AB292BFDDBA5001AF32D /* SelectableButtonStyle.swift in Sources */, F1076BDF2C0DA48A00653A6E /* OnboardingNavigationCoordinatorEvent.swift in Sources */, F1076BF72C0DDA9400653A6E /* ProfileViewEvent.swift in Sources */, + F13089182C1C5E0300FA83EC /* ServiceRegistration.swift in Sources */, F13089042C1714AF00FA83EC /* StorageManager.swift in Sources */, F1076BBD2C09027100653A6E /* CoordinatorView.swift in Sources */, - F1076B612C00FF7500653A6E /* Font+AppFonts.swift in Sources */, A845D94C2BF3711D0039AA1D /* DataProvider.swift in Sources */, F190AB342BFDE2D0001AF32D /* SwipingView.swift in Sources */, F18F1A8B2C10774C005364B7 /* KeychainService.swift in Sources */, @@ -927,20 +968,24 @@ F1076BFB2C0DDCBD00653A6E /* MainTabBarCoordinatorEvent.swift in Sources */, F1076BB62C08FCFE00653A6E /* NavigationControllerCoordinator.swift in Sources */, F13088FD2C1708AB00FA83EC /* FirebaseAuthManaging.swift in Sources */, + F13089162C1C5A1300FA83EC /* ManagerRegistration.swift in Sources */, F130890C2C19B69E00FA83EC /* HomeViewEvent.swift in Sources */, F13088E92C11F58900FA83EC /* JokesRouter.swift in Sources */, F13088F82C16EC0F00FA83EC /* Action.swift in Sources */, F1076BC52C090C0600653A6E /* HomeNavigationCoordinator.swift in Sources */, F1076B672C0101A700653A6E /* CustomNavigationController.swift in Sources */, + F130892D2C202D4500FA83EC /* SignInViewState.swift in Sources */, F1076BFD2C0F0B8500653A6E /* CustomError.swift in Sources */, F1076BD72C0D9F2200653A6E /* DeeplinkHandling.swift in Sources */, F190AB2E2BFDDDA1001AF32D /* BorderedModifier.swift in Sources */, F18F1A812C107656005364B7 /* KeychainManaging.swift in Sources */, - F1076B5A2C00FF2F00653A6E /* FontType.swift in Sources */, - F130890E2C19B78700FA83EC /* SwipingViewFactory.swift in Sources */, + F130891F2C1DA84A00FA83EC /* SwipingViewState.swift in Sources */, + F130890E2C19B78700FA83EC /* SwipingViewCoordinating.swift in Sources */, F18F1A852C1076A8005364B7 /* KeychainManagerError.swift in Sources */, F18F1A952C11B3DB005364B7 /* NetworkingError.swift in Sources */, + F130892B2C202D3E00FA83EC /* SignInViewAction.swift in Sources */, F190AB382BFDE372001AF32D /* ScratchView.swift in Sources */, + F130891D2C1C659100FA83EC /* SwipingViewStore.swift in Sources */, F18F1A972C11B3EB005364B7 /* HTTPHeader.swift in Sources */, F1076BE92C0DB91700653A6E /* ProfileView.swift in Sources */, F1076BB92C08FDA500653A6E /* MainTabBarCoordinator.swift in Sources */, @@ -1606,6 +1651,14 @@ minimumVersion = 10.25.0; }; }; + F13089112C1C55AF00FA83EC /* XCRemoteSwiftPackageReference "ios-dependency-injection" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/strvcom/ios-dependency-injection.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.0.3; + }; + }; F18F1A772C107591005364B7 /* XCRemoteSwiftPackageReference "KeychainAccess" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/kishikawakatsumi/KeychainAccess.git"; @@ -1632,6 +1685,15 @@ package = A8830C4D2BEF8565005CAFEA /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; productName = FirebaseFirestore; }; + F13089122C1C55AF00FA83EC /* DependencyInjection */ = { + isa = XCSwiftPackageProductDependency; + package = F13089112C1C55AF00FA83EC /* XCRemoteSwiftPackageReference "ios-dependency-injection" */; + productName = DependencyInjection; + }; + F13089332C205C3300FA83EC /* DesignSystem */ = { + isa = XCSwiftPackageProductDependency; + productName = DesignSystem; + }; F18F1A782C107591005364B7 /* KeychainAccess */ = { isa = XCSwiftPackageProductDependency; package = F18F1A772C107591005364B7 /* XCRemoteSwiftPackageReference "KeychainAccess" */; diff --git a/App/Course App.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/App/Course App.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index ec1b168..976f403 100644 --- a/App/Course App.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/App/Course App.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "f3035187119b3d85d325ceeb9993feda912fa8e00b088f184770c017c27e8e0f", + "originHash" : "c7aa07fe9b249c130e0e17bc0e9951f12a164ec5a5ed9f0de0c47c2a7a93e8d4", "pins" : [ { "identity" : "abseil-cpp-binary", @@ -100,6 +100,15 @@ "version" : "100.0.0" } }, + { + "identity" : "ios-dependency-injection", + "kind" : "remoteSourceControl", + "location" : "https://github.com/strvcom/ios-dependency-injection.git", + "state" : { + "revision" : "8dbf23b56f11ecc34580745d16141e4d2a4c5129", + "version" : "1.0.3" + } + }, { "identity" : "keychainaccess", "kind" : "remoteSourceControl", diff --git a/App/Course App/App/AppCoordinator.swift b/App/Course App/App/AppCoordinator.swift index 01a6542..5f4f1f7 100644 --- a/App/Course App/App/AppCoordinator.swift +++ b/App/Course App/App/AppCoordinator.swift @@ -6,6 +6,8 @@ // import Combine +import DependencyInjection +import DesignSystem import UIKit final class AppCoordinator: ObservableObject, ViewControllerCoordinator, CancellablesContaining { @@ -18,9 +20,11 @@ final class AppCoordinator: ObservableObject, ViewControllerCoordinator, Cancell } }() + // MARK: Public properties var cancellables = Set() var childCoordinators = [Coordinator]() + var container = Container() @Published var isSignedIn = false } @@ -28,13 +32,23 @@ final class AppCoordinator: ObservableObject, ViewControllerCoordinator, Cancell extension AppCoordinator { func start() { setupAppUI() + assembleDependencyInjectionRegistration() + } +} + +// MARK: - Dependency injection registration +private extension AppCoordinator { + func assembleDependencyInjectionRegistration() { + ManagerRegistration.registerDependencies(to: container) + ServiceRegistration.registerDependecies(to: container) + StoreRegistration.registerDependencies(to: container) } } // MARK: - Factory Methods private extension AppCoordinator { func makeTabBarFlow() -> ViewControllerCoordinator { - let mainTabBarCoordinator = MainTabBarCoordinator() + let mainTabBarCoordinator = MainTabBarCoordinator(container: container) startChildCoordinator(mainTabBarCoordinator) mainTabBarCoordinator.eventPublisher.sink { [weak self] event in self?.handle(event) @@ -44,7 +58,7 @@ private extension AppCoordinator { } func makeSignInFlow() -> ViewControllerCoordinator { - let signInCoordinator = SignInNavigationCoordinator() + let signInCoordinator = SignInNavigationCoordinator(container: container) startChildCoordinator(signInCoordinator) signInCoordinator.eventPublisher.sink { [weak self] event in self?.handle(event) diff --git a/App/Course App/Common/Navigation/Coordinator.swift b/App/Course App/Common/Navigation/Coordinator.swift index c37f7fe..2498223 100644 --- a/App/Course App/Common/Navigation/Coordinator.swift +++ b/App/Course App/Common/Navigation/Coordinator.swift @@ -5,10 +5,12 @@ // Created by Tomáš Duchoslav on 30.05.2024. // +import DependencyInjection import Foundation protocol Coordinator: AnyObject, DeeplinkHandling { var childCoordinators: [Coordinator] { get set } + var container: Container { get } func start() } diff --git a/App/Course App/Common/Protocols/Store.swift b/App/Course App/Common/Protocols/Store.swift new file mode 100644 index 0000000..36c0081 --- /dev/null +++ b/App/Course App/Common/Protocols/Store.swift @@ -0,0 +1,17 @@ +// +// Store.swift +// Course App +// +// Created by Tomáš Duchoslav on 14.06.2024. +// + +import Foundation + +protocol Store { + associatedtype State + associatedtype Action + + @MainActor var viewState: State { get } + + @MainActor func send(action: Action) +} diff --git a/App/Course App/DIRegistration/ManagerRegistration.swift b/App/Course App/DIRegistration/ManagerRegistration.swift new file mode 100644 index 0000000..f6ddaa3 --- /dev/null +++ b/App/Course App/DIRegistration/ManagerRegistration.swift @@ -0,0 +1,37 @@ +// +// ManagerRegistration.swift +// Course App +// +// Created by Tomáš Duchoslav on 14.06.2024. +// + +import DependencyInjection +import Foundation + +enum ManagerRegistration { + static func registerDependencies(to container: Container) { + container.autoregister( + type: StorageManaging.self, + in: .shared, + initializer: StorageManager.init + ) + + container.autoregister( + type: KeychainManaging.self, + in: .shared, + initializer: KeychainManager.init + ) + + container.autoregister( + type: APIManaging.self, + in: .shared, + initializer: APIManager.init + ) + + container.autoregister( + type: FirebaseAuthManaging.self, + in: .shared, + initializer: FirebaseAuthManager.init + ) + } +} diff --git a/App/Course App/DIRegistration/ServiceRegistration.swift b/App/Course App/DIRegistration/ServiceRegistration.swift new file mode 100644 index 0000000..7cad3e6 --- /dev/null +++ b/App/Course App/DIRegistration/ServiceRegistration.swift @@ -0,0 +1,25 @@ +// +// ServiceRegistration.swift +// Course App +// +// Created by Tomáš Duchoslav on 14.06.2024. +// + +import DependencyInjection +import Foundation + +enum ServiceRegistration { + static func registerDependecies(to container: Container) { + container.autoregister( + type: KeychainServicing.self, + in: .shared, + initializer: KeychainService.init + ) + + container.autoregister( + type: JokeServicing.self, + in: .shared, + initializer: JokeService.init + ) + } +} diff --git a/App/Course App/DIRegistration/StoreRegistration.swift b/App/Course App/DIRegistration/StoreRegistration.swift new file mode 100644 index 0000000..e3df20f --- /dev/null +++ b/App/Course App/DIRegistration/StoreRegistration.swift @@ -0,0 +1,25 @@ +// +// StoreRegistration.swift +// Course App +// +// Created by Tomáš Duchoslav on 15.06.2024. +// + +import DependencyInjection +import Foundation + +enum StoreRegistration { + static func registerDependencies(to container: Container) { + container.autoregister( + type: SwipingViewStore.self, + in: .new, + initializer: SwipingViewStore.init + ) + + container.autoregister( + type: SignInViewStore.self, + in: .new, + initializer: SignInViewStore.init + ) + } +} diff --git a/App/Course App/Networking/Storage/StorageManager.swift b/App/Course App/Networking/Storage/StorageManager.swift index 9776e79..5aac7a1 100644 --- a/App/Course App/Networking/Storage/StorageManager.swift +++ b/App/Course App/Networking/Storage/StorageManager.swift @@ -27,7 +27,7 @@ final class StorageManager: StorageManaging { } } - func liked(jokeId: String) async throws -> Bool { + func loadLiked(jokeId: String) async throws -> Bool { let docRef = database.collection("jokesLikes").document(jokeId) do { let document = try await docRef.getDocument() diff --git a/App/Course App/Networking/Storage/StorageManaging.swift b/App/Course App/Networking/Storage/StorageManaging.swift index 9245522..7f46a74 100644 --- a/App/Course App/Networking/Storage/StorageManaging.swift +++ b/App/Course App/Networking/Storage/StorageManaging.swift @@ -10,4 +10,5 @@ import Foundation protocol StorageManaging { func storeLike(jokeId: String, liked: Bool) async throws + func loadLiked(jokeId: String) async throws -> Bool } diff --git a/App/Course App/Scenes/Home/HomeViewController.swift b/App/Course App/Scenes/Home/HomeViewController.swift index 3a60092..6462fbe 100644 --- a/App/Course App/Scenes/Home/HomeViewController.swift +++ b/App/Course App/Scenes/Home/HomeViewController.swift @@ -6,6 +6,7 @@ // import Combine +import DesignSystem import os import SwiftUI import UIKit @@ -109,7 +110,7 @@ extension HomeViewController { for sectionIndex in dataProvider.data.indices { for jokeIndex in dataProvider.data[sectionIndex].jokes.indices { - let isLiked = try await storage.liked(jokeId: dataProvider.data[sectionIndex].jokes[jokeIndex].jokeID) + let isLiked = try await storage.loadLiked(jokeId: dataProvider.data[sectionIndex].jokes[jokeIndex].jokeID) dataProvider.data[sectionIndex].jokes[jokeIndex].isLiked = isLiked } } diff --git a/App/Course App/Scenes/Home/Navigation/HomeNavigationCoordinator.swift b/App/Course App/Scenes/Home/Navigation/HomeNavigationCoordinator.swift index 86eca4c..a4e0ce7 100644 --- a/App/Course App/Scenes/Home/Navigation/HomeNavigationCoordinator.swift +++ b/App/Course App/Scenes/Home/Navigation/HomeNavigationCoordinator.swift @@ -6,10 +6,12 @@ // import Combine +import DependencyInjection import os +import SwiftUI import UIKit -final class HomeNavigationCoordinator: NavigationControllerCoordinator, CancellablesContaining, SwipingViewFactory { +final class HomeNavigationCoordinator: NavigationControllerCoordinator, CancellablesContaining, SwipingViewCoordinating { // MARK: Private properties private(set) lazy var navigationController: UINavigationController = CustomNavigationController() private let logger = Logger() @@ -18,10 +20,16 @@ final class HomeNavigationCoordinator: NavigationControllerCoordinator, Cancella // MARK: Public properties var childCoordinators = [Coordinator]() var cancellables = Set() + var container: Container + // MARK: Lifecycle deinit { logger.info("Deinit HomeNavigationCoordinator") } + + init(container: Container) { + self.container = container + } } // MARK: - EventEmitting @@ -51,12 +59,12 @@ private extension HomeNavigationCoordinator { } // MARK: - Handling events -private extension HomeNavigationCoordinator { +extension HomeNavigationCoordinator { func handle(_ event: HomeViewEvent) { switch event { case let .itemTapped(joke): logger.info("Joke on home screen was tapped \(joke.text)") - navigationController.pushViewController(makeSwipingView(with: joke), animated: true) + navigationController.pushViewController(makeSwipingView(with: joke, isChildCoordinator: true), animated: true) } } } diff --git a/App/Course App/Scenes/Navigation/MainTabBarCoordinator.swift b/App/Course App/Scenes/Navigation/MainTabBarCoordinator.swift index d88a8e6..b49ff84 100644 --- a/App/Course App/Scenes/Navigation/MainTabBarCoordinator.swift +++ b/App/Course App/Scenes/Navigation/MainTabBarCoordinator.swift @@ -6,6 +6,7 @@ // import Combine +import DependencyInjection import os import SwiftUI import UIKit @@ -19,11 +20,16 @@ final class MainTabBarCoordinator: NSObject, TabBarControllerCoordinator, Cancel // MARK: Public Properties var cancellables = Set() var childCoordinators = [Coordinator]() + var container: Container // MARK: Lifecycle deinit { logger.info("Deinit MainTabBarCoordinator") } + + init(container: Container) { + self.container = container + } } // MARK: - Start the coordinator @@ -68,7 +74,7 @@ private extension MainTabBarCoordinator { } func makeHomeFlow() -> ViewControllerCoordinator { - let homeViewCoordinator = HomeNavigationCoordinator() + let homeViewCoordinator = HomeNavigationCoordinator(container: container) startChildCoordinator(homeViewCoordinator) homeViewCoordinator.rootViewController.tabBarItem = UITabBarItem( title: "Categories", @@ -79,14 +85,14 @@ private extension MainTabBarCoordinator { } func makeSwipingFlow() -> ViewControllerCoordinator { - let swipingNavigationCoordinator = SwipingViewNavigationCoordinator() + let swipingNavigationCoordinator = SwipingViewNavigationCoordinator(container: container) startChildCoordinator(swipingNavigationCoordinator) swipingNavigationCoordinator.rootViewController.tabBarItem = UITabBarItem(title: "Random", image: UIImage(systemName: "switch.2"), tag: 1) return swipingNavigationCoordinator } func makeProfileFlow() -> ViewControllerCoordinator { - let profileNavigationCoordinator = ProfileNavigationCoordinator() + let profileNavigationCoordinator = ProfileNavigationCoordinator(container: container) startChildCoordinator(profileNavigationCoordinator) profileNavigationCoordinator.eventPublisher.sink { [weak self] event in self?.handle(event) diff --git a/App/Course App/Scenes/Onboarding/Navigation/OnboardingCoordinatorPresenting.swift b/App/Course App/Scenes/Onboarding/Navigation/OnboardingCoordinatorPresenting.swift index 26ceaad..6a5ed61 100644 --- a/App/Course App/Scenes/Onboarding/Navigation/OnboardingCoordinatorPresenting.swift +++ b/App/Course App/Scenes/Onboarding/Navigation/OnboardingCoordinatorPresenting.swift @@ -14,7 +14,7 @@ protocol OnboardingCoordinatorPresenting { extension OnboardingCoordinatorPresenting where Self: Coordinator, Self: CancellablesContaining, Self: UINavigationControllerDelegate { func makeOnboardingFlow(navigationController: UINavigationController? = nil) -> ViewControllerCoordinator { - let coordinator = OnboardingNavigationCoordinator(navigationController: navigationController) + let coordinator = OnboardingNavigationCoordinator(container: container, navigationController: navigationController) startChildCoordinator(coordinator) if navigationController != nil { diff --git a/App/Course App/Scenes/Onboarding/Navigation/OnboardingNavigationCoordinator.swift b/App/Course App/Scenes/Onboarding/Navigation/OnboardingNavigationCoordinator.swift index 6b1f23d..c01d768 100644 --- a/App/Course App/Scenes/Onboarding/Navigation/OnboardingNavigationCoordinator.swift +++ b/App/Course App/Scenes/Onboarding/Navigation/OnboardingNavigationCoordinator.swift @@ -6,6 +6,7 @@ // import Combine +import DependencyInjection import os import SwiftUI import UIKit @@ -20,12 +21,14 @@ final class OnboardingNavigationCoordinator: NavigationControllerCoordinator, Ca // MARK: Public properties var cancellables = Set() var childCoordinators = [Coordinator]() + var container: Container deinit { logger.info("Deinit OnboardingNavigationCoordinator") } - init(navigationController: UINavigationController? = nil) { + init(container: Container, navigationController: UINavigationController? = nil) { + self.container = container if let navigationController { isPushNavigation = true self.navigationController = navigationController diff --git a/App/Course App/Scenes/Onboarding/OnboardingView.swift b/App/Course App/Scenes/Onboarding/OnboardingView.swift index 7ac61d2..f8cef9a 100644 --- a/App/Course App/Scenes/Onboarding/OnboardingView.swift +++ b/App/Course App/Scenes/Onboarding/OnboardingView.swift @@ -6,6 +6,7 @@ // import Combine +import DesignSystem import SwiftUI struct OnboardingView: View { diff --git a/App/Course App/Scenes/Profile/Navigation/ProfileNavigationCoordinator.swift b/App/Course App/Scenes/Profile/Navigation/ProfileNavigationCoordinator.swift index ab272b6..3314037 100644 --- a/App/Course App/Scenes/Profile/Navigation/ProfileNavigationCoordinator.swift +++ b/App/Course App/Scenes/Profile/Navigation/ProfileNavigationCoordinator.swift @@ -6,6 +6,7 @@ // import Combine +import DependencyInjection import os import SwiftUI import UIKit @@ -19,11 +20,16 @@ final class ProfileNavigationCoordinator: NSObject, NavigationControllerCoordina // MARK: Public properties var cancellables = Set() var childCoordinators = [Coordinator]() + var container: Container // MARK: Lifecycle deinit { logger.info("Deinit ProfileNavigationCoordinator") } + + init(container: Container) { + self.container = container + } } // MARK: - Start coordinator diff --git a/App/Course App/Scenes/SignIn/Navigation/SignInNavigationCoordinator.swift b/App/Course App/Scenes/SignIn/Navigation/SignInNavigationCoordinator.swift index 7a24549..6d74dfb 100644 --- a/App/Course App/Scenes/SignIn/Navigation/SignInNavigationCoordinator.swift +++ b/App/Course App/Scenes/SignIn/Navigation/SignInNavigationCoordinator.swift @@ -6,6 +6,7 @@ // import Combine +import DependencyInjection import os import SwiftUI import UIKit @@ -19,10 +20,16 @@ final class SignInNavigationCoordinator: NavigationControllerCoordinator, Cancel // MARK: Public properties var childCoordinators = [Coordinator]() var cancellables = Set() + var container: Container + // MARK: Lifecycle deinit { logger.info("Deinit SignInVIew") } + + init(container: Container) { + self.container = container + } } // MARK: - Start coordinator @@ -46,12 +53,12 @@ private extension SignInNavigationCoordinator { } func makeSignInView() -> UIViewController { - let signInView = SignInView() - signInView.eventPublisher.sink { [weak self] event in + let store = container.resolve(type: SignInViewStore.self) + store.eventPublisher.sink { [weak self] event in self?.handle(event) } .store(in: &cancellables) - return UIHostingController(rootView: signInView) + return UIHostingController(rootView: SignInView(store: store)) } } diff --git a/App/Course App/Scenes/SignIn/SignInView.swift b/App/Course App/Scenes/SignIn/SignInView.swift index fe4ff1d..83f563a 100644 --- a/App/Course App/Scenes/SignIn/SignInView.swift +++ b/App/Course App/Scenes/SignIn/SignInView.swift @@ -10,53 +10,27 @@ import os import SwiftUI struct SignInView: View { - // MARK: Private properties - @State private var emailField: String - @State private var passwordField: String - private let eventSubject = PassthroughSubject() - private let authManager = FirebaseAuthManager() - private let logger = Logger() + @StateObject private var store: SignInViewStore - // MARK: Lifecycle - init(emailField: String = "Test@test.test", passwordField: String = "test123") { - self.emailField = emailField - self.passwordField = passwordField + init(store: SignInViewStore) { + _store = .init(wrappedValue: store) } var body: some View { Form { - TextField("Email", text: $emailField) - SecureField("password", text: $passwordField) + TextField("Email", text: $store.viewState.emailField) + SecureField("password", text: $store.viewState.passwordField) Button("SignIn") { - signIn() + store.send(action: .signInButtonTapped) } } .navigationTitle("SignIn") - } -} - -// MARK: - Event emitter -extension SignInView: EventEmitting { - var eventPublisher: AnyPublisher { - eventSubject.eraseToAnyPublisher() - } -} - -// MARK: - Functions -private extension SignInView { - @MainActor - func signIn() { - Task { - do { - try await authManager.signIn(Credentials(email: emailField, password: passwordField)) - eventSubject.send(.signedIn) - } catch { - logger.info("ERROR: \(error)") - } + .onFirstAppear { + store.send(action: .viewDidLoad) } } } #Preview { - SignInView() + SignInView(store: SignInViewStore(authManager: FirebaseAuthManager())) } diff --git a/App/Course App/Scenes/SignIn/Store/SignInViewAction.swift b/App/Course App/Scenes/SignIn/Store/SignInViewAction.swift new file mode 100644 index 0000000..76dd884 --- /dev/null +++ b/App/Course App/Scenes/SignIn/Store/SignInViewAction.swift @@ -0,0 +1,13 @@ +// +// SignInViewAction.swift +// Course App +// +// Created by Tomáš Duchoslav on 17.06.2024. +// + +import Foundation + +enum SignInViewAction { + case signInButtonTapped + case viewDidLoad +} diff --git a/App/Course App/Scenes/SignIn/Store/SignInViewState.swift b/App/Course App/Scenes/SignIn/Store/SignInViewState.swift new file mode 100644 index 0000000..1429a86 --- /dev/null +++ b/App/Course App/Scenes/SignIn/Store/SignInViewState.swift @@ -0,0 +1,23 @@ +// +// SignInViewState.swift +// Course App +// +// Created by Tomáš Duchoslav on 17.06.2024. +// + +import Foundation + +struct SignInViewState { + enum Status { + case initial + case signingIn + case ready + case loading + } + + var emailField: String = "" + var passwordField: String = "" + var status: Status = .initial + + static let initial = SignInViewState() +} diff --git a/App/Course App/Scenes/SignIn/Store/SignInViewStore.swift b/App/Course App/Scenes/SignIn/Store/SignInViewStore.swift new file mode 100644 index 0000000..560aaad --- /dev/null +++ b/App/Course App/Scenes/SignIn/Store/SignInViewStore.swift @@ -0,0 +1,59 @@ +// +// SignInViewStore.swift +// Course App +// +// Created by Tomáš Duchoslav on 17.06.2024. +// + +import Combine +import Foundation +import os + +final class SignInViewStore: Store, ObservableObject { + // MARK: Private properties + private let eventSubject = PassthroughSubject() + private let authManager: FirebaseAuthManaging + private let logger = Logger() + + @Published var viewState: SignInViewState = .initial + + init(authManager: FirebaseAuthManaging) { + self.authManager = authManager + } +} + +// MARK: - Event emitter +extension SignInViewStore: EventEmitting { + var eventPublisher: AnyPublisher { + eventSubject.eraseToAnyPublisher() + } +} + +// MARK: - Sending actions +extension SignInViewStore { + @MainActor + func send(action: SignInViewAction) { + switch action { + case .signInButtonTapped: + signIn() + case .viewDidLoad: + viewState.emailField = "Test@test.test" + viewState.passwordField = "test123" + } + } +} + +// MARK: - Functions +private extension SignInViewStore { + @MainActor + func signIn() { + Task { + do { + try await authManager.signIn(Credentials(email: viewState.emailField, password: viewState.passwordField)) + eventSubject.send(.signedIn) + } catch { + logger.info("ERROR: \(error)") + } + } + } +} diff --git a/App/Course App/Scenes/SwipingView/Navigation/SwipingViewCoordinating.swift b/App/Course App/Scenes/SwipingView/Navigation/SwipingViewCoordinating.swift new file mode 100644 index 0000000..5a70e48 --- /dev/null +++ b/App/Course App/Scenes/SwipingView/Navigation/SwipingViewCoordinating.swift @@ -0,0 +1,25 @@ +// +// SwipingViewCoordinating.swift +// Course App +// +// Created by Tomáš Duchoslav on 12.06.2024. +// + +import SwiftUI +import UIKit + +protocol SwipingViewCoordinating: ViewControllerCoordinator { + func makeSwipingView(with joke: Joke?, isChildCoordinator: Bool) -> UIViewController +} + +extension SwipingViewCoordinating where Self: CancellablesContaining, Self: NavigationControllerCoordinator { + func makeSwipingView(with joke: Joke? = nil, isChildCoordinator: Bool = false) -> UIViewController { + let store = container.resolve(type: SwipingViewStore.self) + store.eventPublisher.sink { [weak self] _ in + self?.navigationController.popToRootViewController(animated: true) + } + .store(in: &cancellables) + + return UIHostingController(rootView: SwipingView(store: store)) + } +} diff --git a/App/Course App/Scenes/SwipingView/Navigation/SwipingViewFactory.swift b/App/Course App/Scenes/SwipingView/Navigation/SwipingViewFactory.swift deleted file mode 100644 index c8395c9..0000000 --- a/App/Course App/Scenes/SwipingView/Navigation/SwipingViewFactory.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// SwipingViewFactory.swift -// Course App -// -// Created by Tomáš Duchoslav on 12.06.2024. -// - -import SwiftUI -import UIKit - -protocol SwipingViewFactory { - func makeSwipingView(with joke: Joke?) -> UIViewController -} - -extension SwipingViewFactory { - func makeSwipingView(with joke: Joke? = nil) -> UIViewController { - UIHostingController(rootView: SwipingView(joke: joke)) - } -} diff --git a/App/Course App/Scenes/SwipingView/Navigation/SwipingViewNavigationCoordinator.swift b/App/Course App/Scenes/SwipingView/Navigation/SwipingViewNavigationCoordinator.swift index e174923..50cc1c7 100644 --- a/App/Course App/Scenes/SwipingView/Navigation/SwipingViewNavigationCoordinator.swift +++ b/App/Course App/Scenes/SwipingView/Navigation/SwipingViewNavigationCoordinator.swift @@ -5,22 +5,30 @@ // Created by Tomáš Duchoslav on 30.05.2024. // +import Combine +import DependencyInjection import os import SwiftUI import UIKit -final class SwipingViewNavigationCoordinator: NavigationControllerCoordinator { +final class SwipingViewNavigationCoordinator: NavigationControllerCoordinator, CancellablesContaining, SwipingViewCoordinating { // MARK: Private properties private(set) lazy var navigationController: UINavigationController = CustomNavigationController() private let logger = Logger() // MARK: Public Properties var childCoordinators = [Coordinator]() + var container: Container + var cancellables = Set() // MARK: Lifecycle deinit { logger.info("Deinit SwipingViewNavigationCoordinator") } + + init(container: Container) { + self.container = container + } } // MARK: - Start coordinator @@ -29,10 +37,3 @@ extension SwipingViewNavigationCoordinator { navigationController.setViewControllers([makeSwipingView()], animated: false) } } - -// MARK: - Factory methods -private extension SwipingViewNavigationCoordinator { - func makeSwipingView() -> UIViewController { - UIHostingController(rootView: SwipingView()) - } -} diff --git a/App/Course App/Scenes/SwipingView/Store/SwipingViewAction.swift b/App/Course App/Scenes/SwipingView/Store/SwipingViewAction.swift new file mode 100644 index 0000000..80f7e4a --- /dev/null +++ b/App/Course App/Scenes/SwipingView/Store/SwipingViewAction.swift @@ -0,0 +1,15 @@ +// +// SwipingViewAction.swift +// Course App +// +// Created by Tomáš Duchoslav on 15.06.2024. +// + +import Foundation + +enum SwipingViewAction { + case viewDidLoad + case dataLoaded([Joke]) + case didLike(Joke, Bool) + case noMoreJokes +} diff --git a/App/Course App/Scenes/SwipingView/Store/SwipingViewState.swift b/App/Course App/Scenes/SwipingView/Store/SwipingViewState.swift new file mode 100644 index 0000000..85425f2 --- /dev/null +++ b/App/Course App/Scenes/SwipingView/Store/SwipingViewState.swift @@ -0,0 +1,21 @@ +// +// SwipingViewState.swift +// Course App +// +// Created by Tomáš Duchoslav on 15.06.2024. +// + +import Foundation + +struct SwipingViewState { + enum Status { + case initial + case loading + case ready + } + + var jokes: [Joke] = [] + var status: Status = .initial + + static let initial = SwipingViewState() +} diff --git a/App/Course App/Scenes/SwipingView/Store/SwipingViewStore.swift b/App/Course App/Scenes/SwipingView/Store/SwipingViewStore.swift new file mode 100644 index 0000000..725ff13 --- /dev/null +++ b/App/Course App/Scenes/SwipingView/Store/SwipingViewStore.swift @@ -0,0 +1,126 @@ +// +// SwipingViewStore.swift +// Course App +// +// Created by Tomáš Duchoslav on 14.06.2024. +// + +import Combine +import Foundation +import os + +final class SwipingViewStore: Store, ObservableObject { + // MARK: Public properties + @Published var viewState: SwipingViewState = .initial + + // MARK: Private properties + private let jokeService: JokeServicing + private let storage: StorageManaging + private let keychainService: KeychainServicing + private let logger = Logger() + private var category: String? = nil + private let eventSubject = PassthroughSubject() + private var isChildCoordinator = false + + init(/*joke: Joke? = nil, isChildCoordinator: Bool = false,*/ storage: StorageManaging, keychainSevice: KeychainServicing, jokeService: JokeServicing) { + self.storage = storage + self.keychainService = keychainSevice + self.jokeService = jokeService +// self.isChildCoordinator = isChildCoordinator +// self.category = joke?.categories.first +// if let joke { +// viewState.jokes.append(joke) +// } + } +} + +// MARK: - Event emitter +extension SwipingViewStore: EventEmitting { + var eventPublisher: AnyPublisher { + eventSubject.eraseToAnyPublisher() + } +} + + +// MARK: - Action sender +extension SwipingViewStore { + @MainActor + func send(action: SwipingViewAction) { + switch action { + case .viewDidLoad: + viewState.status = .loading + loadRandomJokes() + case let .dataLoaded(jokes): + viewState.jokes.append(contentsOf: jokes) + viewState.status = .ready + case let .didLike(joke, liked): + storeJokeLike(joke: joke, liked: liked) + removeCard(of: joke) + if viewState.jokes.isEmpty { + send(action: .noMoreJokes) + } + case .noMoreJokes: + if isChildCoordinator { + eventSubject.send(.dismiss) + } else { + viewState.status = .loading + loadRandomJokes() + } + } + } +} + +// MARK: - Functions +private extension SwipingViewStore { + // MARK: Loading random jokes + func loadRandomJokes() { + Task { + try await withThrowingTaskGroup(of: JokeResponse.self) { [weak self] group in + guard let self else { + return + } + + // swiftlint:disable:next no_magic_numbers + for _ in 1...5 { + group.addTask { + if let category = self.category { + try await self.jokeService.loadJokeForCategory(category) + } else { + try await self.jokeService.loadRandomJoke() + } + } + } + + var jokes: [Joke] = [] + for try await jokeResponse in group { + let isLiked = try await storage.loadLiked(jokeId: jokeResponse.id) + jokes.append(Joke(jokeResponse: jokeResponse, isLiked: isLiked)) + } + + await send(action: .dataLoaded(jokes)) + } + } + } + + // MARK: Storing joke like + func storeJokeLike(joke: Joke, liked: Bool) { + Task { + try await storage.storeLike(jokeId: joke.jokeID, liked: liked) + } + } + + // MARK: Remove joke + @MainActor + func removeCard(of joke: Joke) { + if let index = viewState.jokes.firstIndex(of: joke) { + loggerInfo(message: "INFO: Card number \(index) removed from Jokes array") + viewState.jokes.remove(at: index) + loggerInfo(message: "INFO: Cards count is \(viewState.jokes.count)") + } + } + + // MARK: Logger + func loggerInfo(message: String) { + logger.info("\(message)") + } +} diff --git a/App/Course App/Scenes/SwipingView/SwipingView.swift b/App/Course App/Scenes/SwipingView/SwipingView.swift index 1b07258..8cf6d2d 100644 --- a/App/Course App/Scenes/SwipingView/SwipingView.swift +++ b/App/Course App/Scenes/SwipingView/SwipingView.swift @@ -14,121 +14,61 @@ struct SwipingView: View { static let paddingDivider: CGFloat = 20 static let sizeDivider = 1.2 static let sizeWidthMultiplicator = 1.5 - static let fiveCard = 5 } // MARK: Private properties - private let logger = Logger() - private let jokeService = JokeService(apiManager: APIManager()) - private let category: String? - @State private var jokes: [Joke] = [] - private let storage = StorageManager() + @StateObject private var store: SwipingViewStore - init(joke: Joke? = nil) { - self.category = joke?.categories.first - if let joke { - self.jokes.append(joke) - } + init(store: SwipingViewStore) { + _store = .init(wrappedValue: store) } // MARK: View var body: some View { GeometryReader { geometry in - HStack { - Spacer() - - VStack { - ZStack { - ForEach(jokes, id: \.self) { joke in - SwipingCard( - configuration: SwipingCard.Configuration( - title: joke.categories.first ?? "", - description: joke.text, - isLiked: joke.isLiked ?? false - ), - swipeStateAction: { action in - switch action { - case let .finished(direction): - storeJokeLike(jokeId: joke.jokeID, liked: direction == .left) - removeCard(of: joke) - checkCardStack() - default: - break + if store.viewState.status == .loading { + ProgressView() + .progressViewStyle(.circular) + } else { + HStack { + Spacer() + + VStack { + ZStack { + ForEach(store.viewState.jokes, id: \.self) { joke in + SwipingCard( + configuration: SwipingCard.Configuration( + title: joke.categories.first ?? "", + description: joke.text, + isLiked: joke.isLiked ?? false + ), + swipeStateAction: { action in + switch action { + case let .finished(direction): + store.send(action: .didLike(joke, direction == .left)) + default: + break + } } - } - ) + ) + } } + .padding(.top, geometry.size.height / Constants.paddingDivider) + .frame(width: geometry.size.width / Constants.sizeDivider, height: (geometry.size.width / Constants.sizeDivider) * Constants.sizeWidthMultiplicator) + + Spacer() } - .padding(.top, geometry.size.height / Constants.paddingDivider) - .frame(width: geometry.size.width / Constants.sizeDivider, height: (geometry.size.width / Constants.sizeDivider) * Constants.sizeWidthMultiplicator) - Spacer() } - Spacer() } } .navigationTitle("Random") .onFirstAppear { - loadRandomJokes() + store.send(action: .viewDidLoad) } } } -#Preview { - SwipingView() -} - -// MARK: - Functions -extension SwipingView { - // MARK: Loading jokes - func loadRandomJokes() { - Task { - try await withThrowingTaskGroup(of: JokeResponse.self) { group in - // swiftlint:disable:next no_magic_numbers - for _ in 1...10 { - group.addTask { - if let category { - try await jokeService.loadJokeForCategory(category) - } else { - try await jokeService.loadRandomJoke() - } - } - } - - for try await jokeResponse in group { - let isLiked = try await storage.liked(jokeId: jokeResponse.id) - jokes.append(Joke(jokeResponse: jokeResponse, isLiked: isLiked)) - } - logger.info("INFO: Count cards is \(jokes.count).") - } - } - } - - // MARK: Storing joke like - func storeJokeLike(jokeId: String, liked: Bool) { - Task { - try await storage.storeLike(jokeId: jokeId, liked: liked) - } - } - - // MARK: Check for jokes count - func checkCardStack() { - if jokes.count <= Constants.fiveCard { - loadRandomJokes() - } - } - - // MARK: Remove joke - func removeCard(of joke: Joke) { - if let index = jokes.firstIndex(of: joke) { - loggerInfo(message: "INFO: Card number \(index) removed from Jokes array") - jokes.remove(at: index) - loggerInfo(message: "INFO: Cards count is \(jokes.count)") - } - } - - // MARK: Logger - func loggerInfo(message: String) { - logger.info("\(message)") - } -} +//#Preview { +// SwipingView(store: SwipingViewStore()) +//} diff --git a/App/Course App/Scenes/SwipingView/SwipingViewEvent.swift b/App/Course App/Scenes/SwipingView/SwipingViewEvent.swift new file mode 100644 index 0000000..446ce01 --- /dev/null +++ b/App/Course App/Scenes/SwipingView/SwipingViewEvent.swift @@ -0,0 +1,12 @@ +// +// SwipingViewEvent.swift +// Course App +// +// Created by Tomáš Duchoslav on 15.06.2024. +// + +import Foundation + +enum SwipingViewEvent { + case dismiss +} diff --git a/App/Course App/SharedUI/Extensions/UINavigationController+NavBarUI.swift b/App/Course App/SharedUI/Extensions/UINavigationController+NavBarUI.swift index 41312dd..18e570e 100644 --- a/App/Course App/SharedUI/Extensions/UINavigationController+NavBarUI.swift +++ b/App/Course App/SharedUI/Extensions/UINavigationController+NavBarUI.swift @@ -5,6 +5,7 @@ // Created by Tomáš Duchoslav on 24.05.2024. // +import DesignSystem import UIKit extension UINavigationController { diff --git a/App/Course App/SharedUI/Styles/NavigationButtonStyle.swift b/App/Course App/SharedUI/Styles/NavigationButtonStyle.swift index c8f7628..26a64d1 100644 --- a/App/Course App/SharedUI/Styles/NavigationButtonStyle.swift +++ b/App/Course App/SharedUI/Styles/NavigationButtonStyle.swift @@ -5,6 +5,7 @@ // Created by Tomáš Duchoslav on 01.06.2024. // +import DesignSystem import SwiftUI struct NavigationButtonStyle: ButtonStyle { diff --git a/App/Course App/SharedUI/ViewModifiers/TextStyle.swift b/App/Course App/SharedUI/ViewModifiers/TextStyle.swift index 33ded6e..3876159 100644 --- a/App/Course App/SharedUI/ViewModifiers/TextStyle.swift +++ b/App/Course App/SharedUI/ViewModifiers/TextStyle.swift @@ -5,6 +5,7 @@ // Created by Tomáš Duchoslav on 24.05.2024. // +import DesignSystem import SwiftUI private struct TextStyle: ViewModifier { diff --git a/App/Course App/SharedUI/Views/SwiftUI/ScratchView.swift b/App/Course App/SharedUI/Views/SwiftUI/ScratchView.swift index 99af11b..1fe6d82 100644 --- a/App/Course App/SharedUI/Views/SwiftUI/ScratchView.swift +++ b/App/Course App/SharedUI/Views/SwiftUI/ScratchView.swift @@ -5,6 +5,7 @@ // Created by Tomáš Duchoslav on 22.05.2024. // +import DesignSystem import SwiftUI struct Line { diff --git a/App/Course App/SharedUI/Views/SwiftUI/SwipingCard.swift b/App/Course App/SharedUI/Views/SwiftUI/SwipingCard.swift index 43fb9d6..d3067ba 100644 --- a/App/Course App/SharedUI/Views/SwiftUI/SwipingCard.swift +++ b/App/Course App/SharedUI/Views/SwiftUI/SwipingCard.swift @@ -5,6 +5,7 @@ // Created by Tomáš Duchoslav on 22.05.2024. // +import DesignSystem import SwiftUI struct SwipingCard: View { diff --git a/App/DesignSystem/.gitignore b/App/DesignSystem/.gitignore new file mode 100644 index 0000000..0023a53 --- /dev/null +++ b/App/DesignSystem/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/App/DesignSystem/Package.swift b/App/DesignSystem/Package.swift new file mode 100644 index 0000000..75225e3 --- /dev/null +++ b/App/DesignSystem/Package.swift @@ -0,0 +1,24 @@ +// swift-tools-version: 5.10 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "DesignSystem", + platforms: [.iOS(.v16)], + products: [ + // Products define the executables and libraries a package produces, making them visible to other packages. + .library( + name: "DesignSystem", + targets: ["DesignSystem"]), + ], + targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products from dependencies. + .target( + name: "DesignSystem"), + .testTarget( + name: "DesignSystemTests", + dependencies: ["DesignSystem"]), + ] +) diff --git a/App/Course App/SharedUI/DesignSystem/CornerRadiusSize.swift b/App/DesignSystem/Sources/DesignSystem/CornerRadiusSize.swift similarity index 80% rename from App/Course App/SharedUI/DesignSystem/CornerRadiusSize.swift rename to App/DesignSystem/Sources/DesignSystem/CornerRadiusSize.swift index 7b6aa34..1ee3235 100644 --- a/App/Course App/SharedUI/DesignSystem/CornerRadiusSize.swift +++ b/App/DesignSystem/Sources/DesignSystem/CornerRadiusSize.swift @@ -7,7 +7,7 @@ import Foundation -enum CornerRadiusSize: CGFloat { +public enum CornerRadiusSize: CGFloat { case `default` = 10 case extra = 15 } diff --git a/App/Course App/SharedUI/Extensions/Font+AppFonts.swift b/App/DesignSystem/Sources/DesignSystem/Font+AppFonts.swift similarity index 95% rename from App/Course App/SharedUI/Extensions/Font+AppFonts.swift rename to App/DesignSystem/Sources/DesignSystem/Font+AppFonts.swift index 172445d..4f46647 100644 --- a/App/Course App/SharedUI/Extensions/Font+AppFonts.swift +++ b/App/DesignSystem/Sources/DesignSystem/Font+AppFonts.swift @@ -7,7 +7,7 @@ import SwiftUI -extension Font { +public extension Font { static func regular(with size: FontSize) -> Font { Font.custom(FontType.regular.rawValue, size: size.rawValue) } diff --git a/App/Course App/SharedUI/DesignSystem/FontSize.swift b/App/DesignSystem/Sources/DesignSystem/FontSize.swift similarity index 86% rename from App/Course App/SharedUI/DesignSystem/FontSize.swift rename to App/DesignSystem/Sources/DesignSystem/FontSize.swift index 303bc57..d9e9d26 100644 --- a/App/Course App/SharedUI/DesignSystem/FontSize.swift +++ b/App/DesignSystem/Sources/DesignSystem/FontSize.swift @@ -7,7 +7,7 @@ import Foundation -enum FontSize: CGFloat { +public enum FontSize: CGFloat { case size28 = 28 case size22 = 22 case size18 = 18 diff --git a/App/Course App/SharedUI/DesignSystem/FontType.swift b/App/DesignSystem/Sources/DesignSystem/FontType.swift similarity index 88% rename from App/Course App/SharedUI/DesignSystem/FontType.swift rename to App/DesignSystem/Sources/DesignSystem/FontType.swift index 6a07b26..8adaceb 100644 --- a/App/Course App/SharedUI/DesignSystem/FontType.swift +++ b/App/DesignSystem/Sources/DesignSystem/FontType.swift @@ -7,7 +7,7 @@ import Foundation -enum FontType: String { +public enum FontType: String { case regular = "Poppins-Regular" case bold = "Poppins-Bold" case mediumItalic = "Poppins-MediumItalic" diff --git a/App/Course App/SharedUI/DesignSystem/OpacityVisibility.swift b/App/DesignSystem/Sources/DesignSystem/OpacityVisibility.swift similarity index 80% rename from App/Course App/SharedUI/DesignSystem/OpacityVisibility.swift rename to App/DesignSystem/Sources/DesignSystem/OpacityVisibility.swift index f20222c..99dac4f 100644 --- a/App/Course App/SharedUI/DesignSystem/OpacityVisibility.swift +++ b/App/DesignSystem/Sources/DesignSystem/OpacityVisibility.swift @@ -7,7 +7,7 @@ import Foundation -enum OpacityVisibility: CGFloat { +public enum OpacityVisibility: CGFloat { case `default` = 1 case half = 0.5 } diff --git a/App/Course App/SharedUI/DesignSystem/PaddingSize.swift b/App/DesignSystem/Sources/DesignSystem/PaddingSize.swift similarity index 82% rename from App/Course App/SharedUI/DesignSystem/PaddingSize.swift rename to App/DesignSystem/Sources/DesignSystem/PaddingSize.swift index 013a4a6..40fe0f8 100644 --- a/App/Course App/SharedUI/DesignSystem/PaddingSize.swift +++ b/App/DesignSystem/Sources/DesignSystem/PaddingSize.swift @@ -7,7 +7,7 @@ import Foundation -enum PaddingSize: CGFloat { +public enum PaddingSize: CGFloat { case `default` = 10 case extra = 15 } diff --git a/App/Course App/SharedUI/DesignSystem/TextType.swift b/App/DesignSystem/Sources/DesignSystem/TextType.swift similarity index 92% rename from App/Course App/SharedUI/DesignSystem/TextType.swift rename to App/DesignSystem/Sources/DesignSystem/TextType.swift index f50e5a8..0a73fbe 100644 --- a/App/Course App/SharedUI/DesignSystem/TextType.swift +++ b/App/DesignSystem/Sources/DesignSystem/TextType.swift @@ -8,7 +8,7 @@ import SwiftUI import UIKit -enum TextType { +public enum TextType { case navigationTitle case sectionTitle case baseText @@ -16,7 +16,7 @@ enum TextType { } // MARK: - TextType attributes SwiftUI -extension TextType { +public extension TextType { var font: Font { switch self { case .navigationTitle: @@ -36,7 +36,7 @@ extension TextType { } // MARK: - TextType attributes UIKit -extension TextType { +public extension TextType { var uiFont: UIFont { switch self { case .navigationTitle: diff --git a/App/Course App/SharedUI/Extensions/UIFont+AppFonts.swift b/App/DesignSystem/Sources/DesignSystem/UIFont+AppFonts.swift similarity index 95% rename from App/Course App/SharedUI/Extensions/UIFont+AppFonts.swift rename to App/DesignSystem/Sources/DesignSystem/UIFont+AppFonts.swift index 9cfa32b..c11fa3a 100644 --- a/App/Course App/SharedUI/Extensions/UIFont+AppFonts.swift +++ b/App/DesignSystem/Sources/DesignSystem/UIFont+AppFonts.swift @@ -7,7 +7,7 @@ import UIKit // swiftlint:disable force_unwrapping -extension UIFont { +public extension UIFont { static func regular(with size: FontSize) -> UIFont { UIFont(name: FontType.regular.rawValue, size: size.rawValue)! } diff --git a/App/DesignSystem/Tests/DesignSystemTests/DesignSystemTests.swift b/App/DesignSystem/Tests/DesignSystemTests/DesignSystemTests.swift new file mode 100644 index 0000000..ae32aef --- /dev/null +++ b/App/DesignSystem/Tests/DesignSystemTests/DesignSystemTests.swift @@ -0,0 +1,12 @@ +import XCTest +@testable import DesignSystem + +final class DesignSystemTests: XCTestCase { + func testExample() throws { + // XCTest Documentation + // https://developer.apple.com/documentation/xctest + + // Defining Test Cases and Test Methods + // https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods + } +}