diff --git a/iOS/Layover/Layover.xcodeproj/project.pbxproj b/iOS/Layover/Layover.xcodeproj/project.pbxproj index 4d31b5d..68fef10 100644 --- a/iOS/Layover/Layover.xcodeproj/project.pbxproj +++ b/iOS/Layover/Layover.xcodeproj/project.pbxproj @@ -38,6 +38,25 @@ 1972CCDA2B13A4BA00C3C762 /* SignUpWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1972CCD92B13A4BA00C3C762 /* SignUpWorker.swift */; }; 1972CCDF2B14C9B000C3C762 /* Notification.Name+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1972CCDE2B14C9B000C3C762 /* Notification.Name+.swift */; }; 19743C052B06940D001E405A /* PlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19743C042B06940D001E405A /* PlayerView.swift */; }; + 19A169232B176C5F00DB34C0 /* TagPlayListPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1691D2B176C5F00DB34C0 /* TagPlayListPresenter.swift */; }; + 19A169242B176C5F00DB34C0 /* TagPlayListWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1691E2B176C5F00DB34C0 /* TagPlayListWorker.swift */; }; + 19A169252B176C5F00DB34C0 /* TagPlayListRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1691F2B176C5F00DB34C0 /* TagPlayListRouter.swift */; }; + 19A169262B176C5F00DB34C0 /* TagPlayListModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A169202B176C5F00DB34C0 /* TagPlayListModels.swift */; }; + 19A169272B176C5F00DB34C0 /* TagPlayListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A169212B176C5F00DB34C0 /* TagPlayListViewController.swift */; }; + 19A169282B176C5F00DB34C0 /* TagPlayListInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A169222B176C5F00DB34C0 /* TagPlayListInteractor.swift */; }; + 19A1692A2B176D6E00DB34C0 /* TagPlayListConfigurator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A169292B176D6E00DB34C0 /* TagPlayListConfigurator.swift */; }; + 19A1692D2B17750B00DB34C0 /* Post.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1692C2B17750B00DB34C0 /* Post.swift */; }; + 19A169302B1776CA00DB34C0 /* TagPlayListCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1692F2B1776CA00DB34C0 /* TagPlayListCollectionViewCell.swift */; }; + 19A169362B178EA500DB34C0 /* PostList.json in Resources */ = {isa = PBXBuildFile; fileRef = 19A169352B178EA500DB34C0 /* PostList.json */; }; + 19A169382B17BCA800DB34C0 /* PostDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A169372B17BCA800DB34C0 /* PostDTO.swift */; }; + 19A1693A2B17BCC400DB34C0 /* MemberDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A169392B17BCC400DB34C0 /* MemberDTO.swift */; }; + 19A1693C2B17BD1C00DB34C0 /* BoardDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1693B2B17BD1C00DB34C0 /* BoardDTO.swift */; }; + 19A169402B17C10300DB34C0 /* PostEndPointFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1693F2B17C10300DB34C0 /* PostEndPointFactory.swift */; }; + 19A169422B17C70C00DB34C0 /* Member.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A169412B17C70C00DB34C0 /* Member.swift */; }; + 19A169442B17C71C00DB34C0 /* Board.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A169432B17C71C00DB34C0 /* Board.swift */; }; + 19A169472B17D12500DB34C0 /* MockTagPlayListWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A169462B17D12500DB34C0 /* MockTagPlayListWorker.swift */; }; + 19A169492B181AE300DB34C0 /* Sequence+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A169482B181AE300DB34C0 /* Sequence+.swift */; }; + 19A1694D2B18327E00DB34C0 /* sample.jpeg in Resources */ = {isa = PBXBuildFile; fileRef = 19A1694C2B18327E00DB34C0 /* sample.jpeg */; }; 19AACFCA2B0F7C3B0088143E /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19AACFC92B0F7C3B0088143E /* Response.swift */; }; 19AACFCC2B0F7D730088143E /* LoginDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19AACFCB2B0F7D730088143E /* LoginDTO.swift */; }; 19C7AFCE2B02410F003B35F2 /* AuthManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19C7AFCD2B02410F003B35F2 /* AuthManager.swift */; }; @@ -57,6 +76,12 @@ 835A61A22B068115002F22A5 /* PlaybackInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 835A619C2B068115002F22A5 /* PlaybackInteractor.swift */; }; 835A61A62B0B4DDD002F22A5 /* Dashboard-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 835A61A52B0B4DDD002F22A5 /* Dashboard-Regular.ttf */; }; 835A61A92B0B5A31002F22A5 /* LoginConfigurator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 835A61A82B0B5A31002F22A5 /* LoginConfigurator.swift */; }; + 836C33992B1843BE00ECAFB0 /* SettingScenePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 836C33932B1843BE00ECAFB0 /* SettingScenePresenter.swift */; }; + 836C339A2B1843BE00ECAFB0 /* SettingSceneWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 836C33942B1843BE00ECAFB0 /* SettingSceneWorker.swift */; }; + 836C339B2B1843BE00ECAFB0 /* SettingSceneRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 836C33952B1843BE00ECAFB0 /* SettingSceneRouter.swift */; }; + 836C339C2B1843BE00ECAFB0 /* SettingSceneModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 836C33962B1843BE00ECAFB0 /* SettingSceneModels.swift */; }; + 836C339D2B1843BE00ECAFB0 /* SettingSceneViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 836C33972B1843BE00ECAFB0 /* SettingSceneViewController.swift */; }; + 836C339E2B1843BE00ECAFB0 /* SettingSceneInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 836C33982B1843BE00ECAFB0 /* SettingSceneInteractor.swift */; }; 836C33872B15A29600ECAFB0 /* Toast.swift in Sources */ = {isa = PBXBuildFile; fileRef = 836C33862B15A29600ECAFB0 /* Toast.swift */; }; 83C35E1B2B108C3500D8DD5C /* PlaybackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83C35E1A2B108C3500D8DD5C /* PlaybackView.swift */; }; 83C35E1E2B10923C00D8DD5C /* PlaybackCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83C35E1D2B10923C00D8DD5C /* PlaybackCell.swift */; }; @@ -106,7 +131,6 @@ FC7E45462AFEB62B004F155A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FC7E45442AFEB62B004F155A /* LaunchScreen.storyboard */; }; FC7E45512AFEB62C004F155A /* LayoverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC7E45502AFEB62C004F155A /* LayoverTests.swift */; }; FC7E456A2AFEC06E004F155A /* .swiftlint.yml in Resources */ = {isa = PBXBuildFile; fileRef = FC7E45692AFEC06E004F155A /* .swiftlint.yml */; }; - FC7E45902AFF746E004F155A /* DummyWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC7E458F2AFF746E004F155A /* DummyWorker.swift */; }; FC930E752B0CD75C00AA48E3 /* ProfilePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC930E6F2B0CD75C00AA48E3 /* ProfilePresenter.swift */; }; FC930E772B0CD75C00AA48E3 /* ProfileRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC930E712B0CD75C00AA48E3 /* ProfileRouter.swift */; }; FC930E782B0CD75C00AA48E3 /* ProfileModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC930E722B0CD75C00AA48E3 /* ProfileModels.swift */; }; @@ -164,6 +188,25 @@ 1972CCD92B13A4BA00C3C762 /* SignUpWorker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpWorker.swift; sourceTree = ""; }; 1972CCDE2B14C9B000C3C762 /* Notification.Name+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Notification.Name+.swift"; sourceTree = ""; }; 19743C042B06940D001E405A /* PlayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerView.swift; sourceTree = ""; }; + 19A1691D2B176C5F00DB34C0 /* TagPlayListPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagPlayListPresenter.swift; sourceTree = ""; }; + 19A1691E2B176C5F00DB34C0 /* TagPlayListWorker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagPlayListWorker.swift; sourceTree = ""; }; + 19A1691F2B176C5F00DB34C0 /* TagPlayListRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagPlayListRouter.swift; sourceTree = ""; }; + 19A169202B176C5F00DB34C0 /* TagPlayListModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagPlayListModels.swift; sourceTree = ""; }; + 19A169212B176C5F00DB34C0 /* TagPlayListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagPlayListViewController.swift; sourceTree = ""; }; + 19A169222B176C5F00DB34C0 /* TagPlayListInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagPlayListInteractor.swift; sourceTree = ""; }; + 19A169292B176D6E00DB34C0 /* TagPlayListConfigurator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagPlayListConfigurator.swift; sourceTree = ""; }; + 19A1692C2B17750B00DB34C0 /* Post.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Post.swift; sourceTree = ""; }; + 19A1692F2B1776CA00DB34C0 /* TagPlayListCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagPlayListCollectionViewCell.swift; sourceTree = ""; }; + 19A169352B178EA500DB34C0 /* PostList.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = PostList.json; sourceTree = ""; }; + 19A169372B17BCA800DB34C0 /* PostDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostDTO.swift; sourceTree = ""; }; + 19A169392B17BCC400DB34C0 /* MemberDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemberDTO.swift; sourceTree = ""; }; + 19A1693B2B17BD1C00DB34C0 /* BoardDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoardDTO.swift; sourceTree = ""; }; + 19A1693F2B17C10300DB34C0 /* PostEndPointFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostEndPointFactory.swift; sourceTree = ""; }; + 19A169412B17C70C00DB34C0 /* Member.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Member.swift; sourceTree = ""; }; + 19A169432B17C71C00DB34C0 /* Board.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Board.swift; sourceTree = ""; }; + 19A169462B17D12500DB34C0 /* MockTagPlayListWorker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockTagPlayListWorker.swift; sourceTree = ""; }; + 19A169482B181AE300DB34C0 /* Sequence+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Sequence+.swift"; sourceTree = ""; }; + 19A1694C2B18327E00DB34C0 /* sample.jpeg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = sample.jpeg; sourceTree = ""; }; 19AACFC52B0F71DF0088143E /* Secrets.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Secrets.xcconfig; sourceTree = ""; }; 19AACFC92B0F7C3B0088143E /* Response.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Response.swift; sourceTree = ""; }; 19AACFCB2B0F7D730088143E /* LoginDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginDTO.swift; sourceTree = ""; }; @@ -186,6 +229,12 @@ 835A61A52B0B4DDD002F22A5 /* Dashboard-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Dashboard-Regular.ttf"; sourceTree = ""; }; 835A61A82B0B5A31002F22A5 /* LoginConfigurator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginConfigurator.swift; sourceTree = ""; }; 835A61AA2B0B85FD002F22A5 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/LaunchScreen.strings; sourceTree = ""; }; + 836C33932B1843BE00ECAFB0 /* SettingScenePresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingScenePresenter.swift; sourceTree = ""; }; + 836C33942B1843BE00ECAFB0 /* SettingSceneWorker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingSceneWorker.swift; sourceTree = ""; }; + 836C33952B1843BE00ECAFB0 /* SettingSceneRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingSceneRouter.swift; sourceTree = ""; }; + 836C33962B1843BE00ECAFB0 /* SettingSceneModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingSceneModels.swift; sourceTree = ""; }; + 836C33972B1843BE00ECAFB0 /* SettingSceneViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingSceneViewController.swift; sourceTree = ""; }; + 836C33982B1843BE00ECAFB0 /* SettingSceneInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingSceneInteractor.swift; sourceTree = ""; }; 836C33862B15A29600ECAFB0 /* Toast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toast.swift; sourceTree = ""; }; 83C35E1A2B108C3500D8DD5C /* PlaybackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaybackView.swift; sourceTree = ""; }; 83C35E1D2B10923C00D8DD5C /* PlaybackCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaybackCell.swift; sourceTree = ""; }; @@ -238,7 +287,6 @@ FC7E454C2AFEB62B004F155A /* LayoverTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LayoverTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; FC7E45502AFEB62C004F155A /* LayoverTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoverTests.swift; sourceTree = ""; }; FC7E45692AFEC06E004F155A /* .swiftlint.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = .swiftlint.yml; sourceTree = ""; }; - FC7E458F2AFF746E004F155A /* DummyWorker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DummyWorker.swift; sourceTree = ""; }; FC930E6F2B0CD75C00AA48E3 /* ProfilePresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfilePresenter.swift; sourceTree = ""; }; FC930E712B0CD75C00AA48E3 /* ProfileRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileRouter.swift; sourceTree = ""; }; FC930E722B0CD75C00AA48E3 /* ProfileModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileModels.swift; sourceTree = ""; }; @@ -326,6 +374,7 @@ 1972CCCE2B12438900C3C762 /* LoginEndPointFactory.swift */, 1972CCD72B13A2EC00C3C762 /* SignUpEndPointFactory.swift */, 193686712B15BCA7008902CD /* UserEndPointFactory.swift */, + 19A1693F2B17C10300DB34C0 /* PostEndPointFactory.swift */, ); path = Factories; sourceTree = ""; @@ -338,6 +387,51 @@ path = UserDefaults; sourceTree = ""; }; + 19A1691C2B176C2C00DB34C0 /* TagPlayList */ = { + isa = PBXGroup; + children = ( + 19A1692E2B1776B200DB34C0 /* Cell */, + 19A169292B176D6E00DB34C0 /* TagPlayListConfigurator.swift */, + 19A1691F2B176C5F00DB34C0 /* TagPlayListRouter.swift */, + 19A169212B176C5F00DB34C0 /* TagPlayListViewController.swift */, + 19A169222B176C5F00DB34C0 /* TagPlayListInteractor.swift */, + 19A1691D2B176C5F00DB34C0 /* TagPlayListPresenter.swift */, + 19A1691E2B176C5F00DB34C0 /* TagPlayListWorker.swift */, + 19A169202B176C5F00DB34C0 /* TagPlayListModels.swift */, + ); + path = TagPlayList; + sourceTree = ""; + }; + 19A1692B2B1774F900DB34C0 /* Models */ = { + isa = PBXGroup; + children = ( + 19A1692C2B17750B00DB34C0 /* Post.swift */, + 19A169412B17C70C00DB34C0 /* Member.swift */, + 19A169432B17C71C00DB34C0 /* Board.swift */, + ); + path = Models; + sourceTree = ""; + }; + 19A1692E2B1776B200DB34C0 /* Cell */ = { + isa = PBXGroup; + children = ( + 19A1692F2B1776CA00DB34C0 /* TagPlayListCollectionViewCell.swift */, + ); + path = Cell; + sourceTree = ""; + }; + 19A169452B17D10500DB34C0 /* Mocks */ = { + isa = PBXGroup; + children = ( + 19A1694C2B18327E00DB34C0 /* sample.jpeg */, + 834B7BD42B14F888002BD176 /* MockSignUpWorker.swift */, + 835783C22B14A41600E7D304 /* MockLoginWorker.swift */, + FC767F832B1214A70088CF9B /* MockUserWorker.swift */, + 19A169462B17D12500DB34C0 /* MockTagPlayListWorker.swift */, + ); + path = Mocks; + sourceTree = ""; + }; 19AACFC82B0F7C200088143E /* DTOs */ = { isa = PBXGroup; children = ( @@ -347,6 +441,9 @@ FC767F922B1220CC0088CF9B /* NicknameDTO.swift */, FC767F942B1222350088CF9B /* ProfileImageDTO.swift */, FC767F962B1224B80088CF9B /* IntroduceDTO.swift */, + 19A169372B17BCA800DB34C0 /* PostDTO.swift */, + 19A169392B17BCC400DB34C0 /* MemberDTO.swift */, + 19A1693B2B17BD1C00DB34C0 /* BoardDTO.swift */, ); path = DTOs; sourceTree = ""; @@ -412,6 +509,19 @@ path = Playback; sourceTree = ""; }; + 836C33922B18436A00ECAFB0 /* SettingScene */ = { + isa = PBXGroup; + children = ( + 836C33932B1843BE00ECAFB0 /* SettingScenePresenter.swift */, + 836C33942B1843BE00ECAFB0 /* SettingSceneWorker.swift */, + 836C33952B1843BE00ECAFB0 /* SettingSceneRouter.swift */, + 836C33962B1843BE00ECAFB0 /* SettingSceneModels.swift */, + 836C33972B1843BE00ECAFB0 /* SettingSceneViewController.swift */, + 836C33982B1843BE00ECAFB0 /* SettingSceneInteractor.swift */, + ); + path = SettingScene; + sourceTree = ""; + }; 83C35E1C2B10923000D8DD5C /* Cell */ = { isa = PBXGroup; children = ( @@ -490,6 +600,7 @@ FC767F9E2B12283D0088CF9B /* PatchProfileImage.json */, FC767F9A2B12283D0088CF9B /* PatchUserName.json */, 835783C52B14A5C800E7D304 /* LoginData.json */, + 19A169352B178EA500DB34C0 /* PostList.json */, ); path = MockData; sourceTree = ""; @@ -530,6 +641,7 @@ FC68E2992B02320F001AABFF /* Network */, FC7E45752AFF6F5B004F155A /* Services */, FC7E45782AFF6F7A004F155A /* Workers */, + 19A1692B2B1774F900DB34C0 /* Models */, FC7E457B2AFF6F9D004F155A /* Scenes */, FC7E45802AFF6FE9004F155A /* DesignSystem */, FC7E45392AFEB623004F155A /* AppDelegate.swift */, @@ -562,10 +674,7 @@ FC7E45782AFF6F7A004F155A /* Workers */ = { isa = PBXGroup; children = ( - FC767F832B1214A70088CF9B /* MockUserWorker.swift */, - FC7E458F2AFF746E004F155A /* DummyWorker.swift */, - 835783C22B14A41600E7D304 /* MockLoginWorker.swift */, - 834B7BD42B14F888002BD176 /* MockSignUpWorker.swift */, + 19A169452B17D10500DB34C0 /* Mocks */, 193686732B15C489008902CD /* UserWorker.swift */, ); path = Workers; @@ -574,6 +683,7 @@ FC7E457B2AFF6F9D004F155A /* Scenes */ = { isa = PBXGroup; children = ( + 836C33922B18436A00ECAFB0 /* SettingScene */, 19BB8A572B07BEE30070B922 /* UIComponents */, 1945520E2B03AEA400299768 /* Configurator.swift */, 835A61962B0680FC002F22A5 /* Playback */, @@ -582,6 +692,7 @@ 194552032B038FC400299768 /* Tabbar */, 194551EB2B037F1E00299768 /* Login */, 1945521A2B0478A100299768 /* Home */, + 19A1691C2B176C2C00DB34C0 /* TagPlayList */, FC930E6E2B0CD73B00AA48E3 /* Profile */, FC5BE1152B148B540036366D /* EditProfile */, 194552272B0479B600299768 /* BaseViewController.swift */, @@ -612,6 +723,7 @@ FCE52FF92B0FCB0A002CDB75 /* URLSession+.swift */, FC767FA42B125F430088CF9B /* UIViewController+.swift */, 1972CCDE2B14C9B000C3C762 /* Notification.Name+.swift */, + 19A169482B181AE300DB34C0 /* Sequence+.swift */, ); path = Extensions; sourceTree = ""; @@ -756,12 +868,14 @@ FC767FA22B12283D0088CF9B /* PatchIntroduce.json in Resources */, FC767FA32B12283D0088CF9B /* PatchProfileImage.json in Resources */, FC4975942B03432800D8627F /* Pretendard-Regular.ttf in Resources */, + 19A1694D2B18327E00DB34C0 /* sample.jpeg in Resources */, FC767F9F2B12283D0088CF9B /* PatchUserName.json in Resources */, FC49758F2B03432800D8627F /* Pretendard-SemiBold.ttf in Resources */, 835A61A62B0B4DDD002F22A5 /* Dashboard-Regular.ttf in Resources */, 835783C62B14A5C800E7D304 /* LoginData.json in Resources */, FC767FA12B12283D0088CF9B /* DeleteUser.json in Resources */, FC7E45462AFEB62B004F155A /* LaunchScreen.storyboard in Resources */, + 19A169362B178EA500DB34C0 /* PostList.json in Resources */, FC767FA02B12283D0088CF9B /* CheckUserName.json in Resources */, FC4975932B03432800D8627F /* Pretendard-Bold.ttf in Resources */, FC7E45432AFEB62B004F155A /* Assets.xcassets in Resources */, @@ -785,16 +899,22 @@ 83C35E1E2B10923C00D8DD5C /* PlaybackCell.swift in Sources */, FC2511A42B045D6C004717BC /* SignUpModels.swift in Sources */, FC767F932B1220CC0088CF9B /* NicknameDTO.swift in Sources */, + 19A169272B176C5F00DB34C0 /* TagPlayListViewController.swift in Sources */, + 19A169252B176C5F00DB34C0 /* TagPlayListRouter.swift in Sources */, FC68E2A12B023326001AABFF /* EndPoint.swift in Sources */, + 19A169492B181AE300DB34C0 /* Sequence+.swift in Sources */, 1972CCDF2B14C9B000C3C762 /* Notification.Name+.swift in Sources */, FC2511AF2B04EAD9004717BC /* MapPresenter.swift in Sources */, 19AACFCA2B0F7C3B0088143E /* Response.swift in Sources */, 836C33872B15A29600ECAFB0 /* Toast.swift in Sources */, FC767F972B1224B80088CF9B /* IntroduceDTO.swift in Sources */, + 19A169302B1776CA00DB34C0 /* TagPlayListCollectionViewCell.swift in Sources */, 1972CCD42B138E6B00C3C762 /* SignUpRouter.swift in Sources */, FC5BE11C2B148D160036366D /* EditProfilePresenter.swift in Sources */, FC68E2A32B0233BC001AABFF /* NetworkError.swift in Sources */, + 836C339C2B1843BE00ECAFB0 /* SettingSceneModels.swift in Sources */, FC3F3BD82B069EB30080E3A6 /* MapCarouselCollectionViewCell.swift in Sources */, + 19A169442B17C71C00DB34C0 /* Board.swift in Sources */, 194552252B0478B400299768 /* HomeViewController.swift in Sources */, 194552222B0478B400299768 /* HomeWorker.swift in Sources */, FC5BE1202B148D170036366D /* EditProfileViewController.swift in Sources */, @@ -805,17 +925,21 @@ FC2511AB2B04EA6B004717BC /* MapConfigurator.swift in Sources */, 1945523B2B05258200299768 /* HomeConfigurator.swift in Sources */, FC5BE11D2B148D160036366D /* EditProfileWorker.swift in Sources */, + 19A1693A2B17BCC400DB34C0 /* MemberDTO.swift in Sources */, 194551F62B037F2D00299768 /* LoginViewController.swift in Sources */, FC767FA52B125F430088CF9B /* UIViewController+.swift in Sources */, FCEE0FF22B036B6000195BBE /* LOButton.swift in Sources */, FC5BE1212B148D170036366D /* EditProfileInteractor.swift in Sources */, FC930E7C2B0CD80800AA48E3 /* ProfileConfigurator.swift in Sources */, + 836C33992B1843BE00ECAFB0 /* SettingScenePresenter.swift in Sources */, FC2511A62B049020004717BC /* SignUpConfigurator.swift in Sources */, 194552392B05230E00299768 /* HomeCarouselCollectionViewCell.swift in Sources */, FC767FAA2B126D080088CF9B /* LOAnnotation.swift in Sources */, + 19A169472B17D12500DB34C0 /* MockTagPlayListWorker.swift in Sources */, 193686722B15BCA7008902CD /* UserEndPointFactory.swift in Sources */, 194551F22B037F2D00299768 /* LoginPresenter.swift in Sources */, 194552242B0478B400299768 /* HomeModels.swift in Sources */, + 19A1692D2B17750B00DB34C0 /* Post.swift in Sources */, 194552022B038B8300299768 /* OSLog+.swift in Sources */, 194552312B04DA1A00299768 /* LOCircleButton.swift in Sources */, 194552262B0478B400299768 /* HomeInteractor.swift in Sources */, @@ -825,23 +949,30 @@ FC4975992B03439000D8627F /* UIFont+.swift in Sources */, FC5BE11E2B148D160036366D /* EditProfileRouter.swift in Sources */, FC767F952B1222350088CF9B /* ProfileImageDTO.swift in Sources */, + 19A169422B17C70C00DB34C0 /* Member.swift in Sources */, 194551F52B037F2D00299768 /* LoginModels.swift in Sources */, 834B7BD52B14F888002BD176 /* MockSignUpWorker.swift in Sources */, + 19A169402B17C10300DB34C0 /* PostEndPointFactory.swift in Sources */, 835A619E2B068115002F22A5 /* PlaybackWorker.swift in Sources */, 1972CCD22B125ED700C3C762 /* UserDefaultStored.swift in Sources */, FC2511AD2B04EACD004717BC /* MapInteractor.swift in Sources */, 19C7AFD62B02584D003B35F2 /* KeychainStored.swift in Sources */, FC930E802B0CFB0B00AA48E3 /* ProfileHeaderView.swift in Sources */, 193686742B15C489008902CD /* UserWorker.swift in Sources */, + 19A169242B176C5F00DB34C0 /* TagPlayListWorker.swift in Sources */, FCE52FF82B0FCAF7002CDB75 /* MockURLProtocol.swift in Sources */, FC930E792B0CD75C00AA48E3 /* ProfileViewController.swift in Sources */, FC7E453A2AFEB623004F155A /* AppDelegate.swift in Sources */, 1972CCD82B13A2EC00C3C762 /* SignUpEndPointFactory.swift in Sources */, + 19A169262B176C5F00DB34C0 /* TagPlayListModels.swift in Sources */, FC767F842B1214A80088CF9B /* MockUserWorker.swift in Sources */, FC930E782B0CD75C00AA48E3 /* ProfileModels.swift in Sources */, FC68E29B2B02325D001AABFF /* Requestable.swift in Sources */, + 836C339B2B1843BE00ECAFB0 /* SettingSceneRouter.swift in Sources */, FC2511A22B045C3F004717BC /* SignUpPresenter.swift in Sources */, FC767FA82B1269DB0088CF9B /* LOAnnotationView.swift in Sources */, + 19A169232B176C5F00DB34C0 /* TagPlayListPresenter.swift in Sources */, + 836C339E2B1843BE00ECAFB0 /* SettingSceneInteractor.swift in Sources */, FC68E29D2B02326A001AABFF /* Responsable.swift in Sources */, FC2511A02B045C0A004717BC /* SignUpInteractor.swift in Sources */, FC767F862B1214C10088CF9B /* CheckUserNameDTO.swift in Sources */, @@ -852,17 +983,20 @@ 83C35E1B2B108C3500D8DD5C /* PlaybackView.swift in Sources */, 835A61A02B068115002F22A5 /* PlaybackModels.swift in Sources */, 19C7AFCE2B02410F003B35F2 /* AuthManager.swift in Sources */, + 836C339D2B1843BE00ECAFB0 /* SettingSceneViewController.swift in Sources */, FC9BB82C2B094E5500EB72A9 /* UICollectionViewLayout+.swift in Sources */, 194552232B0478B400299768 /* HomeRouter.swift in Sources */, 835783C32B14A41600E7D304 /* MockLoginWorker.swift in Sources */, 835A61922B067FEC002F22A5 /* LOTagStackView.swift in Sources */, - FC7E45902AFF746E004F155A /* DummyWorker.swift in Sources */, + 19A169282B176C5F00DB34C0 /* TagPlayListInteractor.swift in Sources */, FC930E7A2B0CD75C00AA48E3 /* ProfileInteractor.swift in Sources */, FC930E752B0CD75C00AA48E3 /* ProfilePresenter.swift in Sources */, 835A61A12B068115002F22A5 /* PlaybackViewController.swift in Sources */, 1945520F2B03AEA400299768 /* Configurator.swift in Sources */, 835A619F2B068115002F22A5 /* PlaybackRouter.swift in Sources */, + 19A1693C2B17BD1C00DB34C0 /* BoardDTO.swift in Sources */, FC2511B12B04EAEC004717BC /* MapModels.swift in Sources */, + 836C339A2B1843BE00ECAFB0 /* SettingSceneWorker.swift in Sources */, FC68E29F2B023315001AABFF /* HTTPMethod.swift in Sources */, 835A619D2B068115002F22A5 /* PlaybackPresenter.swift in Sources */, FCEE0FFA2B03AF8500195BBE /* SignUpViewController.swift in Sources */, @@ -872,6 +1006,8 @@ 1972CCCF2B12438900C3C762 /* LoginEndPointFactory.swift in Sources */, 835A61A92B0B5A31002F22A5 /* LoginConfigurator.swift in Sources */, 194551F72B037F2D00299768 /* LoginInteractor.swift in Sources */, + 19A169382B17BCA800DB34C0 /* PostDTO.swift in Sources */, + 19A1692A2B176D6E00DB34C0 /* TagPlayListConfigurator.swift in Sources */, FC7E453C2AFEB623004F155A /* SceneDelegate.swift in Sources */, FC2511A92B04DAD4004717BC /* MapViewController.swift in Sources */, FCEE0FF62B03804000195BBE /* LOTextField.swift in Sources */, diff --git a/iOS/Layover/Layover/Extensions/Sequence+.swift b/iOS/Layover/Layover/Extensions/Sequence+.swift new file mode 100644 index 0000000..d27798a --- /dev/null +++ b/iOS/Layover/Layover/Extensions/Sequence+.swift @@ -0,0 +1,49 @@ +// +// Sequence+.swift +// Layover +// +// Created by 김인환 on 11/30/23. +// Copyright © 2023 CodeBomber. All rights reserved. +// + +extension Sequence { + func asyncMap( + _ transform: (Element) async throws -> T + ) async rethrows -> [T] { + var values = [T]() + + for element in self { + try await values.append(transform(element)) + } + + return values + } + + func asyncCompactMap( + _ transform: (Element) async throws -> T? + ) async rethrows -> [T] { + var values = [T]() + + for element in self { + if let value = try await transform(element) { + values.append(value) + } + } + + return values + } + + func concurrentMap( + _ transform: @escaping (Element) async throws -> T + ) async throws -> [T] { + let tasks = map { element in + Task { + try await transform(element) + } + } + + return try await tasks.asyncMap { task in + try await task.value + } + } +} diff --git a/iOS/Layover/Layover/Models/Board.swift b/iOS/Layover/Layover/Models/Board.swift new file mode 100644 index 0000000..9570e71 --- /dev/null +++ b/iOS/Layover/Layover/Models/Board.swift @@ -0,0 +1,19 @@ +// +// Board.swift +// Layover +// +// Created by 김인환 on 11/30/23. +// Copyright © 2023 CodeBomber. All rights reserved. +// + +import Foundation + +struct Board { + let identifier: Int + let title: String + let description: String? + let thumbnailImageURL: URL? + let videoURL: URL? + let latitude: Double? + let longitude: Double? +} diff --git a/iOS/Layover/Layover/Models/Member.swift b/iOS/Layover/Layover/Models/Member.swift new file mode 100644 index 0000000..f165fdf --- /dev/null +++ b/iOS/Layover/Layover/Models/Member.swift @@ -0,0 +1,16 @@ +// +// Member.swift +// Layover +// +// Created by 김인환 on 11/30/23. +// Copyright © 2023 CodeBomber. All rights reserved. +// + +import Foundation + +struct Member { + let identifier: Int + let username: String + let introduce: String + let profileImageURL: URL? +} diff --git a/iOS/Layover/Layover/Models/Post.swift b/iOS/Layover/Layover/Models/Post.swift new file mode 100644 index 0000000..24b658c --- /dev/null +++ b/iOS/Layover/Layover/Models/Post.swift @@ -0,0 +1,15 @@ +// +// Post.swift +// Layover +// +// Created by 김인환 on 11/29/23. +// Copyright © 2023 CodeBomber. All rights reserved. +// + +import Foundation + +struct Post { + let member: Member + let board: Board + let tag: [String] +} diff --git a/iOS/Layover/Layover/Network/DTOs/BoardDTO.swift b/iOS/Layover/Layover/Network/DTOs/BoardDTO.swift new file mode 100644 index 0000000..cf1b12d --- /dev/null +++ b/iOS/Layover/Layover/Network/DTOs/BoardDTO.swift @@ -0,0 +1,37 @@ +// +// BoardDTO.swift +// Layover +// +// Created by 김인환 on 11/30/23. +// Copyright © 2023 CodeBomber. All rights reserved. +// + +import Foundation + +struct BoardDTO: Decodable { + let id: Int + let url: String + let videoThumbnail: String + let latitude, longitude, title, content: String + + enum CodingKeys: String, CodingKey { + case id + case url = "url" + case videoThumbnail = "video_thumbnail" + case latitude, longitude, title, content + } +} + +extension BoardDTO { + func toDomain() -> Board { + return Board( + identifier: id, + title: title, + description: content, + thumbnailImageURL: URL(string: videoThumbnail), + videoURL: URL(string: url), + latitude: Double(latitude), + longitude: Double(longitude) + ) + } +} diff --git a/iOS/Layover/Layover/Network/DTOs/MemberDTO.swift b/iOS/Layover/Layover/Network/DTOs/MemberDTO.swift new file mode 100644 index 0000000..e58a073 --- /dev/null +++ b/iOS/Layover/Layover/Network/DTOs/MemberDTO.swift @@ -0,0 +1,31 @@ +// +// MemberDTO.swift +// Layover +// +// Created by 김인환 on 11/30/23. +// Copyright © 2023 CodeBomber. All rights reserved. +// + +import Foundation + +struct MemberDTO: Decodable { + let id: Int + let username, introduce: String + let profileImageURL: String + + enum CodingKeys: String, CodingKey { + case id, username, introduce + case profileImageURL = "profile_image_url" + } +} + +extension MemberDTO { + func toDomain() -> Member { + return Member( + identifier: id, + username: username, + introduce: introduce, + profileImageURL: URL(string: profileImageURL) + ) + } +} diff --git a/iOS/Layover/Layover/Network/DTOs/PostDTO.swift b/iOS/Layover/Layover/Network/DTOs/PostDTO.swift new file mode 100644 index 0000000..3e80d3b --- /dev/null +++ b/iOS/Layover/Layover/Network/DTOs/PostDTO.swift @@ -0,0 +1,25 @@ +// +// PostDTO.swift +// Layover +// +// Created by 김인환 on 11/30/23. +// Copyright © 2023 CodeBomber. All rights reserved. +// + +import Foundation + +struct PostDTO: Decodable { + let member: MemberDTO + let board: BoardDTO + let tag: [String] +} + +extension PostDTO { + func toDomain() -> Post { + return Post( + member: member.toDomain(), + board: board.toDomain(), + tag: tag + ) + } +} diff --git a/iOS/Layover/Layover/Network/EndPoint/Factories/PostEndPointFactory.swift b/iOS/Layover/Layover/Network/EndPoint/Factories/PostEndPointFactory.swift new file mode 100644 index 0000000..f86d505 --- /dev/null +++ b/iOS/Layover/Layover/Network/EndPoint/Factories/PostEndPointFactory.swift @@ -0,0 +1,34 @@ +// +// PostEndPointFactory.swift +// Layover +// +// Created by 김인환 on 11/30/23. +// Copyright © 2023 CodeBomber. All rights reserved. +// + +import Foundation + +protocol PostEndPointFactory { + func makeHomePostListEndPoint() -> EndPoint> + func makeTagSearchPostListEndPoint(by tag: String) -> EndPoint> +} + +final class DefaultPostEndPointFactory: PostEndPointFactory { + func makeHomePostListEndPoint() -> EndPoint> { + return EndPoint( + path: "/board/home", + method: .GET + ) + } + + func makeTagSearchPostListEndPoint(by tag: String) -> EndPoint> { + var queryParameters = [String: String]() + queryParameters.updateValue(tag, forKey: "tag") + + return EndPoint( + path: "/board/tag", + method: .GET, + queryParameters: queryParameters + ) + } +} diff --git a/iOS/Layover/Layover/Network/Mock/MockData/PostList.json b/iOS/Layover/Layover/Network/Mock/MockData/PostList.json new file mode 100644 index 0000000..4553b67 --- /dev/null +++ b/iOS/Layover/Layover/Network/Mock/MockData/PostList.json @@ -0,0 +1,79 @@ +{ + "customCode": "SUCCESS", + "message": "요청이 성공적으로 처리되었습니다.", + "statusCode": 200, + "data": [ + { + "member" : { + "id" : 1, + "username" : "loinsir", + "introduce" : "Hi, my name is hwani", + "profile_image_url" : "https://i.namu.wiki/i/HPFgEkrlX8wtU-agoxsqlzllzfJrcFkDSJYFQxdBHUNyjyZtUpS9zy-7-6lfz4ngzB-1wbLRyhIP4TmnwPwKJ0mUec5403r5TJnI3NZpsYJL6GEVQTmR52YoFGLMbaIe4aGSzh4B4InI9r2g0VV74g.svg" + }, + "board" : { + "id" : 1, + "url" : "https://assets.afcdn.com/video49/20210722/v_645516.m3u8", + "video_thumbnail" : "https://think-note.com/wp-content/uploads/2023/07/eta_3.jpg", + "longitude" : "37.0532156213", + "latitude" : "127.060123123", + "title" : "최강 아이돌", + "content" : "게시글 설명" + }, + "tag" : ["나도몰라요", "너도몰라요"] + }, + { + "member" : { + "id" : 2, + "username" : "chopmozzi", + "introduce" : "Hi, my name is hwani", + "profile_image_url" : "https://m.segye.com/content/image/2023/03/15/20230315514234.jpg" + }, + "board" : { + "id" : 2, + "url" : "https://playertest.longtailvideo.com/adaptive/wowzaid3/playlist.m3u8", + "video_thumbnail" : "video_thumbnail_link", + "longitude" : "37.0532156213", + "latitude" : "127.060123123", + "title" : "프로듀스 101", + "content" : "게시글 설명" + }, + "tag" : ["해시태그", "해시태그2"] + }, + { + "member" : { + "id" : 3, + "username" : "anyukyung", + "introduce" : "Hi, my name is hwani", + "profile_image_url" : "https://images.khan.co.kr/article/2021/11/15/l_2021111502000877900178361.jpg" + }, + "board" : { + "id" : 3, + "url" : "https://sample.vodobox.net/skate_phantom_flex_4k/skate_phantom_flex_4k.m3u8", + "video_thumbnail" : "https://cdnimg.melon.co.kr/resource/image/cds/musicstory/imgUrl20210831030133473.jpg/melon/quality/90/optimize", + "longitude" : "37.0532156213", + "latitude" : "127.060123123", + "title" : "프로미스 나인", + "content" : "게시글 설명" + }, + "tag" : ["해시태그1", "해시태그2"] + }, + { + "member" : { + "id" : 4, + "username" : "layover", + "introduce" : "Hi, my name is hwani", + "profile_image_url" : "profile_image_link" + }, + "board" : { + "id" : 4, + "url" : "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8", + "video_thumbnail" : "https://res.heraldm.com/content/image/2023/04/16/20230416000040_0.jpg", + "longitude" : "37.0532156213", + "latitude" : "127.060123123", + "title" : "아이즈원", + "content" : "게시글 설명2" + }, + "tag" : ["해시태그1", "해시태그6"] + } + ] +} diff --git a/iOS/Layover/Layover/Network/Provider/Provider.swift b/iOS/Layover/Layover/Network/Provider/Provider.swift index 164c529..9a9ab2d 100644 --- a/iOS/Layover/Layover/Network/Provider/Provider.swift +++ b/iOS/Layover/Layover/Network/Provider/Provider.swift @@ -86,14 +86,14 @@ class Provider: ProviderType { func request(url: URL) async throws -> Data { let (data, response) = try await session.data(from: url) try self.checkStatusCode(of: response) - return try data.decode() + return data } func request(url: String) async throws -> Data { guard let url = URL(string: url) else { throw NetworkError.components } let (data, response) = try await session.data(from: url) try self.checkStatusCode(of: response) - return try data.decode() + return data } private func checkStatusCode(of response: URLResponse) throws { diff --git a/iOS/Layover/Layover/Scenes/Profile/ProfileRouter.swift b/iOS/Layover/Layover/Scenes/Profile/ProfileRouter.swift index a609e22..32f45d3 100644 --- a/iOS/Layover/Layover/Scenes/Profile/ProfileRouter.swift +++ b/iOS/Layover/Layover/Scenes/Profile/ProfileRouter.swift @@ -10,13 +10,14 @@ import UIKit protocol ProfileRoutingLogic { func routeToEditProfileViewController() + func routeToSettingSceneViewController() } protocol ProfileDataPassing { var dataStore: ProfileDataStore? { get } } -class ProfileRouter: NSObject, ProfileRoutingLogic, ProfileDataPassing { +final class ProfileRouter: NSObject, ProfileRoutingLogic, ProfileDataPassing { // MARK: - Properties @@ -38,5 +39,9 @@ class ProfileRouter: NSObject, ProfileRoutingLogic, ProfileDataPassing { editProfileViewController.hidesBottomBarWhenPushed = true viewController?.navigationController?.pushViewController(editProfileViewController, animated: true) } - + + func routeToSettingSceneViewController() { + let settingSceneViewController: SettingSceneViewController = SettingSceneViewController() + viewController?.navigationController?.pushViewController(settingSceneViewController, animated: true) + } } diff --git a/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift b/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift index 12d4507..0d85592 100644 --- a/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift +++ b/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift @@ -130,7 +130,7 @@ final class ProfileViewController: BaseViewController { } @objc private func settingButtonDidTap() { - + router?.routeToSettingSceneViewController() } } diff --git a/iOS/Layover/Layover/Scenes/SettingScene/SettingSceneInteractor.swift b/iOS/Layover/Layover/Scenes/SettingScene/SettingSceneInteractor.swift new file mode 100644 index 0000000..9fdf321 --- /dev/null +++ b/iOS/Layover/Layover/Scenes/SettingScene/SettingSceneInteractor.swift @@ -0,0 +1,27 @@ +// +// SettingSceneInteractor.swift +// Layover +// +// Created by 황지웅 on 11/30/23. +// Copyright © 2023 CodeBomber. All rights reserved. +// + +import UIKit + +protocol SettingSceneBusinessLogic { + +} + +protocol SettingSceneDataStore { + +} + +final class SettingSceneInteractor: SettingSceneBusinessLogic, SettingSceneDataStore { + + // MARK: - Properties + + typealias Models = SettingSceneModels + + lazy var worker = SettingSceneWorker() + var presenter: SettingScenePresentationLogic? +} diff --git a/iOS/Layover/Layover/Scenes/SettingScene/SettingSceneModels.swift b/iOS/Layover/Layover/Scenes/SettingScene/SettingSceneModels.swift new file mode 100644 index 0000000..80dcd5b --- /dev/null +++ b/iOS/Layover/Layover/Scenes/SettingScene/SettingSceneModels.swift @@ -0,0 +1,24 @@ +// +// SettingSceneModels.swift +// Layover +// +// Created by 황지웅 on 11/30/23. +// Copyright © 2023 CodeBomber. All rights reserved. +// + +import UIKit + +enum SettingSceneModels { + + enum PerformSettingScene { + struct Request { + var exampleVariable: String? + } + + struct Response { + } + + struct ViewModel { + } + } +} diff --git a/iOS/Layover/Layover/Scenes/SettingScene/SettingScenePresenter.swift b/iOS/Layover/Layover/Scenes/SettingScene/SettingScenePresenter.swift new file mode 100644 index 0000000..0120ed4 --- /dev/null +++ b/iOS/Layover/Layover/Scenes/SettingScene/SettingScenePresenter.swift @@ -0,0 +1,22 @@ +// +// SettingScenePresenter.swift +// Layover +// +// Created by 황지웅 on 11/30/23. +// Copyright © 2023 CodeBomber. All rights reserved. +// + +import UIKit + +protocol SettingScenePresentationLogic { + +} + +final class SettingScenePresenter: SettingScenePresentationLogic { + + // MARK: - Properties + + typealias Models = SettingSceneModels + weak var viewController: SettingSceneDisplayLogic? + +} diff --git a/iOS/Layover/Layover/Scenes/SettingScene/SettingSceneRouter.swift b/iOS/Layover/Layover/Scenes/SettingScene/SettingSceneRouter.swift new file mode 100644 index 0000000..349a89a --- /dev/null +++ b/iOS/Layover/Layover/Scenes/SettingScene/SettingSceneRouter.swift @@ -0,0 +1,27 @@ +// +// SettingSceneRouter.swift +// Layover +// +// Created by 황지웅 on 11/30/23. +// Copyright © 2023 CodeBomber. All rights reserved. +// + +import UIKit + +protocol SettingSceneRoutingLogic { +} + +protocol SettingSceneDataPassing { + var dataStore: SettingSceneDataStore? { get } +} + +final class SettingSceneRouter: NSObject, SettingSceneRoutingLogic, SettingSceneDataPassing { + + // MARK: - Properties + + weak var viewController: SettingSceneViewController? + var dataStore: SettingSceneDataStore? + + // MARK: - Routing + +} diff --git a/iOS/Layover/Layover/Scenes/SettingScene/SettingSceneViewController.swift b/iOS/Layover/Layover/Scenes/SettingScene/SettingSceneViewController.swift new file mode 100644 index 0000000..d28be99 --- /dev/null +++ b/iOS/Layover/Layover/Scenes/SettingScene/SettingSceneViewController.swift @@ -0,0 +1,156 @@ +// +// SettingSceneViewController.swift +// Layover +// +// Created by 황지웅 on 11/30/23. +// Copyright © 2023 CodeBomber. All rights reserved. +// + +import UIKit + +protocol SettingSceneDisplayLogic: AnyObject { +} + +final class SettingSceneViewController: BaseViewController { + + // MARK: - Properties + + typealias Models = SettingSceneModels + var router: (NSObjectProtocol & SettingSceneRoutingLogic & SettingSceneDataPassing)? + var interactor: SettingSceneBusinessLogic? + + private let settingSceneTableView: UITableView = { + let tableView: UITableView = UITableView(frame: .zero, style: .insetGrouped) + tableView.translatesAutoresizingMaskIntoConstraints = false + return tableView + }() + + let policy: [String] = ["이용약관", "개인정보 처리 방침", "위치정보 이용 약관"] + let versionInfo: [String] = ["버전정보"] + let userInfo: [String] = ["로그아웃", "탈퇴"] + // MARK: - Object lifecycle + + override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { + super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) + setup() + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + setup() + } + + // MARK: - Setup + + private func setup() { + let viewController = self + let interactor = SettingSceneInteractor() + let presenter = SettingScenePresenter() + let router = SettingSceneRouter() + + viewController.router = router + viewController.interactor = interactor + interactor.presenter = presenter + presenter.viewController = viewController + router.viewController = viewController + router.dataStore = interactor + } + + // MARK: - View Lifecycle + + override func viewDidLoad() { + super.viewDidLoad() + settingSceneTableView.delegate = self + settingSceneTableView.dataSource = self + settingSceneTableView.register(UITableViewCell.self, forCellReuseIdentifier: UITableViewCell.identifier) + } + + override func setConstraints() { + super.setConstraints() + view.addSubview(settingSceneTableView) + NSLayoutConstraint.activate([ + settingSceneTableView.topAnchor.constraint(equalTo: view.topAnchor), + settingSceneTableView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + settingSceneTableView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + settingSceneTableView.bottomAnchor.constraint(equalTo: view.bottomAnchor) + ]) + } + + override func setUI() { + super.setUI() + setNavigationBar() + } + + private func setNavigationBar() { + self.navigationController?.navigationBar.tintColor = .layoverWhite + self.navigationController?.navigationBar.prefersLargeTitles = true + self.navigationController?.navigationBar.topItem?.title = "설정" + } + + @objc func popViewController() { + self.navigationController?.popViewController(animated: true) + } +} + +extension SettingSceneViewController: SettingSceneDisplayLogic { + +} + +extension SettingSceneViewController: UITableViewDelegate { + +} + +extension SettingSceneViewController: UITableViewDataSource { + func numberOfSections(in tableView: UITableView) -> Int { + return 3 + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + switch section { + case 0: + return 3 + case 1: + return 1 + case 2: + return 2 + default: + return 0 + } + } + + func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { + switch section { + case 0: + return "약관 및 정책" + default: + return nil + } + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: UITableViewCell.identifier, for: indexPath) + var content = cell.defaultContentConfiguration() + switch indexPath.section { + case 0: + content.text = policy[indexPath.row] + cell.contentConfiguration = content + case 1: + content.text = versionInfo[indexPath.row] + content.secondaryText = "1.0.0" + content.prefersSideBySideTextAndSecondaryText = true + cell.contentConfiguration = content + case 2: + content.text = userInfo[indexPath.row] + cell.contentConfiguration = content + default: + break + } + return cell + } +} + +#Preview { + let navi = UINavigationController(rootViewController: UIViewController()) + navi.pushViewController(SettingSceneViewController(), animated: false) + return navi +} diff --git a/iOS/Layover/Layover/Scenes/SettingScene/SettingSceneWorker.swift b/iOS/Layover/Layover/Scenes/SettingScene/SettingSceneWorker.swift new file mode 100644 index 0000000..a1591ca --- /dev/null +++ b/iOS/Layover/Layover/Scenes/SettingScene/SettingSceneWorker.swift @@ -0,0 +1,19 @@ +// +// SettingSceneWorker.swift +// Layover +// +// Created by 황지웅 on 11/30/23. +// Copyright © 2023 CodeBomber. All rights reserved. +// + +import UIKit + +final class SettingSceneWorker { + + // MARK: - Properties + + typealias Models = SettingSceneModels + + // MARK: - Methods + +} diff --git a/iOS/Layover/Layover/Scenes/TagPlayList/Cell/TagPlayListCollectionViewCell.swift b/iOS/Layover/Layover/Scenes/TagPlayList/Cell/TagPlayListCollectionViewCell.swift new file mode 100644 index 0000000..9d3aca8 --- /dev/null +++ b/iOS/Layover/Layover/Scenes/TagPlayList/Cell/TagPlayListCollectionViewCell.swift @@ -0,0 +1,72 @@ +// +// TagPlayListCollectionViewCell.swift +// Layover +// +// Created by 김인환 on 11/29/23. +// Copyright © 2023 CodeBomber. All rights reserved. +// + +import UIKit + +final class TagPlayListCollectionViewCell: UICollectionViewCell { + + // MARK: - UI Components + + private let thumbnailImageView: UIImageView = { + let imageView = UIImageView() + imageView.contentMode = .scaleAspectFill + imageView.backgroundColor = .lightGray + return imageView + }() + + private let titleLabel: UILabel = { + let label = UILabel() + label.font = UIFont.loFont(ofSize: 15, weight: .semibold, type: .pretendard) + label.textAlignment = .natural + return label + }() + + // MARK: - Initializer + + override init(frame: CGRect) { + super.init(frame: frame) + setUI() + setConstraints() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setUI() + setConstraints() + } + + // MARK: - Methods + + private func setConstraints() { + contentView.addSubviews(thumbnailImageView, titleLabel) + contentView.subviews.forEach { $0.translatesAutoresizingMaskIntoConstraints = false } + + NSLayoutConstraint.activate([ + thumbnailImageView.topAnchor.constraint(equalTo: contentView.topAnchor), + thumbnailImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), + thumbnailImageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), + thumbnailImageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), + + titleLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 14), + titleLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -14), + titleLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -14) + ]) + } + + private func setUI() { + clipsToBounds = true + layer.cornerRadius = 10 + backgroundColor = .darkGray + } + + func configure(thumbnailImage: UIImage, title: String) { + thumbnailImageView.image = thumbnailImage + titleLabel.text = title + } + +} diff --git a/iOS/Layover/Layover/Scenes/TagPlayList/TagPlayListConfigurator.swift b/iOS/Layover/Layover/Scenes/TagPlayList/TagPlayListConfigurator.swift new file mode 100644 index 0000000..6e55ef9 --- /dev/null +++ b/iOS/Layover/Layover/Scenes/TagPlayList/TagPlayListConfigurator.swift @@ -0,0 +1,32 @@ +// +// TagPlayListConfigurator.swift +// Layover +// +// Created by 김인환 on 11/29/23. +// Copyright © 2023 CodeBomber. All rights reserved. +// + +import Foundation + +final class TagPlayListConfigurator: Configurator { + + static let shared = TagPlayListConfigurator() + + private init() { } + + func configure(_ viewController: TagPlayListViewController) { + let viewController = viewController + let interactor = TagPlayListInteractor() + let presenter = TagPlayListPresenter() + let worker = MockTagPlayListWorker() + let router = TagPlayListRouter() + + router.viewController = viewController + router.dataStore = interactor + viewController.interactor = interactor + viewController.router = router + interactor.presenter = presenter + interactor.worker = worker + presenter.viewController = viewController + } +} diff --git a/iOS/Layover/Layover/Scenes/TagPlayList/TagPlayListInteractor.swift b/iOS/Layover/Layover/Scenes/TagPlayList/TagPlayListInteractor.swift new file mode 100644 index 0000000..66bac09 --- /dev/null +++ b/iOS/Layover/Layover/Scenes/TagPlayList/TagPlayListInteractor.swift @@ -0,0 +1,63 @@ +// +// TagPlayListInteractor.swift +// Layover +// +// Created by 김인환 on 11/29/23. +// Copyright (c) 2023 CodeBomber. All rights reserved. +// + +import UIKit +import OSLog + +protocol TagPlayListBusinessLogic { + func fetchTitleTag(request: TagPlayListModels.FetchTitleTag.Request) + func fetchPlayList(request: TagPlayListModels.FetchPosts.Request) +} + +protocol TagPlayListDataStore { + var titleTag: String? { get set } + var posts: [Post] { get set } +} + +final class TagPlayListInteractor: TagPlayListBusinessLogic, TagPlayListDataStore { + // MARK: - Properties + + typealias Models = TagPlayListModels + var presenter: TagPlayListPresentationLogic? + var worker: TagPlayListWorkerProtocol? + + // MARK: - DataStore + + var titleTag: String? = "몰라요" + var posts: [Post] = [] + + // MARK: - TagPlayListBusinessLogic + + func fetchTitleTag(request: TagPlayListModels.FetchTitleTag.Request) { + guard let titleTag = titleTag else { return } + presenter?.presentTitleTag(response: Models.FetchTitleTag.Response(titleTag: titleTag)) + } + + func fetchPlayList(request: Models.FetchPosts.Request) { + Task { + guard let titleTag = titleTag, + let posts = await worker?.fetchPlayList(by: titleTag) else { return } + self.posts = posts + do { + let responsePosts = try await posts.concurrentMap { + if let imageURL = $0.board.thumbnailImageURL, + let imageData = await self.worker?.loadImageData(from: imageURL) { + return Models.DisplayedPost(thumbnailImageData: imageData, title: $0.board.title) + } else { + return nil + } + }.compactMap { $0 } + await MainActor.run { + presenter?.presentPlayList(response: Models.FetchPosts.Response(post: responsePosts)) + } + } catch { + os_log(.error, log: .default, "Error: %@", error.localizedDescription) + } + } + } +} diff --git a/iOS/Layover/Layover/Scenes/TagPlayList/TagPlayListModels.swift b/iOS/Layover/Layover/Scenes/TagPlayList/TagPlayListModels.swift new file mode 100644 index 0000000..afff401 --- /dev/null +++ b/iOS/Layover/Layover/Scenes/TagPlayList/TagPlayListModels.swift @@ -0,0 +1,44 @@ +// +// TagPlayListModels.swift +// Layover +// +// Created by 김인환 on 11/29/23. +// Copyright (c) 2023 CodeBomber. All rights reserved. +// + +import UIKit + +enum TagPlayListModels { + // MARK: Use cases + + struct DisplayedPost { + let thumbnailImageData: Data? + let title: String + } + + enum FetchPosts { + struct Request { + } + + struct Response { + let post: [DisplayedPost] + } + + struct ViewModel { + let displayedPost: [DisplayedPost] + } + } + + enum FetchTitleTag { + struct Request { + } + + struct Response { + let titleTag: String + } + + struct ViewModel { + let title: String + } + } +} diff --git a/iOS/Layover/Layover/Scenes/TagPlayList/TagPlayListPresenter.swift b/iOS/Layover/Layover/Scenes/TagPlayList/TagPlayListPresenter.swift new file mode 100644 index 0000000..0bae725 --- /dev/null +++ b/iOS/Layover/Layover/Scenes/TagPlayList/TagPlayListPresenter.swift @@ -0,0 +1,33 @@ +// +// TagPlayListPresenter.swift +// Layover +// +// Created by 김인환 on 11/29/23. +// Copyright (c) 2023 CodeBomber. All rights reserved. +// + +import UIKit + +protocol TagPlayListPresentationLogic { + func presentPlayList(response: TagPlayListModels.FetchPosts.Response) + func presentTitleTag(response: TagPlayListModels.FetchTitleTag.Response) +} + +final class TagPlayListPresenter: TagPlayListPresentationLogic { + + // MARK: - Properties + typealias Models = TagPlayListModels + weak var viewController: TagPlayListDisplayLogic? + + // MARK: - Methods + + func presentPlayList(response: TagPlayListModels.FetchPosts.Response) { + let displayedPosts = response.post + viewController?.displayPlayList(viewModel: Models.FetchPosts.ViewModel(displayedPost: displayedPosts)) + } + + func presentTitleTag(response: TagPlayListModels.FetchTitleTag.Response) { + let titleTag = response.titleTag + viewController?.displayTitle(viewModel: Models.FetchTitleTag.ViewModel(title: titleTag)) + } +} diff --git a/iOS/Layover/Layover/Scenes/TagPlayList/TagPlayListRouter.swift b/iOS/Layover/Layover/Scenes/TagPlayList/TagPlayListRouter.swift new file mode 100644 index 0000000..6e7089c --- /dev/null +++ b/iOS/Layover/Layover/Scenes/TagPlayList/TagPlayListRouter.swift @@ -0,0 +1,25 @@ +// +// TagPlayListRouter.swift +// Layover +// +// Created by 김인환 on 11/29/23. +// Copyright (c) 2023 CodeBomber. All rights reserved. +// + +import UIKit + +protocol TagPlayListRoutingLogic { + +} + +protocol TagPlayListDataPassing { + var dataStore: TagPlayListDataStore? { get } +} + +final class TagPlayListRouter: TagPlayListRoutingLogic, TagPlayListDataPassing { + + // MARK: - Properties + + weak var viewController: TagPlayListViewController? + var dataStore: TagPlayListDataStore? +} diff --git a/iOS/Layover/Layover/Scenes/TagPlayList/TagPlayListViewController.swift b/iOS/Layover/Layover/Scenes/TagPlayList/TagPlayListViewController.swift new file mode 100644 index 0000000..9e08281 --- /dev/null +++ b/iOS/Layover/Layover/Scenes/TagPlayList/TagPlayListViewController.swift @@ -0,0 +1,154 @@ +// +// TagPlayListViewController.swift +// Layover +// +// Created by 김인환 on 11/29/23. +// Copyright (c) 2023 CodeBomber. All rights reserved. + +import UIKit + +protocol TagPlayListDisplayLogic: AnyObject { + func displayPlayList(viewModel: TagPlayListModels.FetchPosts.ViewModel) + func displayTitle(viewModel: TagPlayListModels.FetchTitleTag.ViewModel) +} + +final class TagPlayListViewController: BaseViewController { + + // MARK: - UI Components + + private lazy var collectionView: UICollectionView = { + let layout = UICollectionViewFlowLayout() + layout.minimumLineSpacing = 9 + layout.minimumInteritemSpacing = 9 + layout.scrollDirection = .vertical + layout.itemSize = CGSize(width: (view.bounds.width - 29) / 2, height: ((view.bounds.width - 29) / 2) * 289/173) + let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) + collectionView.backgroundColor = .clear + collectionView.contentInset = UIEdgeInsets(top: 10, left: 10, bottom: 0, right: 10) + collectionView.register(TagPlayListCollectionViewCell.self, + forCellWithReuseIdentifier: TagPlayListCollectionViewCell.identifier) + collectionView.dataSource = self + return collectionView + }() + + // MARK: - Properties + + typealias Models = TagPlayListModels + var interactor: TagPlayListBusinessLogic? + var router: (TagPlayListRoutingLogic & TagPlayListDataPassing)? + + private var displayedPosts: [Models.DisplayedPost] = [] + + // MARK: - Intializer + + override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { + super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) + setup() + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + setup() + } + + // MARK: - Setup + + private func setup() { + TagPlayListConfigurator.shared.configure(self) + } + + // MARK: - View lifecycle + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + fetchPlayList() + } + + // MARK: - Layout + + override func setConstraints() { + super.setConstraints() + view.addSubviews(collectionView) + view.subviews.forEach { $0.translatesAutoresizingMaskIntoConstraints = false } + + NSLayoutConstraint.activate([ + collectionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), + collectionView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor), + collectionView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor), + collectionView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor) + ]) + } + + override func setUI() { + super.setUI() + fetchTitleTag() + } + + // MARK: - Methods + + private func setNavigationBar(with title: String) { + var configuration = UIButton.Configuration.filled() + configuration.baseBackgroundColor = UIColor.primaryPurple + configuration.title = title + configuration.titleTextAttributesTransformer = UIConfigurationTextAttributesTransformer { incoming in + var outgoing = incoming + outgoing.font = UIFont.loFont(ofSize: 13, weight: .bold) + outgoing.foregroundColor = UIColor.layoverWhite + return outgoing + } + + let button = UIButton(configuration: configuration) + button.clipsToBounds = true + button.layer.cornerRadius = 15 + navigationItem.titleView = button + } + + private func fetchTitleTag() { + interactor?.fetchTitleTag(request: Models.FetchTitleTag.Request()) + } + + private func fetchPlayList() { + interactor?.fetchPlayList(request: Models.FetchPosts.Request()) + } +} + +// MARK: - UICollectionViewDataSource + +extension TagPlayListViewController: UICollectionViewDataSource { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return displayedPosts.count + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: TagPlayListCollectionViewCell.identifier, + for: indexPath) as? TagPlayListCollectionViewCell + else { return UICollectionViewCell() } + + let data = displayedPosts[indexPath.item] + guard let imageData = data.thumbnailImageData, + let image = UIImage(data: imageData) + else { return UICollectionViewCell() } + + cell.configure(thumbnailImage: image, title: data.title) + return cell + } +} + +// MARK: - TagPlayListDisplayLogic + +extension TagPlayListViewController: TagPlayListDisplayLogic { + func displayPlayList(viewModel: Models.FetchPosts.ViewModel) { + displayedPosts = viewModel.displayedPost + collectionView.reloadData() + } + + func displayTitle(viewModel: TagPlayListModels.FetchTitleTag.ViewModel) { + setNavigationBar(with: viewModel.title) + } +} + +#Preview { + let navi = UINavigationController(rootViewController: UIViewController()) + navi.pushViewController(TagPlayListViewController(), animated: false) + return navi +} diff --git a/iOS/Layover/Layover/Scenes/TagPlayList/TagPlayListWorker.swift b/iOS/Layover/Layover/Scenes/TagPlayList/TagPlayListWorker.swift new file mode 100644 index 0000000..2643c97 --- /dev/null +++ b/iOS/Layover/Layover/Scenes/TagPlayList/TagPlayListWorker.swift @@ -0,0 +1,58 @@ +// +// TagPlayListWorker.swift +// Layover +// +// Created by 김인환 on 11/29/23. +// Copyright (c) 2023 CodeBomber. All rights reserved. +// + +import UIKit +import OSLog + +protocol TagPlayListWorkerProtocol { + func fetchPlayList(by tag: String) async -> [Post]? + func loadImageData(from url: URL) async -> Data? +} + +final class TagPlayListWorker: TagPlayListWorkerProtocol { + + // MARK: - Properties + + typealias Models = TagPlayListModels + + let provider: ProviderType + let authManager: AuthManager + let postEndPointFactory: PostEndPointFactory + + // MARK: - Initializer + + init(provider: ProviderType = Provider(), + postEndPointFactory: PostEndPointFactory = DefaultPostEndPointFactory(), + authManager: AuthManager = AuthManager.shared) { + self.provider = provider + self.postEndPointFactory = postEndPointFactory + self.authManager = authManager + } + + // MARK: - Methods + + func fetchPlayList(by tag: String) async -> [Post]? { + let endPoint = postEndPointFactory.makeTagSearchPostListEndPoint(by: tag) + do { + let responseData = try await provider.request(with: endPoint) + return responseData.data?.map { $0.toDomain() } + } catch { + os_log(.error, log: .default, "Error occured while fetching post list: %s", error.localizedDescription) + return nil + } + } + + func loadImageData(from url: URL) async -> Data? { + do { + return try await provider.request(url: url) + } catch { + os_log(.error, log: .default, "Error occured while fetching image data: %s", error.localizedDescription) + return nil + } + } +} diff --git a/iOS/Layover/Layover/Workers/DummyWorker.swift b/iOS/Layover/Layover/Workers/DummyWorker.swift deleted file mode 100644 index 93f99f4..0000000 --- a/iOS/Layover/Layover/Workers/DummyWorker.swift +++ /dev/null @@ -1,8 +0,0 @@ -// -// DummyWorker.swift -// Layover -// -// Created by kong on 2023/11/11. -// - -import Foundation diff --git a/iOS/Layover/Layover/Workers/MockLoginWorker.swift b/iOS/Layover/Layover/Workers/Mocks/MockLoginWorker.swift similarity index 100% rename from iOS/Layover/Layover/Workers/MockLoginWorker.swift rename to iOS/Layover/Layover/Workers/Mocks/MockLoginWorker.swift diff --git a/iOS/Layover/Layover/Workers/MockSignUpWorker.swift b/iOS/Layover/Layover/Workers/Mocks/MockSignUpWorker.swift similarity index 100% rename from iOS/Layover/Layover/Workers/MockSignUpWorker.swift rename to iOS/Layover/Layover/Workers/Mocks/MockSignUpWorker.swift diff --git a/iOS/Layover/Layover/Workers/Mocks/MockTagPlayListWorker.swift b/iOS/Layover/Layover/Workers/Mocks/MockTagPlayListWorker.swift new file mode 100644 index 0000000..5707c0a --- /dev/null +++ b/iOS/Layover/Layover/Workers/Mocks/MockTagPlayListWorker.swift @@ -0,0 +1,71 @@ +// +// MockTagPlayListWorker.swift +// Layover +// +// Created by 김인환 on 11/30/23. +// Copyright © 2023 CodeBomber. All rights reserved. +// + +import Foundation +import OSLog + +final class MockTagPlayListWorker: TagPlayListWorkerProtocol { + + // MARK: - Properties + + private let provider: ProviderType = Provider(session: .initMockSession()) + private let headers: [String: String] = ["Content-Type": "application/json", + "Authorization": "mock token"] + + // MARK: - Methods + + func fetchPlayList(by tag: String) async -> [Post]? { + guard let fileLocation = Bundle.main.url(forResource: "PostList", withExtension: "json") else { + return nil + } + + do { + let mockData = try? Data(contentsOf: fileLocation) + MockURLProtocol.requestHandler = { request in + let response = HTTPURLResponse(url: request.url!, + statusCode: 200, + httpVersion: nil, + headerFields: nil) + return (response, mockData, nil) + } + + let endPoint = EndPoint>(path: "/board/tag", + method: .GET, + queryParameters: ["tag": tag], + headers: headers) + + let response = try await provider.request(with: endPoint) + return response.data?.map { $0.toDomain() } + } catch { + os_log(.error, log: .data, "%@", error.localizedDescription) + return nil + } + } + + func loadImageData(from url: URL) async -> Data? { + do { + guard let imageURL = Bundle.main.url(forResource: "sample", withExtension: "jpeg") else { + return nil + } + let mockData = try? Data(contentsOf: imageURL) + MockURLProtocol.requestHandler = { request in + let response = HTTPURLResponse(url: request.url!, + statusCode: 200, + httpVersion: nil, + headerFields: nil) + return (response, mockData, nil) + } + + let data = try await provider.request(url: url) + return data + } catch { + os_log(.error, log: .data, "%@", error.localizedDescription) + return nil + } + } +} diff --git a/iOS/Layover/Layover/Workers/MockUserWorker.swift b/iOS/Layover/Layover/Workers/Mocks/MockUserWorker.swift similarity index 100% rename from iOS/Layover/Layover/Workers/MockUserWorker.swift rename to iOS/Layover/Layover/Workers/Mocks/MockUserWorker.swift diff --git a/iOS/Layover/Layover/Workers/Mocks/sample.jpeg b/iOS/Layover/Layover/Workers/Mocks/sample.jpeg new file mode 100644 index 0000000..a88d861 Binary files /dev/null and b/iOS/Layover/Layover/Workers/Mocks/sample.jpeg differ