diff --git a/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj index 98448be5..f8905f3d 100644 --- a/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj +++ b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj @@ -14,10 +14,9 @@ 092C09B52A484DD900E9B06B /* HomeDeleteViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 092C09B42A484DD900E9B06B /* HomeDeleteViewController.swift */; }; 092C09B72A48596500E9B06B /* DeleteModalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 092C09B62A48596500E9B06B /* DeleteModalView.swift */; }; 092E04B129BD9C86008A5892 /* MissionDetailCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 092E04B029BD9C86008A5892 /* MissionDetailCollectionViewCell.swift */; }; - 0930D37329B4FCAE0000C4AE /* StatisticsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0930D37229B4FCAE0000C4AE /* StatisticsView.swift */; }; 0930DE6229B80550007958DE /* MissionDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0930DE6129B80550007958DE /* MissionDetailViewController.swift */; }; 093DB0372A146BF900ECA5F6 /* MyInfoURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 093DB0362A146BF900ECA5F6 /* MyInfoURL.swift */; }; - 093DB03D2A15FC7800ECA5F6 /* AchieveCalendarResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 093DB03C2A15FC7800ECA5F6 /* AchieveCalendarResponseDTO.swift */; }; + 093DB03D2A15FC7800ECA5F6 /* CalendarReponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 093DB03C2A15FC7800ECA5F6 /* CalendarReponseDTO.swift */; }; 093DB03F2A15FCC100ECA5F6 /* MissionDetailResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 093DB03E2A15FCC100ECA5F6 /* MissionDetailResponseDTO.swift */; }; 0943A9F52A531D0000614761 /* Amplitude in Frameworks */ = {isa = PBXBuildFile; productRef = 0943A9F42A531D0000614761 /* Amplitude */; }; 0943A9F92A53239200614761 /* Bundle+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0943A9F82A53239200614761 /* Bundle+.swift */; }; @@ -26,6 +25,8 @@ 09582B4D29BE277800EF3207 /* DetailHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09582B4C29BE277800EF3207 /* DetailHeaderView.swift */; }; 09582B4F29BEBAFA00EF3207 /* DetailCalendarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09582B4E29BEBAFA00EF3207 /* DetailCalendarViewController.swift */; }; 09582B5129C0BC3600EF3207 /* DetailAchievementViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09582B5029C0BC3600EF3207 /* DetailAchievementViewController.swift */; }; + 095FEE122B9ED15600FF44C0 /* DetailAchieveHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 095FEE102B9ED15600FF44C0 /* DetailAchieveHeaderView.swift */; }; + 095FEE132B9ED15600FF44C0 /* StatisticsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 095FEE112B9ED15600FF44C0 /* StatisticsView.swift */; }; 0960C0D42A38BC6500A3D8DB /* KeychainUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0960C0D32A38BC6500A3D8DB /* KeychainUtil.swift */; }; 0960C0D62A38BC8100A3D8DB /* DefaultKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0960C0D52A38BC8100A3D8DB /* DefaultKeys.swift */; }; 0964BA4A2B0F6BFB00A8984B /* AuthInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0964BA492B0F6BFB00A8984B /* AuthInterceptor.swift */; }; @@ -59,12 +60,35 @@ 099FC98129B3094F005B37E6 /* WeekMonthFSCalendar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 099FC98029B3094F005B37E6 /* WeekMonthFSCalendar.swift */; }; 099FC98329B30A2E005B37E6 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 099FC98229B30A2E005B37E6 /* Utils.swift */; }; 099FC98929B3233D005B37E6 /* CalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 099FC98829B3233D005B37E6 /* CalendarView.swift */; }; - 09A1465F2A192C4900DDC308 /* WeekMissionResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09A1465E2A192C4900DDC308 /* WeekMissionResponseDTO.swift */; }; 09A146652A1964B500DDC308 /* AddAnotherDayResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09A146642A19649A00DDC308 /* AddAnotherDayResponseDTO.swift */; }; - 09A8E48E2B9DBEC700C0F48F /* BaseAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09A8E48D2B9DBEC700C0F48F /* BaseAPI.swift */; }; + 09A8E48E2B9DBEC700C0F48F /* BaseService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09A8E48D2B9DBEC700C0F48F /* BaseService.swift */; }; 09C8602D2AB14B4800C4F4B1 /* FSCalendar in Frameworks */ = {isa = PBXBuildFile; productRef = 09C8602C2AB14B4800C4F4B1 /* FSCalendar */; }; - 09CF56022B09E98A00526C8C /* DetailAchieveHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09CF56012B09E98A00526C8C /* DetailAchieveHeaderView.swift */; }; 09CF56042B09F23800526C8C /* HomeDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09CF56032B09F23800526C8C /* HomeDataSource.swift */; }; + 09DB33E02BA2C0DF00B5F961 /* BaseAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DB33DF2BA2C0DF00B5F961 /* BaseAPI.swift */; }; + 09DC0EEE2BAEC1790075AAC9 /* API_KEY.plist in Resources */ = {isa = PBXBuildFile; fileRef = 09DC0EED2BAEC1790075AAC9 /* API_KEY.plist */; }; + 09DC0EF12BAEC1D70075AAC9 /* AuthFlowControllerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DC0EF02BAEC1D70075AAC9 /* AuthFlowControllerFactory.swift */; }; + 09DC0EF32BAEC1F10075AAC9 /* UpdateFlowcontrollerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DC0EF22BAEC1F10075AAC9 /* UpdateFlowcontrollerFactory.swift */; }; + 09DC0EF52BAEC2030075AAC9 /* HomeFlowControllerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DC0EF42BAEC2030075AAC9 /* HomeFlowControllerFactory.swift */; }; + 09DC0EF72BAEC2140075AAC9 /* MyPageFlowControllerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DC0EF62BAEC2140075AAC9 /* MyPageFlowControllerFactory.swift */; }; + 09DC0EF92BAEC2230075AAC9 /* AchieveFlowControllerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DC0EF82BAEC2230075AAC9 /* AchieveFlowControllerFactory.swift */; }; + 09DC0EFB2BAEC2340075AAC9 /* TabBarFlowControllerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DC0EFA2BAEC2340075AAC9 /* TabBarFlowControllerFactory.swift */; }; + 09DC0EFD2BAEC2710075AAC9 /* UpdateViewControllerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DC0EFC2BAEC2710075AAC9 /* UpdateViewControllerFactory.swift */; }; + 09DC0EFF2BAEC2750075AAC9 /* HomeViewControllerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DC0EFE2BAEC2750075AAC9 /* HomeViewControllerFactory.swift */; }; + 09DC0F012BAEC27A0075AAC9 /* AuthViewControllerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DC0F002BAEC27A0075AAC9 /* AuthViewControllerFactory.swift */; }; + 09DC0F032BAEC2800075AAC9 /* MyPageViewControllerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DC0F022BAEC2800075AAC9 /* MyPageViewControllerFactory.swift */; }; + 09DC0F052BAEC2850075AAC9 /* AchieveViewControllerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DC0F042BAEC2850075AAC9 /* AchieveViewControllerFactory.swift */; }; + 09DC0F072BAEC2890075AAC9 /* TabBarViewControllerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DC0F062BAEC2890075AAC9 /* TabBarViewControllerFactory.swift */; }; + 09DCB84D2BA0146800B6BB74 /* DetailAchievementViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCB84C2BA0146800B6BB74 /* DetailAchievementViewModel.swift */; }; + 09DCB84F2BA0147500B6BB74 /* ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCB84E2BA0147500B6BB74 /* ViewModel.swift */; }; + 09DCB8512BA0156400B6BB74 /* DetailAchievementViewModelImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCB8502BA0156400B6BB74 /* DetailAchievementViewModelImpl.swift */; }; + 09DCB8562BA0308D00B6BB74 /* APIError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCB8552BA0308D00B6BB74 /* APIError.swift */; }; + 09DCB8582BA0309F00B6BB74 /* ErrorReponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCB8572BA0309F00B6BB74 /* ErrorReponse.swift */; }; + 09DCB8602BA031E000B6BB74 /* AchieveManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCB85F2BA031E000B6BB74 /* AchieveManager.swift */; }; + 09DCB8622BA031F600B6BB74 /* AchieveManagerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCB8612BA031F600B6BB74 /* AchieveManagerImpl.swift */; }; + 09DCB8652BA056C800B6BB74 /* DetailAchievementModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCB8642BA056C800B6BB74 /* DetailAchievementModel.swift */; }; + 09DCB8672BA05F5400B6BB74 /* AchievementViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCB8662BA05F5400B6BB74 /* AchievementViewModel.swift */; }; + 09DCB8692BA05F9E00B6BB74 /* AchievementModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCB8682BA05F9E00B6BB74 /* AchievementModel.swift */; }; + 09DCB86B2BA0600600B6BB74 /* AchievementViewModelImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCB86A2BA0600600B6BB74 /* AchievementViewModelImpl.swift */; }; 09DCCD1F2A18ED76003DCF8A /* DailyMissionResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCCD1E2A18ED76003DCF8A /* DailyMissionResponseDTO.swift */; }; 09DCCD232A18EFB0003DCF8A /* MissionService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DCCD222A18EFB0003DCF8A /* MissionService.swift */; }; 09ED941B2B2ABAB7001864EF /* CommonNotificationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09ED941A2B2ABAB7001864EF /* CommonNotificationViewController.swift */; }; @@ -126,7 +150,6 @@ 3B4E12F82A27C12F001D1EC1 /* WithdrawModalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B4E12F72A27C12F001D1EC1 /* WithdrawModalView.swift */; }; 3B4E12FA2A27C4DD001D1EC1 /* Pretendard-Bold.otf in Resources */ = {isa = PBXBuildFile; fileRef = 3B4E12F92A27C4DD001D1EC1 /* Pretendard-Bold.otf */; }; 3B50CB212A40E75400F2E761 /* AddMissionResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B50CB202A40E75400F2E761 /* AddMissionResponseDTO.swift */; }; - 3B570B342BA30E6100418250 /* API_KEY.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B570B332BA30E6100418250 /* API_KEY.plist */; }; 3B5F8F7A29BF8E8D0063A7F8 /* AddMissionProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B5F8F7929BF8E8D0063A7F8 /* AddMissionProtocol.swift */; }; 3B5F8F7F29BF900A0063A7F8 /* DateCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B5F8F7E29BF900A0063A7F8 /* DateCollectionViewCell.swift */; }; 3B5F8F8129BF90190063A7F8 /* NottodoCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B5F8F8029BF90190063A7F8 /* NottodoCollectionViewCell.swift */; }; @@ -177,9 +200,6 @@ 6CF4705B29A68EA9008D145C /* URLConstant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF4705A29A68EA9008D145C /* URLConstant.swift */; }; 6CF4705D29A68F5C008D145C /* NetworkLoggerPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF4705C29A68F5C008D145C /* NetworkLoggerPlugin.swift */; }; 6CF4705F29A69025008D145C /* GeneralResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF4705E29A69025008D145C /* GeneralResponse.swift */; }; - 6CF4706129A69096008D145C /* NetworkConstant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF4706029A69096008D145C /* NetworkConstant.swift */; }; - 6CF4706329A690CD008D145C /* NetworkResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF4706229A690CD008D145C /* NetworkResult.swift */; }; - 6CF4706529A690E5008D145C /* NetworkBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF4706429A690E5008D145C /* NetworkBase.swift */; }; 6CF4706A29A71D71008D145C /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF4706929A71D71008D145C /* Strings.swift */; }; 6CF4706D29A739D9008D145C /* RecommendViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF4706C29A739D9008D145C /* RecommendViewController.swift */; }; 6CF4707029A73A15008D145C /* RecommendActionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF4706F29A73A15008D145C /* RecommendActionViewController.swift */; }; @@ -208,10 +228,9 @@ 092C09B42A484DD900E9B06B /* HomeDeleteViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeDeleteViewController.swift; sourceTree = "<group>"; }; 092C09B62A48596500E9B06B /* DeleteModalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteModalView.swift; sourceTree = "<group>"; }; 092E04B029BD9C86008A5892 /* MissionDetailCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MissionDetailCollectionViewCell.swift; sourceTree = "<group>"; }; - 0930D37229B4FCAE0000C4AE /* StatisticsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatisticsView.swift; sourceTree = "<group>"; }; 0930DE6129B80550007958DE /* MissionDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MissionDetailViewController.swift; sourceTree = "<group>"; }; 093DB0362A146BF900ECA5F6 /* MyInfoURL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyInfoURL.swift; sourceTree = "<group>"; }; - 093DB03C2A15FC7800ECA5F6 /* AchieveCalendarResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AchieveCalendarResponseDTO.swift; sourceTree = "<group>"; }; + 093DB03C2A15FC7800ECA5F6 /* CalendarReponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarReponseDTO.swift; sourceTree = "<group>"; }; 093DB03E2A15FCC100ECA5F6 /* MissionDetailResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MissionDetailResponseDTO.swift; sourceTree = "<group>"; }; 0943A9F82A53239200614761 /* Bundle+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+.swift"; sourceTree = "<group>"; }; 09582B4729BDA7F600EF3207 /* DetailStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailStackView.swift; sourceTree = "<group>"; }; @@ -219,6 +238,8 @@ 09582B4C29BE277800EF3207 /* DetailHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailHeaderView.swift; sourceTree = "<group>"; }; 09582B4E29BEBAFA00EF3207 /* DetailCalendarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailCalendarViewController.swift; sourceTree = "<group>"; }; 09582B5029C0BC3600EF3207 /* DetailAchievementViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailAchievementViewController.swift; sourceTree = "<group>"; }; + 095FEE102B9ED15600FF44C0 /* DetailAchieveHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailAchieveHeaderView.swift; sourceTree = "<group>"; }; + 095FEE112B9ED15600FF44C0 /* StatisticsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatisticsView.swift; sourceTree = "<group>"; }; 0960C0D32A38BC6500A3D8DB /* KeychainUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainUtil.swift; sourceTree = "<group>"; }; 0960C0D52A38BC8100A3D8DB /* DefaultKeys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultKeys.swift; sourceTree = "<group>"; }; 0964BA492B0F6BFB00A8984B /* AuthInterceptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthInterceptor.swift; sourceTree = "<group>"; }; @@ -252,11 +273,34 @@ 099FC98029B3094F005B37E6 /* WeekMonthFSCalendar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeekMonthFSCalendar.swift; sourceTree = "<group>"; }; 099FC98229B30A2E005B37E6 /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = "<group>"; }; 099FC98829B3233D005B37E6 /* CalendarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarView.swift; sourceTree = "<group>"; }; - 09A1465E2A192C4900DDC308 /* WeekMissionResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeekMissionResponseDTO.swift; sourceTree = "<group>"; }; 09A146642A19649A00DDC308 /* AddAnotherDayResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddAnotherDayResponseDTO.swift; sourceTree = "<group>"; }; - 09A8E48D2B9DBEC700C0F48F /* BaseAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseAPI.swift; sourceTree = "<group>"; }; - 09CF56012B09E98A00526C8C /* DetailAchieveHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailAchieveHeaderView.swift; sourceTree = "<group>"; }; + 09A8E48D2B9DBEC700C0F48F /* BaseService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseService.swift; sourceTree = "<group>"; }; 09CF56032B09F23800526C8C /* HomeDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeDataSource.swift; sourceTree = "<group>"; }; + 09DB33DF2BA2C0DF00B5F961 /* BaseAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseAPI.swift; sourceTree = "<group>"; }; + 09DC0EED2BAEC1790075AAC9 /* API_KEY.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = API_KEY.plist; path = ../../../../Desktop/API_KEY.plist; sourceTree = "<group>"; }; + 09DC0EF02BAEC1D70075AAC9 /* AuthFlowControllerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthFlowControllerFactory.swift; sourceTree = "<group>"; }; + 09DC0EF22BAEC1F10075AAC9 /* UpdateFlowcontrollerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateFlowcontrollerFactory.swift; sourceTree = "<group>"; }; + 09DC0EF42BAEC2030075AAC9 /* HomeFlowControllerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeFlowControllerFactory.swift; sourceTree = "<group>"; }; + 09DC0EF62BAEC2140075AAC9 /* MyPageFlowControllerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageFlowControllerFactory.swift; sourceTree = "<group>"; }; + 09DC0EF82BAEC2230075AAC9 /* AchieveFlowControllerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AchieveFlowControllerFactory.swift; sourceTree = "<group>"; }; + 09DC0EFA2BAEC2340075AAC9 /* TabBarFlowControllerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarFlowControllerFactory.swift; sourceTree = "<group>"; }; + 09DC0EFC2BAEC2710075AAC9 /* UpdateViewControllerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateViewControllerFactory.swift; sourceTree = "<group>"; }; + 09DC0EFE2BAEC2750075AAC9 /* HomeViewControllerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewControllerFactory.swift; sourceTree = "<group>"; }; + 09DC0F002BAEC27A0075AAC9 /* AuthViewControllerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthViewControllerFactory.swift; sourceTree = "<group>"; }; + 09DC0F022BAEC2800075AAC9 /* MyPageViewControllerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageViewControllerFactory.swift; sourceTree = "<group>"; }; + 09DC0F042BAEC2850075AAC9 /* AchieveViewControllerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AchieveViewControllerFactory.swift; sourceTree = "<group>"; }; + 09DC0F062BAEC2890075AAC9 /* TabBarViewControllerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarViewControllerFactory.swift; sourceTree = "<group>"; }; + 09DCB84C2BA0146800B6BB74 /* DetailAchievementViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailAchievementViewModel.swift; sourceTree = "<group>"; }; + 09DCB84E2BA0147500B6BB74 /* ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModel.swift; sourceTree = "<group>"; }; + 09DCB8502BA0156400B6BB74 /* DetailAchievementViewModelImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailAchievementViewModelImpl.swift; sourceTree = "<group>"; }; + 09DCB8552BA0308D00B6BB74 /* APIError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIError.swift; sourceTree = "<group>"; }; + 09DCB8572BA0309F00B6BB74 /* ErrorReponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorReponse.swift; sourceTree = "<group>"; }; + 09DCB85F2BA031E000B6BB74 /* AchieveManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AchieveManager.swift; sourceTree = "<group>"; }; + 09DCB8612BA031F600B6BB74 /* AchieveManagerImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AchieveManagerImpl.swift; sourceTree = "<group>"; }; + 09DCB8642BA056C800B6BB74 /* DetailAchievementModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailAchievementModel.swift; sourceTree = "<group>"; }; + 09DCB8662BA05F5400B6BB74 /* AchievementViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AchievementViewModel.swift; sourceTree = "<group>"; }; + 09DCB8682BA05F9E00B6BB74 /* AchievementModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AchievementModel.swift; sourceTree = "<group>"; }; + 09DCB86A2BA0600600B6BB74 /* AchievementViewModelImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AchievementViewModelImpl.swift; sourceTree = "<group>"; }; 09DCCD1E2A18ED76003DCF8A /* DailyMissionResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DailyMissionResponseDTO.swift; sourceTree = "<group>"; }; 09DCCD222A18EFB0003DCF8A /* MissionService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MissionService.swift; sourceTree = "<group>"; }; 09ED941A2B2ABAB7001864EF /* CommonNotificationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommonNotificationViewController.swift; sourceTree = "<group>"; }; @@ -317,7 +361,6 @@ 3B4E12F72A27C12F001D1EC1 /* WithdrawModalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WithdrawModalView.swift; sourceTree = "<group>"; }; 3B4E12F92A27C4DD001D1EC1 /* Pretendard-Bold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Pretendard-Bold.otf"; sourceTree = "<group>"; }; 3B50CB202A40E75400F2E761 /* AddMissionResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddMissionResponseDTO.swift; sourceTree = "<group>"; }; - 3B570B332BA30E6100418250 /* API_KEY.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = API_KEY.plist; sourceTree = "<group>"; }; 3B5F8F7929BF8E8D0063A7F8 /* AddMissionProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddMissionProtocol.swift; sourceTree = "<group>"; }; 3B5F8F7E29BF900A0063A7F8 /* DateCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateCollectionViewCell.swift; sourceTree = "<group>"; }; 3B5F8F8029BF90190063A7F8 /* NottodoCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NottodoCollectionViewCell.swift; sourceTree = "<group>"; }; @@ -360,9 +403,6 @@ 6CF4705A29A68EA9008D145C /* URLConstant.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLConstant.swift; sourceTree = "<group>"; }; 6CF4705C29A68F5C008D145C /* NetworkLoggerPlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkLoggerPlugin.swift; sourceTree = "<group>"; }; 6CF4705E29A69025008D145C /* GeneralResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralResponse.swift; sourceTree = "<group>"; }; - 6CF4706029A69096008D145C /* NetworkConstant.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkConstant.swift; sourceTree = "<group>"; }; - 6CF4706229A690CD008D145C /* NetworkResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkResult.swift; sourceTree = "<group>"; }; - 6CF4706429A690E5008D145C /* NetworkBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkBase.swift; sourceTree = "<group>"; }; 6CF4706929A71D71008D145C /* Strings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = "<group>"; }; 6CF4706C29A739D9008D145C /* RecommendViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecommendViewController.swift; sourceTree = "<group>"; }; 6CF4706F29A73A15008D145C /* RecommendActionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecommendActionViewController.swift; sourceTree = "<group>"; }; @@ -429,6 +469,7 @@ children = ( 0921611A2A5727EF0019CC8C /* AnalyticsEventProtocol.swift */, 0921611C2A57D0920019CC8C /* AmplitudeAnalyticsService.swift */, + 09DCB84E2BA0147500B6BB74 /* ViewModel.swift */, ); path = Protocol; sourceTree = "<group>"; @@ -436,7 +477,7 @@ 093DB02B2A14687300ECA5F6 /* Service */ = { isa = PBXGroup; children = ( - 09A8E48D2B9DBEC700C0F48F /* BaseAPI.swift */, + 09A8E48D2B9DBEC700C0F48F /* BaseService.swift */, 3B892AB52A2FB69200A316BC /* Auth */, 3B892AB22A2FB67000A316BC /* Recommend */, 09A363D12A45F08B003F8DB5 /* Mission */, @@ -459,6 +500,7 @@ 093DB0312A1468F100ECA5F6 /* API */ = { isa = PBXGroup; children = ( + 09DB33DF2BA2C0DF00B5F961 /* BaseAPI.swift */, 0987C83E2B9DD234007EE8DE /* Mission */, 3B892AB32A2FB67B00A316BC /* AuthAPI */, 3B892AB02A2FB63E00A316BC /* Recommend */, @@ -477,6 +519,27 @@ path = Enum; sourceTree = "<group>"; }; + 095FEE0F2B9ED15600FF44C0 /* Views */ = { + isa = PBXGroup; + children = ( + 095FEE102B9ED15600FF44C0 /* DetailAchieveHeaderView.swift */, + 095FEE112B9ED15600FF44C0 /* StatisticsView.swift */, + ); + name = Views; + path = "iOS-NOTTODO/Presentation/Achievement/Views"; + sourceTree = SOURCE_ROOT; + }; + 095FEE142B9ED27F00FF44C0 /* ViewModel */ = { + isa = PBXGroup; + children = ( + 09DCB84C2BA0146800B6BB74 /* DetailAchievementViewModel.swift */, + 09DCB8662BA05F5400B6BB74 /* AchievementViewModel.swift */, + 09DCB86A2BA0600600B6BB74 /* AchievementViewModelImpl.swift */, + 09DCB8502BA0156400B6BB74 /* DetailAchievementViewModelImpl.swift */, + ); + path = ViewModel; + sourceTree = "<group>"; + }; 0987C83E2B9DD234007EE8DE /* Mission */ = { isa = PBXGroup; children = ( @@ -545,6 +608,13 @@ children = ( 0989043D2B81BD50004AAD3C /* CoordinatorFactory.swift */, 0989043F2B81BFAF004AAD3C /* ViewControllerFactory.swift */, + 09DC0EEF2BAEC1B80075AAC9 /* Protocol */, + 09DC0EFC2BAEC2710075AAC9 /* UpdateViewControllerFactory.swift */, + 09DC0EFE2BAEC2750075AAC9 /* HomeViewControllerFactory.swift */, + 09DC0F002BAEC27A0075AAC9 /* AuthViewControllerFactory.swift */, + 09DC0F022BAEC2800075AAC9 /* MyPageViewControllerFactory.swift */, + 09DC0F042BAEC2850075AAC9 /* AchieveViewControllerFactory.swift */, + 09DC0F062BAEC2890075AAC9 /* TabBarViewControllerFactory.swift */, ); path = Factory; sourceTree = "<group>"; @@ -602,7 +672,6 @@ 09A363CE2A45F06C003F8DB5 /* Home */ = { isa = PBXGroup; children = ( - 09A1465E2A192C4900DDC308 /* WeekMissionResponseDTO.swift */, 09A146642A19649A00DDC308 /* AddAnotherDayResponseDTO.swift */, 09DCCD1E2A18ED76003DCF8A /* DailyMissionResponseDTO.swift */, 093DB03E2A15FCC100ECA5F6 /* MissionDetailResponseDTO.swift */, @@ -613,7 +682,7 @@ 09A363CF2A45F074003F8DB5 /* Achieve */ = { isa = PBXGroup; children = ( - 093DB03C2A15FC7800ECA5F6 /* AchieveCalendarResponseDTO.swift */, + 093DB03C2A15FC7800ECA5F6 /* CalendarReponseDTO.swift */, ); path = Achieve; sourceTree = "<group>"; @@ -626,6 +695,45 @@ path = Mission; sourceTree = "<group>"; }; + 09DC0EEF2BAEC1B80075AAC9 /* Protocol */ = { + isa = PBXGroup; + children = ( + 09DC0EF02BAEC1D70075AAC9 /* AuthFlowControllerFactory.swift */, + 09DC0EF22BAEC1F10075AAC9 /* UpdateFlowcontrollerFactory.swift */, + 09DC0EF42BAEC2030075AAC9 /* HomeFlowControllerFactory.swift */, + 09DC0EF62BAEC2140075AAC9 /* MyPageFlowControllerFactory.swift */, + 09DC0EF82BAEC2230075AAC9 /* AchieveFlowControllerFactory.swift */, + 09DC0EFA2BAEC2340075AAC9 /* TabBarFlowControllerFactory.swift */, + ); + path = Protocol; + sourceTree = "<group>"; + }; + 09DCB85B2BA0316500B6BB74 /* Manager */ = { + isa = PBXGroup; + children = ( + 09DCB85C2BA0316A00B6BB74 /* ManagerInterface */, + 09DCB8612BA031F600B6BB74 /* AchieveManagerImpl.swift */, + ); + path = Manager; + sourceTree = "<group>"; + }; + 09DCB85C2BA0316A00B6BB74 /* ManagerInterface */ = { + isa = PBXGroup; + children = ( + 09DCB85F2BA031E000B6BB74 /* AchieveManager.swift */, + ); + path = ManagerInterface; + sourceTree = "<group>"; + }; + 09DCB8632BA056BC00B6BB74 /* Model */ = { + isa = PBXGroup; + children = ( + 09DCB8642BA056C800B6BB74 /* DetailAchievementModel.swift */, + 09DCB8682BA05F9E00B6BB74 /* AchievementModel.swift */, + ); + path = Model; + sourceTree = "<group>"; + }; 09F6717E29CAD68100708725 /* Onboarding */ = { isa = PBXGroup; children = ( @@ -683,8 +791,6 @@ children = ( 3B027AA1299C355800BEB65C /* AchievementViewController.swift */, 09582B5029C0BC3600EF3207 /* DetailAchievementViewController.swift */, - 0930D37229B4FCAE0000C4AE /* StatisticsView.swift */, - 09CF56012B09E98A00526C8C /* DetailAchieveHeaderView.swift */, ); path = ViewControllers; sourceTree = "<group>"; @@ -812,6 +918,7 @@ 3B027A90299C33CA00BEB65C /* Network */ = { isa = PBXGroup; children = ( + 09DCB85B2BA0316500B6BB74 /* Manager */, 155E45672B5FF2DE008628E7 /* External */, 093DB0312A1468F100ECA5F6 /* API */, 093DB02F2A1468CE00ECA5F6 /* DataModel */, @@ -872,6 +979,9 @@ 3B027A9B299C348800BEB65C /* Achievement */ = { isa = PBXGroup; children = ( + 09DCB8632BA056BC00B6BB74 /* Model */, + 095FEE142B9ED27F00FF44C0 /* ViewModel */, + 095FEE0F2B9ED15600FF44C0 /* Views */, 09F6719929CC8ECD00708725 /* ViewControllers */, 09F6719829CC8EC500708725 /* Cell */, ); @@ -902,7 +1012,7 @@ 3B027AA6299C359900BEB65C /* Resource */ = { isa = PBXGroup; children = ( - 3B570B332BA30E6100418250 /* API_KEY.plist */, + 09DC0EED2BAEC1790075AAC9 /* API_KEY.plist */, 3B3EF2F72AF35C90001F79BC /* GoogleService-Info.plist */, 3B027A85299C31B600BEB65C /* Info.plist */, 3B027AAA299C35D000BEB65C /* Assets */, @@ -1187,9 +1297,8 @@ 0964BA492B0F6BFB00A8984B /* AuthInterceptor.swift */, 6CF4705C29A68F5C008D145C /* NetworkLoggerPlugin.swift */, 6CF4705E29A69025008D145C /* GeneralResponse.swift */, - 6CF4706029A69096008D145C /* NetworkConstant.swift */, - 6CF4706229A690CD008D145C /* NetworkResult.swift */, - 6CF4706429A690E5008D145C /* NetworkBase.swift */, + 09DCB8552BA0308D00B6BB74 /* APIError.swift */, + 09DCB8572BA0309F00B6BB74 /* ErrorReponse.swift */, ); path = Base; sourceTree = "<group>"; @@ -1348,7 +1457,7 @@ 3B710A5C2A62D4AB00E95620 /* Settings.bundle in Resources */, 3B027A84299C31B600BEB65C /* LaunchScreen.storyboard in Resources */, 6CC54C1A2A28C3AE00AAD76D /* value.json in Resources */, - 3B570B342BA30E6100418250 /* API_KEY.plist in Resources */, + 09DC0EEE2BAEC1790075AAC9 /* API_KEY.plist in Resources */, 3B3EF2F82AF35C90001F79BC /* GoogleService-Info.plist in Resources */, 6C049A312A595C670085E40B /* logo.mp4 in Resources */, 3B027A81299C31B600BEB65C /* Assets.xcassets in Resources */, @@ -1390,6 +1499,8 @@ 09F6719729CC81B500708725 /* DetailAchievementCollectionViewCell.swift in Sources */, 0960C0D62A38BC8100A3D8DB /* DefaultKeys.swift in Sources */, 3B14A14129A6FDA900F92897 /* UILabel+.swift in Sources */, + 09DCB8672BA05F5400B6BB74 /* AchievementViewModel.swift in Sources */, + 09DC0EF92BAEC2230075AAC9 /* AchieveFlowControllerFactory.swift in Sources */, 09A146652A1964B500DDC308 /* AddAnotherDayResponseDTO.swift in Sources */, 3B027A7C299C31B500BEB65C /* AuthViewController.swift in Sources */, 098904382B81BC16004AAD3C /* AppCoordinator.swift in Sources */, @@ -1404,12 +1515,17 @@ 3B027A94299C340600BEB65C /* UIFont+.swift in Sources */, 6CA208292A191185001C4247 /* UIImageView+.swift in Sources */, 0982DE5A29AE5E6000D933D2 /* CompositionalLayout.swift in Sources */, + 09DC0EF12BAEC1D70075AAC9 /* AuthFlowControllerFactory.swift in Sources */, 098904522B81CA33004AAD3C /* UpdateCoordinatorImpl.swift in Sources */, 098904442B81C18A004AAD3C /* HomeCoordinator.swift in Sources */, + 095FEE122B9ED15600FF44C0 /* DetailAchieveHeaderView.swift in Sources */, 3B482FA3299EA9CB00BCF424 /* TabBarItem.swift in Sources */, 3B027AA0299C353700BEB65C /* AddMissionViewController.swift in Sources */, 098BFD5929B7999E008E80F9 /* MyProfileCollectionViewCell.swift in Sources */, + 09DCB84D2BA0146800B6BB74 /* DetailAchievementViewModel.swift in Sources */, 3B5F8F7A29BF8E8D0063A7F8 /* AddMissionProtocol.swift in Sources */, + 09DC0F012BAEC27A0075AAC9 /* AuthViewControllerFactory.swift in Sources */, + 09DCB8512BA0156400B6BB74 /* DetailAchievementViewModelImpl.swift in Sources */, 09F6718C29CB4AB700708725 /* SubOnboardingCollectionViewCell.swift in Sources */, 0921611D2A57D0920019CC8C /* AmplitudeAnalyticsService.swift in Sources */, 3B027A96299C340C00BEB65C /* UIImage+.swift in Sources */, @@ -1417,6 +1533,7 @@ 098BFD5B29B79B6A008E80F9 /* MyInfoModel.swift in Sources */, 09DCCD1F2A18ED76003DCF8A /* DailyMissionResponseDTO.swift in Sources */, 6CF4707A29A7AAFF008D145C /* PaddingLabel.swift in Sources */, + 09DC0EFD2BAEC2710075AAC9 /* UpdateViewControllerFactory.swift in Sources */, 6CA208272A18FFCF001C4247 /* RecommendResponseDTO.swift in Sources */, 3B4E12F22A27B621001D1EC1 /* NottodoModalViewController.swift in Sources */, 3B027A78299C31B500BEB65C /* AppDelegate.swift in Sources */, @@ -1431,6 +1548,7 @@ 09CF56042B09F23800526C8C /* HomeDataSource.swift in Sources */, 093DB03F2A15FCC100ECA5F6 /* MissionDetailResponseDTO.swift in Sources */, 6CF4705B29A68EA9008D145C /* URLConstant.swift in Sources */, + 09DB33E02BA2C0DF00B5F961 /* BaseAPI.swift in Sources */, 3BD3B5C829B8F82C00D3575B /* AddMissionTextFieldView.swift in Sources */, 3B14518629C618920013DFB4 /* ActionCollectionViewCell.swift in Sources */, 3B80B5D72B7F30E200697250 /* Numbers.swift in Sources */, @@ -1446,7 +1564,7 @@ 3B14A13B29A694C000F92897 /* UITextView+.swift in Sources */, 093DB0372A146BF900ECA5F6 /* MyInfoURL.swift in Sources */, 098BFD5F29B7AECF008E80F9 /* MyInfoHeaderView.swift in Sources */, - 09A8E48E2B9DBEC700C0F48F /* BaseAPI.swift in Sources */, + 09A8E48E2B9DBEC700C0F48F /* BaseService.swift in Sources */, 3B482FA7299EB8FD00BCF424 /* UIViewController+.swift in Sources */, 0960C0D42A38BC6500A3D8DB /* KeychainUtil.swift in Sources */, 3B03D0D82B0F5EF300302872 /* CGSize+.swift in Sources */, @@ -1459,7 +1577,8 @@ 098904302B81BB3A004AAD3C /* Coordinator.swift in Sources */, 3B14A13D29A6FBD300F92897 /* UIView+.swift in Sources */, 09F6719529CBFCD200708725 /* GradientView.swift in Sources */, - 09CF56022B09E98A00526C8C /* DetailAchieveHeaderView.swift in Sources */, + 09DC0F052BAEC2850075AAC9 /* AchieveViewControllerFactory.swift in Sources */, + 09DCB8622BA031F600B6BB74 /* AchieveManagerImpl.swift in Sources */, 3B4E12F82A27C12F001D1EC1 /* WithdrawModalView.swift in Sources */, 6CA208252A18FEEA001C4247 /* RecommendAPI.swift in Sources */, 3B482FA5299EAB8800BCF424 /* TabBarController.swift in Sources */, @@ -1468,16 +1587,21 @@ 09DCCD232A18EFB0003DCF8A /* MissionService.swift in Sources */, 09582B4829BDA7F600EF3207 /* DetailStackView.swift in Sources */, 09F6718429CADB1100708725 /* OnboardingModel.swift in Sources */, + 095FEE132B9ED15600FF44C0 /* StatisticsView.swift in Sources */, + 09DC0EF32BAEC1F10075AAC9 /* UpdateFlowcontrollerFactory.swift in Sources */, 3B027A7A299C31B500BEB65C /* SceneDelegate.swift in Sources */, 0989043E2B81BD50004AAD3C /* CoordinatorFactory.swift in Sources */, 0964BA4A2B0F6BFB00A8984B /* AuthInterceptor.swift in Sources */, 09582B5129C0BC3600EF3207 /* DetailAchievementViewController.swift in Sources */, + 09DCB8582BA0309F00B6BB74 /* ErrorReponse.swift in Sources */, + 09DC0F032BAEC2800075AAC9 /* MyPageViewControllerFactory.swift in Sources */, 0982DE5829AE40FB00D933D2 /* UITabBar+.swift in Sources */, 3BC1A27929C9BE6C0088376B /* AddMissionFooterCollectionReusableView.swift in Sources */, 6C16015829C40112005AE3F5 /* AuthButtonView.swift in Sources */, 3BC19A9329CA1CA800C02803 /* UICollectionViewCell+.swift in Sources */, + 093DB03D2A15FC7800ECA5F6 /* CalendarReponseDTO.swift in Sources */, 098904462B81C191004AAD3C /* MypageCoordinator.swift in Sources */, - 093DB03D2A15FC7800ECA5F6 /* AchieveCalendarResponseDTO.swift in Sources */, + 093DB03D2A15FC7800ECA5F6 /* CalendarReponseDTO.swift in Sources */, 3B0CBA242A461B1C0004F2DB /* RecentMissionResponseDTO.swift in Sources */, 3B5F8F7F29BF900A0063A7F8 /* DateCollectionViewCell.swift in Sources */, 3BC1A27229C9AF310088376B /* MissionHistoryModels.swift in Sources */, @@ -1489,19 +1613,18 @@ 6CD4F8BE29AA495900CCC740 /* RecommendActionCollectionViewCell.swift in Sources */, 6CF4707429A73D4C008D145C /* RecommendCollectionViewCell.swift in Sources */, 3BBB6C8F2A1E7BEA00B8745A /* CollectionViewLeftAlignLayout.swift in Sources */, - 09A1465F2A192C4900DDC308 /* WeekMissionResponseDTO.swift in Sources */, - 0930D37329B4FCAE0000C4AE /* StatisticsView.swift in Sources */, 09582B4D29BE277800EF3207 /* DetailHeaderView.swift in Sources */, 6CF4706A29A71D71008D145C /* Strings.swift in Sources */, 6CF4705F29A69025008D145C /* GeneralResponse.swift in Sources */, 097C003629AB8270008CAEF3 /* MissionListCollectionViewCell.swift in Sources */, 6C16015C29C56DBA005AE3F5 /* MyInfoAccountViewController.swift in Sources */, - 6CF4706129A69096008D145C /* NetworkConstant.swift in Sources */, 09F6718E29CB612B00708725 /* FifthOnboardingViewController.swift in Sources */, 0987C8402B9DD4DC007EE8DE /* MissionAPI.swift in Sources */, 6CD4F8C229AA5AF200CCC740 /* UIButton+.swift in Sources */, 6CA208362A1957CA001C4247 /* AuthService.swift in Sources */, - 6CF4706529A690E5008D145C /* NetworkBase.swift in Sources */, + 09DC0EFF2BAEC2750075AAC9 /* HomeViewControllerFactory.swift in Sources */, + 09DC0EF52BAEC2030075AAC9 /* HomeFlowControllerFactory.swift in Sources */, + 6CA208362A1957CA001C4247 /* AuthService.swift in Sources */, 099FC98129B3094F005B37E6 /* WeekMonthFSCalendar.swift in Sources */, 6C16016429C5E37D005AE3F5 /* MyInfoAccountStackView.swift in Sources */, 3B80B5D52B7F304D00697250 /* adjust+.swift in Sources */, @@ -1509,31 +1632,39 @@ 6CF4707029A73A15008D145C /* RecommendActionViewController.swift in Sources */, 0943A9F92A53239200614761 /* Bundle+.swift in Sources */, 0982DE5429ADCCE000D933D2 /* HomeEmptyCollectionViewCell.swift in Sources */, + 09DCB86B2BA0600600B6BB74 /* AchievementViewModelImpl.swift in Sources */, 3BEEBE972A4B048A0081C936 /* NottodoToastView.swift in Sources */, 3B027AA2299C355800BEB65C /* AchievementViewController.swift in Sources */, 3B5F8F8929BF9EFE0063A7F8 /* AddMissionLabel.swift in Sources */, 09582B4F29BEBAFA00EF3207 /* DetailCalendarViewController.swift in Sources */, 09F6718829CB383800708725 /* ThirdOnboardingViewController.swift in Sources */, 3B37AE2B29C8904800AB7587 /* RecommendKeywordCollectionViewCell.swift in Sources */, + 09DCB84F2BA0147500B6BB74 /* ViewModel.swift in Sources */, 099FC98929B3233D005B37E6 /* CalendarView.swift in Sources */, 09ED941B2B2ABAB7001864EF /* CommonNotificationViewController.swift in Sources */, 6C9628A92A22209E003ADE25 /* LogoOnboardingViewController.swift in Sources */, 09F6718029CAD76C00708725 /* SecondOnboardingViewController.swift in Sources */, + 09DC0EFB2BAEC2340075AAC9 /* TabBarFlowControllerFactory.swift in Sources */, 098A23A42B833F0300265955 /* AuthCoordinatorImpl.swift in Sources */, 098BFD5D29B79CE3008E80F9 /* InfoCollectionViewCell.swift in Sources */, 3B0CBA222A45FC170004F2DB /* UpdateMissionResponseDTO.swift in Sources */, 092C09B72A48596500E9B06B /* DeleteModalView.swift in Sources */, 155E45692B5FF2EE008628E7 /* FirebaseUtil.swift in Sources */, + 09DCB8562BA0308D00B6BB74 /* APIError.swift in Sources */, 098904402B81BFAF004AAD3C /* ViewControllerFactory.swift in Sources */, - 6CF4706329A690CD008D145C /* NetworkResult.swift in Sources */, 3B9532F42A284CC1006510F8 /* ModalProtocol.swift in Sources */, + 09DC0EF72BAEC2140075AAC9 /* MyPageFlowControllerFactory.swift in Sources */, 3B5F8F8329BF90290063A7F8 /* SituationCollectionViewCell.swift in Sources */, 3B4E12F62A27C0BE001D1EC1 /* QuitModalView.swift in Sources */, 6CA208232A18FE78001C4247 /* RecommendService.swift in Sources */, 6CA208342A1956ED001C4247 /* AuthAPI.swift in Sources */, 0921611B2A5727EF0019CC8C /* AnalyticsEventProtocol.swift in Sources */, + 09DCB8692BA05F9E00B6BB74 /* AchievementModel.swift in Sources */, 098904542B81CA47004AAD3C /* UpdateCoordinator.swift in Sources */, 0989044E2B81C216004AAD3C /* MypageCoordinatorImpl.swift in Sources */, + 09DC0F072BAEC2890075AAC9 /* TabBarViewControllerFactory.swift in Sources */, + 09DCB8652BA056C800B6BB74 /* DetailAchievementModel.swift in Sources */, + 09DCB8602BA031E000B6BB74 /* AchieveManager.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Coordinator/AchieveCoordinatorImpl.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Coordinator/AchieveCoordinatorImpl.swift index e36d0316..92252abf 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Coordinator/AchieveCoordinatorImpl.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Coordinator/AchieveCoordinatorImpl.swift @@ -36,7 +36,7 @@ final class AchieveCoordinatorImpl: AchieveCoordinator { navigationController.setViewControllers([viewController], animated: false) } - func showAchieveDetailViewController(selectedDate: Date) { + func showAchieveDetailViewController(selectedDate: String) { let viewController = viewControllerFactory.makeAchieveDetailViewController(coordinator: self, date: selectedDate) viewController.modalTransitionStyle = .crossDissolve viewController.modalPresentationStyle = .overFullScreen diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/AchieveViewControllerFactory.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/AchieveViewControllerFactory.swift new file mode 100644 index 00000000..602f2f31 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/AchieveViewControllerFactory.swift @@ -0,0 +1,38 @@ +// +// AchieveViewControllerFactory.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/23/24. +// + +import Foundation + +extension ViewControllerFactoryImpl { + + func makeAchieveViewModel(coordinator: AchieveCoordinator) -> any AchievementViewModel { + let missionAPI = DefaultMissionService() + let manager = AchieveManagerImpl(missionAPI: missionAPI) + let viewModel = AchievementViewModelImpl(coordinator: coordinator, manager: manager) + return viewModel + } + + func makeAchieveViewController(coordinator: AchieveCoordinator) -> AchievementViewController { + let viewModel = self.makeAchieveViewModel(coordinator: coordinator) + let viewController = AchievementViewController(viewModel: viewModel) + return viewController + } + + func makeAchieveDetailViewModel(coordinator: AchieveCoordinator) -> any DetailAchievementViewModel & DetailAchievementViewModelPresentable { + let missionAPI = DefaultMissionService() + let manager = AchieveManagerImpl(missionAPI: missionAPI) + let viewModel = DetailAchievementViewModelImpl(coordinator: coordinator, manager: manager) + return viewModel + } + + func makeAchieveDetailViewController(coordinator: AchieveCoordinator, date: String) -> DetailAchievementViewController { + let viewModel = self.makeAchieveDetailViewModel(coordinator: coordinator) + viewModel.selectedDate(date) + let viewController = DetailAchievementViewController(viewModel: viewModel) + return viewController + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/AuthViewControllerFactory.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/AuthViewControllerFactory.swift new file mode 100644 index 00000000..f30455f3 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/AuthViewControllerFactory.swift @@ -0,0 +1,51 @@ +// +// AuthViewControllerFactory.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/23/24. +// + +import Foundation + +extension ViewControllerFactoryImpl { + func makeAuthViewController(coordinator: AuthCoordinator) -> AuthViewController { + let viewController = AuthViewController(coordinator: coordinator) + return viewController + } + + func makeNotificationDialogViewController(coordinator: AuthCoordinator, completion: @escaping () -> Void) -> NotificationDialogViewController { + let viewController = NotificationDialogViewController() + viewController.buttonHandler = { + completion() + } + return viewController + } +} + +// Onboarding +extension ViewControllerFactoryImpl { + func makeValueOnboardingViewController(coordinator: AuthCoordinator) -> ValueOnboardingViewController { + let viewController = ValueOnboardingViewController(coordinator: coordinator) + return viewController + } + func makeLogoOnboardingViewController(coordinator: AuthCoordinator) -> LogoOnboardingViewController { + let viewController = LogoOnboardingViewController(coordinator: coordinator) + return viewController + } + func makeSecondOnboardingViewController(coordinator: AuthCoordinator) -> SecondOnboardingViewController { + let viewController = SecondOnboardingViewController(coordinator: coordinator) + return viewController + } + func makeThirdOnboardingViewController(coordinator: AuthCoordinator) -> ThirdOnboardingViewController { + let viewController = ThirdOnboardingViewController(coordinator: coordinator) + return viewController + } + func makeFourthOnboardingViewController(coordinator: AuthCoordinator) -> FourthOnboardingViewController { + let viewController = FourthOnboardingViewController(coordinator: coordinator) + return viewController + } + func makeFifthOnboardingViewController(coordinator: AuthCoordinator) -> FifthOnboardingViewController { + let viewController = FifthOnboardingViewController(coordinator: coordinator) + return viewController + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/HomeViewControllerFactory.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/HomeViewControllerFactory.swift new file mode 100644 index 00000000..f5730ef2 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/HomeViewControllerFactory.swift @@ -0,0 +1,91 @@ +// +// HomeViewControllerFactory.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/23/24. +// + +import Foundation + +// Home +extension ViewControllerFactoryImpl { + func makePopupViewController(coordinator: HomeCoordinator, completion: @escaping () -> Void) -> CommonNotificationViewController { + let viewController = CommonNotificationViewController(coordinator: coordinator) + viewController.tapCloseButton = { + completion() + } + return viewController + } + + func makeHomeViewController(coordinator: HomeCoordinator) -> HomeViewController { + let viewController = HomeViewController(coordinator: coordinator) + return viewController + } +} + +// HomeDetail +extension ViewControllerFactoryImpl { + func makeDeleteViewController(coordinator: HomeCoordinator, completion: @escaping () -> Void) -> HomeDeleteViewController { + let viewController = HomeDeleteViewController(coordinator: coordinator) + viewController.deleteClosure = { + completion() + } + return viewController + } + + func makeMissionDetailViewController(coordinator: HomeCoordinator, id: Int, deleteClosure: @escaping () -> Void, moveDateClosure: @escaping (String) -> Void) -> MissionDetailViewController { + let viewController = MissionDetailViewController(coordinator: coordinator) + viewController.userId = id + viewController.deleteClosure = { + deleteClosure() + } + viewController.moveDateClosure = { date in + moveDateClosure(date) + } + return viewController + } + + func makeModifyViewController(coordinator: HomeCoordinator, id: Int = 0, type: MissionType) -> AddMissionViewController { + let viewController = AddMissionViewController(coordinator: coordinator) + viewController.setViewType(type) + viewController.setMissionId(id) + return viewController + } + + func makeSelectDateViewController(coordinator: HomeCoordinator, data: MissionDetailResponseDTO, id: Int, moveDateClosure: @escaping (String) -> Void) -> DetailCalendarViewController { + let viewController = DetailCalendarViewController(coordinator: coordinator) + viewController.detailModel = data + viewController.userId = id + viewController.movedateClosure = { date in + moveDateClosure(date) + } + return viewController + } +} + +// Add Mission +extension ViewControllerFactoryImpl { + + func makeRecommendViewController(coordinator: HomeCoordinator, date: String) -> RecommendViewController { + let viewController = RecommendViewController(coordinator: coordinator) + viewController.setSelectDate(date) + return viewController + } + + func makeRecommendDetailViewController(coordinator: HomeCoordinator, data: RecommendActionData) -> RecommendActionViewController { + let viewController = RecommendActionViewController(coordinator: coordinator) + viewController.actionHeaderData = data + return viewController + } + + func makeAddViewController(coordinator: HomeCoordinator, data: AddMissionData, type: MissionType) -> AddMissionViewController { + let viewController = AddMissionViewController(coordinator: coordinator) + viewController.setViewType(type) + viewController.setNottodoLabel(data.nottodo ?? "") + viewController.setSituationLabel(data.situation ?? "") + viewController.setActionLabel(data.action ?? "") + viewController.setDate(data.date) + + return viewController + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/MyPageViewControllerFactory.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/MyPageViewControllerFactory.swift new file mode 100644 index 00000000..64d8812f --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/MyPageViewControllerFactory.swift @@ -0,0 +1,25 @@ +// +// MyPageViewControllerFactory.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/23/24. +// + +import Foundation + +extension ViewControllerFactoryImpl { + func makeMyInfoViewController(coordinator: MypageCoordinator) -> MyInfoViewController { + let viewController = MyInfoViewController(coordinator: coordinator) + return viewController + } + + func makeMyInfoAccountViewController(coordinator: MypageCoordinator) -> MyInfoAccountViewController { + let viewController = MyInfoAccountViewController(coordinator: coordinator) + return viewController + } + + func makeWithdrawViewController(coordinator: MypageCoordinator) -> NottodoModalViewController { + let viewController = NottodoModalViewController(coordinator: coordinator) + return viewController + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/AchieveFlowControllerFactory.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/AchieveFlowControllerFactory.swift new file mode 100644 index 00000000..ac76903e --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/AchieveFlowControllerFactory.swift @@ -0,0 +1,13 @@ +// +// AchieveFlowControllerFactory.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/23/24. +// + +import Foundation + +protocol AchieveFlowControllerFactory { + func makeAchieveViewController(coordinator: AchieveCoordinator) -> AchievementViewController + func makeAchieveDetailViewController(coordinator: AchieveCoordinator, date: String) -> DetailAchievementViewController +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/AuthFlowControllerFactory.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/AuthFlowControllerFactory.swift new file mode 100644 index 00000000..45e5cff5 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/AuthFlowControllerFactory.swift @@ -0,0 +1,18 @@ +// +// AuthFlowControllerFactory.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/23/24. +// + +import Foundation +protocol AuthFlowControllerFactory { + func makeValueOnboardingViewController(coordinator: AuthCoordinator) -> ValueOnboardingViewController + func makeLogoOnboardingViewController(coordinator: AuthCoordinator) -> LogoOnboardingViewController + func makeSecondOnboardingViewController(coordinator: AuthCoordinator) -> SecondOnboardingViewController + func makeThirdOnboardingViewController(coordinator: AuthCoordinator) -> ThirdOnboardingViewController + func makeFourthOnboardingViewController(coordinator: AuthCoordinator) -> FourthOnboardingViewController + func makeFifthOnboardingViewController(coordinator: AuthCoordinator) -> FifthOnboardingViewController + func makeAuthViewController(coordinator: AuthCoordinator) -> AuthViewController + func makeNotificationDialogViewController(coordinator: AuthCoordinator, completion: @escaping () -> Void) -> NotificationDialogViewController +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/HomeFlowControllerFactory.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/HomeFlowControllerFactory.swift new file mode 100644 index 00000000..c57717a8 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/HomeFlowControllerFactory.swift @@ -0,0 +1,20 @@ +// +// HomeFlowControllerFactory.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/23/24. +// + +import Foundation + +protocol HomeFlowControllerFactory { + func makePopupViewController(coordinator: HomeCoordinator, completion: @escaping () -> Void) -> CommonNotificationViewController + func makeHomeViewController(coordinator: HomeCoordinator) -> HomeViewController + func makeRecommendViewController(coordinator: HomeCoordinator, date: String) -> RecommendViewController + func makeRecommendDetailViewController(coordinator: HomeCoordinator, data: RecommendActionData) -> RecommendActionViewController + func makeAddViewController(coordinator: HomeCoordinator, data: AddMissionData, type: MissionType) -> AddMissionViewController + func makeDeleteViewController(coordinator: HomeCoordinator, completion: @escaping () -> Void) -> HomeDeleteViewController + func makeMissionDetailViewController(coordinator: HomeCoordinator, id: Int, deleteClosure: @escaping () -> Void, moveDateClosure: @escaping (String) -> Void) -> MissionDetailViewController + func makeModifyViewController(coordinator: HomeCoordinator, id: Int, type: MissionType) -> AddMissionViewController + func makeSelectDateViewController(coordinator: HomeCoordinator, data: MissionDetailResponseDTO, id: Int, moveDateClosure: @escaping (String) -> Void) -> DetailCalendarViewController +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/MyPageFlowControllerFactory.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/MyPageFlowControllerFactory.swift new file mode 100644 index 00000000..0ef669c8 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/MyPageFlowControllerFactory.swift @@ -0,0 +1,14 @@ +// +// MyPageFlowControllerFactory.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/23/24. +// + +import Foundation + +protocol MyPageFlowControllerFactory { + func makeMyInfoViewController(coordinator: MypageCoordinator) -> MyInfoViewController + func makeMyInfoAccountViewController(coordinator: MypageCoordinator) -> MyInfoAccountViewController + func makeWithdrawViewController(coordinator: MypageCoordinator) -> NottodoModalViewController +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/TabBarFlowControllerFactory.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/TabBarFlowControllerFactory.swift new file mode 100644 index 00000000..b37b98ca --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/TabBarFlowControllerFactory.swift @@ -0,0 +1,12 @@ +// +// TabBarFlowControllerFactory.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/23/24. +// + +import UIKit + +protocol TabBarControllerFactory { + func makeTabBarController(_ navigationController: UINavigationController) -> (UITabBarController, [UINavigationController]) +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/UpdateFlowcontrollerFactory.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/UpdateFlowcontrollerFactory.swift new file mode 100644 index 00000000..81aa1343 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/Protocol/UpdateFlowcontrollerFactory.swift @@ -0,0 +1,12 @@ +// +// UpdateFlowcontrollerFactory.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/23/24. +// + +import Foundation + +protocol UpdateFlowcontrollerFactory { + func makeUpdateCheckViewController(coordinator: UpdateCoordinator) -> UpdateCheckViewController +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/TabBarViewControllerFactory.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/TabBarViewControllerFactory.swift new file mode 100644 index 00000000..c608f4af --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/TabBarViewControllerFactory.swift @@ -0,0 +1,24 @@ +// +// TabBarViewControllerFactory.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/23/24. +// + +import UIKit + +extension ViewControllerFactoryImpl { + func makeTabBarController(_: UINavigationController) -> (UITabBarController, [UINavigationController]) { + let tabBarController = TabBarController() + let navigationControllers = tabBarController.setTabBarItems().map(makeNavigationController) + + return (tabBarController, navigationControllers) + } + + func makeNavigationController(_ tabBarItem: UITabBarItem) -> UINavigationController { + let navigationController = UINavigationController() + navigationController.tabBarItem = tabBarItem + navigationController.setNavigationBarHidden(true, animated: false) + return navigationController + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/UpdateViewControllerFactory.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/UpdateViewControllerFactory.swift new file mode 100644 index 00000000..db6d76ed --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/UpdateViewControllerFactory.swift @@ -0,0 +1,15 @@ +// +// UpdateViewControllerFactory.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/23/24. +// + +import Foundation + +extension ViewControllerFactoryImpl { + func makeUpdateCheckViewController(coordinator: UpdateCoordinator) -> UpdateCheckViewController { + let viewController = UpdateCheckViewController(coordinator: coordinator) + return viewController + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/ViewControllerFactory.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/ViewControllerFactory.swift index 94f5cc40..b8381e89 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/ViewControllerFactory.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Factory/ViewControllerFactory.swift @@ -9,224 +9,4 @@ import UIKit protocol ViewControllerFactory: UpdateFlowcontrollerFactory, AuthFlowControllerFactory, HomeFlowControllerFactory, MyPageFlowControllerFactory, AchieveFlowControllerFactory, TabBarControllerFactory, AuthFlowControllerFactory {} -protocol UpdateFlowcontrollerFactory { - func makeUpdateCheckViewController(coordinator: UpdateCoordinator) -> UpdateCheckViewController -} - -protocol AuthFlowControllerFactory { - func makeValueOnboardingViewController(coordinator: AuthCoordinator) -> ValueOnboardingViewController - func makeLogoOnboardingViewController(coordinator: AuthCoordinator) -> LogoOnboardingViewController - func makeSecondOnboardingViewController(coordinator: AuthCoordinator) -> SecondOnboardingViewController - func makeThirdOnboardingViewController(coordinator: AuthCoordinator) -> ThirdOnboardingViewController - func makeFourthOnboardingViewController(coordinator: AuthCoordinator) -> FourthOnboardingViewController - func makeFifthOnboardingViewController(coordinator: AuthCoordinator) -> FifthOnboardingViewController - func makeAuthViewController(coordinator: AuthCoordinator) -> AuthViewController - func makeNotificationDialogViewController(coordinator: AuthCoordinator, completion: @escaping () -> Void) -> NotificationDialogViewController - -} - -protocol HomeFlowControllerFactory { - func makePopupViewController(coordinator: HomeCoordinator, completion: @escaping () -> Void) -> CommonNotificationViewController - func makeHomeViewController(coordinator: HomeCoordinator) -> HomeViewController - func makeRecommendViewController(coordinator: HomeCoordinator, date: String) -> RecommendViewController - func makeRecommendDetailViewController(coordinator: HomeCoordinator, data: RecommendActionData) -> RecommendActionViewController - func makeAddViewController(coordinator: HomeCoordinator, data: AddMissionData, type: MissionType) -> AddMissionViewController - func makeDeleteViewController(coordinator: HomeCoordinator, completion: @escaping () -> Void) -> HomeDeleteViewController - func makeMissionDetailViewController(coordinator: HomeCoordinator, id: Int, deleteClosure: @escaping () -> Void, moveDateClosure: @escaping (String) -> Void) -> MissionDetailViewController - func makeModifyViewController(coordinator: HomeCoordinator, id: Int, type: MissionType) -> AddMissionViewController - func makeSelectDateViewController(coordinator: HomeCoordinator, data: MissionDetailResponseDTO, id: Int, moveDateClosure: @escaping (String) -> Void) -> DetailCalendarViewController -} - -protocol MyPageFlowControllerFactory { - func makeMyInfoViewController(coordinator: MypageCoordinator) -> MyInfoViewController - func makeMyInfoAccountViewController(coordinator: MypageCoordinator) -> MyInfoAccountViewController - func makeWithdrawViewController(coordinator: MypageCoordinator) -> NottodoModalViewController -} - -protocol AchieveFlowControllerFactory { - func makeAchieveViewController(coordinator: AchieveCoordinator) -> AchievementViewController - func makeAchieveDetailViewController(coordinator: AchieveCoordinator, date: Date) -> DetailAchievementViewController -} - -protocol TabBarControllerFactory { - func makeTabBarController(_ navigationController: UINavigationController) -> (UITabBarController, [UINavigationController]) -} - final class ViewControllerFactoryImpl: ViewControllerFactory {} - -// update -extension ViewControllerFactoryImpl { - func makeUpdateCheckViewController(coordinator: UpdateCoordinator) -> UpdateCheckViewController { - let viewController = UpdateCheckViewController(coordinator: coordinator) - return viewController - } -} -// onboarding -extension ViewControllerFactoryImpl { - func makeValueOnboardingViewController(coordinator: AuthCoordinator) -> ValueOnboardingViewController { - let viewController = ValueOnboardingViewController(coordinator: coordinator) - return viewController - } - func makeLogoOnboardingViewController(coordinator: AuthCoordinator) -> LogoOnboardingViewController { - let viewController = LogoOnboardingViewController(coordinator: coordinator) - return viewController - } - func makeSecondOnboardingViewController(coordinator: AuthCoordinator) -> SecondOnboardingViewController { - let viewController = SecondOnboardingViewController(coordinator: coordinator) - return viewController - } - func makeThirdOnboardingViewController(coordinator: AuthCoordinator) -> ThirdOnboardingViewController { - let viewController = ThirdOnboardingViewController(coordinator: coordinator) - return viewController - } - func makeFourthOnboardingViewController(coordinator: AuthCoordinator) -> FourthOnboardingViewController { - let viewController = FourthOnboardingViewController(coordinator: coordinator) - return viewController - } - func makeFifthOnboardingViewController(coordinator: AuthCoordinator) -> FifthOnboardingViewController { - let viewController = FifthOnboardingViewController(coordinator: coordinator) - return viewController - } -} -// auth -extension ViewControllerFactoryImpl { - func makeAuthViewController(coordinator: AuthCoordinator) -> AuthViewController { - let viewController = AuthViewController(coordinator: coordinator) - return viewController - } - - func makeNotificationDialogViewController(coordinator: AuthCoordinator, completion: @escaping () -> Void) -> NotificationDialogViewController { - let viewController = NotificationDialogViewController() - viewController.buttonHandler = { - completion() - } - return viewController - } -} -// home -extension ViewControllerFactoryImpl { - func makePopupViewController(coordinator: HomeCoordinator, completion: @escaping () -> Void) -> CommonNotificationViewController { - let viewController = CommonNotificationViewController(coordinator: coordinator) - viewController.tapCloseButton = { - completion() - } - return viewController - } - - func makeHomeViewController(coordinator: HomeCoordinator) -> HomeViewController { - let viewController = HomeViewController(coordinator: coordinator) - return viewController - } -} - -// add mission -extension ViewControllerFactoryImpl { - - func makeRecommendViewController(coordinator: HomeCoordinator, date: String) -> RecommendViewController { - let viewController = RecommendViewController(coordinator: coordinator) - viewController.setSelectDate(date) - return viewController - } - - func makeRecommendDetailViewController(coordinator: HomeCoordinator, data: RecommendActionData) -> RecommendActionViewController { - let viewController = RecommendActionViewController(coordinator: coordinator) - viewController.actionHeaderData = data - return viewController - } - - func makeAddViewController(coordinator: HomeCoordinator, data: AddMissionData, type: MissionType) -> AddMissionViewController { - let viewController = AddMissionViewController(coordinator: coordinator) - viewController.setViewType(type) - viewController.setNottodoLabel(data.nottodo ?? "") - viewController.setSituationLabel(data.situation ?? "") - viewController.setActionLabel(data.action ?? "") - viewController.setDate(data.date) - - return viewController - } -} -// home detail -extension ViewControllerFactoryImpl { - func makeDeleteViewController(coordinator: HomeCoordinator, completion: @escaping () -> Void) -> HomeDeleteViewController { - let viewController = HomeDeleteViewController(coordinator: coordinator) - viewController.deleteClosure = { - completion() - } - return viewController - } - - func makeMissionDetailViewController(coordinator: HomeCoordinator, id: Int, deleteClosure: @escaping () -> Void, moveDateClosure: @escaping (String) -> Void) -> MissionDetailViewController { - let viewController = MissionDetailViewController(coordinator: coordinator) - viewController.userId = id - viewController.deleteClosure = { - deleteClosure() - } - viewController.moveDateClosure = { date in - moveDateClosure(date) - } - return viewController - } - - func makeModifyViewController(coordinator: HomeCoordinator, id: Int = 0, type: MissionType) -> AddMissionViewController { - let viewController = AddMissionViewController(coordinator: coordinator) - viewController.setViewType(type) - viewController.setMissionId(id) - return viewController - } - - func makeSelectDateViewController(coordinator: HomeCoordinator, data: MissionDetailResponseDTO, id: Int, moveDateClosure: @escaping (String) -> Void) -> DetailCalendarViewController { - let viewController = DetailCalendarViewController(coordinator: coordinator) - viewController.detailModel = data - viewController.userId = id - viewController.movedateClosure = { date in - moveDateClosure(date) - } - return viewController - } -} -// mypage -extension ViewControllerFactoryImpl { - func makeMyInfoViewController(coordinator: MypageCoordinator) -> MyInfoViewController { - let viewController = MyInfoViewController(coordinator: coordinator) - return viewController - } - - func makeMyInfoAccountViewController(coordinator: MypageCoordinator) -> MyInfoAccountViewController { - let viewController = MyInfoAccountViewController(coordinator: coordinator) - return viewController - } - - func makeWithdrawViewController(coordinator: MypageCoordinator) -> NottodoModalViewController { - let viewController = NottodoModalViewController(coordinator: coordinator) - return viewController - } -} -// achieve -extension ViewControllerFactoryImpl { - func makeAchieveViewController(coordinator: AchieveCoordinator) -> AchievementViewController { - let viewController = AchievementViewController(coordinator: coordinator) - return viewController - } - - func makeAchieveDetailViewController(coordinator: AchieveCoordinator, date: Date) -> DetailAchievementViewController { - let viewController = DetailAchievementViewController(coordinator: coordinator) - viewController.selectedDate = date - return viewController - } -} -// tabbar -extension ViewControllerFactoryImpl { - func makeTabBarController(_: UINavigationController) -> (UITabBarController, [UINavigationController]) { - let tabBarController = TabBarController() - let navigationControllers = tabBarController.setTabBarItems().map(makeNavigationController) - - return (tabBarController, navigationControllers) - } - - func makeNavigationController(_ tabBarItem: UITabBarItem) -> UINavigationController { - let navigationController = UINavigationController() - navigationController.tabBarItem = tabBarItem - navigationController.setNavigationBarHidden(true, animated: false) - return navigationController - } -} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Protocol/AchieveCoordinator.swift b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Protocol/AchieveCoordinator.swift index cd5ca4c9..1782522a 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Protocol/AchieveCoordinator.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Coordinator/Protocol/AchieveCoordinator.swift @@ -9,5 +9,5 @@ import Foundation protocol AchieveCoordinator: Coordinator { func showAchieveViewController() - func showAchieveDetailViewController(selectedDate: Date) + func showAchieveDetailViewController(selectedDate: String) } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/UIView+.swift b/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/UIView+.swift index 8409d7be..c3580482 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/UIView+.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/UIView+.swift @@ -13,4 +13,22 @@ extension UIView { func addSubviews(_ views: UIView...) { views.forEach { self.addSubview($0) } } + + func makeCornerRound (radius: CGFloat) { + layer.cornerRadius = radius + layer.masksToBounds = true + } + + func makeBorder(width: CGFloat, color: UIColor) { + layer.borderWidth = width + layer.borderColor = color.cgColor + } + + func roundCorners(corners: UIRectCorner, radius: CGFloat) { + let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius)) + let mask = CAShapeLayer() + mask.path = path.cgPath + layer.mask = mask + } + } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Global/Protocol/ViewModel.swift b/iOS-NOTTODO/iOS-NOTTODO/Global/Protocol/ViewModel.swift new file mode 100644 index 00000000..ad0a11c6 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Global/Protocol/ViewModel.swift @@ -0,0 +1,15 @@ +// +// ViewModel.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/12/24. +// + +import Foundation + +protocol ViewModel where Self: AnyObject { + associatedtype Input + associatedtype Output + + func transform(input: Input) -> Output +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Service/BaseAPI.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/API/BaseAPI.swift similarity index 96% rename from iOS-NOTTODO/iOS-NOTTODO/Network/Service/BaseAPI.swift rename to iOS-NOTTODO/iOS-NOTTODO/Network/API/BaseAPI.swift index 239fa320..19d7aa7d 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/Service/BaseAPI.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/API/BaseAPI.swift @@ -2,7 +2,7 @@ // BaseAPI.swift // iOS-NOTTODO // -// Created by JEONGEUN KIM on 3/10/24. +// Created by JEONGEUN KIM on 3/14/24. // import Foundation diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Base/APIError.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Base/APIError.swift new file mode 100644 index 00000000..e260a0c4 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/Base/APIError.swift @@ -0,0 +1,20 @@ +// +// APIError.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/12/24. +// + +import Foundation + +public enum APIError: Error, Equatable { + case network(statusCode: Int, response: ErrorResponse) + case unknown + case tokenReissuanceFailed + + init(error: Error, statusCode: Int? = 0, response: ErrorResponse) { + guard let statusCode else { self = .unknown ; return } + + self = .network(statusCode: statusCode, response: response) + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Base/ErrorReponse.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Base/ErrorReponse.swift new file mode 100644 index 00000000..3aa21914 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/Base/ErrorReponse.swift @@ -0,0 +1,13 @@ +// +// ErrorReponse.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/12/24. +// + +import Foundation + +public struct ErrorResponse: Decodable, Equatable { + public let statusCode: String + public let responseMessage: String +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Base/NetworkBase.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Base/NetworkBase.swift deleted file mode 100644 index e86fbf59..00000000 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/Base/NetworkBase.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// NetworkBaes.swift -// iOS-NOTTODO -// -// Created by 김민서 on 2023/02/23. -// - -import Foundation - -struct NetworkBase { - - static func judgeStatus<T: Codable>(by statusCode: Int, _ data: Data, _ t: T.Type) -> NetworkResult<Any> { - let decoder = JSONDecoder() - guard let decodedData = try? decoder.decode(GeneralResponse<T>.self, from: data) - else { return .pathErr } - print(decodedData) - switch statusCode { - case 200: - return .success(decodedData.data as Any) - case 201..<300: - return .success(decodedData.status) - case 400..<500: - return .requestErr(decodedData.status) - case 500: - return .serverErr - default: - return .networkFail - } - } -} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Base/NetworkConstant.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Base/NetworkConstant.swift deleted file mode 100644 index 97f80f4d..00000000 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/Base/NetworkConstant.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// NetworkConstant.swift -// iOS-NOTTODO -// -// Created by 김민서 on 2023/02/23. -// - -import Foundation - -struct NetworkConstant { - static let noTokenHeader = ["Content-Type": "application/json"] - static var hasTokenHeader = ["Content-Type": "application/json", - "Authorization": "\(KeychainUtil.getAccessToken())"] -} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Base/NetworkResult.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Base/NetworkResult.swift deleted file mode 100644 index 2ead150d..00000000 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/Base/NetworkResult.swift +++ /dev/null @@ -1,16 +0,0 @@ -// -// NetworkResult.swift -// iOS-NOTTODO -// -// Created by 김민서 on 2023/02/23. -// - -import Foundation - -enum NetworkResult<T> { - case success(T) // 서버 통신 성공했을 때, - case requestErr(T) // 요청 에러 발생했을 때, - case pathErr // 경로 에러 발생했을 때, - case serverErr // 서버의 내부적 에러가 발생했을 때, - case networkFail // 네트워크 연결 실패했을 때 -} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Achieve/AchieveCalendarResponseDTO.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Achieve/AchieveCalendarResponseDTO.swift deleted file mode 100644 index 333d7794..00000000 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Achieve/AchieveCalendarResponseDTO.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// AchieveCalendarResponseDTO.swift -// iOS-NOTTODO -// -// Created by JEONGEUN KIM on 2023/05/18. -// - -import Foundation - -struct AchieveCalendarResponseDTO: Codable { - let actionDate: String - let percentage: Float - - func toDate(dateString: String) -> Date? { - let dateFormatter = DateFormatter() - dateFormatter.dateFormat = "yyyy.MM.dd" - dateFormatter.timeZone = TimeZone(identifier: "UTC") - if let date = dateFormatter.date(from: dateString) { - return date - } else { - return nil - } - } - - func convert() -> [Date: Float] { - guard let date = self.toDate(dateString: actionDate) else { return [:]} - return [date: percentage] - } -} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Achieve/CalendarReponseDTO.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Achieve/CalendarReponseDTO.swift new file mode 100644 index 00000000..ccb86e5d --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Achieve/CalendarReponseDTO.swift @@ -0,0 +1,37 @@ +// +// AchieveCalendarResponseDTO.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 2023/05/18. +// + +import Foundation + +struct CalendarReponseDTO: Codable { + let actionDate: String + let percentage: Float + + func toDate(dateString: String) -> Date? { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "yyyy.MM.dd" + dateFormatter.timeZone = TimeZone(identifier: "UTC") + if let date = dateFormatter.date(from: dateString) { + return date + } else { + return nil + } + } + + func convert() -> [Date: Float] { + guard let date = self.toDate(dateString: actionDate) else { return [:]} + return [date: percentage] + } +} + +extension Array where Element == CalendarReponseDTO { + + func toData(month: Date) -> CalendarEventData { + let calendarData = Dictionary(uniqueKeysWithValues: self.map { ($0.actionDate, $0.percentage) }) + return CalendarEventData(month: month, percentages: calendarData) + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Home/DailyMissionResponseDTO.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Home/DailyMissionResponseDTO.swift index 01d2cb30..e8764274 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Home/DailyMissionResponseDTO.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Home/DailyMissionResponseDTO.swift @@ -29,3 +29,13 @@ struct DailyMissionResponseDTO: Codable, Hashable { lhs.uuid == rhs.uuid } } + +extension DailyMissionResponseDTO { + + func toData(selectedDate: String) -> AchieveDetailData { + return AchieveDetailData(title: self.title, + situation: self.situationName, + status: self.completionStatus, + selectedDate: selectedDate) + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Home/WeekMissionResponseDTO.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Home/WeekMissionResponseDTO.swift deleted file mode 100644 index 099e8f0a..00000000 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/DataModel/Home/WeekMissionResponseDTO.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// WeekMissionResponseDTO.swift -// iOS-NOTTODO -// -// Created by JEONGEUN KIM on 2023/05/21. -// - -import Foundation - -// MARK: - WeekMissionResponseDTO - -struct WeekMissionResponseDTO: Codable { - let actionDate: String - let percentage: Float - - func toDate(dateString: String) -> Date? { - let dateFormatter = DateFormatter() - dateFormatter.dateFormat = "yyyy.MM.dd" - dateFormatter.timeZone = TimeZone(identifier: "UTC") - if let date = dateFormatter.date(from: dateString) { - return date - } else { - return nil - } - } - - func convert() -> [Date: Float] { - guard let date = self.toDate(dateString: actionDate) else { return [:]} - return [date: percentage] - } -} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/AchieveManagerImpl.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/AchieveManagerImpl.swift new file mode 100644 index 00000000..ec826beb --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/AchieveManagerImpl.swift @@ -0,0 +1,33 @@ +// +// AchieveManagerImpl.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/12/24. +// + +import Foundation +import Combine + +final class AchieveManagerImpl: AchieveManager { + + private let missionAPI: MissionServiceProtocol + private let cancelBag = Set<AnyCancellable>() + + init(missionAPI: MissionServiceProtocol) { + self.missionAPI = missionAPI + } + + func getDailyMission(date: String) -> AnyPublisher<[AchieveDetailData], Error> { + missionAPI.getDailyMission(date: date) + .map { $0.data?.map { $0.toData(selectedDate: date) } } + .compactMap { $0 } + .eraseToAnyPublisher() + } + + func getAchieveCalendar(month: Date) -> AnyPublisher<CalendarEventData, Error> { + return missionAPI.getAchieveCalendar(month: month.formattedString(format: "yyyy-MM")) + .map { $0.data?.toData(month: month) } + .compactMap { $0 } + .eraseToAnyPublisher() + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/ManagerInterface/AchieveManager.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/ManagerInterface/AchieveManager.swift new file mode 100644 index 00000000..ae72ad78 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/Manager/ManagerInterface/AchieveManager.swift @@ -0,0 +1,14 @@ +// +// AchieveManager.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/12/24. +// + +import Foundation +import Combine + +protocol AchieveManager { + func getDailyMission(date: String) -> AnyPublisher<[AchieveDetailData], Error> + func getAchieveCalendar(month: Date) -> AnyPublisher<CalendarEventData, Error> +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Service/BaseService.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Service/BaseService.swift new file mode 100644 index 00000000..c89f452c --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/Service/BaseService.swift @@ -0,0 +1,95 @@ +// +// BaseAPI.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/10/24. +// + +import Foundation +import Combine + +import Alamofire +import Moya + +final class BaseService<Target: TargetType> { + + typealias API = Target + + // MARK: - Properties + + var cancelBag = Set<AnyCancellable>() + + lazy var provider = self.defaultProvider + + private lazy var defaultProvider: MoyaProvider<API> = { + let configuration = URLSessionConfiguration.default + configuration.timeoutIntervalForRequest = 10 + configuration.timeoutIntervalForResource = 10 + configuration.requestCachePolicy = .reloadIgnoringLocalCacheData + let interceptor = AuthInterceptor.shared + let session = Session(configuration: configuration, interceptor: interceptor) + let provider = MoyaProvider<API>( + session: session, + plugins: [MoyaLoggingPlugin()] + ) + return provider + }() + + // MARK: - Initializers + + public init() {} +} + +// MARK: - Providers + +extension BaseService { + var `default`: BaseService { + self.provider = self.defaultProvider + return self + } +} + +// MARK: - MakeRequests + +extension BaseService { + + func requestWithCombine<T: Decodable>(_ target: API) -> AnyPublisher<T, Error> { + return Future { promise in + self.provider.request(target) { response in + switch response { + case .success(let value): + do { + let decoder = JSONDecoder() + let body = try decoder.decode(T.self, from: value.data) + promise(.success(body)) + } catch let error { + promise(.failure(error)) + } + case .failure(let error): + if case MoyaError.underlying(let error, _) = error, + case AFError.requestRetryFailed(let retryError, _) = error, + let retryError = retryError as? APIError, + retryError == APIError.tokenReissuanceFailed { + promise(.failure(retryError)) + } else { + promise(.failure(error)) + } + } + } + }.eraseToAnyPublisher() + } + + // status codea만 사용하는 경우 + func requestWithCombineNoResult(_ target: API) -> AnyPublisher<Int, Error> { + return Future { promise in + self.provider.request(target) { response in + switch response { + case .success(let value): + promise(.success(value.statusCode)) + case .failure(let error): + promise(.failure(error)) + } + } + }.eraseToAnyPublisher() + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/Service/Mission/MissionService.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/Service/Mission/MissionService.swift index 941f1c3b..69531f97 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/Service/Mission/MissionService.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/Service/Mission/MissionService.swift @@ -6,13 +6,32 @@ // import Foundation +import Combine import Moya +// 전체 수정 후 - 네이밍 변경 MissionAPI +protocol MissionServiceProtocol { + func getDailyMission(date: String) -> AnyPublisher<DailyMissionData, Error> + func getAchieveCalendar(month: String) -> AnyPublisher<CalendarData, Error> +} + +typealias DefaultMissionService = BaseService<MissionAPI> + +extension DefaultMissionService: MissionServiceProtocol { + + func getDailyMission(date: String) -> AnyPublisher<DailyMissionData, Error> { + return requestWithCombine(MissionAPI.dailyMission(date: date)) + } + + func getAchieveCalendar(month: String) -> AnyPublisher<CalendarData, Error> { + return requestWithCombine(MissionAPI.achieveCalendar(month: month)) + } +} + typealias DailyMissionData = GeneralArrayResponse<DailyMissionResponseDTO> -typealias WeekMissionData = GeneralArrayResponse<WeekMissionResponseDTO> typealias DetailMissionData = GeneralResponse<MissionDetailResponseDTO> -typealias AchieveCalendarData = GeneralArrayResponse<AchieveCalendarResponseDTO> +typealias CalendarData = GeneralArrayResponse<CalendarReponseDTO> typealias RecentMissionData = GeneralArrayResponse<RecentMissionResponseDTO> typealias UpdateMissionData = GeneralResponse<UpdateMissionResponseDTO> typealias AddMissionsData = GeneralResponse<AddMissionResponseDTO> @@ -21,10 +40,10 @@ typealias UpdateMissionStatus = GeneralResponse<DailyMissionResponseDTO> protocol MissionServiceType { func getDailyMission(date: String, completion: @escaping (DailyMissionData?) -> Void) - func getWeeklyMissoin(startDate: String, completion: @escaping (WeekMissionData?) -> Void) + func getWeeklyMissoin(startDate: String, completion: @escaping (CalendarData?) -> Void) func getDetailMission(id: Int, completion: @escaping (DetailMissionData?) -> Void) func particularMissionDates(id: Int, completion: @escaping (GeneralArrayResponse<String>) -> Void) - func getAchieveCalendar(month: String, completion: @escaping (AchieveCalendarData?) -> Void) + func getAchieveCalendar(month: String, completion: @escaping (CalendarData?) -> Void) func getRecentMission(completion: @escaping (RecentMissionData?) -> Void) func deleteMission(id: Int, completion: @escaping (GeneralResponse<VoidType>?) -> Void) func patchUpdateMissionStatus(id: Int, status: String, completion: @escaping (UpdateMissionStatus?) -> Void) @@ -58,12 +77,12 @@ final class MissionService: MissionServiceType { } } - func getWeeklyMissoin(startDate: String, completion: @escaping (WeekMissionData?) -> Void) { + func getWeeklyMissoin(startDate: String, completion: @escaping (CalendarData?) -> Void) { provider.request(.missionWeekly(startDate: startDate)) { result in switch result { case .success(let response): do { - let response = try response.map(WeekMissionData?.self) + let response = try response.map(CalendarData?.self) completion(response) } catch let err { print(err.localizedDescription, 500) @@ -93,12 +112,12 @@ final class MissionService: MissionServiceType { } } - func getAchieveCalendar(month: String, completion: @escaping (AchieveCalendarData?) -> Void) { + func getAchieveCalendar(month: String, completion: @escaping (CalendarData?) -> Void) { provider.request(.achieveCalendar(month: month)) { result in switch result { case .success(let response): do { - let response = try response.map(AchieveCalendarData?.self) + let response = try response.map(CalendarData?.self) completion(response) } catch let err { print(err.localizedDescription, 500) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Cell/DetailAchievementCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Cell/DetailAchievementCollectionViewCell.swift index c665491c..a1ca442f 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Cell/DetailAchievementCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Cell/DetailAchievementCollectionViewCell.swift @@ -18,8 +18,8 @@ final class DetailAchievementCollectionViewCell: UICollectionViewCell { // MARK: - UI Components - let tagLabel = PaddingLabel(padding: UIEdgeInsets(top: 4, left: 12, bottom: 4, right: 12)) - let titleLabel = UILabel() + private let tagLabel = PaddingLabel(padding: UIEdgeInsets(top: 4, left: 12, bottom: 4, right: 12)) + private let titleLabel = UILabel() private let checkImage = UIImageView() // MARK: - Life Cycle @@ -48,9 +48,9 @@ extension DetailAchievementCollectionViewCell { $0.layer.backgroundColor = UIColor.bg?.cgColor $0.font = .Pretendard(.medium, size: 14) $0.textColor = .gray1 - $0.layer.cornerRadius = 25/2 + $0.makeCornerRound(radius: 25/2) } - + titleLabel.do { $0.font = .Pretendard(.semiBold, size: 16) $0.textColor = .gray2 @@ -65,12 +65,12 @@ extension DetailAchievementCollectionViewCell { private func setLayout() { contentView.addSubviews(tagLabel, titleLabel, checkImage) - + tagLabel.snp.makeConstraints { $0.top.equalToSuperview().offset(22) $0.leading.equalToSuperview().inset(28) } - + titleLabel.snp.makeConstraints { $0.top.equalTo(tagLabel.snp.bottom).offset(7) $0.leading.equalToSuperview().inset(28) @@ -85,14 +85,10 @@ extension DetailAchievementCollectionViewCell { } } - func configure(model: DailyMissionResponseDTO) { - tagLabel.text = model.situationName + func configure(model: AchieveDetailData) { + tagLabel.text = model.situation titleLabel.text = model.title titleLabel.lineBreakMode = .byTruncatingTail - - switch model.completionStatus { - case .CHECKED: checkImage.isHidden = false - case .UNCHECKED: checkImage.isHidden = true - } + checkImage.isHidden = model.status == .UNCHECKED } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Model/AchievementModel.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Model/AchievementModel.swift new file mode 100644 index 00000000..2105b07a --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Model/AchievementModel.swift @@ -0,0 +1,13 @@ +// +// AchievementModel.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/12/24. +// + +import Foundation + +struct CalendarEventData { + let month: Date + let percentages: [String: Float] +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Model/DetailAchievementModel.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Model/DetailAchievementModel.swift new file mode 100644 index 00000000..e6518698 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Model/DetailAchievementModel.swift @@ -0,0 +1,26 @@ +// +// DetailAchievementModel.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/12/24. +// + +import Foundation + +struct AchieveDetailData: Hashable { + + let title: String + let situation: String + let status: CompletionStatus + let selectedDate: String + + func formatDateString() -> String { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "yyyy-MM-dd" + + guard let date = dateFormatter.date(from: selectedDate) else { return "" } + + dateFormatter.dateFormat = "yyyy년 MM월 dd일" + return dateFormatter.string(from: date) + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift index e222eab0..648d1ecf 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift @@ -6,6 +6,7 @@ // import UIKit +import Combine import FSCalendar import Then @@ -15,22 +16,26 @@ final class AchievementViewController: UIViewController { // MARK: - Properties - private weak var coordinator: AchieveCoordinator? - private var currentPage = Date() - private var dataSource: [String: Float] = [:] + private let viewWillAppearSubject = PassthroughSubject<Date, Never>() + private let eventCellSubject = PassthroughSubject<Date, Never>() + private let monthSubject = PassthroughSubject<Date, Never>() + private let dataSource = CurrentValueSubject<[String: Float], Never>([:]) + private var cancelBag = Set<AnyCancellable>() - private lazy var safeArea = self.view.safeAreaLayoutGuide + private let viewModel: any AchievementViewModel // MARK: - UI Components + private lazy var safeArea = self.view.safeAreaLayoutGuide private let scrollView = UIScrollView() private let achievementLabel = UILabel() - private let monthCalendar = CalendarView(calendarScope: .month, scrollDirection: .horizontal) + private let monthCalendar = CalendarView(scope: .month) private let statisticsView = StatisticsView() // MARK: - init - init(coordinator: AchieveCoordinator) { - self.coordinator = coordinator + + init(viewModel: some AchievementViewModel) { + self.viewModel = viewModel super.init(nibName: nil, bundle: nil) } @@ -42,13 +47,9 @@ final class AchievementViewController: UIViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Achieve.viewAccomplish) - if let today = monthCalendar.calendar.today { - monthCalendar.configureYearMonth(to: Utils.dateFormatterString(format: I18N.yearMonthTitle, date: today)) - monthCalendar.calendar.currentPage = today - requestMonthAPI(month: Utils.dateFormatterString(format: "yyyy-MM", date: today)) - } + viewWillAppearSubject.send(Date()) + AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Achieve.viewAccomplish) } override func viewDidLoad() { @@ -56,6 +57,7 @@ final class AchievementViewController: UIViewController { setUI() setLayout() + setBindings() } } @@ -78,16 +80,9 @@ extension AchievementViewController { } monthCalendar.do { - $0.layer.cornerRadius = 12 - $0.layer.borderWidth = 1 - $0.layer.borderColor = UIColor.gray4?.cgColor - $0.calendar.register(MissionCalendarCell.self, forCellReuseIdentifier: MissionCalendarCell.identifier) - $0.calendar.delegate = self - $0.calendar.dataSource = self - $0.monthCalendarClosure = { [weak self] month in - guard let self else { return } - self.requestMonthAPI(month: month) - } + $0.makeCornerRound(radius: 12) + $0.makeBorder(width: 1, color: .gray4!) + $0.configure(delegate: self, datasource: self) } } @@ -118,86 +113,79 @@ extension AchievementViewController { $0.bottom.equalTo(scrollView.snp.bottom).inset(20) } } + + func setBindings() { + let input = AchievementViewModelInput(viewWillAppearSubject: viewWillAppearSubject, + calendarCellTapped: eventCellSubject, + currentMonthSubject: monthSubject) + + let output = viewModel.transform(input: input) + + output.viewWillAppearSubject + .receive(on: RunLoop.main) + .sink { [weak self] item in + self?.updateCalendar(with: item) + } + .store(in: &cancelBag) + } + + func updateCalendar(with data: CalendarEventData) { + self.dataSource.send(data.percentages) + self.monthCalendar.reloadCollectionView() + self.monthCalendar.currentPage(date: data.month) + self.monthCalendar.configureYearMonth(to: data.month.formattedString(format: I18N.yearMonthTitle)) + } } -extension AchievementViewController: FSCalendarDelegate, FSCalendarDataSource, FSCalendarDelegateAppearance { +extension AchievementViewController: FSCalendarDelegate { func calendarCurrentPageDidChange(_ calendar: FSCalendar) { - self.currentPage = calendar.currentPage - requestMonthAPI(month: Utils.dateFormatterString(format: "yyyy-MM", date: calendar.currentPage)) - monthCalendar.configureYearMonth(to: Utils.dateFormatterString(format: I18N.yearMonthTitle, date: calendar.currentPage)) + self.monthSubject.send(calendar.currentPage) + monthCalendar.configureYearMonth(to: calendar.currentPage.formattedString(format: I18N.yearMonthTitle)) } - func calendar(_ calendar: FSCalendar, titleFor date: Date) -> String? { - Utils.dateFormatterString(format: "dd", date: date) + func calendar(_ calendar: FSCalendar, cellFor date: Date, at position: FSCalendarMonthPosition) -> FSCalendarCell { + + guard let cell = calendar.dequeueReusableCell(withIdentifier: MissionCalendarCell.identifier, for: date, at: position) as? MissionCalendarCell else { return FSCalendarCell() } + + guard let percentage = self.dataSource.value[date.formattedString()] else { return cell } + cell.configure(percent: percentage) + + return cell } func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) { calendar.appearance.selectionColor = .clear calendar.appearance.titleSelectionColor = .white - let dateString = Utils.dateFormatterString(format: "yyyy-MM-dd", date: date) - if self.dataSource.contains(where: { $0.key == dateString }) { - coordinator?.showAchieveDetailViewController(selectedDate: date) - } + + self.eventCellSubject.send(date) } +} + +extension AchievementViewController: FSCalendarDataSource { - func calendar(_ calendar: FSCalendar, shouldSelect date: Date, at monthPosition: FSCalendarMonthPosition) -> Bool { - Calendar.current.isDate(date, equalTo: calendar.currentPage, toGranularity: .month) + func calendar(_ calendar: FSCalendar, titleFor date: Date) -> String? { + date.formattedString(format: "dd") } +} + +extension AchievementViewController: FSCalendarDelegateAppearance { func calendar(_ calendar: FSCalendar, appearance: FSCalendarAppearance, titleSelectionColorFor date: Date) -> UIColor? { - titleColorFor(date: date) + if let percentage = self.dataSource.value[date.formattedString()], percentage == 1.0 { + return .black + } + return .white } func calendar(_ calendar: FSCalendar, appearance: FSCalendarAppearance, titleDefaultColorFor date: Date) -> UIColor? { - let currentMonth = Calendar.current.component(.month, from: currentPage) + let currentMonth = Calendar.current.component(.month, from: calendar.currentPage) let dateMonth = Calendar.current.component(.month, from: date) - if let percentage = getPercentage(for: date) { - return percentage == 1.0 ? .black : .white - } else { + guard let percentage = self.dataSource.value[date.formattedString()] else { return currentMonth != dateMonth ? .gray3 : .white } - } - - func calendar(_ calendar: FSCalendar, cellFor date: Date, at position: FSCalendarMonthPosition) -> FSCalendarCell { - let cell = calendar.dequeueReusableCell(withIdentifier: MissionCalendarCell.identifier, for: date, at: position) as! MissionCalendarCell - - guard let percentage = getPercentage(for: date) else { return cell } - cell.configure(percent: percentage) - - return cell - } -} - -// MARK: - Others - -extension AchievementViewController { - - func requestMonthAPI(month: String) { - MissionService.shared.getAchieveCalendar(month: month) { [weak self] response in - - guard let self, let response = response, let data = response.data else { return } - - let calendarData = data.compactMap { ($0.actionDate, $0.percentage) } - self.dataSource = Dictionary(uniqueKeysWithValues: calendarData) - self.monthCalendar.calendar.collectionView.reloadData() - } - } - - private func getPercentage(for date: Date) -> Float? { - - let dateString = Utils.dateFormatterString(format: nil, date: date) - return self.dataSource[dateString] - } - - private func titleColorFor(date: Date) -> UIColor { - - if let percentage = getPercentage(for: date) { - return percentage == 1.0 ? .black : .white - } - - return .white + return percentage == 1.0 ? .black : .white } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift index 493ebb57..b20cf2c4 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchievementViewController.swift @@ -6,6 +6,7 @@ // import UIKit +import Combine import Then import SnapKit @@ -16,30 +17,30 @@ final class DetailAchievementViewController: UIViewController { typealias CellRegistration = UICollectionView.CellRegistration typealias HeaderRegistration = UICollectionView.SupplementaryRegistration - typealias Item = DailyMissionResponseDTO + typealias Item = AchieveDetailData typealias DataSource = UICollectionViewDiffableDataSource<Section, Item> typealias SnapShot = NSDiffableDataSourceSnapshot<Section, Item> - + enum Section: Int, Hashable { case main } - - var selectedDate: Date? - + + private let viewWillAppearSubject = PassthroughSubject<Void, Never>() + private let dismissSubject = PassthroughSubject<Void, Never>() + private var cancelBag = Set<AnyCancellable>() + private var dataSource: DataSource? - - private lazy var safeArea = self.view.safeAreaLayoutGuide - - private weak var coordinator: AchieveCoordinator? + private var viewModel: any DetailAchievementViewModel // MARK: - UI Components + private lazy var safeArea = self.view.safeAreaLayoutGuide private var collectionView = UICollectionView(frame: .zero, collectionViewLayout: .init()) // MARK: - init - init(coordinator: AchieveCoordinator) { - self.coordinator = coordinator + init(viewModel: some DetailAchievementViewModel) { + self.viewModel = viewModel super.init(nibName: nil, bundle: nil) } @@ -52,10 +53,7 @@ final class DetailAchievementViewController: UIViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - if let selectedDate = selectedDate { - requestDetailAPI(date: Utils.dateFormatterString(format: "YYYY-MM-dd", - date: selectedDate)) - } + viewWillAppearSubject.send(()) } override func viewDidLoad() { @@ -65,6 +63,7 @@ final class DetailAchievementViewController: UIViewController { setLayout() setDataSource() setSnapShot() + setBindings() } override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { @@ -74,7 +73,7 @@ final class DetailAchievementViewController: UIViewController { if !collectionView.frame.contains(location) { AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Achieve.closeDailyMissionModal) - coordinator?.dismiss() + dismissSubject.send(()) } } } @@ -88,7 +87,7 @@ extension DetailAchievementViewController { collectionView.do { $0.collectionViewLayout = layout() - $0.layer.cornerRadius = 15 + $0.makeCornerRound(radius: 15) $0.backgroundColor = .white $0.bounces = false $0.autoresizingMask = [.flexibleWidth, .flexibleHeight] @@ -106,17 +105,13 @@ extension DetailAchievementViewController { } private func setDataSource() { - let cellRegistration = CellRegistration<DetailAchievementCollectionViewCell, Item> {cell, _, item in cell.configure(model: item) } - // header memory leak 해결 [weak self] - let headerRegistration = HeaderRegistration<DetailAchieveHeaderView>(elementKind: UICollectionView.elementKindSectionHeader) { [weak self] headerView, _, _ in - if let date = self?.selectedDate { - headerView.configure(text: Utils.dateFormatterString(format: "YYYY년 MM월 dd일", - date: date)) - } + let headerRegistration = HeaderRegistration<DetailAchieveHeaderView>(elementKind: UICollectionView.elementKindSectionHeader) { [weak self] headerView, _, indexPath in + guard let item = self?.dataSource?.itemIdentifier(for: indexPath) else { return } + headerView.configure(text: item.formatDateString()) } dataSource = DataSource(collectionView: collectionView, cellProvider: { collectionView, indexPath, item in @@ -128,32 +123,34 @@ extension DetailAchievementViewController { dataSource?.supplementaryViewProvider = { collectionView, _, indexPath in return collectionView.dequeueConfiguredReusableSupplementary(using: headerRegistration, for: indexPath) + } } - private func setSnapShot() { + private func setBindings() { + let input = DetailAchievementViewModelInput(viewWillAppearSubject: viewWillAppearSubject, dismissSubject: dismissSubject) - var snapShot = SnapShot() - defer { - dataSource?.apply(snapShot, animatingDifferences: false) - } + let output = viewModel.transform(input: input) - snapShot.appendSections([.main]) - snapShot.appendItems([], toSection: .main) + output.viewWillAppearSubject + .receive(on: RunLoop.main) + .sink { [weak self] item in + self?.setSnapShot(with: item) + AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Achieve.appearDailyMissionModal(total: item.count)) + } + .store(in: &cancelBag) } - private func updateData(item: [DailyMissionResponseDTO]) { - - guard var snapshot = dataSource?.snapshot() else { return } + private func setSnapShot(with items: [AchieveDetailData] = []) { + var snapShot = SnapShot() - snapshot.appendItems(item, toSection: .main) - dataSource?.apply(snapshot) + snapShot.appendSections([.main]) + snapShot.appendItems(items, toSection: .main) - AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Achieve.appearDailyMissionModal(total: item.count)) + dataSource?.applySnapshotUsingReloadData(snapShot) } private func layout() -> UICollectionViewCompositionalLayout { - var config = UICollectionLayoutListConfiguration(appearance: .plain) config.headerMode = .supplementary config.backgroundColor = .clear @@ -174,14 +171,3 @@ extension DetailAchievementViewController { return UICollectionViewCompositionalLayout.list(using: config) } } - -extension DetailAchievementViewController { - private func requestDetailAPI(date: String) { - MissionService.shared.getDailyMission(date: date) { [weak self] response in - guard let self else { return } - guard let response = response else { return } - guard let data = response.data else { return } - self.updateData(item: data) - } - } -} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/AchievementViewModel.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/AchievementViewModel.swift new file mode 100644 index 00000000..b9c4c1c3 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/AchievementViewModel.swift @@ -0,0 +1,22 @@ +// +// AchievementViewModel.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/12/24. +// + +import Foundation +import Combine + +protocol AchievementViewModelPresentable {} +protocol AchievementViewModel: ViewModel where Input == AchievementViewModelInput, Output == AchievementViewModelOutput {} + +struct AchievementViewModelInput { + let viewWillAppearSubject: PassthroughSubject<Date, Never> + let calendarCellTapped: PassthroughSubject<Date, Never> + let currentMonthSubject: PassthroughSubject<Date, Never> +} + +struct AchievementViewModelOutput { + let viewWillAppearSubject: AnyPublisher<CalendarEventData, Never> +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/AchievementViewModelImpl.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/AchievementViewModelImpl.swift new file mode 100644 index 00000000..38244c5b --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/AchievementViewModelImpl.swift @@ -0,0 +1,62 @@ +// +// AchievementViewModelImpl.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/12/24. +// + +import Foundation +import Combine + +final class AchievementViewModelImpl: AchievementViewModel { + + private weak var coordinator: AchieveCoordinator? + private var manager: AchieveManager + private var cancelBag = Set<AnyCancellable>() + + init(coordinator: AchieveCoordinator, manager: AchieveManager) { + self.coordinator = coordinator + self.manager = manager + } + + let eventSubject = PassthroughSubject<CalendarEventData, Never>() + let dataSource = CurrentValueSubject<[String: Float], Never>([:]) + + func transform(input: AchievementViewModelInput) -> AchievementViewModelOutput { + + input.viewWillAppearSubject + .merge(with: input.currentMonthSubject) + .removeDuplicates() + .sink { [weak self] month in + guard let self = self else { return } + self.getCalendarEvent(month: month) + } + .store(in: &cancelBag) + + input.calendarCellTapped + .filter { [weak self] date -> Bool in + guard let percentage = self?.dataSource.value[date.formattedString()] else { + return false + } + return percentage != 0.0 + } + .map { $0.formattedString() } + .sink { [weak self] date in + self?.coordinator?.showAchieveDetailViewController(selectedDate: date) + } + .store(in: &cancelBag) + + return Output(viewWillAppearSubject: eventSubject.eraseToAnyPublisher()) + } + + func getCalendarEvent(month: Date) { + manager.getAchieveCalendar(month: month) + .sink(receiveCompletion: { event in + print("completion: \(event)") + }, receiveValue: { data in + self.eventSubject.send(data) + self.dataSource.send(data.percentages) + }) + .store(in: &cancelBag) + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/DetailAchievementViewModel.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/DetailAchievementViewModel.swift new file mode 100644 index 00000000..59b6f828 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/DetailAchievementViewModel.swift @@ -0,0 +1,23 @@ +// +// DetailAchievementViewModel.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/12/24. +// + +import Foundation +import Combine + +protocol DetailAchievementViewModelPresentable { + func selectedDate(_ date: String) +} +protocol DetailAchievementViewModel: ViewModel where Input == DetailAchievementViewModelInput, Output == DetailAchievementViewModelOutput {} + +struct DetailAchievementViewModelInput { + let viewWillAppearSubject: PassthroughSubject<Void, Never> + let dismissSubject: PassthroughSubject<Void, Never> +} + +struct DetailAchievementViewModelOutput { + let viewWillAppearSubject: AnyPublisher<[AchieveDetailData], Never> +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/DetailAchievementViewModelImpl.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/DetailAchievementViewModelImpl.swift new file mode 100644 index 00000000..6944a6c9 --- /dev/null +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewModel/DetailAchievementViewModelImpl.swift @@ -0,0 +1,59 @@ +// +// DetailAchievementViewModelImpl.swift +// iOS-NOTTODO +// +// Created by JEONGEUN KIM on 3/12/24. +// + +import Foundation +import Combine + +final class DetailAchievementViewModelImpl: DetailAchievementViewModel { + + private weak var coordinator: AchieveCoordinator? + private var manager: AchieveManager + private var selectedDate: String? + private var cancelBag = Set<AnyCancellable>() + + init(coordinator: AchieveCoordinator, manager: AchieveManager) { + self.coordinator = coordinator + self.manager = manager + } + + let missionsSubject = PassthroughSubject<[AchieveDetailData], Never>() + + func transform(input: DetailAchievementViewModelInput) -> DetailAchievementViewModelOutput { + + input.viewWillAppearSubject + .sink(receiveValue: { [weak self] _ in + guard let self else { return } + guard let selectedDate = self.selectedDate else { return } + self.getDailyMission(date: selectedDate) + }) + .store(in: &cancelBag) + + input.dismissSubject + .sink {[weak self] _ in + self?.coordinator?.dismiss() + } + .store(in: &cancelBag) + + return Output(viewWillAppearSubject: missionsSubject.eraseToAnyPublisher()) + } + + func getDailyMission(date: String) { + manager.getDailyMission(date: date) + .sink(receiveCompletion: { event in + print("completion: \(event)") + }, receiveValue: { data in + self.missionsSubject.send(data) + }) + .store(in: &cancelBag) + } +} + +extension DetailAchievementViewModelImpl: DetailAchievementViewModelPresentable { + func selectedDate(_ date: String) { + self.selectedDate = date + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchieveHeaderView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Views/DetailAchieveHeaderView.swift similarity index 100% rename from iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/DetailAchieveHeaderView.swift rename to iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Views/DetailAchieveHeaderView.swift diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/StatisticsView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Views/StatisticsView.swift similarity index 100% rename from iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/StatisticsView.swift rename to iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/Views/StatisticsView.swift diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/ActionCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/ActionCollectionViewCell.swift index f98cadae..97290817 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/ActionCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/ActionCollectionViewCell.swift @@ -76,9 +76,8 @@ final class ActionCollectionViewCell: UICollectionViewCell, AddMissionMenu { extension ActionCollectionViewCell { private func setUI() { backgroundColor = .clear - layer.borderColor = UIColor.gray3?.cgColor - layer.cornerRadius = 12 - layer.borderWidth = 1 + makeCornerRound(radius: 12) + makeBorder(width: 1, color: .gray3!) checkImage.image = .icChecked stackView.axis = .vertical foldStackView.do { diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/DateCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/DateCollectionViewCell.swift index 213e292c..da7f522e 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/DateCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/DateCollectionViewCell.swift @@ -28,7 +28,7 @@ final class DateCollectionViewCell: UICollectionViewCell, AddMissionMenu { private let titleLabel = TitleLabel(title: I18N.date) private let subTitleLabel = SubTitleLabel(subTitle: I18N.subDateTitle, colorText: nil) - let calendarView = CalendarView(calendarScope: .month, scrollDirection: .horizontal) + let calendarView = CalendarView(scope: .month) private let warningLabel = UILabel() private let stackView = UIStackView() @@ -92,9 +92,8 @@ final class DateCollectionViewCell: UICollectionViewCell, AddMissionMenu { extension DateCollectionViewCell { private func setUI() { backgroundColor = .clear - layer.borderColor = UIColor.gray3?.cgColor - layer.cornerRadius = 12 - layer.borderWidth = 1 + makeCornerRound(radius: 12) + makeBorder(width: 1, color: .gray3!) calendarImage.image = .icCalendar dayLabel.font = .Pretendard(.medium, size: 15) stackView.axis = .vertical @@ -121,9 +120,8 @@ extension DateCollectionViewCell { } calendarView.do { - $0.calendar.backgroundColor = .clear $0.backgroundColor = .clear - $0.calendar.delegate = self + $0.configure(delegate: self, datasource: nil) } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/GoalCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/GoalCollectionViewCell.swift index 6ae137fa..c3b945bf 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/GoalCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/GoalCollectionViewCell.swift @@ -81,9 +81,8 @@ final class GoalCollectionViewCell: UICollectionViewCell, AddMissionMenu { private extension GoalCollectionViewCell { func setUI() { backgroundColor = .clear - layer.borderColor = UIColor.gray3?.cgColor - layer.cornerRadius = 12 - layer.borderWidth = 1 + makeCornerRound(radius: 12) + makeBorder(width: 1, color: .gray3!) checkImage.image = .icChecked stackView.axis = .vertical [nottodoStackView, goalStackView].forEach { @@ -109,7 +108,7 @@ private extension GoalCollectionViewCell { [nottodoTag, goalTag].forEach { $0.backgroundColor = .gray2 - $0.layer.cornerRadius = 20 / 2 + $0.makeCornerRound(radius: 20 / 2) $0.clipsToBounds = true $0.textColor = .gray4 $0.font = .Pretendard(.medium, size: 12) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/NottodoCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/NottodoCollectionViewCell.swift index 08888fb6..0b50f58b 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/NottodoCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/NottodoCollectionViewCell.swift @@ -76,9 +76,8 @@ final class NottodoCollectionViewCell: UICollectionViewCell, AddMissionMenu { extension NottodoCollectionViewCell { private func setUI() { backgroundColor = .clear - layer.borderColor = UIColor.gray3?.cgColor - layer.cornerRadius = 12 - layer.borderWidth = 1 + makeCornerRound(radius: 12) + makeBorder(width: 1, color: .gray3!) historyCollectionView.backgroundColor = .clear historyCollectionView.indicatorStyle = .white checkImage.image = .icChecked diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/RecommendKeywordCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/RecommendKeywordCollectionViewCell.swift index e1303e65..5913f6f8 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/RecommendKeywordCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/RecommendKeywordCollectionViewCell.swift @@ -43,7 +43,7 @@ final class RecommendKeywordCollectionViewCell: UICollectionViewCell { private extension RecommendKeywordCollectionViewCell { func setUI() { - contentView.layer.cornerRadius = contentView.frame.height / 2 + contentView.makeCornerRound(radius: contentView.frame.height / 2) contentView.clipsToBounds = true recommendLabel.do { $0.backgroundColor = .gray2 diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/SituationCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/SituationCollectionViewCell.swift index 70a198b3..ab710d16 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/SituationCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/Cells/SituationCollectionViewCell.swift @@ -76,9 +76,8 @@ final class SituationCollectionViewCell: UICollectionViewCell, AddMissionMenu { private extension SituationCollectionViewCell { func setUI() { backgroundColor = .clear - layer.borderColor = UIColor.gray3?.cgColor - layer.cornerRadius = 12 - layer.borderWidth = 1 + makeCornerRound(radius: 12) + makeBorder(width: 1, color: .gray3!) checkImage.image = .icChecked stackView.axis = .vertical foldStackView.do { diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/ViewControllers/AddMissionViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/ViewControllers/AddMissionViewController.swift index 04ac3be0..01afc324 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/ViewControllers/AddMissionViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/AddMission/ViewControllers/AddMissionViewController.swift @@ -159,7 +159,7 @@ extension AddMissionViewController { } addButton.do { - $0.layer.cornerRadius = 26 / 2 + $0.makeCornerRound(radius: 26 / 2) $0.titleLabel?.font = .Pretendard(.medium, size: 15) $0.setTitleColor(.gray3, for: .disabled) $0.setTitleColor(.gray1, for: .normal) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Auth/AuthButtonView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Auth/AuthButtonView.swift index f639d8a2..e17f2b35 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Auth/AuthButtonView.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Auth/AuthButtonView.swift @@ -39,7 +39,7 @@ extension AuthButtonView { buttonView.do { $0.backgroundColor = color - $0.layer.cornerRadius = 5 + $0.makeCornerRound(radius: 5) } buttonIcon.image = icon diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift index 5958ecfb..74595132 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/CalendarView.swift @@ -11,35 +11,52 @@ import FSCalendar import Then import SnapKit -protocol CalendarViewDelegate: AnyObject { +protocol WeekCalendarDelegate: AnyObject { func todayBtnTapped() } +protocol MonthCalendarDelegate: AnyObject { + + func changeMonth(with month: String) +} final class CalendarView: UIView { // MARK: - Properties - let today = Date() - var monthCalendarClosure: ((_ month: String) -> Void)? - weak var delegate: CalendarViewDelegate? + private enum CalendarMoveType { + case previous + case next + + var monthOffset: Int { + switch self { + case .previous: + return -1 + case .next: + return 1 + } + } + } + + weak var delegate: WeekCalendarDelegate? // MARK: - UI Components private let yearMonthLabel = UILabel() - let todayButton = UIButton(configuration: .filled()) + private let todayButton = UIButton(configuration: .filled()) private let horizonStackView = UIStackView() private let leftButton = UIButton() private let rightButton = UIButton() - var calendar = WeekMonthFSCalendar() + let calendar: WeekMonthFSCalendar // MARK: - Life Cycle - init(calendarScope: FSCalendarScope, scrollDirection: FSCalendarScrollDirection) { + init(scope: FSCalendarScope) { + self.calendar = WeekMonthFSCalendar(calendarScope: scope) super.init(frame: .zero) - setCalendar(scope: calendarScope, scrollDirection: scrollDirection) + setUI() - setLayout(scope: calendarScope) + setLayout(scope: scope) } required init?(coder: NSCoder) { @@ -51,11 +68,6 @@ final class CalendarView: UIView { extension CalendarView { - private func setCalendar(scope: FSCalendarScope, scrollDirection: FSCalendarScrollDirection) { - - calendar = WeekMonthFSCalendar(calendarScope: scope, scrollDirection: scrollDirection) - } - private func setUI() { backgroundColor = .ntdBlack @@ -63,7 +75,7 @@ extension CalendarView { yearMonthLabel.do { $0.font = .Pretendard(.medium, size: 18) $0.textColor = .white - $0.text = Utils.dateFormatterString(format: I18N.yearMonthTitle, date: Date()) + $0.text = Date().formattedString(format: I18N.yearMonthTitle) } todayButton.do { @@ -100,68 +112,76 @@ extension CalendarView { } calendar.do { + $0.currentPage = Date() $0.collectionView.register(MissionCalendarCell.self, forCellWithReuseIdentifier: MissionCalendarCell.identifier) } } private func setLayout(scope: FSCalendarScope) { - switch scope { case .week: - addSubviews(calendar, yearMonthLabel, todayButton) - - yearMonthLabel.snp.makeConstraints { - $0.top.equalToSuperview().offset(23) - $0.leading.equalToSuperview().offset(20) - } - - todayButton.snp.makeConstraints { - $0.top.equalTo(yearMonthLabel.snp.top) - $0.trailing.equalToSuperview().inset(18) - $0.size.equalTo(CGSize(width: 60, height: 30)) - } - - calendar.snp.makeConstraints { - $0.top.equalTo(yearMonthLabel.snp.bottom).offset(8) - $0.directionalHorizontalEdges.equalToSuperview().inset(11) - $0.bottom.equalToSuperview().inset(20) - } - + setWeekLayout() case .month: - addSubviews(horizonStackView, calendar) - horizonStackView.addArrangedSubviews(leftButton, yearMonthLabel, rightButton) - - [leftButton, rightButton].forEach { - $0.snp.makeConstraints { - $0.size.equalTo(CGSize(width: 25, height: 25)) - } - } - - horizonStackView.snp.makeConstraints { - $0.top.equalToSuperview().offset(25) - $0.centerX.equalToSuperview() - $0.height.equalTo(25) - } - - calendar.snp.makeConstraints { - $0.top.equalTo(horizonStackView.snp.bottom).offset(20) - $0.directionalHorizontalEdges.equalToSuperview().inset(15) - $0.bottom.equalToSuperview().inset(25) - } + setMonthLayout() @unknown default: return } } - func scrollCurrentPage(calendar: WeekMonthFSCalendar, isPrev: Bool) { + private func setWeekLayout() { + addSubviews(calendar, yearMonthLabel, todayButton) + + yearMonthLabel.snp.makeConstraints { + $0.top.equalToSuperview().offset(23) + $0.leading.equalToSuperview().offset(20) + } - let gregorian = Calendar(identifier: .gregorian) - calendar.setCurrentPage( gregorian.date(byAdding: calendar.scope == .week ? .weekOfMonth : .month, value: isPrev ? -1 : 1, to: calendar.currentPage)!, animated: true) - let monthDateFormatter = DateFormatter() - monthDateFormatter.dateFormat = "yyyy-MM" - let stringDate = monthDateFormatter.string(from: calendar.currentPage) - monthCalendarClosure?(stringDate) + todayButton.snp.makeConstraints { + $0.top.equalTo(yearMonthLabel.snp.top) + $0.trailing.equalToSuperview().inset(18) + $0.size.equalTo(CGSize(width: 60, height: 30)) + } + + calendar.snp.makeConstraints { + $0.top.equalTo(yearMonthLabel.snp.bottom).offset(8) + $0.directionalHorizontalEdges.equalToSuperview().inset(11) + $0.bottom.equalToSuperview().inset(20) + } + } + + private func setMonthLayout() { + addSubviews(horizonStackView, calendar) + horizonStackView.addArrangedSubviews(leftButton, yearMonthLabel, rightButton) + + [leftButton, rightButton].forEach { + $0.snp.makeConstraints { + $0.size.equalTo(CGSize(width: 25, height: 25)) + } + } + + horizonStackView.snp.makeConstraints { + $0.top.equalToSuperview().offset(25) + $0.centerX.equalToSuperview() + $0.height.equalTo(25) + } + + calendar.snp.makeConstraints { + $0.top.equalTo(horizonStackView.snp.bottom).offset(20) + $0.directionalHorizontalEdges.equalToSuperview().inset(15) + $0.bottom.equalToSuperview().inset(25) + } + } + + private func changeMonth(with type: CalendarMoveType) { + let calendar = Calendar.current + let currentPage = self.calendar.currentPage + var dateComponents = DateComponents() + dateComponents.month = type.monthOffset + + guard let changedCurrentPage = calendar.date(byAdding: dateComponents, to: currentPage) else { return } + + self.calendar.setCurrentPage(changedCurrentPage, animated: true) } } @@ -171,22 +191,18 @@ extension CalendarView { @objc func prevBtnTapped(_sender: UIButton) { - scrollCurrentPage(calendar: calendar, isPrev: true) + changeMonth(with: .previous) } @objc func nextBtnTapped(_sender: UIButton) { - scrollCurrentPage(calendar: calendar, isPrev: false) + changeMonth(with: .next) } -} - -extension CalendarView { @objc func todayBtnTapped(_sender: UIButton) { delegate?.todayBtnTapped() } - } extension CalendarView { @@ -197,7 +213,7 @@ extension CalendarView { } } - func configure(delegate: FSCalendarDelegate, datasource: FSCalendarDataSource) { + func configure(delegate: FSCalendarDelegate?, datasource: FSCalendarDataSource?) { calendar.delegate = delegate calendar.dataSource = datasource } @@ -218,4 +234,16 @@ extension CalendarView { $0.bottom.equalToSuperview().inset(45) } } + + func today() -> Date? { + return calendar.today + } + + func currentPage(date: Date) { + calendar.currentPage = date + } + + func select(date: Date?) { + calendar.select(date) + } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/WeekMonthFSCalendar.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/WeekMonthFSCalendar.swift index 9dd31f33..e5db9f24 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/WeekMonthFSCalendar.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Calendar/WeekMonthFSCalendar.swift @@ -13,16 +13,13 @@ final class WeekMonthFSCalendar: FSCalendar { // MARK: - Life Cycle - override init(frame: CGRect) { - super.init(frame: frame) - } - - init(calendarScope: FSCalendarScope, scrollDirection: FSCalendarScrollDirection) { + init(calendarScope: FSCalendarScope) { super.init(frame: .zero) self.scope = calendarScope - self.scrollDirection = scrollDirection + + setUI() configure(scope: calendarScope) - weekdayTitleStyle(scope: calendarScope) + weekdayTitleStyle() } required init?(coder: NSCoder) { @@ -33,15 +30,18 @@ final class WeekMonthFSCalendar: FSCalendar { // MARK: - Methods extension WeekMonthFSCalendar { - func configure(scope: FSCalendarScope) { + + func setUI() { + locale = Locale(identifier: "ko_KR") calendarHeaderView.isHidden = true headerHeight = 0 backgroundColor = .clear + scrollDirection = .horizontal appearance.titleDefaultColor = .white appearance.titleFont = .Pretendard(.medium, size: 14) - + appearance.subtitleSelectionColor = .white appearance.subtitleDefaultColor = .white appearance.subtitleFont = .Pretendard(.medium, size: 14) @@ -49,7 +49,10 @@ extension WeekMonthFSCalendar { appearance.todayColor = .clear appearance.selectionColor = .clear appearance.borderDefaultColor = .clear - + } + + func configure(scope: FSCalendarScope) { + switch scope { case .week: calendarWeekdayView.removeFromSuperview() @@ -67,17 +70,13 @@ extension WeekMonthFSCalendar { } } - func weekdayTitleStyle(scope: FSCalendarScope) { - switch self.scope { - case .week: - return - case .month: - I18N.weekDay.forEach { - calendarWeekdayView.weekdayLabels[Int($0) ?? 0].text = $0 - } - appearance.headerMinimumDissolvedAlpha = 0 - @unknown default: - return + func weekdayTitleStyle() { + guard self.scope == .month else { return } + + I18N.weekDay.enumerated().forEach { index, day in + calendarWeekdayView.weekdayLabels[index].text = day } + appearance.headerMinimumDissolvedAlpha = 0 + } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/CommonNotificationViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/CommonNotificationViewController.swift index 5a046d73..8adf44f7 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/CommonNotificationViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/CommonNotificationViewController.swift @@ -61,7 +61,7 @@ extension CommonNotificationViewController { backgroundView.do { $0.backgroundColor = .white - $0.layer.cornerRadius = 15 + $0.makeCornerRound(radius: 15) } titleLabel.do { @@ -96,7 +96,7 @@ extension CommonNotificationViewController { bottomView.do { $0.backgroundColor = .gray5 $0.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner] - $0.layer.cornerRadius = 15 + $0.makeCornerRound(radius: 15) } blackButton.do { @@ -104,7 +104,7 @@ extension CommonNotificationViewController { $0.setTitle(I18N.commonModalTitle, for: .normal) $0.setTitleColor(.white, for: .normal) $0.titleLabel?.font = .Pretendard(.medium, size: 13) - $0.layer.cornerRadius = 20 + $0.makeCornerRound(radius: 20) $0.addTarget(self, action: #selector(didFormButtonTap), for: .touchUpInside) } @@ -113,7 +113,7 @@ extension CommonNotificationViewController { $0.setTitle(I18N.commonModalTitle, for: .normal) $0.setTitleColor(.ntdBlack, for: .normal) $0.titleLabel?.font = .Pretendard(.semiBold, size: 20) - $0.layer.cornerRadius = 8 + $0.makeCornerRound(radius: 8) $0.addTarget(self, action: #selector(didFormButtonTap), for: .touchUpInside) } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/DeleteModalView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/DeleteModalView.swift index 36b2f97f..33c192fa 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/DeleteModalView.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/DeleteModalView.swift @@ -45,7 +45,7 @@ final class DeleteModalView: UIView { extension DeleteModalView { private func setUI() { backgroundColor = .white - layer.cornerRadius = 15 + makeCornerRound(radius: 15) trashImage.do { $0.image = .icTrashbin diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/QuitModalView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/QuitModalView.swift index 1c5f1623..385013a0 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/QuitModalView.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/QuitModalView.swift @@ -57,7 +57,7 @@ extension QuitModalView { extension QuitModalView { private func setUI() { backgroundColor = .white - layer.cornerRadius = 15 + makeCornerRound(radius: 15) modalImageView.image = .quit1 diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/WithdrawModalView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/WithdrawModalView.swift index 04ed54c2..96828ac6 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/WithdrawModalView.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Modal/WithdrawModalView.swift @@ -47,7 +47,7 @@ extension WithdrawModalView { extension WithdrawModalView { private func setUI() { backgroundColor = .white - layer.cornerRadius = 15 + makeCornerRound(radius: 15) modalImageView.image = .quit2 modalTitleLabel.do { @@ -69,7 +69,7 @@ extension WithdrawModalView { surveyButton.do { $0.setTitle(I18N.surveyButton, for: .normal) $0.backgroundColor = .black - $0.layer.cornerRadius = 7 + $0.makeCornerRound(radius: 7) $0.titleLabel?.font = .Pretendard(.medium, size: 14) $0.setTitleColor(.white, for: .normal) $0.addTarget(self, action: #selector(buttonDidTapped), for: .touchUpInside) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Toast/NottodoToastView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Toast/NottodoToastView.swift index a1ea1804..9b2633b6 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Toast/NottodoToastView.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Toast/NottodoToastView.swift @@ -36,7 +36,7 @@ final class NottodoToastView: UIView { extension NottodoToastView { private func setUI() { backgroundColor = .white - layer.cornerRadius = 12 + makeCornerRound(radius: 12) layer.makeShadow(color: .black, alpha: 0.25, x: 0, y: 4, blur: 30, spread: 0) isUserInteractionEnabled = false diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Utils.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Utils.swift index 650c0290..ed4a78c4 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Utils.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/Utils.swift @@ -86,3 +86,12 @@ final class Utils { return version } } + +extension Date { + func formattedString(format: String? = nil, locale: Locale = Locale(identifier: "ko_KR")) -> String { + let formatter = DateFormatter() + formatter.dateFormat = format ?? "yyyy-MM-dd" + formatter.locale = locale + return formatter.string(from: self) + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/MissionDetailCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/MissionDetailCollectionViewCell.swift index ca1475b1..ca51915f 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/MissionDetailCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/MissionDetailCollectionViewCell.swift @@ -49,8 +49,7 @@ extension MissionDetailCollectionViewCell { missionTagLabel.do { $0.backgroundColor = .bg - $0.layer.masksToBounds = true - $0.layer.cornerRadius = 12.5 + $0.makeCornerRound(radius: 12.5) $0.font = .Pretendard(.medium, size: 14) $0.textColor = .gray1 } @@ -63,14 +62,12 @@ extension MissionDetailCollectionViewCell { accumulateView.do { $0.backgroundColor = .green2?.withAlphaComponent(0.3) - $0.layer.masksToBounds = true - $0.layer.cornerRadius = 34 + $0.makeCornerRound(radius: 34) } accumulateSubView.do { $0.backgroundColor = .green2 - $0.layer.masksToBounds = true - $0.layer.cornerRadius = 28 + $0.makeCornerRound(radius: 28) } accumulateLabel.do { diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/MissionListCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/MissionListCollectionViewCell.swift index 6063f709..09ce1a15 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/MissionListCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/Cells/MissionListCollectionViewCell.swift @@ -35,7 +35,7 @@ final class MissionListCollectionViewCell: UICollectionViewCell { override func layoutSublayers(of layer: CALayer) { super.layoutSublayers(of: layer) - contentView.layer.cornerRadius = 10 + contentView.makeCornerRound(radius: 10) } override init(frame: CGRect) { @@ -63,7 +63,7 @@ extension MissionListCollectionViewCell { backgroundColor = .clear contentView.backgroundColor = .white contentView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMinXMaxYCorner] - contentView.layer.cornerRadius = 10 + contentView.makeCornerRound(radius: 10) checkButton.do { $0.backgroundColor = .clear @@ -75,7 +75,7 @@ extension MissionListCollectionViewCell { tagLabel.do { $0.backgroundColor = .bg $0.clipsToBounds = true - $0.layer.cornerRadius = 12 + $0.makeCornerRound(radius: 12) $0.textColor = isTapped ? .gray7 : .gray1 $0.font = .Pretendard(.medium, size: 14) } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/DetailCalendarViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/DetailCalendarViewController.swift index 444b1fd8..839a7059 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/DetailCalendarViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/DetailCalendarViewController.swift @@ -33,7 +33,7 @@ final class DetailCalendarViewController: UIViewController { // MARK: - UI Components - private let monthCalendar = CalendarView(calendarScope: .month, scrollDirection: .horizontal) + private let monthCalendar = CalendarView(scope: .month) private let completeButton = UIButton() private let subLabel = UILabel() @@ -91,7 +91,7 @@ extension DetailCalendarViewController { } monthCalendar.do { - $0.layer.cornerRadius = 12 + $0.makeCornerRound(radius: 12) $0.configure(delegate: self, datasource: self) } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift index 60fa3a53..dde112d6 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift @@ -32,7 +32,7 @@ final class HomeViewController: UIViewController { private var missionCollectionView = UICollectionView(frame: .zero, collectionViewLayout: .init()) private lazy var missionDataSource = HomeDataSource(collectionView: missionCollectionView, missionList: missionList) - private let weekCalendar = CalendarView(calendarScope: .week, scrollDirection: .horizontal) + private let weekCalendar = CalendarView(scope: .week) private let addButton = UIButton() // MARK: - init @@ -99,7 +99,7 @@ extension HomeViewController { private func setLayout() { view.addSubviews(weekCalendar, missionCollectionView, addButton) - weekCalendar.calendar.select(today) + weekCalendar.select(date: today) weekCalendar.snp.makeConstraints { $0.top.equalTo(safeArea) @@ -123,7 +123,7 @@ extension HomeViewController { // MARK: - Action -extension HomeViewController: CalendarViewDelegate { +extension HomeViewController: WeekCalendarDelegate { @objc func addBtnTapped(_sender: UIButton) { @@ -136,7 +136,7 @@ extension HomeViewController: CalendarViewDelegate { AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Home.clickReturnToday) - weekCalendar.calendar.select(today) + weekCalendar.select(date: today) weekCalendar.configureYearMonth(to: Utils.dateFormatterString(format: I18N.yearMonthTitle, date: today)) requestDailyMissionAPI(date: Utils.dateFormatterString(format: nil, date: today)) } @@ -182,7 +182,7 @@ extension HomeViewController: UICollectionViewDelegate { }, moveDateClosure: { [weak self] date in guard let self else { return } let modifiedDate: Date = date.toDate(withFormat: "YYYY.MM.dd") - self.weekCalendar.calendar.select(modifiedDate) + self.weekCalendar.select(date: modifiedDate) self.requestDailyMissionAPI(date: Utils.dateFormatterString(format: nil, date: modifiedDate)) }) } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/MissionDetailViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/MissionDetailViewController.swift index 2c658f60..6cf80724 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/MissionDetailViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/MissionDetailViewController.swift @@ -86,7 +86,7 @@ extension MissionDetailViewController { headerView.do { $0.backgroundColor = .white $0.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] - $0.layer.cornerRadius = 10 + $0.makeCornerRound(radius: 10) } collectionView.do { diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfoAccount/ViewControllers/MyInfoAccountStackView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfoAccount/ViewControllers/MyInfoAccountStackView.swift index 1dba8e4e..8a8e8022 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfoAccount/ViewControllers/MyInfoAccountStackView.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfoAccount/ViewControllers/MyInfoAccountStackView.swift @@ -55,7 +55,7 @@ final class MyInfoAccountStackView: UIView { extension MyInfoAccountStackView { private func setUI(title: String, isHidden: Bool) { - layer.cornerRadius = 10 + makeCornerRound(radius: 10) titleLabel.do { $0.text = title diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfoAccount/ViewControllers/MyInfoAccountViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfoAccount/ViewControllers/MyInfoAccountViewController.swift index d9be53a3..6eeb0588 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfoAccount/ViewControllers/MyInfoAccountViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfoAccount/ViewControllers/MyInfoAccountViewController.swift @@ -81,12 +81,12 @@ private extension MyInfoAccountViewController { $0.axis = .vertical $0.spacing = 0 $0.distribution = .equalSpacing - $0.layer.cornerRadius = 10 + $0.makeCornerRound(radius: 10) $0.backgroundColor = .gray1 } logoutView.do { - $0.layer.cornerRadius = 10 + $0.makeCornerRound(radius: 10) $0.backgroundColor = .gray1 } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/NotificatoinDialog/ViewControllers/NotificationDialogViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/NotificatoinDialog/ViewControllers/NotificationDialogViewController.swift index f5af2feb..92e6e2b1 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/NotificatoinDialog/ViewControllers/NotificationDialogViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/NotificatoinDialog/ViewControllers/NotificationDialogViewController.swift @@ -75,7 +75,7 @@ extension NotificationDialogViewController { } buttonStackView.do { - $0.layer.cornerRadius = 17 + $0.makeCornerRound(radius: 17) $0.backgroundColor = .none } @@ -93,7 +93,7 @@ extension NotificationDialogViewController { bottomButton.do { $0.backgroundColor = .white - $0.layer.cornerRadius = 25 + $0.makeCornerRound(radius: 25) $0.titleLabel?.font = .Pretendard(.semiBold, size: 16) $0.setTitleColor(.black, for: .normal) $0.setTitle(I18N.notiDialogButton, for: .normal) @@ -102,7 +102,7 @@ extension NotificationDialogViewController { alertView.do { $0.backgroundColor = .none - $0.layer.cornerRadius = 17 + $0.makeCornerRound(radius: 17) } verticalView.backgroundColor = .notiGreen diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/Cell/OnboardingCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/Cell/OnboardingCollectionViewCell.swift index c6f26905..54025edc 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/Cell/OnboardingCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/Cell/OnboardingCollectionViewCell.swift @@ -45,10 +45,8 @@ final class OnboardingCollectionViewCell: UICollectionViewCell { extension OnboardingCollectionViewCell { private func setUI() { contentView.backgroundColor = .gray1 - contentView.layer.cornerRadius = 10 - contentView.layer.masksToBounds = true - contentView.layer.borderWidth = 1 - contentView.layer.borderColor = UIColor.clear.cgColor + contentView.makeCornerRound(radius: 10) + contentView.makeBorder(width: 1, color: .clear) titleLabel.do { $0.font = .Pretendard(.regular, size: 15) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/Cell/SubOnboardingCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/Cell/SubOnboardingCollectionViewCell.swift index 4a4446b6..c6a3f432 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/Cell/SubOnboardingCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/Cell/SubOnboardingCollectionViewCell.swift @@ -39,12 +39,10 @@ final class SubOnboardingCollectionViewCell: UICollectionViewCell { extension SubOnboardingCollectionViewCell { private func setUI() { contentView.backgroundColor = .gray1 - contentView.layer.cornerRadius = 35 - contentView.layer.masksToBounds = true + contentView.makeCornerRound(radius: 35) tagLabel.do { - $0.layer.cornerRadius = 8 - $0.layer.masksToBounds = true + $0.makeCornerRound(radius: 8) $0.backgroundColor = UIColor.gray8 $0.font = .Pretendard(.regular, size: 10) $0.textColor = .gray6 diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/LogoOnboardingViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/LogoOnboardingViewController.swift index ac7e72e4..06ca87ba 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/LogoOnboardingViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/LogoOnboardingViewController.swift @@ -87,7 +87,7 @@ extension LogoOnboardingViewController { nextButton.do { $0.isHidden = true $0.backgroundColor = .white - $0.layer.cornerRadius = 25 + $0.makeCornerRound(radius: 25) $0.titleLabel?.font = .Pretendard(.semiBold, size: 16) $0.setTitleColor(.black, for: .normal) $0.setTitle(I18N.firstButton, for: .normal) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/ThirdOnboardingViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/ThirdOnboardingViewController.swift index b798cca5..fe83a872 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/ThirdOnboardingViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/ThirdOnboardingViewController.swift @@ -73,7 +73,7 @@ extension ThirdOnboardingViewController { nextButton.do { $0.backgroundColor = isTapped ? .white : .gray2 $0.isUserInteractionEnabled = isTapped - $0.layer.cornerRadius = 25 + $0.makeCornerRound(radius: 25) $0.titleLabel?.font = .Pretendard(.semiBold, size: 16) $0.setTitleColor(isTapped ? .black :.gray4, for: .normal) $0.setTitle(I18N.thirdButton, for: .normal) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Recommend/Cells/RecommendCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Recommend/Cells/RecommendCollectionViewCell.swift index e2128a61..2d6692bf 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Recommend/Cells/RecommendCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Recommend/Cells/RecommendCollectionViewCell.swift @@ -45,16 +45,14 @@ extension RecommendCollectionViewCell { private func setUI() { contentView.do { $0.backgroundColor = .gray1 - $0.layer.masksToBounds = true - $0.layer.cornerRadius = 12 + $0.makeCornerRound(radius: 12) } tagLabel.do { $0.font = .Pretendard(.medium, size: 14) $0.textColor = .white $0.backgroundColor = .gray2 - $0.layer.masksToBounds = true - $0.layer.cornerRadius = 25 / 2 + $0.makeCornerRound(radius: 25 / 2) } titleLabel.do { diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Recommend/ViewControllers/RecommendViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Recommend/ViewControllers/RecommendViewController.swift index 59439d11..e9699faa 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Recommend/ViewControllers/RecommendViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Recommend/ViewControllers/RecommendViewController.swift @@ -89,7 +89,7 @@ private extension RecommendViewController { $0.setTitle(I18N.addAction, for: .normal) $0.setTitleColor(.ntdBlack, for: .normal) $0.titleLabel?.font = .Pretendard(.semiBold, size: 16) - $0.layer.cornerRadius = 25 + $0.makeCornerRound(radius: 25) $0.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside) } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/RecommendAction/Cells/RecommendActionCollectionViewCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/RecommendAction/Cells/RecommendActionCollectionViewCell.swift index 3eb3684d..2437fd2a 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/RecommendAction/Cells/RecommendActionCollectionViewCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/RecommendAction/Cells/RecommendActionCollectionViewCell.swift @@ -49,10 +49,8 @@ extension RecommendActionCollectionViewCell { private func setUI() { contentView.backgroundColor = .gray1 - contentView.layer.masksToBounds = true - contentView.layer.cornerRadius = 10 - contentView.layer.borderWidth = 1 - contentView.layer.borderColor = UIColor.clear.cgColor + contentView.makeCornerRound(radius: 10) + contentView.makeBorder(width: 1, color: .clear) stackView.axis = .vertical titleLabel.do { diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/RecommendAction/Cells/RecommendActionHeaderView.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/RecommendAction/Cells/RecommendActionHeaderView.swift index 79d660a6..713cb473 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/RecommendAction/Cells/RecommendActionHeaderView.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/RecommendAction/Cells/RecommendActionHeaderView.swift @@ -52,8 +52,7 @@ extension RecommendActionHeaderView { $0.font = .Pretendard(.medium, size: 14) $0.textColor = .white $0.backgroundColor = .gray2 - $0.layer.masksToBounds = true - $0.layer.cornerRadius = 25 / 2 + $0.makeCornerRound(radius: 25 / 2) } titleLabel.do {