From 5b34e7a495d62b30694313f4d47e90638bc963f0 Mon Sep 17 00:00:00 2001 From: kong <1018dbrud@gmail.com> Date: Thu, 7 Dec 2023 14:31:25 +0900 Subject: [PATCH 01/12] =?UTF-8?q?=E2=9C=A8=20=EC=9C=84=EB=8F=84=EA=B2=BD?= =?UTF-8?q?=EB=8F=84=20=EA=B8=B0=EB=B0=98=20fetchPosts=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/Layover/Layover.xcodeproj/project.pbxproj | 17 +++--- iOS/Layover/Layover/Models/Board.swift | 4 +- .../Layover/Network/DTOs/BoardDTO.swift | 7 +-- .../Factories/PostEndPointFactory.swift | 13 +++++ .../Layover/Scenes/Map/LOAnnotation.swift | 6 +-- .../Layover/Scenes/Map/MapConfigurator.swift | 2 + .../Layover/Scenes/Map/MapInteractor.swift | 23 +++++++- .../Layover/Scenes/Map/MapModels.swift | 36 ++++++++++++- .../Layover/Scenes/Map/MapPresenter.swift | 22 +++++++- .../Scenes/Map/MapViewController.swift | 53 ++++++++++++++----- .../Layover/Scenes/Map/MapWorker.swift | 50 +++++++++++++++++ .../Scenes/Map/Views/LOAnnotationView.swift | 5 +- 12 files changed, 202 insertions(+), 36 deletions(-) create mode 100644 iOS/Layover/Layover/Scenes/Map/MapWorker.swift diff --git a/iOS/Layover/Layover.xcodeproj/project.pbxproj b/iOS/Layover/Layover.xcodeproj/project.pbxproj index bc9bbf8..85fb8d2 100644 --- a/iOS/Layover/Layover.xcodeproj/project.pbxproj +++ b/iOS/Layover/Layover.xcodeproj/project.pbxproj @@ -172,6 +172,8 @@ FC68E2A32B0233BC001AABFF /* NetworkError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC68E2A22B0233BC001AABFF /* NetworkError.swift */; }; FC68E2A52B0233D3001AABFF /* Provider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC68E2A42B0233D3001AABFF /* Provider.swift */; }; FC70BB2B2B20718A00B37CBE /* VideoPickerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC70BB2A2B20718A00B37CBE /* VideoPickerManager.swift */; }; + FC70BB2F2B20B66300B37CBE /* MapWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC70BB2E2B20B66300B37CBE /* MapWorker.swift */; }; + FC70BB332B20C96200B37CBE /* LOAnnotation.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC70BB322B20C96200B37CBE /* LOAnnotation.swift */; }; FC767F842B1214A80088CF9B /* MockUserWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC767F832B1214A70088CF9B /* MockUserWorker.swift */; }; FC767F862B1214C10088CF9B /* CheckUserNameDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC767F852B1214C10088CF9B /* CheckUserNameDTO.swift */; }; FC767F932B1220CC0088CF9B /* NicknameDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC767F922B1220CC0088CF9B /* NicknameDTO.swift */; }; @@ -184,7 +186,6 @@ FC767FA32B12283D0088CF9B /* PatchProfileImage.json in Resources */ = {isa = PBXBuildFile; fileRef = FC767F9E2B12283D0088CF9B /* PatchProfileImage.json */; }; FC767FA52B125F430088CF9B /* UIViewController+.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC767FA42B125F430088CF9B /* UIViewController+.swift */; }; FC767FA82B1269DB0088CF9B /* LOAnnotationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC767FA72B1269DB0088CF9B /* LOAnnotationView.swift */; }; - FC767FAA2B126D080088CF9B /* LOAnnotation.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC767FA92B126D080088CF9B /* LOAnnotation.swift */; }; FC7E453A2AFEB623004F155A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC7E45392AFEB623004F155A /* AppDelegate.swift */; }; FC7E453C2AFEB623004F155A /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC7E453B2AFEB623004F155A /* SceneDelegate.swift */; }; FC7E45432AFEB62B004F155A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = FC7E45422AFEB62B004F155A /* Assets.xcassets */; }; @@ -383,6 +384,8 @@ FC68E2A22B0233BC001AABFF /* NetworkError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkError.swift; sourceTree = ""; }; FC68E2A42B0233D3001AABFF /* Provider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Provider.swift; sourceTree = ""; }; FC70BB2A2B20718A00B37CBE /* VideoPickerManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPickerManager.swift; sourceTree = ""; }; + FC70BB2E2B20B66300B37CBE /* MapWorker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapWorker.swift; sourceTree = ""; }; + FC70BB322B20C96200B37CBE /* LOAnnotation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LOAnnotation.swift; sourceTree = ""; }; FC767F832B1214A70088CF9B /* MockUserWorker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockUserWorker.swift; sourceTree = ""; }; FC767F852B1214C10088CF9B /* CheckUserNameDTO.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheckUserNameDTO.swift; sourceTree = ""; }; FC767F922B1220CC0088CF9B /* NicknameDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NicknameDTO.swift; sourceTree = ""; }; @@ -395,7 +398,6 @@ FC767F9E2B12283D0088CF9B /* PatchProfileImage.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = PatchProfileImage.json; sourceTree = ""; }; FC767FA42B125F430088CF9B /* UIViewController+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+.swift"; sourceTree = ""; }; FC767FA72B1269DB0088CF9B /* LOAnnotationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LOAnnotationView.swift; sourceTree = ""; }; - FC767FA92B126D080088CF9B /* LOAnnotation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LOAnnotation.swift; sourceTree = ""; }; FC7E45362AFEB623004F155A /* Layover.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Layover.app; sourceTree = BUILT_PRODUCTS_DIR; }; FC7E45392AFEB623004F155A /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; FC7E453B2AFEB623004F155A /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -611,8 +613,6 @@ 19A169372B17BCA800DB34C0 /* PostDTO.swift */, 19A169392B17BCC400DB34C0 /* MemberDTO.swift */, 19A1693B2B17BD1C00DB34C0 /* BoardDTO.swift */, - 836C338C2B15D91F00ECAFB0 /* VideoDTO.swift */, - 836C338E2B160CC700ECAFB0 /* MemberDTO.swift */, 8321A2FE2B1E428C000A12AF /* ReportDTO.swift */, FC4084C52B1F1C5B00CE4727 /* UploadPostDTO.swift */, FC4084C92B1F291200CE4727 /* UploadVideoDTO.swift */, @@ -750,13 +750,14 @@ isa = PBXGroup; children = ( FC767FA62B1269980088CF9B /* Views */, - FC767FA92B126D080088CF9B /* LOAnnotation.swift */, + FC70BB322B20C96200B37CBE /* LOAnnotation.swift */, FC2511A82B04DAD4004717BC /* MapViewController.swift */, FC2511AC2B04EACD004717BC /* MapInteractor.swift */, FC2511AE2B04EAD9004717BC /* MapPresenter.swift */, - FC2511AA2B04EA6B004717BC /* MapConfigurator.swift */, + FC70BB2E2B20B66300B37CBE /* MapWorker.swift */, FC2511B02B04EAEC004717BC /* MapModels.swift */, 836C33902B17629400ECAFB0 /* MapRouter.swift */, + FC2511AA2B04EA6B004717BC /* MapConfigurator.swift */, ); path = Map; sourceTree = ""; @@ -1198,6 +1199,7 @@ FC3752342B170A620000D439 /* EditVideoViewController.swift in Sources */, 19A169442B17C71C00DB34C0 /* Board.swift in Sources */, 194552252B0478B400299768 /* HomeViewController.swift in Sources */, + FC70BB2F2B20B66300B37CBE /* MapWorker.swift in Sources */, FC4084C62B1F1C5B00CE4727 /* UploadPostDTO.swift in Sources */, 194552222B0478B400299768 /* HomeWorker.swift in Sources */, 194C21BD2B1B728600C62645 /* StubAuthManager.swift in Sources */, @@ -1224,7 +1226,6 @@ 8321A2F12B1E1026000A12AF /* ReportModels.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 */, @@ -1283,10 +1284,10 @@ FC68E29D2B02326A001AABFF /* Responsable.swift in Sources */, FC2511A02B045C0A004717BC /* SignUpInteractor.swift in Sources */, FC767F862B1214C10088CF9B /* CheckUserNameDTO.swift in Sources */, - 836C338D2B15D91F00ECAFB0 /* VideoDTO.swift in Sources */, FC4084C42B1F14F600CE4727 /* UploadPostEndPointFactory.swift in Sources */, FC5BE1232B1490660036366D /* EditProfileConfigurator.swift in Sources */, 1945522A2B04883800299768 /* UIView+.swift in Sources */, + FC70BB332B20C96200B37CBE /* LOAnnotation.swift in Sources */, FCE52FFA2B0FCB0A002CDB75 /* URLSession+.swift in Sources */, 19743C052B06940D001E405A /* PlayerView.swift in Sources */, 1915D6E52B1FB82000CE1DD0 /* CheckSignUpDTO.swift in Sources */, diff --git a/iOS/Layover/Layover/Models/Board.swift b/iOS/Layover/Layover/Models/Board.swift index 9570e71..8f2df33 100644 --- a/iOS/Layover/Layover/Models/Board.swift +++ b/iOS/Layover/Layover/Models/Board.swift @@ -14,6 +14,6 @@ struct Board { let description: String? let thumbnailImageURL: URL? let videoURL: URL? - let latitude: Double? - let longitude: Double? + let latitude: Double + let longitude: Double } diff --git a/iOS/Layover/Layover/Network/DTOs/BoardDTO.swift b/iOS/Layover/Layover/Network/DTOs/BoardDTO.swift index 0eca599..7620f10 100644 --- a/iOS/Layover/Layover/Network/DTOs/BoardDTO.swift +++ b/iOS/Layover/Layover/Network/DTOs/BoardDTO.swift @@ -12,7 +12,8 @@ struct BoardDTO: Decodable { let id: Int let encodedVideoURL: String let videoThumbnailURL: String - let latitude, longitude, title, content: String + let latitude, longitude: Double + let title, content: String enum CodingKeys: String, CodingKey { case id @@ -30,8 +31,8 @@ extension BoardDTO { description: content, thumbnailImageURL: URL(string: videoThumbnailURL), videoURL: URL(string: encodedVideoURL), - latitude: Double(latitude), - longitude: Double(longitude) + latitude: latitude, + longitude: longitude ) } } diff --git a/iOS/Layover/Layover/Network/EndPoint/Factories/PostEndPointFactory.swift b/iOS/Layover/Layover/Network/EndPoint/Factories/PostEndPointFactory.swift index f86d505..6395fd5 100644 --- a/iOS/Layover/Layover/Network/EndPoint/Factories/PostEndPointFactory.swift +++ b/iOS/Layover/Layover/Network/EndPoint/Factories/PostEndPointFactory.swift @@ -10,6 +10,7 @@ import Foundation protocol PostEndPointFactory { func makeHomePostListEndPoint() -> EndPoint> + func makeMapPostListEndPoint(latitude: Double, longitude: Double) -> EndPoint> func makeTagSearchPostListEndPoint(by tag: String) -> EndPoint> } @@ -21,6 +22,18 @@ final class DefaultPostEndPointFactory: PostEndPointFactory { ) } + func makeMapPostListEndPoint(latitude: Double, longitude: Double) -> EndPoint> { + var queryParameters = [String: String]() + queryParameters.updateValue(String(latitude), forKey: "latitude") + queryParameters.updateValue(String(longitude), forKey: "longitude") + + return EndPoint( + path: "/board/map", + method: .GET, + queryParameters: queryParameters + ) + } + func makeTagSearchPostListEndPoint(by tag: String) -> EndPoint> { var queryParameters = [String: String]() queryParameters.updateValue(tag, forKey: "tag") diff --git a/iOS/Layover/Layover/Scenes/Map/LOAnnotation.swift b/iOS/Layover/Layover/Scenes/Map/LOAnnotation.swift index f2435b9..31cef2a 100644 --- a/iOS/Layover/Layover/Scenes/Map/LOAnnotation.swift +++ b/iOS/Layover/Layover/Scenes/Map/LOAnnotation.swift @@ -10,10 +10,10 @@ import MapKit final class LOAnnotation: NSObject, MKAnnotation { @objc dynamic var coordinate: CLLocationCoordinate2D - let thumbnailURL: URL + let thumbnailImageData: Data - init(coordinate: CLLocationCoordinate2D, thumbnailURL: URL) { + init(coordinate: CLLocationCoordinate2D, thumbnailImageData: Data) { self.coordinate = coordinate - self.thumbnailURL = thumbnailURL + self.thumbnailImageData = thumbnailImageData } } diff --git a/iOS/Layover/Layover/Scenes/Map/MapConfigurator.swift b/iOS/Layover/Layover/Scenes/Map/MapConfigurator.swift index 07c9318..40d83f8 100644 --- a/iOS/Layover/Layover/Scenes/Map/MapConfigurator.swift +++ b/iOS/Layover/Layover/Scenes/Map/MapConfigurator.swift @@ -20,11 +20,13 @@ final class MapConfigurator: Configurator { let interactor = MapInteractor() let presenter = MapPresenter() let router = MapRouter() + let worker = MapWorker() let videoFileWorker = VideoFileWorker() router.viewController = viewController viewController.interactor = interactor interactor.presenter = presenter + interactor.worker = worker interactor.videoFileWorker = videoFileWorker presenter.viewController = viewController viewController.router = router diff --git a/iOS/Layover/Layover/Scenes/Map/MapInteractor.swift b/iOS/Layover/Layover/Scenes/Map/MapInteractor.swift index 3e3684b..1c5ebfa 100644 --- a/iOS/Layover/Layover/Scenes/Map/MapInteractor.swift +++ b/iOS/Layover/Layover/Scenes/Map/MapInteractor.swift @@ -14,12 +14,16 @@ protocol MapBusinessLogic { func fetchVideos() func moveToPlaybackScene(with: MapModels.MoveToPlaybackScene.Request) func playPosts(with: MapModels.PlayPosts.Request) + + @discardableResult + func fetchPosts(latitude: Double, longitude: Double) -> Task<[MapModels.Post], Never> func selectVideo(with request: MapModels.SelectVideo.Request) } protocol MapDataStore { var postPlayStartIndex: Int? { get set } var posts: [Post]? { get set } +// var posts: [MapModels.Post]? { get set } var index: Int? { get set } var selectedVideoURL: URL? { get set } } @@ -31,6 +35,7 @@ final class MapInteractor: NSObject, MapBusinessLogic, MapDataStore { typealias Models = MapModels var presenter: MapPresentationLogic? var videoFileWorker: VideoFileWorker? + var worker: MapWorkerProtocol? private let locationManager = CLLocationManager() private var latitude: Double? @@ -38,6 +43,7 @@ final class MapInteractor: NSObject, MapBusinessLogic, MapDataStore { var postPlayStartIndex: Int? var posts: [Post]? +// var posts: [Models.Post]? var index: Int? var selectedVideoURL: URL? @@ -59,11 +65,11 @@ final class MapInteractor: NSObject, MapBusinessLogic, MapDataStore { "https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8", "https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8"] .compactMap { URL(string: $0) } - presenter?.presentFetchedVideos(with: MapModels.FetchVideo.Reponse(videoURLs: dummyURLs)) + presenter?.presentFetchedVideos(with: MapModels.FetchVideo.Response(videoURLs: dummyURLs)) } func moveToPlaybackScene(with request: Models.MoveToPlaybackScene.Request) { - posts = request.videos + posts = request.videos.map { .init(member: $0.member, board: $0.board, tag: $0.tag) } index = request.index presenter?.presentPlaybackScene() } @@ -73,6 +79,19 @@ final class MapInteractor: NSObject, MapBusinessLogic, MapDataStore { presenter?.presentPlaybackScene() } + func fetchPosts(latitude: Double, longitude: Double) -> Task<[MapModels.Post], Never> { + Task { + let posts = await worker?.fetchPosts(latitude: latitude, + longitude: longitude) + guard let posts else { return [] } + let response = Models.FetchPosts.Response(posts: posts) + await MainActor.run { + presenter?.presentFetchedPosts(with: response) + } + return posts + } + } + func selectVideo(with request: Models.SelectVideo.Request) { selectedVideoURL = videoFileWorker?.copyToNewURL(at: request.videoURL) } diff --git a/iOS/Layover/Layover/Scenes/Map/MapModels.swift b/iOS/Layover/Layover/Scenes/Map/MapModels.swift index e13f637..e7f0613 100644 --- a/iOS/Layover/Layover/Scenes/Map/MapModels.swift +++ b/iOS/Layover/Layover/Scenes/Map/MapModels.swift @@ -10,6 +10,20 @@ import Foundation enum MapModels { + struct Post { + let member: Member + let board: Board + let tag: [String] + let thumnailImageData: Data + } + + struct DisplayedPost: Hashable { + let thumbnailImageData: Data + let videoURL: URL + let latitude: Double + let longitude: Double + } + // MARK: - Fetch Video Use Cases enum FetchVideo { struct Request { @@ -17,7 +31,7 @@ enum MapModels { var longitude: Double } - struct Reponse { + struct Response { var videoURLs: [URL] } @@ -31,6 +45,26 @@ enum MapModels { } } + enum FetchPosts { + struct Request { + var latitude: Double + var longitude: Double + } + + struct Response { + var posts: [Post] + } + + struct ViewModel { + var displayedPosts: [DisplayedPost] + +// struct VideoDataSource: Hashable { +// var id = UUID() +// var videoURL: URL +// } + } + } + // MARK: - Move To Playback Scene enum MoveToPlaybackScene { diff --git a/iOS/Layover/Layover/Scenes/Map/MapPresenter.swift b/iOS/Layover/Layover/Scenes/Map/MapPresenter.swift index 29b30f6..b16eda1 100644 --- a/iOS/Layover/Layover/Scenes/Map/MapPresenter.swift +++ b/iOS/Layover/Layover/Scenes/Map/MapPresenter.swift @@ -11,7 +11,8 @@ import Foundation protocol MapPresentationLogic { func presentCurrentLocation() func presentDefaultLocation() - func presentFetchedVideos(with response: MapModels.FetchVideo.Reponse) + func presentFetchedVideos(with response: MapModels.FetchVideo.Response) + func presentFetchedPosts(with response: MapModels.FetchPosts.Response) func presentPlaybackScene() } @@ -30,11 +31,28 @@ final class MapPresenter: MapPresentationLogic { // TODO: 위치 관련 기능 사용 불가, 디폴트 위치로 이동 } - func presentFetchedVideos(with response: MapModels.FetchVideo.Reponse) { + func presentFetchedVideos(with response: MapModels.FetchVideo.Response) { let viewModel = MapModels.FetchVideo.ViewModel(videoDataSources: response.videoURLs.map { .init(videoURL: $0) }) viewController?.displayFetchedVideos(viewModel: viewModel) } + func presentFetchedPosts(with response: MapModels.FetchPosts.Response) { + let displayedPost = response.posts + .map { post -> Models.DisplayedPost? in + if let videoURL = post.board.videoURL { + return .init(thumbnailImageData: post.thumnailImageData, + videoURL: videoURL, + latitude: post.board.latitude, + longitude: post.board.longitude) + } else { + return nil + } + }.compactMap { $0 } + + let viewModel = Models.FetchPosts.ViewModel(displayedPosts: displayedPost) + viewController?.dispalyFetchedPosts(viewModel: viewModel) + } + func presentPlaybackScene() { viewController?.routeToPlayback() } diff --git a/iOS/Layover/Layover/Scenes/Map/MapViewController.swift b/iOS/Layover/Layover/Scenes/Map/MapViewController.swift index 327651a..9d21d2f 100644 --- a/iOS/Layover/Layover/Scenes/Map/MapViewController.swift +++ b/iOS/Layover/Layover/Scenes/Map/MapViewController.swift @@ -11,6 +11,7 @@ import UIKit protocol MapDisplayLogic: AnyObject { func displayFetchedVideos(viewModel: MapModels.FetchVideo.ViewModel) + func dispalyFetchedPosts(viewModel: MapModels.FetchPosts.ViewModel) func routeToPlayback() } @@ -27,7 +28,7 @@ final class MapViewController: BaseViewController { return mapView }() - private let searchButton: UIButton = { + private lazy var searchButton: UIButton = { var configuration = UIButton.Configuration.filled() var titleContainer = AttributeContainer() titleContainer.font = UIFont.loFont(type: .body3) @@ -39,6 +40,7 @@ final class MapViewController: BaseViewController { configuration.background.strokeColor = .grey500 configuration.background.strokeWidth = 1 let button = UIButton(configuration: configuration) + button.addTarget(self, action: #selector(searchButtonDidTap), for: .touchUpInside) return button }() @@ -62,7 +64,7 @@ final class MapViewController: BaseViewController { return collectionView }() - private lazy var carouselDatasource = UICollectionViewDiffableDataSource(collectionView: carouselCollectionView) { collectionView, indexPath, item in + private lazy var carouselDatasource = UICollectionViewDiffableDataSource(collectionView: carouselCollectionView) { collectionView, indexPath, item in guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MapCarouselCollectionViewCell.identifier, for: indexPath) as? MapCarouselCollectionViewCell else { return UICollectionViewCell() } cell.configure(url: item.videoURL) @@ -94,9 +96,11 @@ final class MapViewController: BaseViewController { override func viewDidLoad() { super.viewDidLoad() interactor?.checkLocationAuthorizationStatus() + print(mapView.centerCoordinate) + interactor?.fetchPosts(latitude: mapView.centerCoordinate.latitude, + longitude: mapView.centerCoordinate.longitude) setCollectionViewDataSource() setDelegation() - createMapAnnotation() } // MARK: - UI + Layout @@ -170,10 +174,10 @@ final class MapViewController: BaseViewController { return UICollectionViewCompositionalLayout(section: section) } - private func createMapAnnotation() { - let annotation = LOAnnotation(coordinate: CLLocationCoordinate2D(latitude: 36.3276544, - longitude: 127.427232), - thumbnailURL: URL(string: "https://i.ibb.co/qML8vdN/2023-11-25-9-08-01.png")!) + private func createMapAnnotation(post: Models.DisplayedPost) { + let annotation = LOAnnotation(coordinate: CLLocationCoordinate2D(latitude: post.latitude, + longitude: post.longitude), + thumbnailImageData: post.thumbnailImageData) mapView.addAnnotation(annotation) } @@ -191,8 +195,15 @@ final class MapViewController: BaseViewController { carouselDatasource.apply(snapshot) } + @objc private func searchButtonDidTap() { + searchButton.isHidden = true + interactor?.fetchPosts(latitude: mapView.centerCoordinate.latitude, + longitude: mapView.centerCoordinate.longitude) + } + @objc private func currentLocationButtonDidTap() { mapView.setUserTrackingMode(.follow, animated: true) + mapView.removeAnnotations(mapView.annotations) } @objc private func uploadButtonDidTap() { @@ -207,17 +218,19 @@ extension MapViewController: MKMapViewDelegate { func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? { guard !(annotation is MKUserLocation) else { return nil } - var annotationView: MKAnnotationView? - annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: LOAnnotationView.identifier, - for: annotation) - return annotationView + if let annotaion = annotation as? LOAnnotation { + let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: LOAnnotationView.identifier, + for: annotation) as? LOAnnotationView + annotationView?.setThumbnailImage(data: annotaion.thumbnailImageData) + return annotationView + } + return nil } func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) { guard let annotation = view.annotation, !(annotation is MKUserLocation) else { return } mapView.setCenter(annotation.coordinate, animated: true) animateAnnotationSelection(for: view, isSelected: true) - interactor?.fetchVideos() } func mapView(_ mapView: MKMapView, didDeselect view: MKAnnotationView) { @@ -225,6 +238,12 @@ extension MapViewController: MKMapViewDelegate { deleteCarouselDatasource() } + func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) { + if mapView.userTrackingMode != .follow { + searchButton.isHidden = false + } + } + } // MARK: - VideoPickerDelegate @@ -251,7 +270,17 @@ extension MapViewController: MapDisplayLogic { var snapshot: NSDiffableDataSourceSnapshot = NSDiffableDataSourceSnapshot() snapshot.appendSections([UUID()]) snapshot.appendItems(viewModel.videoDataSources) +// carouselDatasource.apply(snapshot) + } + + func dispalyFetchedPosts(viewModel: MapModels.FetchPosts.ViewModel) { + var snapshot: NSDiffableDataSourceSnapshot = NSDiffableDataSourceSnapshot() + snapshot.appendSections([UUID()]) + snapshot.appendItems(viewModel.displayedPosts) carouselDatasource.apply(snapshot) + + mapView.removeAnnotations(mapView.annotations) + viewModel.displayedPosts.forEach { createMapAnnotation(post: $0) } } func routeToPlayback() { diff --git a/iOS/Layover/Layover/Scenes/Map/MapWorker.swift b/iOS/Layover/Layover/Scenes/Map/MapWorker.swift new file mode 100644 index 0000000..3d4830e --- /dev/null +++ b/iOS/Layover/Layover/Scenes/Map/MapWorker.swift @@ -0,0 +1,50 @@ +// +// MapWorker.swift +// Layover +// +// Created by kong on 2023/12/06. +// Copyright © 2023 CodeBomber. All rights reserved. +// + +import Foundation + +protocol MapWorkerProtocol { + func fetchPosts(latitude: Double, longitude: Double) async -> [MapModels.Post]? +} + +final class MapWorker: MapWorkerProtocol { + + // MARK: - Properties + + typealias Models = MapModels + private let provider: ProviderType + private let postEndPointFactory: PostEndPointFactory + + // MARK: - Methods + + init(provider: ProviderType = Provider(), + postEndPointFactory: PostEndPointFactory = DefaultPostEndPointFactory()) { + self.provider = provider + self.postEndPointFactory = postEndPointFactory + } + + func fetchPosts(latitude: Double, longitude: Double) async -> [Models.Post]? { + let endPoint = postEndPointFactory.makeMapPostListEndPoint(latitude: latitude, longitude: longitude) + do { + let response = try await provider.request(with: endPoint) + guard var data = response.data else { return nil } + + let posts = try await data.asyncMap { postDTO -> Models.Post in + let thumnailImageData = try await provider.request(url: postDTO.board.videoThumbnailURL) + return .init(member: postDTO.member.toDomain(), + board: postDTO.board.toDomain(), + tag: postDTO.tag, + thumnailImageData: thumnailImageData) + } + return posts + } catch { + return nil + } + } + +} diff --git a/iOS/Layover/Layover/Scenes/Map/Views/LOAnnotationView.swift b/iOS/Layover/Layover/Scenes/Map/Views/LOAnnotationView.swift index 39dcd56..3af259b 100644 --- a/iOS/Layover/Layover/Scenes/Map/Views/LOAnnotationView.swift +++ b/iOS/Layover/Layover/Scenes/Map/Views/LOAnnotationView.swift @@ -51,7 +51,6 @@ final class LOAnnotationView: MKAnnotationView { // MARK: - View Lifecycle override func prepareForDisplay() { super.prepareForDisplay() - setThumbnailImage() } override func layoutSubviews() { @@ -61,8 +60,8 @@ final class LOAnnotationView: MKAnnotationView { // MARK: - Methods - func setThumbnailImage() { - self.thumbnailImageView.image = .checkmark + func setThumbnailImage(data: Data) { + self.thumbnailImageView.image = UIImage(data: data) } private func render() { From eea935dd6e747a2e93535b409903e67d8350ffa5 Mon Sep 17 00:00:00 2001 From: kong <1018dbrud@gmail.com> Date: Thu, 7 Dec 2023 15:09:01 +0900 Subject: [PATCH 02/12] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20fetchPosts=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Layover/Scenes/Map/MapInteractor.swift | 35 +++++++++++-------- .../Scenes/Map/MapViewController.swift | 8 ++--- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/iOS/Layover/Layover/Scenes/Map/MapInteractor.swift b/iOS/Layover/Layover/Scenes/Map/MapInteractor.swift index 1c5ebfa..dba7932 100644 --- a/iOS/Layover/Layover/Scenes/Map/MapInteractor.swift +++ b/iOS/Layover/Layover/Scenes/Map/MapInteractor.swift @@ -16,7 +16,10 @@ protocol MapBusinessLogic { func playPosts(with: MapModels.PlayPosts.Request) @discardableResult - func fetchPosts(latitude: Double, longitude: Double) -> Task<[MapModels.Post], Never> + func fetchPosts() -> Task<[MapModels.Post], Never> + + @discardableResult + func fetchPost(latitude: Double, longitude: Double) -> Task<[MapModels.Post], Never> func selectVideo(with request: MapModels.SelectVideo.Request) } @@ -38,8 +41,6 @@ final class MapInteractor: NSObject, MapBusinessLogic, MapDataStore { var worker: MapWorkerProtocol? private let locationManager = CLLocationManager() - private var latitude: Double? - private var longitude: Double? var postPlayStartIndex: Int? var posts: [Post]? @@ -79,10 +80,24 @@ final class MapInteractor: NSObject, MapBusinessLogic, MapDataStore { presenter?.presentPlaybackScene() } - func fetchPosts(latitude: Double, longitude: Double) -> Task<[MapModels.Post], Never> { + func fetchPosts() -> Task<[MapModels.Post], Never> { + Task { + locationManager.startUpdatingLocation() + guard let coordinate = locationManager.location?.coordinate else { return [] } + let posts = await worker?.fetchPosts(latitude: coordinate.latitude, + longitude: coordinate.longitude) + guard let posts else { return [] } + let response = Models.FetchPosts.Response(posts: posts) + await MainActor.run { + presenter?.presentFetchedPosts(with: response) + } + return posts + } + } + + func fetchPost(latitude: Double, longitude: Double) -> Task<[MapModels.Post], Never> { Task { - let posts = await worker?.fetchPosts(latitude: latitude, - longitude: longitude) + let posts = await worker?.fetchPosts(latitude: latitude, longitude: longitude) guard let posts else { return [] } let response = Models.FetchPosts.Response(posts: posts) await MainActor.run { @@ -114,12 +129,4 @@ extension MapInteractor: CLLocationManagerDelegate { func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) { checkCurrentLocationAuthorization(for: manager.authorizationStatus) } - - func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { - if let location = locations.first { - longitude = location.coordinate.longitude - latitude = location.coordinate.latitude - } - } - } diff --git a/iOS/Layover/Layover/Scenes/Map/MapViewController.swift b/iOS/Layover/Layover/Scenes/Map/MapViewController.swift index 9d21d2f..4e5b5f8 100644 --- a/iOS/Layover/Layover/Scenes/Map/MapViewController.swift +++ b/iOS/Layover/Layover/Scenes/Map/MapViewController.swift @@ -96,9 +96,7 @@ final class MapViewController: BaseViewController { override func viewDidLoad() { super.viewDidLoad() interactor?.checkLocationAuthorizationStatus() - print(mapView.centerCoordinate) - interactor?.fetchPosts(latitude: mapView.centerCoordinate.latitude, - longitude: mapView.centerCoordinate.longitude) + interactor?.fetchPosts() setCollectionViewDataSource() setDelegation() } @@ -197,8 +195,8 @@ final class MapViewController: BaseViewController { @objc private func searchButtonDidTap() { searchButton.isHidden = true - interactor?.fetchPosts(latitude: mapView.centerCoordinate.latitude, - longitude: mapView.centerCoordinate.longitude) + interactor?.fetchPost(latitude: mapView.centerCoordinate.latitude, + longitude: mapView.centerCoordinate.longitude) } @objc private func currentLocationButtonDidTap() { From 6653a8ff68275b6ba01d8c68e3c57e6024c7f0a0 Mon Sep 17 00:00:00 2001 From: kong <1018dbrud@gmail.com> Date: Thu, 7 Dec 2023 20:49:36 +0900 Subject: [PATCH 03/12] =?UTF-8?q?=E2=9C=A8=20=EC=8A=A4=ED=81=AC=EB=A1=A4?= =?UTF-8?q?=EA=B3=BC=20=EB=A7=B5=20=ED=95=80=20=ED=8F=AC=EC=BB=A4=EC=8B=B1?= =?UTF-8?q?=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Layover/Scenes/Map/LOAnnotation.swift | 8 +++- .../Layover/Scenes/Map/MapModels.swift | 1 + .../Layover/Scenes/Map/MapPresenter.swift | 3 +- .../Scenes/Map/MapViewController.swift | 37 +++++++++++++++---- 4 files changed, 40 insertions(+), 9 deletions(-) diff --git a/iOS/Layover/Layover/Scenes/Map/LOAnnotation.swift b/iOS/Layover/Layover/Scenes/Map/LOAnnotation.swift index 31cef2a..8129a5b 100644 --- a/iOS/Layover/Layover/Scenes/Map/LOAnnotation.swift +++ b/iOS/Layover/Layover/Scenes/Map/LOAnnotation.swift @@ -9,11 +9,17 @@ import MapKit final class LOAnnotation: NSObject, MKAnnotation { + @objc dynamic var coordinate: CLLocationCoordinate2D + let boardID: Int let thumbnailImageData: Data - init(coordinate: CLLocationCoordinate2D, thumbnailImageData: Data) { + init(coordinate: CLLocationCoordinate2D, + boardID: Int, + thumbnailImageData: Data) { self.coordinate = coordinate + self.boardID = boardID self.thumbnailImageData = thumbnailImageData } + } diff --git a/iOS/Layover/Layover/Scenes/Map/MapModels.swift b/iOS/Layover/Layover/Scenes/Map/MapModels.swift index e7f0613..2bb1bfd 100644 --- a/iOS/Layover/Layover/Scenes/Map/MapModels.swift +++ b/iOS/Layover/Layover/Scenes/Map/MapModels.swift @@ -18,6 +18,7 @@ enum MapModels { } struct DisplayedPost: Hashable { + let boardID: Int let thumbnailImageData: Data let videoURL: URL let latitude: Double diff --git a/iOS/Layover/Layover/Scenes/Map/MapPresenter.swift b/iOS/Layover/Layover/Scenes/Map/MapPresenter.swift index b16eda1..c52d10b 100644 --- a/iOS/Layover/Layover/Scenes/Map/MapPresenter.swift +++ b/iOS/Layover/Layover/Scenes/Map/MapPresenter.swift @@ -40,7 +40,8 @@ final class MapPresenter: MapPresentationLogic { let displayedPost = response.posts .map { post -> Models.DisplayedPost? in if let videoURL = post.board.videoURL { - return .init(thumbnailImageData: post.thumnailImageData, + return .init(boardID: post.board.identifier, + thumbnailImageData: post.thumnailImageData, videoURL: videoURL, latitude: post.board.latitude, longitude: post.board.longitude) diff --git a/iOS/Layover/Layover/Scenes/Map/MapViewController.swift b/iOS/Layover/Layover/Scenes/Map/MapViewController.swift index 4e5b5f8..a0616c5 100644 --- a/iOS/Layover/Layover/Scenes/Map/MapViewController.swift +++ b/iOS/Layover/Layover/Scenes/Map/MapViewController.swift @@ -80,6 +80,7 @@ final class MapViewController: BaseViewController { private let videoPickerManager: VideoPickerManager = VideoPickerManager() private lazy var carouselCollectionViewHeight: NSLayoutConstraint = carouselCollectionView.heightAnchor.constraint(equalToConstant: 0) + private var displayedPost: [Models.DisplayedPost] = [] // MARK: - Life Cycle @@ -157,13 +158,14 @@ final class MapViewController: BaseViewController { trailing: inset) section.visibleItemsInvalidationHandler = { (items, offset, environment) in let containerWidth = environment.container.contentSize.width - items.forEach { item in + items.forEach { [weak self] item in let distanceFromCenter = abs((item.center.x - offset.x) - environment.container.contentSize.width / 2.0) let scale = max(maximumZoomScale - (distanceFromCenter / containerWidth), minumumZoomScale) item.transform = CGAffineTransform(scaleX: scale, y: scale) - let cell = self.carouselCollectionView.cellForItem(at: item.indexPath) as? MapCarouselCollectionViewCell + let cell = self?.carouselCollectionView.cellForItem(at: item.indexPath) as? MapCarouselCollectionViewCell if scale >= maximumZoomScale * 0.9 { cell?.loopingPlayerView.play() + self?.selectAnnotation(at: item.indexPath) } else { cell?.loopingPlayerView.pause() } @@ -175,6 +177,7 @@ final class MapViewController: BaseViewController { private func createMapAnnotation(post: Models.DisplayedPost) { let annotation = LOAnnotation(coordinate: CLLocationCoordinate2D(latitude: post.latitude, longitude: post.longitude), + boardID: post.boardID, thumbnailImageData: post.thumbnailImageData) mapView.addAnnotation(annotation) } @@ -187,10 +190,15 @@ final class MapViewController: BaseViewController { } } - private func deleteCarouselDatasource() { - var snapshot: NSDiffableDataSourceSnapshot = carouselDatasource.snapshot() - snapshot.deleteAllItems() - carouselDatasource.apply(snapshot) + private func selectAnnotation(at indexPath: IndexPath) { + let focusedPost = displayedPost[indexPath.row] + let focusedAnnotaion = mapView.annotations + .map { $0 as? LOAnnotation } + .compactMap { $0 } + .filter { $0.boardID == focusedPost.boardID } + .first + guard let focusedAnnotaion else { return } + mapView.selectAnnotation(focusedAnnotaion, animated: true) } @objc private func searchButtonDidTap() { @@ -229,11 +237,24 @@ extension MapViewController: MKMapViewDelegate { guard let annotation = view.annotation, !(annotation is MKUserLocation) else { return } mapView.setCenter(annotation.coordinate, animated: true) animateAnnotationSelection(for: view, isSelected: true) + + if let annotaion = annotation as? LOAnnotation { + // 선택된 pin 정보와 datasource를 비교해 selected item을 찾음 + let snapshot = carouselDatasource.snapshot() + guard let selectedItemIdentifiers = carouselDatasource.snapshot().itemIdentifiers.filter({ post in + return post.boardID == annotaion.boardID + }).first else { return } + guard let section = snapshot.sectionIdentifier(containingItem: selectedItemIdentifiers), + let itemIndex = snapshot.indexOfItem(selectedItemIdentifiers), + let sectionIndex = snapshot.indexOfSection(section) else { return } + carouselCollectionView.scrollToItem(at: IndexPath(item: itemIndex, section: sectionIndex), + at: .centeredHorizontally, + animated: true) + } } func mapView(_ mapView: MKMapView, didDeselect view: MKAnnotationView) { animateAnnotationSelection(for: view, isSelected: false) - deleteCarouselDatasource() } func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) { @@ -272,6 +293,8 @@ extension MapViewController: MapDisplayLogic { } func dispalyFetchedPosts(viewModel: MapModels.FetchPosts.ViewModel) { + displayedPost = viewModel.displayedPosts + var snapshot: NSDiffableDataSourceSnapshot = NSDiffableDataSourceSnapshot() snapshot.appendSections([UUID()]) snapshot.appendItems(viewModel.displayedPosts) From 734d7a2dcddbc8839b1f46301519fb6580f72ab1 Mon Sep 17 00:00:00 2001 From: kong <1018dbrud@gmail.com> Date: Fri, 8 Dec 2023 00:39:51 +0900 Subject: [PATCH 04/12] =?UTF-8?q?=F0=9F=90=9B=20=EC=96=B4=EB=85=B8?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EC=85=98=EC=9D=B4=20deselect=EB=90=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EB=B2=84=EA=B7=B8=20=ED=94=BD?= =?UTF-8?q?=EC=8A=A4,=20=EC=8D=B8=EB=84=A4=EC=9D=BC=20concurrent=20downloa?= =?UTF-8?q?d=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/Layover/Layover/Scenes/Map/MapViewController.swift | 1 + iOS/Layover/Layover/Scenes/Map/MapWorker.swift | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/iOS/Layover/Layover/Scenes/Map/MapViewController.swift b/iOS/Layover/Layover/Scenes/Map/MapViewController.swift index a0616c5..aaa1d7c 100644 --- a/iOS/Layover/Layover/Scenes/Map/MapViewController.swift +++ b/iOS/Layover/Layover/Scenes/Map/MapViewController.swift @@ -198,6 +198,7 @@ final class MapViewController: BaseViewController { .filter { $0.boardID == focusedPost.boardID } .first guard let focusedAnnotaion else { return } + mapView.setCenter(focusedAnnotaion.coordinate, animated: true) mapView.selectAnnotation(focusedAnnotaion, animated: true) } diff --git a/iOS/Layover/Layover/Scenes/Map/MapWorker.swift b/iOS/Layover/Layover/Scenes/Map/MapWorker.swift index 3d4830e..61712fa 100644 --- a/iOS/Layover/Layover/Scenes/Map/MapWorker.swift +++ b/iOS/Layover/Layover/Scenes/Map/MapWorker.swift @@ -32,10 +32,9 @@ final class MapWorker: MapWorkerProtocol { let endPoint = postEndPointFactory.makeMapPostListEndPoint(latitude: latitude, longitude: longitude) do { let response = try await provider.request(with: endPoint) - guard var data = response.data else { return nil } - - let posts = try await data.asyncMap { postDTO -> Models.Post in - let thumnailImageData = try await provider.request(url: postDTO.board.videoThumbnailURL) + guard let data = response.data else { return nil } + let posts = try await data.concurrentMap { postDTO -> Models.Post in + let thumnailImageData = try await self.provider.request(url: postDTO.board.videoThumbnailURL) return .init(member: postDTO.member.toDomain(), board: postDTO.board.toDomain(), tag: postDTO.tag, From b07d399856a21201efed7529362ec3ba3267de6e Mon Sep 17 00:00:00 2001 From: kong <1018dbrud@gmail.com> Date: Fri, 8 Dec 2023 03:01:51 +0900 Subject: [PATCH 05/12] =?UTF-8?q?=E2=9C=A8=20=EC=85=80=EC=97=90=20?= =?UTF-8?q?=EC=8D=B8=EB=84=A4=EC=9D=BC=20=EB=B7=B0=20=EC=B6=94=EA=B0=80,?= =?UTF-8?q?=20=EC=9E=AC=EC=83=9D=ED=99=94=EB=A9=B4=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Layover/Scenes/Map/MapInteractor.swift | 25 +------------ .../Layover/Scenes/Map/MapModels.swift | 17 --------- .../Layover/Scenes/Map/MapPresenter.swift | 6 --- .../Layover/Scenes/Map/MapRouter.swift | 2 +- .../Scenes/Map/MapViewController.swift | 16 ++------ .../Views/MapCarouselCollectionViewCell.swift | 37 ++++++++++++++++--- 6 files changed, 39 insertions(+), 64 deletions(-) diff --git a/iOS/Layover/Layover/Scenes/Map/MapInteractor.swift b/iOS/Layover/Layover/Scenes/Map/MapInteractor.swift index dba7932..abf790d 100644 --- a/iOS/Layover/Layover/Scenes/Map/MapInteractor.swift +++ b/iOS/Layover/Layover/Scenes/Map/MapInteractor.swift @@ -11,8 +11,6 @@ import Foundation protocol MapBusinessLogic { func checkLocationAuthorizationStatus() - func fetchVideos() - func moveToPlaybackScene(with: MapModels.MoveToPlaybackScene.Request) func playPosts(with: MapModels.PlayPosts.Request) @discardableResult @@ -26,8 +24,6 @@ protocol MapBusinessLogic { protocol MapDataStore { var postPlayStartIndex: Int? { get set } var posts: [Post]? { get set } -// var posts: [MapModels.Post]? { get set } - var index: Int? { get set } var selectedVideoURL: URL? { get set } } @@ -44,7 +40,6 @@ final class MapInteractor: NSObject, MapBusinessLogic, MapDataStore { var postPlayStartIndex: Int? var posts: [Post]? -// var posts: [Models.Post]? var index: Int? var selectedVideoURL: URL? @@ -57,24 +52,6 @@ final class MapInteractor: NSObject, MapBusinessLogic, MapDataStore { checkCurrentLocationAuthorization(for: locationManager.authorizationStatus) } - func fetchVideos() { - // TODO: worker 네트워크 호출 - let dummyURLs: [URL] = ["http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8", - "https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8", - "http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8", - "https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8", - "https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8", - "https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8"] - .compactMap { URL(string: $0) } - presenter?.presentFetchedVideos(with: MapModels.FetchVideo.Response(videoURLs: dummyURLs)) - } - - func moveToPlaybackScene(with request: Models.MoveToPlaybackScene.Request) { - posts = request.videos.map { .init(member: $0.member, board: $0.board, tag: $0.tag) } - index = request.index - presenter?.presentPlaybackScene() - } - func playPosts(with request: MapModels.PlayPosts.Request) { postPlayStartIndex = request.selectedIndex presenter?.presentPlaybackScene() @@ -87,6 +64,7 @@ final class MapInteractor: NSObject, MapBusinessLogic, MapDataStore { let posts = await worker?.fetchPosts(latitude: coordinate.latitude, longitude: coordinate.longitude) guard let posts else { return [] } + self.posts = posts.map { .init(member: $0.member, board: $0.board, tag: $0.tag) } let response = Models.FetchPosts.Response(posts: posts) await MainActor.run { presenter?.presentFetchedPosts(with: response) @@ -99,6 +77,7 @@ final class MapInteractor: NSObject, MapBusinessLogic, MapDataStore { Task { let posts = await worker?.fetchPosts(latitude: latitude, longitude: longitude) guard let posts else { return [] } + self.posts = posts.map { .init(member: $0.member, board: $0.board, tag: $0.tag) } let response = Models.FetchPosts.Response(posts: posts) await MainActor.run { presenter?.presentFetchedPosts(with: response) diff --git a/iOS/Layover/Layover/Scenes/Map/MapModels.swift b/iOS/Layover/Layover/Scenes/Map/MapModels.swift index 2bb1bfd..6f757d5 100644 --- a/iOS/Layover/Layover/Scenes/Map/MapModels.swift +++ b/iOS/Layover/Layover/Scenes/Map/MapModels.swift @@ -68,23 +68,6 @@ enum MapModels { // MARK: - Move To Playback Scene - enum MoveToPlaybackScene { - struct Request { - let index: Int - let videos: [Post] - } - - struct Response { - let index: Int - let videos: [Post] - } - - struct ViewModel { - let index: Int - let videos: [Post] - } - } - enum PlayPosts { struct Request { let selectedIndex: Int diff --git a/iOS/Layover/Layover/Scenes/Map/MapPresenter.swift b/iOS/Layover/Layover/Scenes/Map/MapPresenter.swift index c52d10b..8981119 100644 --- a/iOS/Layover/Layover/Scenes/Map/MapPresenter.swift +++ b/iOS/Layover/Layover/Scenes/Map/MapPresenter.swift @@ -11,7 +11,6 @@ import Foundation protocol MapPresentationLogic { func presentCurrentLocation() func presentDefaultLocation() - func presentFetchedVideos(with response: MapModels.FetchVideo.Response) func presentFetchedPosts(with response: MapModels.FetchPosts.Response) func presentPlaybackScene() } @@ -31,11 +30,6 @@ final class MapPresenter: MapPresentationLogic { // TODO: 위치 관련 기능 사용 불가, 디폴트 위치로 이동 } - func presentFetchedVideos(with response: MapModels.FetchVideo.Response) { - let viewModel = MapModels.FetchVideo.ViewModel(videoDataSources: response.videoURLs.map { .init(videoURL: $0) }) - viewController?.displayFetchedVideos(viewModel: viewModel) - } - func presentFetchedPosts(with response: MapModels.FetchPosts.Response) { let displayedPost = response.posts .map { post -> Models.DisplayedPost? in diff --git a/iOS/Layover/Layover/Scenes/Map/MapRouter.swift b/iOS/Layover/Layover/Scenes/Map/MapRouter.swift index 949d6c8..eb1319a 100644 --- a/iOS/Layover/Layover/Scenes/Map/MapRouter.swift +++ b/iOS/Layover/Layover/Scenes/Map/MapRouter.swift @@ -32,7 +32,7 @@ final class MapRouter: MapRoutingLogic, MapDataPassing { let destination = playbackViewController.router?.dataStore else { return } destination.parentView = .other - destination.index = source.index + destination.index = source.postPlayStartIndex destination.posts = source.posts viewController?.navigationController?.pushViewController(playbackViewController, animated: true) } diff --git a/iOS/Layover/Layover/Scenes/Map/MapViewController.swift b/iOS/Layover/Layover/Scenes/Map/MapViewController.swift index aaa1d7c..acd91d7 100644 --- a/iOS/Layover/Layover/Scenes/Map/MapViewController.swift +++ b/iOS/Layover/Layover/Scenes/Map/MapViewController.swift @@ -10,7 +10,6 @@ import MapKit import UIKit protocol MapDisplayLogic: AnyObject { - func displayFetchedVideos(viewModel: MapModels.FetchVideo.ViewModel) func dispalyFetchedPosts(viewModel: MapModels.FetchPosts.ViewModel) func routeToPlayback() } @@ -67,7 +66,8 @@ final class MapViewController: BaseViewController { private lazy var carouselDatasource = UICollectionViewDiffableDataSource(collectionView: carouselCollectionView) { collectionView, indexPath, item in guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MapCarouselCollectionViewCell.identifier, for: indexPath) as? MapCarouselCollectionViewCell else { return UICollectionViewCell() } - cell.configure(url: item.videoURL) + cell.setVideo(url: item.videoURL) + cell.configure(thumbnailImageData: item.thumbnailImageData) return cell } @@ -164,10 +164,10 @@ final class MapViewController: BaseViewController { item.transform = CGAffineTransform(scaleX: scale, y: scale) let cell = self?.carouselCollectionView.cellForItem(at: item.indexPath) as? MapCarouselCollectionViewCell if scale >= maximumZoomScale * 0.9 { - cell?.loopingPlayerView.play() + cell?.play() self?.selectAnnotation(at: item.indexPath) } else { - cell?.loopingPlayerView.pause() + cell?.pause() } } } @@ -199,7 +199,6 @@ final class MapViewController: BaseViewController { .first guard let focusedAnnotaion else { return } mapView.setCenter(focusedAnnotaion.coordinate, animated: true) - mapView.selectAnnotation(focusedAnnotaion, animated: true) } @objc private func searchButtonDidTap() { @@ -286,13 +285,6 @@ extension MapViewController: VideoPickerDelegate { extension MapViewController: MapDisplayLogic { - func displayFetchedVideos(viewModel: ViewModel) { - var snapshot: NSDiffableDataSourceSnapshot = NSDiffableDataSourceSnapshot() - snapshot.appendSections([UUID()]) - snapshot.appendItems(viewModel.videoDataSources) -// carouselDatasource.apply(snapshot) - } - func dispalyFetchedPosts(viewModel: MapModels.FetchPosts.ViewModel) { displayedPost = viewModel.displayedPosts diff --git a/iOS/Layover/Layover/Scenes/Map/Views/MapCarouselCollectionViewCell.swift b/iOS/Layover/Layover/Scenes/Map/Views/MapCarouselCollectionViewCell.swift index 8dd963f..bc388ba 100644 --- a/iOS/Layover/Layover/Scenes/Map/Views/MapCarouselCollectionViewCell.swift +++ b/iOS/Layover/Layover/Scenes/Map/Views/MapCarouselCollectionViewCell.swift @@ -11,7 +11,13 @@ import AVFoundation final class MapCarouselCollectionViewCell: UICollectionViewCell { - private(set) var loopingPlayerView = LoopingPlayerView() + private let loopingPlayerView = LoopingPlayerView() + + private let thumbnailImageView: UIImageView = { + let imageView: UIImageView = UIImageView() + imageView.contentMode = .scaleAspectFill + return imageView + }() override init(frame: CGRect) { super.init(frame: frame) @@ -25,21 +31,42 @@ final class MapCarouselCollectionViewCell: UICollectionViewCell { render() } - func configure(url: URL) { + func setVideo(url: URL) { + loopingPlayerView.disable() loopingPlayerView.prepareVideo(with: url, timeRange: CMTimeRange(start: .zero, duration: CMTime(value: 1800, timescale: 600))) loopingPlayerView.player?.isMuted = true } + func configure(thumbnailImageData: Data) { + thumbnailImageView.image = UIImage(data: thumbnailImageData) + } + + func play() { + loopingPlayerView.play() + thumbnailImageView.isHidden = true + } + + func pause() { + loopingPlayerView.pause() + thumbnailImageView.isHidden = false + } + + private func setUI() { backgroundColor = .background - contentView.addSubview(loopingPlayerView) - loopingPlayerView.translatesAutoresizingMaskIntoConstraints = false + contentView.addSubviews(loopingPlayerView, thumbnailImageView) + contentView.subviews.forEach { $0.translatesAutoresizingMaskIntoConstraints = false } NSLayoutConstraint.activate([ loopingPlayerView.topAnchor.constraint(equalTo: contentView.topAnchor), loopingPlayerView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), loopingPlayerView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), - loopingPlayerView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor) + loopingPlayerView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), + + thumbnailImageView.topAnchor.constraint(equalTo: contentView.topAnchor), + thumbnailImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), + thumbnailImageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), + thumbnailImageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor) ]) } From 1e9a48ccee66b28296cdb25019cc3e185d57cffd Mon Sep 17 00:00:00 2001 From: kong <1018dbrud@gmail.com> Date: Fri, 8 Dec 2023 03:14:40 +0900 Subject: [PATCH 06/12] =?UTF-8?q?=F0=9F=94=A5=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=EC=BD=94=EB=93=9C=20=EB=B0=8F=20=EC=A3=BC?= =?UTF-8?q?=EC=84=9D=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Layover/Scenes/Map/MapModels.swift | 24 ------------------- .../Scenes/Map/MapViewController.swift | 1 - 2 files changed, 25 deletions(-) diff --git a/iOS/Layover/Layover/Scenes/Map/MapModels.swift b/iOS/Layover/Layover/Scenes/Map/MapModels.swift index 6f757d5..eff5605 100644 --- a/iOS/Layover/Layover/Scenes/Map/MapModels.swift +++ b/iOS/Layover/Layover/Scenes/Map/MapModels.swift @@ -26,25 +26,6 @@ enum MapModels { } // MARK: - Fetch Video Use Cases - enum FetchVideo { - struct Request { - var latitude: Double - var longitude: Double - } - - struct Response { - var videoURLs: [URL] - } - - struct ViewModel { - var videoDataSources: [VideoDataSource] - - struct VideoDataSource: Hashable { - var id = UUID() - var videoURL: URL - } - } - } enum FetchPosts { struct Request { @@ -58,11 +39,6 @@ enum MapModels { struct ViewModel { var displayedPosts: [DisplayedPost] - -// struct VideoDataSource: Hashable { -// var id = UUID() -// var videoURL: URL -// } } } diff --git a/iOS/Layover/Layover/Scenes/Map/MapViewController.swift b/iOS/Layover/Layover/Scenes/Map/MapViewController.swift index acd91d7..294ffad 100644 --- a/iOS/Layover/Layover/Scenes/Map/MapViewController.swift +++ b/iOS/Layover/Layover/Scenes/Map/MapViewController.swift @@ -74,7 +74,6 @@ final class MapViewController: BaseViewController { // MARK: - Properties typealias Models = MapModels - typealias ViewModel = Models.FetchVideo.ViewModel var interactor: MapBusinessLogic? var router: (MapRoutingLogic & MapDataPassing)? From c98c037e49e1297124ac380a147e3a5428bf778b Mon Sep 17 00:00:00 2001 From: chopmozzi <44396392+chopmozzi@users.noreply.github.com> Date: Fri, 8 Dec 2023 03:50:51 +0900 Subject: [PATCH 07/12] =?UTF-8?q?:wrench:=20playback=20scene=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Layover/Scenes/Playback/PlaybackInteractor.swift | 9 +++++---- .../Layover/Scenes/Playback/PlaybackPresenter.swift | 6 +++--- .../Layover/Scenes/Playback/PlaybackViewController.swift | 6 +++--- iOS/Layover/Layover/Scenes/Playback/PlaybackWorker.swift | 5 ++--- .../Layover/Workers/Mocks/MockPlaybackWorker.swift | 5 ++--- 5 files changed, 15 insertions(+), 16 deletions(-) diff --git a/iOS/Layover/Layover/Scenes/Playback/PlaybackInteractor.swift b/iOS/Layover/Layover/Scenes/Playback/PlaybackInteractor.swift index 7a4e746..1c33b88 100644 --- a/iOS/Layover/Layover/Scenes/Playback/PlaybackInteractor.swift +++ b/iOS/Layover/Layover/Scenes/Playback/PlaybackInteractor.swift @@ -71,13 +71,14 @@ final class PlaybackInteractor: PlaybackBusinessLogic, PlaybackDataStore { } func moveInitialPlaybackCell() { - let response: Models.SetInitialPlaybackCell.Response = Models.SetInitialPlaybackCell.Response(indexPathRow: index ?? 0) - guard let parentView else { return } + guard let parentView, + let index + else { return } switch parentView { case .home, .myProfile: - presenter?.presentMoveInitialPlaybackCell(with: response) + presenter?.presentMoveInitialPlaybackCell(with: Models.SetInitialPlaybackCell.Response(indexPathRow: index)) case .other: - presenter?.presentSetCellIfInfinite() + presenter?.presentSetCellIfInfinite(with: Models.SetInitialPlaybackCell.Response(indexPathRow: index + 1)) } } diff --git a/iOS/Layover/Layover/Scenes/Playback/PlaybackPresenter.swift b/iOS/Layover/Layover/Scenes/Playback/PlaybackPresenter.swift index e5dd71b..800816c 100644 --- a/iOS/Layover/Layover/Scenes/Playback/PlaybackPresenter.swift +++ b/iOS/Layover/Layover/Scenes/Playback/PlaybackPresenter.swift @@ -10,7 +10,7 @@ import UIKit protocol PlaybackPresentationLogic { func presentVideoList(with response: PlaybackModels.LoadPlaybackVideoList.Response) - func presentSetCellIfInfinite() + func presentSetCellIfInfinite(with response: PlaybackModels.SetInitialPlaybackCell.Response) func presentMoveCellNext(with response: PlaybackModels.DisplayPlaybackVideo.Response) func presentSetInitialPlaybackCell(with response: PlaybackModels.SetInitialPlaybackCell.Response) func presentMoveInitialPlaybackCell(with response: PlaybackModels.SetInitialPlaybackCell.Response) @@ -54,8 +54,8 @@ final class PlaybackPresenter: PlaybackPresentationLogic { viewController?.displayVideoList(viewModel: viewModel) } - func presentSetCellIfInfinite() { - viewController?.displayMoveCellIfinfinite() + func presentSetCellIfInfinite(with response: PlaybackModels.SetInitialPlaybackCell.Response) { + viewController?.displayMoveCellIfinfinite(viewModel: Models.SetInitialPlaybackCell.ViewModel(indexPathRow: response.indexPathRow)) } // MARK: - UseCase Set Init Playback Scene diff --git a/iOS/Layover/Layover/Scenes/Playback/PlaybackViewController.swift b/iOS/Layover/Layover/Scenes/Playback/PlaybackViewController.swift index 38e7a43..3237fb1 100644 --- a/iOS/Layover/Layover/Scenes/Playback/PlaybackViewController.swift +++ b/iOS/Layover/Layover/Scenes/Playback/PlaybackViewController.swift @@ -13,7 +13,7 @@ import OSLog protocol PlaybackDisplayLogic: AnyObject { func displayVideoList(viewModel: PlaybackModels.LoadPlaybackVideoList.ViewModel) - func displayMoveCellIfinfinite() + func displayMoveCellIfinfinite(viewModel: PlaybackModels.SetInitialPlaybackCell.ViewModel) func stopPrevPlayerAndPlayCurPlayer(viewModel: PlaybackModels.DisplayPlaybackVideo.ViewModel) func setInitialPlaybackCell(viewModel: PlaybackModels.SetInitialPlaybackCell.ViewModel) func moveInitialPlaybackCell(viewModel: PlaybackModels.SetInitialPlaybackCell.ViewModel) @@ -179,8 +179,8 @@ extension PlaybackViewController: PlaybackDisplayLogic { dataSource?.apply(snapshot, animatingDifferences: false) } - func displayMoveCellIfinfinite() { - playbackCollectionView.setContentOffset(.init(x: playbackCollectionView.contentOffset.x, y: playbackCollectionView.bounds.height), animated: false) + func displayMoveCellIfinfinite(viewModel: Models.SetInitialPlaybackCell.ViewModel) { + playbackCollectionView.setContentOffset(.init(x: playbackCollectionView.contentOffset.x, y: playbackCollectionView.bounds.height * CGFloat(viewModel.indexPathRow)), animated: false) } func stopPrevPlayerAndPlayCurPlayer(viewModel: PlaybackModels.DisplayPlaybackVideo.ViewModel) { diff --git a/iOS/Layover/Layover/Scenes/Playback/PlaybackWorker.swift b/iOS/Layover/Layover/Scenes/Playback/PlaybackWorker.swift index 3c52051..110342d 100644 --- a/iOS/Layover/Layover/Scenes/Playback/PlaybackWorker.swift +++ b/iOS/Layover/Layover/Scenes/Playback/PlaybackWorker.swift @@ -32,9 +32,8 @@ final class PlaybackWorker: PlaybackWorkerProtocol { func makeInfiniteScroll(posts: [Post]) -> [Post] { var tempVideos: [Post] = posts - let tempFirstCellIndex: Int = posts.count == 1 ? 1 : 0 - let tempLastVideo: Post = posts[tempFirstCellIndex] - let tempFirstVideo: Post = posts[1] + let tempLastVideo: Post = posts[posts.count - 1] + let tempFirstVideo: Post = posts[0] tempVideos.insert(tempLastVideo, at: 0) tempVideos.append(tempFirstVideo) return tempVideos diff --git a/iOS/Layover/Layover/Workers/Mocks/MockPlaybackWorker.swift b/iOS/Layover/Layover/Workers/Mocks/MockPlaybackWorker.swift index c44d931..f144b53 100644 --- a/iOS/Layover/Layover/Workers/Mocks/MockPlaybackWorker.swift +++ b/iOS/Layover/Layover/Workers/Mocks/MockPlaybackWorker.swift @@ -25,9 +25,8 @@ final class MockPlaybackWorker: PlaybackWorkerProtocol { func makeInfiniteScroll(posts: [Post]) -> [Post] { var tempVideos: [Post] = posts - let tempFirstCellIndex: Int = posts.count == 1 ? 1 : 0 - let tempLastVideo: Post = posts[tempFirstCellIndex] - let tempFirstVideo: Post = posts[1] + let tempLastVideo: Post = posts[posts.count - 1] + let tempFirstVideo: Post = posts[0] tempVideos.insert(tempLastVideo, at: 0) tempVideos.append(tempFirstVideo) return tempVideos From 7153470238380ee24bc1c73b4ae55d6efdecb15a Mon Sep 17 00:00:00 2001 From: kong <1018dbrud@gmail.com> Date: Fri, 8 Dec 2023 04:39:33 +0900 Subject: [PATCH 08/12] =?UTF-8?q?=F0=9F=94=A7=20=EC=98=A4=ED=83=88?= =?UTF-8?q?=EC=9E=90=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/Layover/Layover/Scenes/Map/MapInteractor.swift | 4 ++-- iOS/Layover/Layover/Scenes/Map/MapModels.swift | 4 ++-- iOS/Layover/Layover/Scenes/Map/MapPresenter.swift | 2 +- iOS/Layover/Layover/Scenes/Map/MapWorker.swift | 6 +++--- .../Layover/Scenes/UploadPost/UploadPostInteractor.swift | 2 +- .../Layover/Scenes/UploadPost/UploadPostModels.swift | 4 ++-- .../Layover/Scenes/UploadPost/UploadPostPresenter.swift | 8 ++++---- .../Scenes/UploadPost/UploadPostViewController.swift | 2 +- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/iOS/Layover/Layover/Scenes/Map/MapInteractor.swift b/iOS/Layover/Layover/Scenes/Map/MapInteractor.swift index abf790d..6ce08b1 100644 --- a/iOS/Layover/Layover/Scenes/Map/MapInteractor.swift +++ b/iOS/Layover/Layover/Scenes/Map/MapInteractor.swift @@ -64,7 +64,7 @@ final class MapInteractor: NSObject, MapBusinessLogic, MapDataStore { let posts = await worker?.fetchPosts(latitude: coordinate.latitude, longitude: coordinate.longitude) guard let posts else { return [] } - self.posts = posts.map { .init(member: $0.member, board: $0.board, tag: $0.tag) } + self.posts = posts.map { .init(member: $0.member, board: $0.board, tag: $0.tags) } let response = Models.FetchPosts.Response(posts: posts) await MainActor.run { presenter?.presentFetchedPosts(with: response) @@ -77,7 +77,7 @@ final class MapInteractor: NSObject, MapBusinessLogic, MapDataStore { Task { let posts = await worker?.fetchPosts(latitude: latitude, longitude: longitude) guard let posts else { return [] } - self.posts = posts.map { .init(member: $0.member, board: $0.board, tag: $0.tag) } + self.posts = posts.map { .init(member: $0.member, board: $0.board, tag: $0.tags) } let response = Models.FetchPosts.Response(posts: posts) await MainActor.run { presenter?.presentFetchedPosts(with: response) diff --git a/iOS/Layover/Layover/Scenes/Map/MapModels.swift b/iOS/Layover/Layover/Scenes/Map/MapModels.swift index eff5605..5b52c1c 100644 --- a/iOS/Layover/Layover/Scenes/Map/MapModels.swift +++ b/iOS/Layover/Layover/Scenes/Map/MapModels.swift @@ -13,8 +13,8 @@ enum MapModels { struct Post { let member: Member let board: Board - let tag: [String] - let thumnailImageData: Data + let tags: [String] + let thumbnailImageData: Data } struct DisplayedPost: Hashable { diff --git a/iOS/Layover/Layover/Scenes/Map/MapPresenter.swift b/iOS/Layover/Layover/Scenes/Map/MapPresenter.swift index 8981119..3f1b77b 100644 --- a/iOS/Layover/Layover/Scenes/Map/MapPresenter.swift +++ b/iOS/Layover/Layover/Scenes/Map/MapPresenter.swift @@ -35,7 +35,7 @@ final class MapPresenter: MapPresentationLogic { .map { post -> Models.DisplayedPost? in if let videoURL = post.board.videoURL { return .init(boardID: post.board.identifier, - thumbnailImageData: post.thumnailImageData, + thumbnailImageData: post.thumbnailImageData, videoURL: videoURL, latitude: post.board.latitude, longitude: post.board.longitude) diff --git a/iOS/Layover/Layover/Scenes/Map/MapWorker.swift b/iOS/Layover/Layover/Scenes/Map/MapWorker.swift index 61712fa..dd13c13 100644 --- a/iOS/Layover/Layover/Scenes/Map/MapWorker.swift +++ b/iOS/Layover/Layover/Scenes/Map/MapWorker.swift @@ -34,11 +34,11 @@ final class MapWorker: MapWorkerProtocol { let response = try await provider.request(with: endPoint) guard let data = response.data else { return nil } let posts = try await data.concurrentMap { postDTO -> Models.Post in - let thumnailImageData = try await self.provider.request(url: postDTO.board.videoThumbnailURL) + let thumbnailImageData = try await self.provider.request(url: postDTO.board.videoThumbnailURL) return .init(member: postDTO.member.toDomain(), board: postDTO.board.toDomain(), - tag: postDTO.tag, - thumnailImageData: thumnailImageData) + tags: postDTO.tag, + thumbnailImageData: thumbnailImageData) } return posts } catch { diff --git a/iOS/Layover/Layover/Scenes/UploadPost/UploadPostInteractor.swift b/iOS/Layover/Layover/Scenes/UploadPost/UploadPostInteractor.swift index b3d7fdf..8b98f67 100644 --- a/iOS/Layover/Layover/Scenes/UploadPost/UploadPostInteractor.swift +++ b/iOS/Layover/Layover/Scenes/UploadPost/UploadPostInteractor.swift @@ -74,7 +74,7 @@ final class UploadPostInteractor: NSObject, UploadPostBusinessLogic, UploadPostD do { let image = try await generator.image(at: .zero).image await MainActor.run { - presenter?.presentThumnailImage(with: Models.FetchThumbnail.Response(thumnailImage: image)) + presenter?.presentThumbnailImage(with: Models.FetchThumbnail.Response(thumbnailImage: image)) } return true } catch let error { diff --git a/iOS/Layover/Layover/Scenes/UploadPost/UploadPostModels.swift b/iOS/Layover/Layover/Scenes/UploadPost/UploadPostModels.swift index e722fff..aa13b66 100644 --- a/iOS/Layover/Layover/Scenes/UploadPost/UploadPostModels.swift +++ b/iOS/Layover/Layover/Scenes/UploadPost/UploadPostModels.swift @@ -40,10 +40,10 @@ enum UploadPostModels { } struct Response { - let thumnailImage: CGImage + let thumbnailImage: CGImage } struct ViewModel { - let thumnailImage: UIImage + let thumbnailImage: UIImage } } diff --git a/iOS/Layover/Layover/Scenes/UploadPost/UploadPostPresenter.swift b/iOS/Layover/Layover/Scenes/UploadPost/UploadPostPresenter.swift index f87cef8..7d4510b 100644 --- a/iOS/Layover/Layover/Scenes/UploadPost/UploadPostPresenter.swift +++ b/iOS/Layover/Layover/Scenes/UploadPost/UploadPostPresenter.swift @@ -10,7 +10,7 @@ import UIKit protocol UploadPostPresentationLogic { func presentTags(with response: UploadPostModels.FetchTags.Response) - func presentThumnailImage(with response: UploadPostModels.FetchThumbnail.Response) + func presentThumbnailImage(with response: UploadPostModels.FetchThumbnail.Response) func presentCurrentAddress(with response: UploadPostModels.FetchCurrentAddress.Response) func presentUploadButton(with response: UploadPostModels.CanUploadPost.Response) // func presentUploadProgress(with response: UploadPostModels.UploadPost.Response) @@ -27,9 +27,9 @@ final class UploadPostPresenter: UploadPostPresentationLogic { viewController?.displayTags(viewModel: Models.FetchTags.ViewModel(tags: response.tags)) } - func presentThumnailImage(with response: UploadPostModels.FetchThumbnail.Response) { - let image = UIImage(cgImage: response.thumnailImage) - viewController?.displayThumbnail(viewModel: Models.FetchThumbnail.ViewModel(thumnailImage: image)) + func presentThumbnailImage(with response: UploadPostModels.FetchThumbnail.Response) { + let image = UIImage(cgImage: response.thumbnailImage) + viewController?.displayThumbnail(viewModel: Models.FetchThumbnail.ViewModel(thumbnailImage: image)) } func presentCurrentAddress(with response: UploadPostModels.FetchCurrentAddress.Response) { diff --git a/iOS/Layover/Layover/Scenes/UploadPost/UploadPostViewController.swift b/iOS/Layover/Layover/Scenes/UploadPost/UploadPostViewController.swift index f16890e..0836844 100644 --- a/iOS/Layover/Layover/Scenes/UploadPost/UploadPostViewController.swift +++ b/iOS/Layover/Layover/Scenes/UploadPost/UploadPostViewController.swift @@ -258,7 +258,7 @@ extension UploadPostViewController: UploadPostDisplayLogic { } func displayThumbnail(viewModel: UploadPostModels.FetchThumbnail.ViewModel) { - thumbnailImageView.image = viewModel.thumnailImage + thumbnailImageView.image = viewModel.thumbnailImage } func displayCurrentAddress(viewModel: UploadPostModels.FetchCurrentAddress.ViewModel) { From 4723e92b1b7b1ae332dc06885286cf708e69a0ec Mon Sep 17 00:00:00 2001 From: chopmozzi <44396392+chopmozzi@users.noreply.github.com> Date: Fri, 8 Dec 2023 04:51:57 +0900 Subject: [PATCH 09/12] =?UTF-8?q?:recycle:=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit first, Last를 통한 바인딩으로 개선 --- iOS/Layover/Layover/Scenes/Playback/PlaybackWorker.swift | 5 +++-- iOS/Layover/Layover/Workers/Mocks/MockPlaybackWorker.swift | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/iOS/Layover/Layover/Scenes/Playback/PlaybackWorker.swift b/iOS/Layover/Layover/Scenes/Playback/PlaybackWorker.swift index 110342d..4da29f9 100644 --- a/iOS/Layover/Layover/Scenes/Playback/PlaybackWorker.swift +++ b/iOS/Layover/Layover/Scenes/Playback/PlaybackWorker.swift @@ -31,9 +31,10 @@ final class PlaybackWorker: PlaybackWorkerProtocol { } func makeInfiniteScroll(posts: [Post]) -> [Post] { + guard let tempLastVideo: Post = posts.last, + let tempFirstVideo: Post = posts.first + else { return posts } var tempVideos: [Post] = posts - let tempLastVideo: Post = posts[posts.count - 1] - let tempFirstVideo: Post = posts[0] tempVideos.insert(tempLastVideo, at: 0) tempVideos.append(tempFirstVideo) return tempVideos diff --git a/iOS/Layover/Layover/Workers/Mocks/MockPlaybackWorker.swift b/iOS/Layover/Layover/Workers/Mocks/MockPlaybackWorker.swift index f144b53..a3155fc 100644 --- a/iOS/Layover/Layover/Workers/Mocks/MockPlaybackWorker.swift +++ b/iOS/Layover/Layover/Workers/Mocks/MockPlaybackWorker.swift @@ -24,9 +24,10 @@ final class MockPlaybackWorker: PlaybackWorkerProtocol { } func makeInfiniteScroll(posts: [Post]) -> [Post] { + guard let tempLastVideo: Post = posts.last, + let tempFirstVideo: Post = posts.first + else { return posts } var tempVideos: [Post] = posts - let tempLastVideo: Post = posts[posts.count - 1] - let tempFirstVideo: Post = posts[0] tempVideos.insert(tempLastVideo, at: 0) tempVideos.append(tempFirstVideo) return tempVideos From 3391f5a603f87ac80bf597eb405f5400bdcc256c Mon Sep 17 00:00:00 2001 From: kong <1018dbrud@gmail.com> Date: Fri, 8 Dec 2023 11:07:19 +0900 Subject: [PATCH 10/12] =?UTF-8?q?=F0=9F=90=9B=20EditTagVC=EC=9D=98=20route?= =?UTF-8?q?ToBack=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/Layover/Layover/Scenes/EditTag/EditTagRouter.swift | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/iOS/Layover/Layover/Scenes/EditTag/EditTagRouter.swift b/iOS/Layover/Layover/Scenes/EditTag/EditTagRouter.swift index d499382..8fb8e31 100644 --- a/iOS/Layover/Layover/Scenes/EditTag/EditTagRouter.swift +++ b/iOS/Layover/Layover/Scenes/EditTag/EditTagRouter.swift @@ -27,11 +27,13 @@ final class EditTagRouter: NSObject, EditTagRoutingLogic, EditTagDataPassing { // MARK: - Routing func routeToBack() { - guard let presentingViewController = viewController?.presentingViewController as? UITabBarController, - let selectedViewController = presentingViewController.selectedViewController as? UINavigationController, - let previousViewController = selectedViewController.viewControllers.last as? UploadPostViewController, + guard let presentingViewController = viewController?.presentingViewController as? UINavigationController, + let tabbarViewController = presentingViewController.viewControllers.last as? UITabBarController, + let selectedViewcontroller = tabbarViewController.selectedViewController as? UINavigationController, + let previousViewController = selectedViewcontroller.viewControllers.last as? UploadPostViewController, var destination = previousViewController.router?.dataStore else { return } + destination.tags = dataStore?.tags viewController?.dismiss(animated: true) } From 68f1618eabb6e031de3ce2dab5a530b56c8b509e Mon Sep 17 00:00:00 2001 From: anyukyung <1018dbrud@gmail.com> Date: Fri, 8 Dec 2023 22:30:58 +0900 Subject: [PATCH 11/12] =?UTF-8?q?=F0=9F=90=9B=20=EC=8A=A4=ED=81=AC?= =?UTF-8?q?=EB=A1=A4=EC=97=90=EB=94=B0=EB=9D=BC=20=EB=A7=B5=ED=95=80=20?= =?UTF-8?q?=ED=95=98=EC=9D=B4=EB=9D=BC=EC=9D=B4=ED=8A=B8=EB=90=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/Layover/Layover/Scenes/Map/MapViewController.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/iOS/Layover/Layover/Scenes/Map/MapViewController.swift b/iOS/Layover/Layover/Scenes/Map/MapViewController.swift index 294ffad..c45ae7f 100644 --- a/iOS/Layover/Layover/Scenes/Map/MapViewController.swift +++ b/iOS/Layover/Layover/Scenes/Map/MapViewController.swift @@ -197,7 +197,7 @@ final class MapViewController: BaseViewController { .filter { $0.boardID == focusedPost.boardID } .first guard let focusedAnnotaion else { return } - mapView.setCenter(focusedAnnotaion.coordinate, animated: true) + mapView.selectAnnotation(focusedAnnotaion, animated: true) } @objc private func searchButtonDidTap() { @@ -248,7 +248,7 @@ extension MapViewController: MKMapViewDelegate { let sectionIndex = snapshot.indexOfSection(section) else { return } carouselCollectionView.scrollToItem(at: IndexPath(item: itemIndex, section: sectionIndex), at: .centeredHorizontally, - animated: true) + animated: false) } } From 0eb01e5a727392593905f3a46e2564d2b5ca0f29 Mon Sep 17 00:00:00 2001 From: anyukyung <1018dbrud@gmail.com> Date: Sat, 9 Dec 2023 00:46:41 +0900 Subject: [PATCH 12/12] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20=EC=98=A4=ED=83=88?= =?UTF-8?q?=EC=9E=90=20=EC=88=98=EC=A0=95,=20=EB=AA=A8=EB=8D=B8=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC,=20datapass=20=EB=A9=94=EC=86=8C=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EB=93=B1=20=EC=BD=94=EB=93=9C=20=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/Layover/Layover.xcodeproj/project.pbxproj | 4 ++++ .../Layover/Models/ThumbnailLoadedPost.swift | 16 ++++++++++++++++ .../Layover/Scenes/Map/MapInteractor.swift | 18 +++++++++--------- iOS/Layover/Layover/Scenes/Map/MapModels.swift | 9 +-------- .../Layover/Scenes/Map/MapPresenter.swift | 2 +- iOS/Layover/Layover/Scenes/Map/MapRouter.swift | 13 ++++++------- .../Layover/Scenes/Map/MapViewController.swift | 4 ++-- iOS/Layover/Layover/Scenes/Map/MapWorker.swift | 6 +++--- 8 files changed, 42 insertions(+), 30 deletions(-) create mode 100644 iOS/Layover/Layover/Models/ThumbnailLoadedPost.swift diff --git a/iOS/Layover/Layover.xcodeproj/project.pbxproj b/iOS/Layover/Layover.xcodeproj/project.pbxproj index f29421d..3985a3b 100644 --- a/iOS/Layover/Layover.xcodeproj/project.pbxproj +++ b/iOS/Layover/Layover.xcodeproj/project.pbxproj @@ -206,6 +206,7 @@ FCEE0FF22B036B6000195BBE /* LOButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCEE0FF12B036B6000195BBE /* LOButton.swift */; }; FCEE0FF62B03804000195BBE /* LOTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCEE0FF52B03804000195BBE /* LOTextField.swift */; }; FCEE0FFA2B03AF8500195BBE /* SignUpViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCEE0FF92B03AF8400195BBE /* SignUpViewController.swift */; }; + FCFEDD7F2B23690E0038E875 /* ThumbnailLoadedPost.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCFEDD7E2B23690E0038E875 /* ThumbnailLoadedPost.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -423,6 +424,7 @@ FCEE0FF12B036B6000195BBE /* LOButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LOButton.swift; sourceTree = ""; }; FCEE0FF52B03804000195BBE /* LOTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LOTextField.swift; sourceTree = ""; }; FCEE0FF92B03AF8400195BBE /* SignUpViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SignUpViewController.swift; path = ../SignUpViewController.swift; sourceTree = ""; }; + FCFEDD7E2B23690E0038E875 /* ThumbnailLoadedPost.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThumbnailLoadedPost.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -578,6 +580,7 @@ 19A169412B17C70C00DB34C0 /* Member.swift */, 19A169432B17C71C00DB34C0 /* Board.swift */, FC4084CB2B1F2F5D00CE4727 /* UploadPost.swift */, + FCFEDD7E2B23690E0038E875 /* ThumbnailLoadedPost.swift */, ); path = Models; sourceTree = ""; @@ -1277,6 +1280,7 @@ FC7E453A2AFEB623004F155A /* AppDelegate.swift in Sources */, FC0E80282B1A0BBB00EF56D6 /* UploadPostViewController.swift in Sources */, 1972CCD82B13A2EC00C3C762 /* SignUpEndPointFactory.swift in Sources */, + FCFEDD7F2B23690E0038E875 /* ThumbnailLoadedPost.swift in Sources */, 8321A2F32B1E1026000A12AF /* ReportInteractor.swift in Sources */, 19A169262B176C5F00DB34C0 /* TagPlayListModels.swift in Sources */, 8321A2F22B1E1026000A12AF /* ReportViewController.swift in Sources */, diff --git a/iOS/Layover/Layover/Models/ThumbnailLoadedPost.swift b/iOS/Layover/Layover/Models/ThumbnailLoadedPost.swift new file mode 100644 index 0000000..ae893b3 --- /dev/null +++ b/iOS/Layover/Layover/Models/ThumbnailLoadedPost.swift @@ -0,0 +1,16 @@ +// +// ThumbnailLoadedPost.swift +// Layover +// +// Created by kong on 2023/12/09. +// Copyright © 2023 CodeBomber. All rights reserved. +// + +import Foundation + +struct ThumbnailLoadedPost { + let member: Member + let board: Board + let tags: [String] + let thumbnailImageData: Data +} diff --git a/iOS/Layover/Layover/Scenes/Map/MapInteractor.swift b/iOS/Layover/Layover/Scenes/Map/MapInteractor.swift index 6ce08b1..eaedf4d 100644 --- a/iOS/Layover/Layover/Scenes/Map/MapInteractor.swift +++ b/iOS/Layover/Layover/Scenes/Map/MapInteractor.swift @@ -14,10 +14,10 @@ protocol MapBusinessLogic { func playPosts(with: MapModels.PlayPosts.Request) @discardableResult - func fetchPosts() -> Task<[MapModels.Post], Never> + func fetchPosts() -> Task @discardableResult - func fetchPost(latitude: Double, longitude: Double) -> Task<[MapModels.Post], Never> + func fetchPost(latitude: Double, longitude: Double) -> Task func selectVideo(with request: MapModels.SelectVideo.Request) } @@ -57,32 +57,32 @@ final class MapInteractor: NSObject, MapBusinessLogic, MapDataStore { presenter?.presentPlaybackScene() } - func fetchPosts() -> Task<[MapModels.Post], Never> { + func fetchPosts() -> Task { Task { locationManager.startUpdatingLocation() - guard let coordinate = locationManager.location?.coordinate else { return [] } + guard let coordinate = locationManager.location?.coordinate else { return false } let posts = await worker?.fetchPosts(latitude: coordinate.latitude, longitude: coordinate.longitude) - guard let posts else { return [] } + guard let posts else { return false } self.posts = posts.map { .init(member: $0.member, board: $0.board, tag: $0.tags) } let response = Models.FetchPosts.Response(posts: posts) await MainActor.run { presenter?.presentFetchedPosts(with: response) } - return posts + return true } } - func fetchPost(latitude: Double, longitude: Double) -> Task<[MapModels.Post], Never> { + func fetchPost(latitude: Double, longitude: Double) -> Task { Task { let posts = await worker?.fetchPosts(latitude: latitude, longitude: longitude) - guard let posts else { return [] } + guard let posts else { return false } self.posts = posts.map { .init(member: $0.member, board: $0.board, tag: $0.tags) } let response = Models.FetchPosts.Response(posts: posts) await MainActor.run { presenter?.presentFetchedPosts(with: response) } - return posts + return true } } diff --git a/iOS/Layover/Layover/Scenes/Map/MapModels.swift b/iOS/Layover/Layover/Scenes/Map/MapModels.swift index 5b52c1c..76c5856 100644 --- a/iOS/Layover/Layover/Scenes/Map/MapModels.swift +++ b/iOS/Layover/Layover/Scenes/Map/MapModels.swift @@ -10,13 +10,6 @@ import Foundation enum MapModels { - struct Post { - let member: Member - let board: Board - let tags: [String] - let thumbnailImageData: Data - } - struct DisplayedPost: Hashable { let boardID: Int let thumbnailImageData: Data @@ -34,7 +27,7 @@ enum MapModels { } struct Response { - var posts: [Post] + var posts: [ThumbnailLoadedPost] } struct ViewModel { diff --git a/iOS/Layover/Layover/Scenes/Map/MapPresenter.swift b/iOS/Layover/Layover/Scenes/Map/MapPresenter.swift index 3f1b77b..ded0094 100644 --- a/iOS/Layover/Layover/Scenes/Map/MapPresenter.swift +++ b/iOS/Layover/Layover/Scenes/Map/MapPresenter.swift @@ -45,7 +45,7 @@ final class MapPresenter: MapPresentationLogic { }.compactMap { $0 } let viewModel = Models.FetchPosts.ViewModel(displayedPosts: displayedPost) - viewController?.dispalyFetchedPosts(viewModel: viewModel) + viewController?.displayFetchedPosts(viewModel: viewModel) } func presentPlaybackScene() { diff --git a/iOS/Layover/Layover/Scenes/Map/MapRouter.swift b/iOS/Layover/Layover/Scenes/Map/MapRouter.swift index eb1319a..f161ebc 100644 --- a/iOS/Layover/Layover/Scenes/Map/MapRouter.swift +++ b/iOS/Layover/Layover/Scenes/Map/MapRouter.swift @@ -40,13 +40,12 @@ final class MapRouter: MapRoutingLogic, MapDataPassing { func routeToEditVideo() { let nextViewController = EditVideoViewController() guard let source = dataStore, - var destination = nextViewController.router?.dataStore, - let videoURL = source.selectedVideoURL - else { return } - - // Data Passing - destination.videoURL = videoURL - nextViewController.hidesBottomBarWhenPushed = true + var destination = nextViewController.router?.dataStore else { return } + passDataToEditVideo(source: source, destination: &destination) viewController?.navigationController?.pushViewController(nextViewController, animated: true) } + + private func passDataToEditVideo(source: MapDataStore, destination: inout EditVideoDataStore) { + destination.videoURL = source.selectedVideoURL + } } diff --git a/iOS/Layover/Layover/Scenes/Map/MapViewController.swift b/iOS/Layover/Layover/Scenes/Map/MapViewController.swift index c45ae7f..a1c59f6 100644 --- a/iOS/Layover/Layover/Scenes/Map/MapViewController.swift +++ b/iOS/Layover/Layover/Scenes/Map/MapViewController.swift @@ -10,7 +10,7 @@ import MapKit import UIKit protocol MapDisplayLogic: AnyObject { - func dispalyFetchedPosts(viewModel: MapModels.FetchPosts.ViewModel) + func displayFetchedPosts(viewModel: MapModels.FetchPosts.ViewModel) func routeToPlayback() } @@ -284,7 +284,7 @@ extension MapViewController: VideoPickerDelegate { extension MapViewController: MapDisplayLogic { - func dispalyFetchedPosts(viewModel: MapModels.FetchPosts.ViewModel) { + func displayFetchedPosts(viewModel: MapModels.FetchPosts.ViewModel) { displayedPost = viewModel.displayedPosts var snapshot: NSDiffableDataSourceSnapshot = NSDiffableDataSourceSnapshot() diff --git a/iOS/Layover/Layover/Scenes/Map/MapWorker.swift b/iOS/Layover/Layover/Scenes/Map/MapWorker.swift index dd13c13..2b48cbb 100644 --- a/iOS/Layover/Layover/Scenes/Map/MapWorker.swift +++ b/iOS/Layover/Layover/Scenes/Map/MapWorker.swift @@ -9,7 +9,7 @@ import Foundation protocol MapWorkerProtocol { - func fetchPosts(latitude: Double, longitude: Double) async -> [MapModels.Post]? + func fetchPosts(latitude: Double, longitude: Double) async -> [ThumbnailLoadedPost]? } final class MapWorker: MapWorkerProtocol { @@ -28,12 +28,12 @@ final class MapWorker: MapWorkerProtocol { self.postEndPointFactory = postEndPointFactory } - func fetchPosts(latitude: Double, longitude: Double) async -> [Models.Post]? { + func fetchPosts(latitude: Double, longitude: Double) async -> [ThumbnailLoadedPost]? { let endPoint = postEndPointFactory.makeMapPostListEndPoint(latitude: latitude, longitude: longitude) do { let response = try await provider.request(with: endPoint) guard let data = response.data else { return nil } - let posts = try await data.concurrentMap { postDTO -> Models.Post in + let posts = try await data.concurrentMap { postDTO -> ThumbnailLoadedPost in let thumbnailImageData = try await self.provider.request(url: postDTO.board.videoThumbnailURL) return .init(member: postDTO.member.toDomain(), board: postDTO.board.toDomain(),