From d200120a6b624d7ed25776855da9ca339680e80f Mon Sep 17 00:00:00 2001 From: loinsir Date: Tue, 12 Dec 2023 22:32:40 +0900 Subject: [PATCH 01/12] =?UTF-8?q?:wrench:=20=EC=9D=B8=EC=BD=94=EB=94=A9=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=ED=95=84=EB=93=9C=20DTO=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80,=20enum=20=ED=83=80=EC=9E=85=EC=9C=BC=EB=A1=9C=20deco?= =?UTF-8?q?de=20=EA=B0=80=EB=8A=A5=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Layover/Network/DTOs/BoardDTO.swift | 22 +++++++++++++++++++ .../Network/Mock/MockData/PostList.json | 15 ++++++++----- .../Network/Mock/MockData/PostListMore.json | 12 ++++++---- .../Mocks/MockDatas/PostList.json | 3 ++- .../Mocks/MockDatas/PostListMore.json | 3 ++- 5 files changed, 44 insertions(+), 11 deletions(-) diff --git a/iOS/Layover/Layover/Network/DTOs/BoardDTO.swift b/iOS/Layover/Layover/Network/DTOs/BoardDTO.swift index 7620f10..74c6696 100644 --- a/iOS/Layover/Layover/Network/DTOs/BoardDTO.swift +++ b/iOS/Layover/Layover/Network/DTOs/BoardDTO.swift @@ -14,12 +14,34 @@ struct BoardDTO: Decodable { let videoThumbnailURL: String let latitude, longitude: Double let title, content: String + let status: BoardStatus + + enum BoardStatus: String, Decodable { + case running + case waiting + case failure + case complete + case deleted + case inactive + + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + let rawValue = try container.decode(String.self) + + if let value = BoardStatus(rawValue: rawValue.lowercased()) { + self = value + } else { + self = .inactive + } + } + } enum CodingKeys: String, CodingKey { case id case encodedVideoURL = "encoded_video_url" case videoThumbnailURL = "video_thumbnail_url" case latitude, longitude, title, content + case status } } diff --git a/iOS/Layover/Layover/Network/Mock/MockData/PostList.json b/iOS/Layover/Layover/Network/Mock/MockData/PostList.json index a5904dc..7edcbf7 100644 --- a/iOS/Layover/Layover/Network/Mock/MockData/PostList.json +++ b/iOS/Layover/Layover/Network/Mock/MockData/PostList.json @@ -16,7 +16,8 @@ "longitude" : 37.0532156213, "latitude" : 127.060123123, "title" : "최강 아이돌", - "content" : "게시글 설명" + "content" : "게시글 설명", + "status": "COMPLETE" }, "tag" : ["나도몰라요", "너도몰라요"] }, @@ -34,7 +35,8 @@ "longitude" : 37.0532156213, "latitude" : 127.060123123, "title" : "프로듀스 101", - "content" : "게시글 설명" + "content" : "게시글 설명", + "status": "COMPLETE" }, "tag" : ["해시태그", "해시태그2"] }, @@ -52,7 +54,8 @@ "longitude" : 37.0532156213, "latitude" : 127.060123123, "title" : "프로미스 나인", - "content" : "게시글 설명" + "content" : "게시글 설명", + "status": "COMPLETE" }, "tag" : ["해시태그1", "해시태그2"] }, @@ -70,7 +73,8 @@ "longitude" : 37.0532156213, "latitude" : 127.060123123, "title" : "아이즈원", - "content" : "게시글 설명2" + "content" : "게시글 설명2", + "status": "COMPLETE" }, "tag" : ["해시태그1", "해시태그6"] }, @@ -88,7 +92,8 @@ "longitude" : 37.0532156213, "latitude" : 127.060123123, "title" : "아이즈원", - "content" : "게시글 설명2" + "content" : "게시글 설명2", + "status": "COMPLETE" }, "tag" : ["해시태그1", "해시태그6"] } diff --git a/iOS/Layover/Layover/Network/Mock/MockData/PostListMore.json b/iOS/Layover/Layover/Network/Mock/MockData/PostListMore.json index a04244c..731eb3c 100644 --- a/iOS/Layover/Layover/Network/Mock/MockData/PostListMore.json +++ b/iOS/Layover/Layover/Network/Mock/MockData/PostListMore.json @@ -16,7 +16,8 @@ "longitude" : 37.0532156213, "latitude" : 127.060123123, "title" : "최강 아이돌", - "content" : "게시글 설명" + "content" : "게시글 설명", + "status": "COMPLETE" }, "tag" : ["나도몰라요", "너도몰라요"] }, @@ -34,7 +35,8 @@ "longitude" : 37.0532156213, "latitude" : 127.060123123, "title" : "프로듀스 101", - "content" : "게시글 설명" + "content" : "게시글 설명", + "status": "COMPLETE" }, "tag" : ["해시태그", "해시태그2"] }, @@ -52,7 +54,8 @@ "longitude" : 37.0532156213, "latitude" : 127.060123123, "title" : "프로미스 나인", - "content" : "게시글 설명" + "content" : "게시글 설명", + "status": "COMPLETE" }, "tag" : ["해시태그1", "해시태그2"] }, @@ -70,7 +73,8 @@ "longitude" : 37.0532156213, "latitude" : 127.060123123, "title" : "아이즈원", - "content" : "게시글 설명2" + "content" : "게시글 설명2", + "status": "COMPLETE" }, "tag" : ["해시태그1", "해시태그6"] } diff --git a/iOS/Layover/LayoverTests/Mocks/MockDatas/PostList.json b/iOS/Layover/LayoverTests/Mocks/MockDatas/PostList.json index 80a8aad..a521daf 100644 --- a/iOS/Layover/LayoverTests/Mocks/MockDatas/PostList.json +++ b/iOS/Layover/LayoverTests/Mocks/MockDatas/PostList.json @@ -16,7 +16,8 @@ "longitude" : 37.0532156213, "latitude" : 127.060123123, "title" : "최강 아이돌", - "content" : "게시글 설명" + "content" : "게시글 설명", + "status": "COMPLETE" }, "tag" : ["아이브", "yujin"] } diff --git a/iOS/Layover/LayoverTests/Mocks/MockDatas/PostListMore.json b/iOS/Layover/LayoverTests/Mocks/MockDatas/PostListMore.json index edd56f4..e73327f 100644 --- a/iOS/Layover/LayoverTests/Mocks/MockDatas/PostListMore.json +++ b/iOS/Layover/LayoverTests/Mocks/MockDatas/PostListMore.json @@ -16,7 +16,8 @@ "longitude" : 35.001828282, "latitude" : 100.060123123, "title" : "아이브의 멤버", - "content" : "게시글 설명설명설명" + "content" : "게시글 설명설명설명", + "status": "COMPLETE" }, "tag" : ["Ive", "wonyoung"] } From f8d075ecb9ea298b4809f3338540574512c7ec01 Mon Sep 17 00:00:00 2001 From: loinsir Date: Tue, 12 Dec 2023 22:55:15 +0900 Subject: [PATCH 02/12] =?UTF-8?q?:wrench:=20BoardDTO=EC=9D=98=20status=20?= =?UTF-8?q?=ED=95=84=EB=93=9C=20=EC=B6=94=EA=B0=80=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=A5=B8=20=ED=85=8C=EC=8A=A4=ED=8A=B8=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/Layover/Layover/Models/Board.swift | 1 + .../Layover/Network/DTOs/BoardDTO.swift | 43 ++++++++++--------- .../Scenes/Profile/ProfileInteractor.swift | 8 ++-- .../Scenes/Profile/ProfileModels.swift | 11 ++--- .../Profile/ProfileViewController.swift | 6 +-- .../Scenes/Home/HomeInteractorTests.swift | 1 + .../Scenes/Home/HomeWorkerTests.swift | 1 + .../Profile/ProfileInteractorTests.swift | 10 +++-- .../Profile/ProfilePresenterTests.swift | 17 +++++--- .../TagPlayListInteractorTests.swift | 4 ++ .../TagPlayList/TagPlayListWorkerTests.swift | 1 + iOS/Layover/LayoverTests/Seeds.swift | 16 +++++-- 12 files changed, 71 insertions(+), 48 deletions(-) diff --git a/iOS/Layover/Layover/Models/Board.swift b/iOS/Layover/Layover/Models/Board.swift index 8f2df33..10173fc 100644 --- a/iOS/Layover/Layover/Models/Board.swift +++ b/iOS/Layover/Layover/Models/Board.swift @@ -16,4 +16,5 @@ struct Board { let videoURL: URL? let latitude: Double let longitude: Double + let status: BoardStatus } diff --git a/iOS/Layover/Layover/Network/DTOs/BoardDTO.swift b/iOS/Layover/Layover/Network/DTOs/BoardDTO.swift index 74c6696..bd77d02 100644 --- a/iOS/Layover/Layover/Network/DTOs/BoardDTO.swift +++ b/iOS/Layover/Layover/Network/DTOs/BoardDTO.swift @@ -16,26 +16,6 @@ struct BoardDTO: Decodable { let title, content: String let status: BoardStatus - enum BoardStatus: String, Decodable { - case running - case waiting - case failure - case complete - case deleted - case inactive - - init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - let rawValue = try container.decode(String.self) - - if let value = BoardStatus(rawValue: rawValue.lowercased()) { - self = value - } else { - self = .inactive - } - } - } - enum CodingKeys: String, CodingKey { case id case encodedVideoURL = "encoded_video_url" @@ -45,6 +25,26 @@ struct BoardDTO: Decodable { } } +enum BoardStatus: String, Decodable { + case running + case waiting + case failure + case complete + case deleted + case inactive + + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + let rawValue = try container.decode(String.self) + + if let value = BoardStatus(rawValue: rawValue.lowercased()) { + self = value + } else { + self = .inactive + } + } +} + extension BoardDTO { func toDomain() -> Board { return Board( @@ -54,7 +54,8 @@ extension BoardDTO { thumbnailImageURL: URL(string: videoThumbnailURL), videoURL: URL(string: encodedVideoURL), latitude: latitude, - longitude: longitude + longitude: longitude, + status: status ) } } diff --git a/iOS/Layover/Layover/Scenes/Profile/ProfileInteractor.swift b/iOS/Layover/Layover/Scenes/Profile/ProfileInteractor.swift index 4ea5cc2..4f73f63 100644 --- a/iOS/Layover/Layover/Scenes/Profile/ProfileInteractor.swift +++ b/iOS/Layover/Layover/Scenes/Profile/ProfileInteractor.swift @@ -106,22 +106,22 @@ final class ProfileInteractor: ProfileBusinessLogic, ProfileDataStore { } } - private func fetchPosts() async -> [Models.Post] { + private func fetchPosts() async -> [Models.DisplayedPost] { guard let fetchedPosts = await userWorker?.fetchPosts(at: fetchPostsPage, of: profileId), fetchedPosts.count > 0 else { return [] } posts += fetchedPosts - var responsePosts = [Models.Post]() + var responsePosts = [Models.DisplayedPost]() for post in fetchedPosts { guard let thumbnailURL = post.board.thumbnailImageURL, let profileImageData = await userWorker?.fetchImageData(with: thumbnailURL) else { - responsePosts.append(.init(id: post.board.identifier, thumbnailImageData: nil)) + responsePosts.append(.init(id: post.board.identifier, thumbnailImageData: nil, status: post.board.status)) continue } - responsePosts.append(Models.Post(id: post.board.identifier, thumbnailImageData: profileImageData)) + responsePosts.append(Models.DisplayedPost(id: post.board.identifier, thumbnailImageData: profileImageData, status: post.board.status)) } return responsePosts diff --git a/iOS/Layover/Layover/Scenes/Profile/ProfileModels.swift b/iOS/Layover/Layover/Scenes/Profile/ProfileModels.swift index 384bf2e..743a9d2 100644 --- a/iOS/Layover/Layover/Scenes/Profile/ProfileModels.swift +++ b/iOS/Layover/Layover/Scenes/Profile/ProfileModels.swift @@ -16,9 +16,10 @@ enum ProfileModels { let profileImageData: Data? } - struct Post: Hashable { + struct DisplayedPost: Hashable { let id: Int let thumbnailImageData: Data? + let status: BoardStatus } enum FetchProfile { @@ -28,12 +29,12 @@ enum ProfileModels { struct Response { let userProfile: Profile - let posts: [Post] + let posts: [DisplayedPost] } struct ViewModel { let userProfile: Profile - let posts: [Post] + let posts: [DisplayedPost] } } @@ -42,11 +43,11 @@ enum ProfileModels { } struct Response { - let posts: [Post] + let posts: [DisplayedPost] } struct ViewModel { - let posts: [Post] + let posts: [DisplayedPost] } } diff --git a/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift b/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift index 0320159..a5e7b27 100644 --- a/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift +++ b/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift @@ -158,7 +158,7 @@ final class ProfileViewController: BaseViewController { cell.editButton.addTarget(self, action: #selector(self.editbuttonDidTap), for: .touchUpInside) } - let postCellRegistration = UICollectionView.CellRegistration { cell, indexPath, itemIdentifier in + let postCellRegistration = UICollectionView.CellRegistration { cell, indexPath, itemIdentifier in if let imageData = itemIdentifier.thumbnailImageData, let image = UIImage(data: imageData) { @@ -177,7 +177,7 @@ final class ProfileViewController: BaseViewController { case .posts: return collectionView.dequeueConfiguredReusableCell(using: postCellRegistration, for: indexPath, - item: itemIdentifier as? Models.Post) + item: itemIdentifier as? Models.DisplayedPost) } } collectionView.dataSource = collectionViewDatasource @@ -247,7 +247,7 @@ extension ProfileViewController: UICollectionViewDelegate { case .profile: return case .posts: - guard let post = collectionViewDatasource?.itemIdentifier(for: indexPath) as? Models.Post else { return } + guard let post = collectionViewDatasource?.itemIdentifier(for: indexPath) as? Models.DisplayedPost else { return } interactor?.showPostDetail(with: Models.ShowPostDetail.Request(startIndex: indexPath.item)) } } diff --git a/iOS/Layover/LayoverTests/Scenes/Home/HomeInteractorTests.swift b/iOS/Layover/LayoverTests/Scenes/Home/HomeInteractorTests.swift index e564631..8eeb363 100644 --- a/iOS/Layover/LayoverTests/Scenes/Home/HomeInteractorTests.swift +++ b/iOS/Layover/LayoverTests/Scenes/Home/HomeInteractorTests.swift @@ -78,6 +78,7 @@ final class HomeInteractorTests: XCTestCase { XCTAssertEqual(spy.presentPostsReceivedResponse.posts[0].board.longitude, Seeds.Posts.post1.board.longitude, "fetchPost()가 presenter에게 올바른 데이터를 저장했다.") XCTAssertEqual(spy.presentPostsReceivedResponse.posts[0].board.thumbnailImageURL, Seeds.Posts.post1.board.thumbnailImageURL, "fetchPost()가 presenter에게 올바른 데이터를 저장했다.") XCTAssertEqual(spy.presentPostsReceivedResponse.posts[0].board.videoURL, Seeds.Posts.post1.board.videoURL, "fetchPost()가 presenter에게 올바른 데이터를 저장했다.") + XCTAssertEqual(spy.presentPostsReceivedResponse.posts[0].board.status, Seeds.Posts.post1.board.status, "fetchPost()가 presenter에게 올바른 데이터를 저장했다.") XCTAssertEqual(spy.presentPostsReceivedResponse.posts[0].member.username, Seeds.Posts.post1.member.username, "fetchPost()가 presenter에게 올바른 데이터를 저장했다.") XCTAssertEqual(spy.presentPostsReceivedResponse.posts[0].member.introduce, Seeds.Posts.post1.member.introduce, "fetchPost()가 presenter에게 올바른 데이터를 저장했다.") XCTAssertEqual(spy.presentPostsReceivedResponse.posts[0].member.profileImageURL, Seeds.Posts.post1.member.profileImageURL, "fetchPost()가 presenter에게 올바른 데이터를 저장했다.") diff --git a/iOS/Layover/LayoverTests/Scenes/Home/HomeWorkerTests.swift b/iOS/Layover/LayoverTests/Scenes/Home/HomeWorkerTests.swift index 356a310..e1f1a3b 100644 --- a/iOS/Layover/LayoverTests/Scenes/Home/HomeWorkerTests.swift +++ b/iOS/Layover/LayoverTests/Scenes/Home/HomeWorkerTests.swift @@ -68,6 +68,7 @@ final class HomeWorkerTests: XCTestCase { XCTAssertEqual(result![0].board.videoURL, Seeds.Posts.post1.board.videoURL) XCTAssertEqual(result![0].board.latitude, Seeds.Posts.post1.board.latitude) XCTAssertEqual(result![0].board.longitude, Seeds.Posts.post1.board.longitude) + XCTAssertEqual(result![0].board.status, Seeds.Posts.post1.board.status) XCTAssertEqual(result![0].member.identifier, Seeds.Posts.post1.member.identifier) XCTAssertEqual(result![0].member.username, Seeds.Posts.post1.member.username) XCTAssertEqual(result![0].member.introduce, Seeds.Posts.post1.member.introduce) diff --git a/iOS/Layover/LayoverTests/Scenes/Profile/ProfileInteractorTests.swift b/iOS/Layover/LayoverTests/Scenes/Profile/ProfileInteractorTests.swift index 1efa357..0ff4bc3 100644 --- a/iOS/Layover/LayoverTests/Scenes/Profile/ProfileInteractorTests.swift +++ b/iOS/Layover/LayoverTests/Scenes/Profile/ProfileInteractorTests.swift @@ -79,6 +79,7 @@ final class ProfileInteractorTests: XCTestCase { XCTAssertEqual(presentationLogicSpy.presentProfileResponse.posts.count, 1) XCTAssertEqual(presentationLogicSpy.presentProfileResponse.posts[0].id, Seeds.Posts.post1.board.identifier) XCTAssertEqual(presentationLogicSpy.presentProfileResponse.posts[0].thumbnailImageData, Seeds.sampleImageData) + XCTAssertEqual(presentationLogicSpy.presentProfileResponse.posts[0].status, Seeds.Posts.post1.board.status) XCTAssertEqual(presentationLogicSpy.presentProfileResponse.userProfile.username, Seeds.Members.getMember1.username) XCTAssertEqual(presentationLogicSpy.presentProfileResponse.userProfile.introduce, Seeds.Members.getMember1.introduce) XCTAssertEqual(presentationLogicSpy.presentProfileResponse.userProfile.profileImageData, Seeds.sampleImageData, "presentProfileResponse에는 fetchProfile의 결과가 올바르게 담겼다.") @@ -93,10 +94,11 @@ final class ProfileInteractorTests: XCTestCase { _ = await sut.fetchMorePosts(with: Models.FetchMorePosts.Request()).value // assert - XCTAssertTrue(presentationLogicSpy.presentMorePostsCalled, "fetchMorePosts을 호출해서 presentMorePosts을 호출했다") - XCTAssertEqual(presentationLogicSpy.presentMorePostsResponse.posts.count, 1, "presentMorePostsResponse에는 fetchPosts의 결과가 담겼다") - XCTAssertEqual(presentationLogicSpy.presentMorePostsResponse.posts[0].id, Seeds.Posts.post1.board.identifier, "presentMorePostsResponse에는 fetchPosts의 결과가 담겼다") - XCTAssertEqual(presentationLogicSpy.presentMorePostsResponse.posts[0].thumbnailImageData, Seeds.sampleImageData, "presentMorePostsResponse에는 fetchPosts의 결과가 담겼다") + XCTAssertTrue(presentationLogicSpy.presentMorePostsCalled, "fetchMorePosts을 호출하여 presentMorePosts을 호출했다") + XCTAssertEqual(presentationLogicSpy.presentMorePostsResponse.posts.count, 1, "fetchMorePosts를 호출하여 올바른 데이터를 전달했다") + XCTAssertEqual(presentationLogicSpy.presentMorePostsResponse.posts[0].id, Seeds.Posts.post1.board.identifier) + XCTAssertEqual(presentationLogicSpy.presentMorePostsResponse.posts[0].thumbnailImageData, Seeds.sampleImageData) + XCTAssertEqual(presentationLogicSpy.presentMorePostsResponse.posts[0].status, Seeds.Posts.post1.board.status, "fetchMorePosts를 실행하여 presenter에 올바른 데이터를 전달했다") } func test_showPostDetail을_호출하면_자신의_playbackStartIndex에_값을_저장하고_presenter의_presentPostDetail을_호출한다() async { diff --git a/iOS/Layover/LayoverTests/Scenes/Profile/ProfilePresenterTests.swift b/iOS/Layover/LayoverTests/Scenes/Profile/ProfilePresenterTests.swift index fa71998..dcef93f 100644 --- a/iOS/Layover/LayoverTests/Scenes/Profile/ProfilePresenterTests.swift +++ b/iOS/Layover/LayoverTests/Scenes/Profile/ProfilePresenterTests.swift @@ -71,8 +71,8 @@ final class ProfilePresenterTests: XCTestCase { introduce: Seeds.Members.getMember1.introduce, profileImageData: Seeds.sampleImageData), posts: [.init(id: Seeds.Posts.post1.board.identifier, - - thumbnailImageData: Seeds.sampleImageData)]) + thumbnailImageData: Seeds.sampleImageData, + status: Seeds.Posts.post1.board.status)]) // act sut.presentProfile(with: response) @@ -82,8 +82,9 @@ final class ProfilePresenterTests: XCTestCase { XCTAssertEqual(spy.displayProfileViewModel.userProfile.introduce, Seeds.Members.getMember1.introduce) XCTAssertEqual(spy.displayProfileViewModel.userProfile.profileImageData, Seeds.sampleImageData) XCTAssertEqual(spy.displayProfileViewModel.posts.count, 1) - XCTAssertEqual(spy.displayProfileViewModel.posts.first?.id, Seeds.Posts.post1.board.identifier) - XCTAssertEqual(spy.displayProfileViewModel.posts.first?.thumbnailImageData, Seeds.sampleImageData, "presentprofile은 올바른 데이터를 뷰에 전달했다") + XCTAssertEqual(spy.displayProfileViewModel.posts[0].id, Seeds.Posts.post1.board.identifier) + XCTAssertEqual(spy.displayProfileViewModel.posts[0].status, Seeds.Posts.post1.board.status) + XCTAssertEqual(spy.displayProfileViewModel.posts[0].thumbnailImageData, Seeds.sampleImageData, "presentprofile은 올바른 데이터를 뷰에 전달했다") } func test_presentMorePosts을_호출하면_displayMorePosts를_호출하고_올바른_데이터를_전달한다() { @@ -93,14 +94,16 @@ final class ProfilePresenterTests: XCTestCase { // act let response = Models.FetchMorePosts.Response(posts: [.init(id: Seeds.Posts.post1.board.identifier, - thumbnailImageData: Seeds.sampleImageData)]) + thumbnailImageData: Seeds.sampleImageData, + status: Seeds.Posts.post1.board.status)]) sut.presentMorePosts(with: response) // assert XCTAssertTrue(spy.displayMorePostsCalled, "presentMorePosts은 displayMorePosts을 호출했다") XCTAssertEqual(spy.displayMorePostsViewModel.posts.count, 1) - XCTAssertEqual(spy.displayMorePostsViewModel.posts.first?.id, Seeds.Posts.post1.board.identifier) - XCTAssertEqual(spy.displayMorePostsViewModel.posts.first?.thumbnailImageData, Seeds.sampleImageData, "presentMorePosts은 올바른 데이터를 뷰에 전달했다") + XCTAssertEqual(spy.displayMorePostsViewModel.posts[0].id, Seeds.Posts.post1.board.identifier) + XCTAssertEqual(spy.displayMorePostsViewModel.posts[0].status, Seeds.Posts.post1.board.status) + XCTAssertEqual(spy.displayMorePostsViewModel.posts[0].thumbnailImageData, Seeds.sampleImageData, "presentMorePosts은 올바른 데이터를 뷰에 전달했다") } func test_presentPostDetail을_호출하면_routeToPostDetail을_호출한다() { diff --git a/iOS/Layover/LayoverTests/Scenes/TagPlayList/TagPlayListInteractorTests.swift b/iOS/Layover/LayoverTests/Scenes/TagPlayList/TagPlayListInteractorTests.swift index acf4c1e..858d1fe 100644 --- a/iOS/Layover/LayoverTests/Scenes/TagPlayList/TagPlayListInteractorTests.swift +++ b/iOS/Layover/LayoverTests/Scenes/TagPlayList/TagPlayListInteractorTests.swift @@ -112,6 +112,7 @@ final class TagPlayListInteractorTests: XCTestCase { XCTAssertEqual(sut.posts[0].board.longitude, Seeds.Posts.post1.board.longitude) XCTAssertEqual(sut.posts[0].board.videoURL, Seeds.Posts.post1.board.videoURL) XCTAssertEqual(sut.posts[0].board.thumbnailImageURL, Seeds.Posts.post1.board.thumbnailImageURL) + XCTAssertEqual(sut.posts[0].board.status, Seeds.Posts.post1.board.status) XCTAssertEqual(sut.posts[0].member.identifier, Seeds.Posts.post1.member.identifier) XCTAssertEqual(sut.posts[0].member.username, Seeds.Posts.post1.member.username) XCTAssertEqual(sut.posts[0].member.introduce, Seeds.Posts.post1.member.introduce) @@ -141,6 +142,7 @@ final class TagPlayListInteractorTests: XCTestCase { && $0.board.videoURL == Seeds.Posts.post2.board.videoURL && $0.board.latitude == Seeds.Posts.post2.board.latitude && $0.board.longitude == Seeds.Posts.post2.board.longitude + && $0.board.status == Seeds.Posts.post2.board.status && $0.member.identifier == Seeds.Posts.post2.member.identifier && $0.member.username == Seeds.Posts.post2.member.username && $0.member.introduce == Seeds.Posts.post2.member.introduce @@ -157,6 +159,7 @@ final class TagPlayListInteractorTests: XCTestCase { XCTAssertEqual(sut.posts[0].board.longitude, Seeds.Posts.post1.board.longitude) XCTAssertEqual(sut.posts[0].board.videoURL, Seeds.Posts.post1.board.videoURL) XCTAssertEqual(sut.posts[0].board.thumbnailImageURL, Seeds.Posts.post1.board.thumbnailImageURL) + XCTAssertEqual(sut.posts[0].board.status, Seeds.Posts.post1.board.status) XCTAssertEqual(sut.posts[0].member.identifier, Seeds.Posts.post1.member.identifier) XCTAssertEqual(sut.posts[0].member.username, Seeds.Posts.post1.member.username) XCTAssertEqual(sut.posts[0].member.introduce, Seeds.Posts.post1.member.introduce) @@ -219,6 +222,7 @@ final class TagPlayListInteractorTests: XCTestCase { && $0.board.videoURL == Seeds.Posts.post1.board.videoURL && $0.board.latitude == Seeds.Posts.post1.board.latitude && $0.board.longitude == Seeds.Posts.post1.board.longitude + && $0.board.status == Seeds.Posts.post1.board.status && $0.member.identifier == Seeds.Posts.post1.member.identifier && $0.member.username == Seeds.Posts.post1.member.username && $0.member.introduce == Seeds.Posts.post1.member.introduce diff --git a/iOS/Layover/LayoverTests/Scenes/TagPlayList/TagPlayListWorkerTests.swift b/iOS/Layover/LayoverTests/Scenes/TagPlayList/TagPlayListWorkerTests.swift index 6c9de3f..abb84d3 100644 --- a/iOS/Layover/LayoverTests/Scenes/TagPlayList/TagPlayListWorkerTests.swift +++ b/iOS/Layover/LayoverTests/Scenes/TagPlayList/TagPlayListWorkerTests.swift @@ -66,6 +66,7 @@ final class TagPlayListWorkerTests: XCTestCase { XCTAssertEqual(result![0].board.videoURL, Seeds.Posts.post1.board.videoURL) XCTAssertEqual(result![0].board.latitude, Seeds.Posts.post1.board.latitude) XCTAssertEqual(result![0].board.longitude, Seeds.Posts.post1.board.longitude) + XCTAssertEqual(result![0].board.status, Seeds.Posts.post1.board.status) XCTAssertEqual(result![0].member.identifier, Seeds.Posts.post1.member.identifier) XCTAssertEqual(result![0].member.username, Seeds.Posts.post1.member.username) XCTAssertEqual(result![0].member.profileImageURL, Seeds.Posts.post1.member.profileImageURL) diff --git a/iOS/Layover/LayoverTests/Seeds.swift b/iOS/Layover/LayoverTests/Seeds.swift index be979e5..f64fb9a 100644 --- a/iOS/Layover/LayoverTests/Seeds.swift +++ b/iOS/Layover/LayoverTests/Seeds.swift @@ -24,7 +24,9 @@ class Seeds { thumbnailImageURL: URL(string: "https://think-note.com/wp-content/uploads/2023/07/eta_3.jpg")!, videoURL: URL(string: "https://devstreaming-cdn.apple.com/videos/streaming/examples/img_bipbop_adv_example_fmp4/master.m3u8")!, latitude: 127.060123123, - longitude: 37.0532156213), + longitude: 37.0532156213, + status: .complete + ), tag: ["아이브", "yujin"]) // PostListMore.json에 정의된 데이터 @@ -38,7 +40,9 @@ class Seeds { thumbnailImageURL: URL(string: "https://img.etoday.co.kr/pto_db/2022/11/600/20221108175829_1816470_1200_1800.jpg")!, videoURL: URL(string: "https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8")!, latitude: 100.060123123, - longitude: 35.001828282), + longitude: 35.001828282, + status: .complete + ), tag: ["Ive", "wonyoung"]) static let thumbnailImageNilPost = Post(member: Member(identifier: 1, @@ -51,7 +55,9 @@ class Seeds { thumbnailImageURL: nil, videoURL: URL(string: "https://devstreaming-cdn.apple.com/videos/streaming/examples/img_bipbop_adv_example_fmp4/master.m3u8")!, latitude: 127.060123123, - longitude: 37.0532156213), + longitude: 37.0532156213, + status: .complete + ), tag: ["아이브", "yujin"]) static let videoURLNilPost = Post(member: Member(identifier: 1, @@ -64,7 +70,9 @@ class Seeds { thumbnailImageURL: nil, videoURL: nil, latitude: 127.060123123, - longitude: 37.0532156213), + longitude: 37.0532156213, + status: .complete + ), tag: ["아이브", "yujin"]) } From cebbc55ab997ed8005fdfa2c8ca8e3c58121c72d Mon Sep 17 00:00:00 2001 From: loinsir Date: Tue, 12 Dec 2023 22:57:50 +0900 Subject: [PATCH 03/12] =?UTF-8?q?:wrench:=20=EB=8F=84=EB=A9=94=EC=9D=B8=20?= =?UTF-8?q?=EB=AA=A8=EB=8D=B8=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Scenes/Profile/ProfileInteractor.swift | 4 ++-- .../Layover/Scenes/Profile/ProfileModels.swift | 8 ++++---- .../Scenes/Profile/ProfilePresenter.swift | 4 ++-- .../Scenes/Profile/ProfileViewController.swift | 4 ++-- .../Scenes/Profile/ProfileInteractorTests.swift | 16 ++++++++-------- .../Scenes/Profile/ProfilePresenterTests.swift | 16 ++++++++-------- 6 files changed, 26 insertions(+), 26 deletions(-) diff --git a/iOS/Layover/Layover/Scenes/Profile/ProfileInteractor.swift b/iOS/Layover/Layover/Scenes/Profile/ProfileInteractor.swift index 4f73f63..d06c67a 100644 --- a/iOS/Layover/Layover/Scenes/Profile/ProfileInteractor.swift +++ b/iOS/Layover/Layover/Scenes/Profile/ProfileInteractor.swift @@ -76,7 +76,7 @@ final class ProfileInteractor: ProfileBusinessLogic, ProfileDataStore { let response = Models.FetchProfile.Response(userProfile: ProfileModels.Profile(username: userProfile.username, introduce: userProfile.introduce, profileImageData: imageData), - posts: posts) + displayedPosts: posts) await MainActor.run { presenter?.presentProfile(with: response) } @@ -96,7 +96,7 @@ final class ProfileInteractor: ProfileBusinessLogic, ProfileDataStore { return false } - let response = Models.FetchMorePosts.Response(posts: fetchedPosts) + let response = Models.FetchMorePosts.Response(displayedPosts: fetchedPosts) await MainActor.run { presenter?.presentMorePosts(with: response) diff --git a/iOS/Layover/Layover/Scenes/Profile/ProfileModels.swift b/iOS/Layover/Layover/Scenes/Profile/ProfileModels.swift index 743a9d2..55aec64 100644 --- a/iOS/Layover/Layover/Scenes/Profile/ProfileModels.swift +++ b/iOS/Layover/Layover/Scenes/Profile/ProfileModels.swift @@ -29,12 +29,12 @@ enum ProfileModels { struct Response { let userProfile: Profile - let posts: [DisplayedPost] + let displayedPosts: [DisplayedPost] } struct ViewModel { let userProfile: Profile - let posts: [DisplayedPost] + let displayedPosts: [DisplayedPost] } } @@ -43,11 +43,11 @@ enum ProfileModels { } struct Response { - let posts: [DisplayedPost] + let displayedPosts: [DisplayedPost] } struct ViewModel { - let posts: [DisplayedPost] + let displayedPosts: [DisplayedPost] } } diff --git a/iOS/Layover/Layover/Scenes/Profile/ProfilePresenter.swift b/iOS/Layover/Layover/Scenes/Profile/ProfilePresenter.swift index 88cb0c5..b597700 100644 --- a/iOS/Layover/Layover/Scenes/Profile/ProfilePresenter.swift +++ b/iOS/Layover/Layover/Scenes/Profile/ProfilePresenter.swift @@ -25,12 +25,12 @@ final class ProfilePresenter: ProfilePresentationLogic { func presentProfile(with response: Models.FetchProfile.Response) { let viewModel = Models.FetchProfile.ViewModel(userProfile: response.userProfile, - posts: response.posts) + displayedPosts: response.displayedPosts) viewController?.displayProfile(viewModel: viewModel) } func presentMorePosts(with response: ProfileModels.FetchMorePosts.Response) { - let viewModel = Models.FetchMorePosts.ViewModel(posts: response.posts) + let viewModel = Models.FetchMorePosts.ViewModel(displayedPosts: response.displayedPosts) viewController?.displayMorePosts(viewModel: viewModel) } diff --git a/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift b/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift index a5e7b27..bfa0f35 100644 --- a/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift +++ b/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift @@ -213,13 +213,13 @@ extension ProfileViewController: ProfileDisplayLogic { var snapshot = NSDiffableDataSourceSnapshot() snapshot.appendSections([.profile, .posts]) snapshot.appendItems([viewModel.userProfile], toSection: .profile) - snapshot.appendItems(viewModel.posts, toSection: .posts) + snapshot.appendItems(viewModel.displayedPosts, toSection: .posts) collectionViewDatasource?.apply(snapshot) } func displayMorePosts(viewModel: ProfileModels.FetchMorePosts.ViewModel) { guard var snapshot = collectionViewDatasource?.snapshot(for: .posts) else { return } - snapshot.append(viewModel.posts) + snapshot.append(viewModel.displayedPosts) collectionViewDatasource?.apply(snapshot, to: .posts) } diff --git a/iOS/Layover/LayoverTests/Scenes/Profile/ProfileInteractorTests.swift b/iOS/Layover/LayoverTests/Scenes/Profile/ProfileInteractorTests.swift index 0ff4bc3..a1aa249 100644 --- a/iOS/Layover/LayoverTests/Scenes/Profile/ProfileInteractorTests.swift +++ b/iOS/Layover/LayoverTests/Scenes/Profile/ProfileInteractorTests.swift @@ -76,10 +76,10 @@ final class ProfileInteractorTests: XCTestCase { // assert XCTAssertTrue(presentationLogicSpy.presentProfileCalled, "fetchProfile을 호출해서 presentProfile을 호출했다") - XCTAssertEqual(presentationLogicSpy.presentProfileResponse.posts.count, 1) - XCTAssertEqual(presentationLogicSpy.presentProfileResponse.posts[0].id, Seeds.Posts.post1.board.identifier) - XCTAssertEqual(presentationLogicSpy.presentProfileResponse.posts[0].thumbnailImageData, Seeds.sampleImageData) - XCTAssertEqual(presentationLogicSpy.presentProfileResponse.posts[0].status, Seeds.Posts.post1.board.status) + XCTAssertEqual(presentationLogicSpy.presentProfileResponse.displayedPosts.count, 1) + XCTAssertEqual(presentationLogicSpy.presentProfileResponse.displayedPosts[0].id, Seeds.Posts.post1.board.identifier) + XCTAssertEqual(presentationLogicSpy.presentProfileResponse.displayedPosts[0].thumbnailImageData, Seeds.sampleImageData) + XCTAssertEqual(presentationLogicSpy.presentProfileResponse.displayedPosts[0].status, Seeds.Posts.post1.board.status) XCTAssertEqual(presentationLogicSpy.presentProfileResponse.userProfile.username, Seeds.Members.getMember1.username) XCTAssertEqual(presentationLogicSpy.presentProfileResponse.userProfile.introduce, Seeds.Members.getMember1.introduce) XCTAssertEqual(presentationLogicSpy.presentProfileResponse.userProfile.profileImageData, Seeds.sampleImageData, "presentProfileResponse에는 fetchProfile의 결과가 올바르게 담겼다.") @@ -95,10 +95,10 @@ final class ProfileInteractorTests: XCTestCase { // assert XCTAssertTrue(presentationLogicSpy.presentMorePostsCalled, "fetchMorePosts을 호출하여 presentMorePosts을 호출했다") - XCTAssertEqual(presentationLogicSpy.presentMorePostsResponse.posts.count, 1, "fetchMorePosts를 호출하여 올바른 데이터를 전달했다") - XCTAssertEqual(presentationLogicSpy.presentMorePostsResponse.posts[0].id, Seeds.Posts.post1.board.identifier) - XCTAssertEqual(presentationLogicSpy.presentMorePostsResponse.posts[0].thumbnailImageData, Seeds.sampleImageData) - XCTAssertEqual(presentationLogicSpy.presentMorePostsResponse.posts[0].status, Seeds.Posts.post1.board.status, "fetchMorePosts를 실행하여 presenter에 올바른 데이터를 전달했다") + XCTAssertEqual(presentationLogicSpy.presentMorePostsResponse.displayedPosts.count, 1, "fetchMorePosts를 호출하여 올바른 데이터를 전달했다") + XCTAssertEqual(presentationLogicSpy.presentMorePostsResponse.displayedPosts[0].id, Seeds.Posts.post1.board.identifier) + XCTAssertEqual(presentationLogicSpy.presentMorePostsResponse.displayedPosts[0].thumbnailImageData, Seeds.sampleImageData) + XCTAssertEqual(presentationLogicSpy.presentMorePostsResponse.displayedPosts[0].status, Seeds.Posts.post1.board.status, "fetchMorePosts를 실행하여 presenter에 올바른 데이터를 전달했다") } func test_showPostDetail을_호출하면_자신의_playbackStartIndex에_값을_저장하고_presenter의_presentPostDetail을_호출한다() async { diff --git a/iOS/Layover/LayoverTests/Scenes/Profile/ProfilePresenterTests.swift b/iOS/Layover/LayoverTests/Scenes/Profile/ProfilePresenterTests.swift index dcef93f..e63fe3b 100644 --- a/iOS/Layover/LayoverTests/Scenes/Profile/ProfilePresenterTests.swift +++ b/iOS/Layover/LayoverTests/Scenes/Profile/ProfilePresenterTests.swift @@ -81,10 +81,10 @@ final class ProfilePresenterTests: XCTestCase { XCTAssertEqual(spy.displayProfileViewModel.userProfile.username, Seeds.Members.getMember1.username) XCTAssertEqual(spy.displayProfileViewModel.userProfile.introduce, Seeds.Members.getMember1.introduce) XCTAssertEqual(spy.displayProfileViewModel.userProfile.profileImageData, Seeds.sampleImageData) - XCTAssertEqual(spy.displayProfileViewModel.posts.count, 1) - XCTAssertEqual(spy.displayProfileViewModel.posts[0].id, Seeds.Posts.post1.board.identifier) - XCTAssertEqual(spy.displayProfileViewModel.posts[0].status, Seeds.Posts.post1.board.status) - XCTAssertEqual(spy.displayProfileViewModel.posts[0].thumbnailImageData, Seeds.sampleImageData, "presentprofile은 올바른 데이터를 뷰에 전달했다") + XCTAssertEqual(spy.displayProfileViewModel.displayedPosts.count, 1) + XCTAssertEqual(spy.displayProfileViewModel.displayedPosts[0].id, Seeds.Posts.post1.board.identifier) + XCTAssertEqual(spy.displayProfileViewModel.displayedPosts[0].status, Seeds.Posts.post1.board.status) + XCTAssertEqual(spy.displayProfileViewModel.displayedPosts[0].thumbnailImageData, Seeds.sampleImageData, "presentprofile은 올바른 데이터를 뷰에 전달했다") } func test_presentMorePosts을_호출하면_displayMorePosts를_호출하고_올바른_데이터를_전달한다() { @@ -100,10 +100,10 @@ final class ProfilePresenterTests: XCTestCase { // assert XCTAssertTrue(spy.displayMorePostsCalled, "presentMorePosts은 displayMorePosts을 호출했다") - XCTAssertEqual(spy.displayMorePostsViewModel.posts.count, 1) - XCTAssertEqual(spy.displayMorePostsViewModel.posts[0].id, Seeds.Posts.post1.board.identifier) - XCTAssertEqual(spy.displayMorePostsViewModel.posts[0].status, Seeds.Posts.post1.board.status) - XCTAssertEqual(spy.displayMorePostsViewModel.posts[0].thumbnailImageData, Seeds.sampleImageData, "presentMorePosts은 올바른 데이터를 뷰에 전달했다") + XCTAssertEqual(spy.displayMorePostsViewModel.displayedPosts.count, 1) + XCTAssertEqual(spy.displayMorePostsViewModel.displayedPosts[0].id, Seeds.Posts.post1.board.identifier) + XCTAssertEqual(spy.displayMorePostsViewModel.displayedPosts[0].status, Seeds.Posts.post1.board.status) + XCTAssertEqual(spy.displayMorePostsViewModel.displayedPosts[0].thumbnailImageData, Seeds.sampleImageData, "presentMorePosts은 올바른 데이터를 뷰에 전달했다") } func test_presentPostDetail을_호출하면_routeToPostDetail을_호출한다() { From e9dd9e29c82df95c2ac63f17f178c4f8a3b82fa2 Mon Sep 17 00:00:00 2001 From: loinsir Date: Tue, 12 Dec 2023 23:12:06 +0900 Subject: [PATCH 04/12] =?UTF-8?q?:sparkles:=20=EC=9D=B8=EC=BD=94=EB=94=A9?= =?UTF-8?q?=20=EC=A4=91=EC=9D=B8=20=EC=98=81=EC=83=81=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=ED=95=84=20=EB=B7=B0=EC=97=90=EC=84=9C=20indicator=20=ED=91=9C?= =?UTF-8?q?=EC=8B=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Scenes/Profile/ProfileConfigurator.swift | 1 - .../Profile/ProfileViewController.swift | 4 ++- .../Views/ThumbnailCollectionViewCell.swift | 27 ++++++++++++++++--- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/iOS/Layover/Layover/Scenes/Profile/ProfileConfigurator.swift b/iOS/Layover/Layover/Scenes/Profile/ProfileConfigurator.swift index 4f12272..38cdd17 100644 --- a/iOS/Layover/Layover/Scenes/Profile/ProfileConfigurator.swift +++ b/iOS/Layover/Layover/Scenes/Profile/ProfileConfigurator.swift @@ -19,7 +19,6 @@ final class ProfileConfigurator: Configurator { func configure(_ viewController: ViewController) { let viewController = viewController let interactor = ProfileInteractor() -// let worker = MockUserWorker() let worker = UserWorker() let presenter = ProfilePresenter() let router = ProfileRouter() diff --git a/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift b/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift index bfa0f35..8d58c2a 100644 --- a/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift +++ b/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift @@ -162,7 +162,9 @@ final class ProfileViewController: BaseViewController { if let imageData = itemIdentifier.thumbnailImageData, let image = UIImage(data: imageData) { - cell.configure(image: image) + cell.configure(image: image, status: itemIdentifier.status) + } else { + cell.configure(image: nil, status: itemIdentifier.status) } } diff --git a/iOS/Layover/Layover/Scenes/Profile/Views/ThumbnailCollectionViewCell.swift b/iOS/Layover/Layover/Scenes/Profile/Views/ThumbnailCollectionViewCell.swift index 497cef7..9bbc8c1 100644 --- a/iOS/Layover/Layover/Scenes/Profile/Views/ThumbnailCollectionViewCell.swift +++ b/iOS/Layover/Layover/Scenes/Profile/Views/ThumbnailCollectionViewCell.swift @@ -19,6 +19,14 @@ final class ThumbnailCollectionViewCell: UICollectionViewCell { return imageView }() + private let spinner: UIActivityIndicatorView = { + let indicator = UIActivityIndicatorView(style: .large) + indicator.color = .primaryPurple + indicator.hidesWhenStopped = true + indicator.stopAnimating() + return indicator + }() + // MARK: - Object Lifecycle override init(frame: CGRect) { @@ -35,8 +43,15 @@ final class ThumbnailCollectionViewCell: UICollectionViewCell { // MARK: - Methods - func configure(image: UIImage) { + func configure(image: UIImage?, status: BoardStatus) { thumbnailImageView.image = image + + switch status { + case .complete: + spinner.stopAnimating() + default: + spinner.startAnimating() + } } private func setUI() { @@ -46,13 +61,17 @@ final class ThumbnailCollectionViewCell: UICollectionViewCell { } private func setConstraints() { - contentView.addSubview(thumbnailImageView) - thumbnailImageView.translatesAutoresizingMaskIntoConstraints = false + contentView.addSubviews(thumbnailImageView, spinner) + 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) + thumbnailImageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), + + spinner.centerXAnchor.constraint(equalTo: contentView.centerXAnchor), + spinner.centerYAnchor.constraint(equalTo: contentView.centerYAnchor) ]) } From 4179fae6794d3602c6a46e2d00c6b2ae55a76318 Mon Sep 17 00:00:00 2001 From: loinsir Date: Tue, 12 Dec 2023 23:17:06 +0900 Subject: [PATCH 05/12] =?UTF-8?q?:sparkles:=20=ED=94=84=EB=A1=9C=ED=95=84?= =?UTF-8?q?=20=ED=83=AD=20=EB=B7=B0=20Pull=20To=20Refresh=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 --- .../Profile/ProfileViewController.swift | 33 +++++++++++++++---- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift b/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift index 8d58c2a..95f9b7c 100644 --- a/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift +++ b/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift @@ -45,6 +45,13 @@ final class ProfileViewController: BaseViewController { private var collectionViewDatasource: UICollectionViewDiffableDataSource? + private lazy var refreshControl: UIRefreshControl = { + let refreshControl = UIRefreshControl() + refreshControl.tintColor = .primaryPurple + refreshControl.addTarget(self, action: #selector(pullToRefresh(_:)), for: .valueChanged) + return refreshControl + }() + // MARK: - Properties typealias Models = ProfileModels @@ -68,18 +75,22 @@ final class ProfileViewController: BaseViewController { // MARK: - View Lifecycle - override func viewDidLoad() { - super.viewDidLoad() - setNavigationBar() - setDataSource() - } - override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) fetchProfile() } + // MARK: - UI, Layout + + override func setUI() { + super.setUI() + setRefreshControl() + setNavigationBar() + setDataSource() + } + override func setConstraints() { + super.setConstraints() view.addSubview(collectionView) collectionView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ @@ -92,6 +103,10 @@ final class ProfileViewController: BaseViewController { // MARK: - Methods + private func setRefreshControl() { + collectionView.refreshControl = refreshControl + } + private func createLayout() -> UICollectionViewCompositionalLayout { let layout = UICollectionViewCompositionalLayout { section, _ in guard let section: Section = Section(rawValue: section) else { return nil } @@ -205,6 +220,9 @@ final class ProfileViewController: BaseViewController { router?.routeToSetting() } + @objc private func pullToRefresh(_ sender: UIRefreshControl) { + fetchProfile() + } } // MARK: - ProfileDisplayLogic @@ -217,6 +235,7 @@ extension ProfileViewController: ProfileDisplayLogic { snapshot.appendItems([viewModel.userProfile], toSection: .profile) snapshot.appendItems(viewModel.displayedPosts, toSection: .posts) collectionViewDatasource?.apply(snapshot) + refreshControl.endRefreshing() } func displayMorePosts(viewModel: ProfileModels.FetchMorePosts.ViewModel) { @@ -249,7 +268,7 @@ extension ProfileViewController: UICollectionViewDelegate { case .profile: return case .posts: - guard let post = collectionViewDatasource?.itemIdentifier(for: indexPath) as? Models.DisplayedPost else { return } + guard collectionViewDatasource?.itemIdentifier(for: indexPath) is Models.DisplayedPost else { return } interactor?.showPostDetail(with: Models.ShowPostDetail.Request(startIndex: indexPath.item)) } } From 87dff92a8d4bfbf349af2d31ad9b73c98c74455c Mon Sep 17 00:00:00 2001 From: loinsir Date: Tue, 12 Dec 2023 23:20:57 +0900 Subject: [PATCH 06/12] =?UTF-8?q?:sparkles:=20=EC=9D=B8=EC=BD=94=EB=94=A9?= =?UTF-8?q?=20=EC=A4=91=EC=9D=B8=20=EC=98=81=EC=83=81=20=EC=85=80=20?= =?UTF-8?q?=ED=84=B0=EC=B9=98=20=EC=8B=9C=20=ED=99=94=EB=A9=B4=20=EC=A0=84?= =?UTF-8?q?=ED=99=98=20=EC=A0=9C=ED=95=9C=20=EB=B0=8F=20=ED=86=A0=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EB=A9=94=EC=8B=9C=EC=A7=80=20=EC=B6=9C=EB=A0=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Layover/Scenes/Profile/ProfileViewController.swift | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift b/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift index 95f9b7c..5990a65 100644 --- a/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift +++ b/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift @@ -268,8 +268,13 @@ extension ProfileViewController: UICollectionViewDelegate { case .profile: return case .posts: - guard collectionViewDatasource?.itemIdentifier(for: indexPath) is Models.DisplayedPost else { return } - interactor?.showPostDetail(with: Models.ShowPostDetail.Request(startIndex: indexPath.item)) + guard let post = collectionViewDatasource?.itemIdentifier(for: indexPath) as? Models.DisplayedPost else { return } + switch post.status { + case .complete: + interactor?.showPostDetail(with: Models.ShowPostDetail.Request(startIndex: indexPath.item)) + default: + Toast.shared.showToast(message: "인코딩 중인 영상입니다. 잠시만 기다려주세요!") + } } } } From 4ba5dae26dd953037fe4c57d4465d41b597ad800 Mon Sep 17 00:00:00 2001 From: loinsir Date: Tue, 12 Dec 2023 23:27:02 +0900 Subject: [PATCH 07/12] =?UTF-8?q?:bug:=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=EC=9D=84=20=ED=95=98=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EC=9D=8C=EC=97=90=EB=8F=84,=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=82=AD=EC=A0=9C=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=EB=90=98=EB=8A=94=20=EB=B2=84=EA=B7=B8=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 --- .../EditProfile/EditProfileInteractor.swift | 32 ++++++++++--------- .../EditProfile/EditProfileModels.swift | 1 + .../EditProfileViewController.swift | 1 + 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileInteractor.swift b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileInteractor.swift index 0c1c444..b998975 100644 --- a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileInteractor.swift +++ b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileInteractor.swift @@ -141,22 +141,24 @@ final class EditProfileInteractor: EditProfileBusinessLogic, EditProfileDataStor return false } - // 이미지 변경 시도 - if let profileImageData = request.profileImageData, let profileImageExtension = request.profileImageExtension { - guard await profileImageEditRequest(into: profileImageData, fileExtension: profileImageExtension) else { - await MainActor.run { - presenter?.presentProfile(with: Models.EditProfile.Response(isSuccess: false)) + // 이미지 변경 요청 + if request.profileImageChanged { + if let profileImageData = request.profileImageData, let profileImageExtension = request.profileImageExtension { + guard await profileImageEditRequest(into: profileImageData, fileExtension: profileImageExtension) else { + await MainActor.run { + presenter?.presentProfile(with: Models.EditProfile.Response(isSuccess: false)) + } + return false } - return false - } - } else { // 이미지 변경이 없는 경우 이미지 삭제 시도 - guard await userWorker?.setProfileImageDefault() == true else { - await MainActor.run { - presenter?.presentProfile(with: Models.EditProfile.Response(isSuccess: false)) + } else { // 이미지 데이터 없는 경우 이미지 삭제 시도 + guard await userWorker?.setProfileImageDefault() == true else { + await MainActor.run { + presenter?.presentProfile(with: Models.EditProfile.Response(isSuccess: false)) + } + return false } - return false + profileImageData = nil } - profileImageData = nil } await MainActor.run { @@ -168,7 +170,7 @@ final class EditProfileInteractor: EditProfileBusinessLogic, EditProfileDataStor private func nicknameEditRequest(into nickname: String) async -> Bool { if self.nickname != nickname { - guard let response = await userWorker?.modifyNickname(to: nickname) else { + guard await userWorker?.modifyNickname(to: nickname) != nil else { os_log(.error, log: .data, "Edit Profile Error") return false } @@ -178,7 +180,7 @@ final class EditProfileInteractor: EditProfileBusinessLogic, EditProfileDataStor } private func introduceEditRequest(into introduce: String?) async -> Bool { - guard let modifyIntroduceResponse = await userWorker?.modifyIntroduce(to: introduce ?? "") else { + guard await userWorker?.modifyIntroduce(to: introduce ?? "") != nil else { os_log(.error, log: .data, "Edit Profile Error") return false } diff --git a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileModels.swift b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileModels.swift index 2ed4719..ce04d7c 100644 --- a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileModels.swift +++ b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileModels.swift @@ -93,6 +93,7 @@ enum EditProfileModels { struct Request { let nickname: String let introduce: String? + let profileImageChanged: Bool let profileImageData: Data? let profileImageExtension: String? } diff --git a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileViewController.swift b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileViewController.swift index 353498d..a8f87a1 100644 --- a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileViewController.swift +++ b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileViewController.swift @@ -223,6 +223,7 @@ final class EditProfileViewController: BaseViewController { let request = Models.EditProfile.Request(nickname: nickname, introduce: introduce, + profileImageChanged: changedProfileImageData != nil, profileImageData: changedProfileImageData, profileImageExtension: changedProfileImageExtension) showLoading() From 5a857b6821fff3e7632f234a6313ef0b1ea48d4d Mon Sep 17 00:00:00 2001 From: loinsir Date: Tue, 12 Dec 2023 23:31:43 +0900 Subject: [PATCH 08/12] =?UTF-8?q?:bug:=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=EC=9D=84=20=ED=95=98=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EC=9D=8C=EC=97=90=EB=8F=84,=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EB=B3=80=EA=B2=BD=EB=90=98?= =?UTF-8?q?=EB=8A=94=20=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Layover/Scenes/EditProfile/EditProfileInteractor.swift | 2 +- .../Layover/Scenes/EditProfile/EditProfileModels.swift | 2 +- .../Scenes/EditProfile/EditProfileViewController.swift | 5 ++++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileInteractor.swift b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileInteractor.swift index b998975..fc5ff12 100644 --- a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileInteractor.swift +++ b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileInteractor.swift @@ -142,7 +142,7 @@ final class EditProfileInteractor: EditProfileBusinessLogic, EditProfileDataStor } // 이미지 변경 요청 - if request.profileImageChanged { + if request.changeProfileImageNeeded { if let profileImageData = request.profileImageData, let profileImageExtension = request.profileImageExtension { guard await profileImageEditRequest(into: profileImageData, fileExtension: profileImageExtension) else { await MainActor.run { diff --git a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileModels.swift b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileModels.swift index ce04d7c..066b2b9 100644 --- a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileModels.swift +++ b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileModels.swift @@ -93,7 +93,7 @@ enum EditProfileModels { struct Request { let nickname: String let introduce: String? - let profileImageChanged: Bool + let changeProfileImageNeeded: Bool let profileImageData: Data? let profileImageExtension: String? } diff --git a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileViewController.swift b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileViewController.swift index a8f87a1..8045d49 100644 --- a/iOS/Layover/Layover/Scenes/EditProfile/EditProfileViewController.swift +++ b/iOS/Layover/Layover/Scenes/EditProfile/EditProfileViewController.swift @@ -95,6 +95,7 @@ final class EditProfileViewController: BaseViewController { guard let self else { return } self.changedProfileImageData = nil self.changedProfileImageExtension = nil + self.changeProfileImageNeeded = true self.profileImageView.image = UIImage.profile self.profileImageDataChanged() } @@ -114,6 +115,7 @@ final class EditProfileViewController: BaseViewController { var interactor: EditProfileBusinessLogic? private var isValidNickname = true + private var changeProfileImageNeeded = false private var changedProfileImageData: Data? private var changedProfileImageExtension: String? @@ -223,7 +225,7 @@ final class EditProfileViewController: BaseViewController { let request = Models.EditProfile.Request(nickname: nickname, introduce: introduce, - profileImageChanged: changedProfileImageData != nil, + changeProfileImageNeeded: changeProfileImageNeeded, profileImageData: changedProfileImageData, profileImageExtension: changedProfileImageExtension) showLoading() @@ -253,6 +255,7 @@ extension EditProfileViewController: PHPickerViewControllerDelegate { self.profileImageView.image = UIImage(contentsOfFile: temporaryCopyFileURL.path()) self.changedProfileImageExtension = pathExtension self.changedProfileImageData = try? Data(contentsOf: temporaryCopyFileURL) + self.changeProfileImageNeeded = true self.profileImageDataChanged() } try FileManager.default.removeItem(at: temporaryCopyFileURL) From 6d98566279c403ee123228830385e4b924fc0801 Mon Sep 17 00:00:00 2001 From: loinsir Date: Tue, 12 Dec 2023 23:34:19 +0900 Subject: [PATCH 09/12] =?UTF-8?q?:white=5Fcheck=5Fmark:=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LayoverTests/Scenes/Profile/ProfilePresenterTests.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/iOS/Layover/LayoverTests/Scenes/Profile/ProfilePresenterTests.swift b/iOS/Layover/LayoverTests/Scenes/Profile/ProfilePresenterTests.swift index e63fe3b..0accc6b 100644 --- a/iOS/Layover/LayoverTests/Scenes/Profile/ProfilePresenterTests.swift +++ b/iOS/Layover/LayoverTests/Scenes/Profile/ProfilePresenterTests.swift @@ -70,7 +70,7 @@ final class ProfilePresenterTests: XCTestCase { let response = Models.FetchProfile.Response(userProfile: ProfileModels.Profile(username: Seeds.Members.getMember1.username, introduce: Seeds.Members.getMember1.introduce, profileImageData: Seeds.sampleImageData), - posts: [.init(id: Seeds.Posts.post1.board.identifier, + displayedPosts: [.init(id: Seeds.Posts.post1.board.identifier, thumbnailImageData: Seeds.sampleImageData, status: Seeds.Posts.post1.board.status)]) // act @@ -93,7 +93,7 @@ final class ProfilePresenterTests: XCTestCase { sut.viewController = spy // act - let response = Models.FetchMorePosts.Response(posts: [.init(id: Seeds.Posts.post1.board.identifier, + let response = Models.FetchMorePosts.Response(displayedPosts: [.init(id: Seeds.Posts.post1.board.identifier, thumbnailImageData: Seeds.sampleImageData, status: Seeds.Posts.post1.board.status)]) sut.presentMorePosts(with: response) From 38b2a8160985aae8ac0d5a82c94cf9f5483f08f6 Mon Sep 17 00:00:00 2001 From: loinsir Date: Tue, 12 Dec 2023 23:50:20 +0900 Subject: [PATCH 10/12] =?UTF-8?q?:bug:=20display=20item=20hashable=20?= =?UTF-8?q?=EC=98=A4=EB=A5=98=EB=A1=9C=20=EC=9D=B8=ED=95=9C=20=ED=81=AC?= =?UTF-8?q?=EB=9E=98=EC=8B=9C=20=EB=B2=84=EA=B7=B8=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/Profile/ProfileModels.swift | 3 ++- .../Layover/Scenes/Profile/ProfileViewController.swift | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/iOS/Layover/Layover/Scenes/Profile/ProfileModels.swift b/iOS/Layover/Layover/Scenes/Profile/ProfileModels.swift index 55aec64..9873c8c 100644 --- a/iOS/Layover/Layover/Scenes/Profile/ProfileModels.swift +++ b/iOS/Layover/Layover/Scenes/Profile/ProfileModels.swift @@ -16,7 +16,8 @@ enum ProfileModels { let profileImageData: Data? } - struct DisplayedPost: Hashable { + struct DisplayedPost: Hashable, Identifiable { + let uuid = UUID() let id: Int let thumbnailImageData: Data? let status: BoardStatus diff --git a/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift b/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift index 5990a65..ef064cb 100644 --- a/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift +++ b/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift @@ -234,8 +234,9 @@ extension ProfileViewController: ProfileDisplayLogic { snapshot.appendSections([.profile, .posts]) snapshot.appendItems([viewModel.userProfile], toSection: .profile) snapshot.appendItems(viewModel.displayedPosts, toSection: .posts) - collectionViewDatasource?.apply(snapshot) - refreshControl.endRefreshing() + collectionViewDatasource?.apply(snapshot) { [weak self] in + self?.refreshControl.endRefreshing() + } } func displayMorePosts(viewModel: ProfileModels.FetchMorePosts.ViewModel) { From 046d94b752031b9f2837acaaded04a8aea82eddc Mon Sep 17 00:00:00 2001 From: loinsir Date: Wed, 13 Dec 2023 14:21:51 +0900 Subject: [PATCH 11/12] =?UTF-8?q?:wrench:=20=EC=BD=94=EB=93=9C=20=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=B0=98=EC=98=81=20=EC=88=98=EC=A0=95,=20diffable?= =?UTF-8?q?DataSource=20=EC=B6=A9=EB=8F=8C=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Layover/Scenes/Profile/ProfileInteractor.swift | 3 ++- iOS/Layover/Layover/Scenes/Profile/ProfileModels.swift | 3 +-- .../Layover/Scenes/Profile/ProfileViewController.swift | 2 +- .../Profile/Views/ThumbnailCollectionViewCell.swift | 8 +++++--- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/iOS/Layover/Layover/Scenes/Profile/ProfileInteractor.swift b/iOS/Layover/Layover/Scenes/Profile/ProfileInteractor.swift index d06c67a..21a3254 100644 --- a/iOS/Layover/Layover/Scenes/Profile/ProfileInteractor.swift +++ b/iOS/Layover/Layover/Scenes/Profile/ProfileInteractor.swift @@ -56,11 +56,12 @@ final class ProfileInteractor: ProfileBusinessLogic, ProfileDataStore { func fetchProfile(with request: ProfileModels.FetchProfile.Request) -> Task { fetchPostsPage = 1 canFetchMorePosts = true + posts = [] + return Task { guard let userProfile = await userWorker?.fetchProfile(by: profileId) else { return false } - posts = [] nickname = userProfile.username introduce = userProfile.introduce diff --git a/iOS/Layover/Layover/Scenes/Profile/ProfileModels.swift b/iOS/Layover/Layover/Scenes/Profile/ProfileModels.swift index 9873c8c..55aec64 100644 --- a/iOS/Layover/Layover/Scenes/Profile/ProfileModels.swift +++ b/iOS/Layover/Layover/Scenes/Profile/ProfileModels.swift @@ -16,8 +16,7 @@ enum ProfileModels { let profileImageData: Data? } - struct DisplayedPost: Hashable, Identifiable { - let uuid = UUID() + struct DisplayedPost: Hashable { let id: Int let thumbnailImageData: Data? let status: BoardStatus diff --git a/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift b/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift index ef064cb..bebd22a 100644 --- a/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift +++ b/iOS/Layover/Layover/Scenes/Profile/ProfileViewController.swift @@ -258,7 +258,7 @@ extension ProfileViewController: UICollectionViewDelegate { let scrollOffset = scrollView.contentOffset.y let height = scrollView.bounds.height - if scrollOffset > contentHeight - height { + if scrollOffset > 0 && scrollOffset > contentHeight - height { fetchPosts() } } diff --git a/iOS/Layover/Layover/Scenes/Profile/Views/ThumbnailCollectionViewCell.swift b/iOS/Layover/Layover/Scenes/Profile/Views/ThumbnailCollectionViewCell.swift index 9bbc8c1..d343b58 100644 --- a/iOS/Layover/Layover/Scenes/Profile/Views/ThumbnailCollectionViewCell.swift +++ b/iOS/Layover/Layover/Scenes/Profile/Views/ThumbnailCollectionViewCell.swift @@ -20,8 +20,8 @@ final class ThumbnailCollectionViewCell: UICollectionViewCell { }() private let spinner: UIActivityIndicatorView = { - let indicator = UIActivityIndicatorView(style: .large) - indicator.color = .primaryPurple + let indicator = UIActivityIndicatorView(style: .medium) + indicator.backgroundColor = .clear indicator.hidesWhenStopped = true indicator.stopAnimating() return indicator @@ -49,13 +49,15 @@ final class ThumbnailCollectionViewCell: UICollectionViewCell { switch status { case .complete: spinner.stopAnimating() + thumbnailImageView.alpha = 1.0 default: spinner.startAnimating() + thumbnailImageView.alpha = 0.5 } } private func setUI() { - backgroundColor = .primaryPurple + backgroundColor = .darkGrey layer.cornerRadius = 10 clipsToBounds = true } From 36b3bfba2c104daef9a06783af56235c60754541 Mon Sep 17 00:00:00 2001 From: loinsir Date: Wed, 13 Dec 2023 14:28:21 +0900 Subject: [PATCH 12/12] =?UTF-8?q?:white=5Fcheck=5Fmark:=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=ED=95=84=20=ED=83=AD=20=EB=B7=B0=20=EC=8A=A4=ED=81=AC?= =?UTF-8?q?=EB=A1=A4=20=EA=B4=80=EB=A0=A8=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BC=80=EC=9D=B4=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Profile/ProfileViewControllerTests.swift | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/iOS/Layover/LayoverTests/Scenes/Profile/ProfileViewControllerTests.swift b/iOS/Layover/LayoverTests/Scenes/Profile/ProfileViewControllerTests.swift index 5cdb587..788fbf1 100644 --- a/iOS/Layover/LayoverTests/Scenes/Profile/ProfileViewControllerTests.swift +++ b/iOS/Layover/LayoverTests/Scenes/Profile/ProfileViewControllerTests.swift @@ -75,7 +75,7 @@ final class ProfileViewControllerTests: XCTestCase { XCTAssertTrue(spy.fetchProfileCalled, "viewWillAppear가 호출되어 fetchProfile이 호출했다") } - func test_스크롤이_바닥에_닿아_scrollViewBeginDecelerating이_호출되면_fetchPosts가_호출된다() { + func test_스크롤이_바닥에_닿아_scrollViewBeginDecelerating이_호출되면_fetchMorePosts가_호출된다() { // arrange let spy = ProfileBusinessLogicSpy() sut.interactor = spy @@ -91,7 +91,7 @@ final class ProfileViewControllerTests: XCTestCase { XCTAssertTrue(spy.fetchMorePostsCalled, "스크롤이 바닥에 닿아 scrollViewBeginDecelerating이 호출되어 fetchPosts가 호출했다") } - func test_스크롤이_바닥에_닿지_않고_scrollViewBeginDecelerating이_호출되면_fetchPosts가_호출된다() { + func test_스크롤이_바닥에_닿지_않고_scrollViewBeginDecelerating이_호출되면_fetchMorePosts가_호출된다() { // arrange let spy = ProfileBusinessLogicSpy() sut.interactor = spy @@ -106,4 +106,20 @@ final class ProfileViewControllerTests: XCTestCase { // assert XCTAssertFalse(spy.fetchMorePostsCalled, "스크롤이 바닥에 닿지 않고 scrollViewBeginDecelerating이 호출되어 fetchPosts가 호출되지 않았다") } + + func test_스크롤을_아래로_당겨서_scrollViewBeginDecelerating이_호출되면_fetchMorePosts가_호출되지_않았다() { + // arrange + let spy = ProfileBusinessLogicSpy() + sut.interactor = spy + let scrollView = UIScrollView(frame: .init(x: 0, y: 0, width: 100, height: 100)) + scrollView.contentSize.height = 50 + scrollView.contentOffset.y = -100 + + + // act + sut.scrollViewWillBeginDecelerating(scrollView) + + // assert + XCTAssertFalse(spy.fetchMorePostsCalled, "스크롤이 아래로 당겨져서 scrollViewBeginDecelerating이 호출되어 fetchPosts가 호출되었다") + } }