Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
7416901
feat: PopupOverview 엔티티 구현
MinwooJe Mar 12, 2025
fbfaa5c
feat: PopupOverviewDTO 구현
MinwooJe Mar 12, 2025
3e096c8
feat: InterestCategoryDTO 구현
MinwooJe Mar 12, 2025
401c481
refactor: InterestCategory의 다른 레이어 의존성 제거
MinwooJe Mar 12, 2025
4a38bcc
refactor: userInterestPopups의 키 타입을 InterestCategoryDTO로 변경
MinwooJe Mar 12, 2025
9372cb8
rename: InterestCategory 폴더 변경
MinwooJe Mar 12, 2025
1164707
refactor: MainCategory -> PopupSectionCategory 이름 변경 및 구조 수정
MinwooJe Mar 12, 2025
8342424
refactor: PopupOverview DTO/엔티티에 popupId 필드 추가
MinwooJe Mar 12, 2025
12556f6
rename: 파일 순서 변경
MinwooJe Mar 12, 2025
a7fad14
refactor: 전체보기 관련 APIConstant 수정
MinwooJe Mar 12, 2025
da6f100
feat: 전체보기 data fetch 레포지토리 로직 구현
MinwooJe Mar 12, 2025
ec2fd74
hotfix: toEntity 메서드의 리턴 값에 popupId 추가
MinwooJe Mar 12, 2025
6386573
feat: feat: 전체보기 data fetch 유즈케이스 로직 구현
MinwooJe Mar 12, 2025
df5a7d5
feat: 전체보기 뷰모델 구현
MinwooJe Mar 17, 2025
9f0f1fa
feat: 전체보기 테이블 뷰 셀 구현
MinwooJe Mar 17, 2025
bdd39c9
feat: 전체보기 뷰 컨트롤러 구현
MinwooJe Mar 17, 2025
64ae6b0
feat: CategoryMapper 구현
MinwooJe Mar 17, 2025
8f09770
feat: 전체보기화면 내비게이션 바 기능 구현
MinwooJe Mar 17, 2025
b240ce5
hotfix: fetchMore 주석처리 및 reloadData 메인스레드에서 실행하도록 수정
MinwooJe Mar 17, 2025
b1453d3
refactor: 전체보기 버튼을 레이블과 이미지 뷰로 변경
MinwooJe Mar 17, 2025
e670810
feat: 헤더 탭 시 화면 전환 구현
MinwooJe Mar 17, 2025
0e652dc
feat: 컬렉션 뷰 셀 탭 시 화면전환 구현
MinwooJe Mar 17, 2025
7041b90
rename: CategoryMapper 폴더 이동
MinwooJe Mar 17, 2025
2776ffc
refactor: 메인화면 Mock 데이터 상수화
MinwooJe Mar 17, 2025
d962b6c
design: 주소 이미지 교체
MinwooJe Mar 17, 2025
86d6028
design: 지금 놓치면 안될 팝업스토어 주소 스택뷰 레이아웃 수정
MinwooJe Mar 17, 2025
5652ee6
feat: Mock 데이터 추가
MinwooJe Mar 17, 2025
5ef17c0
refactor: InterestCategory의 프레젠테이션 레이어 의존성 제거 및 MainSceneDataSource 관…
MinwooJe Mar 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 73 additions & 1 deletion Popcorn-iOS.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
56 changes: 56 additions & 0 deletions Popcorn-iOS/Source/Data/DTO/Common/InterestCategoryDTO.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//
// InterestCategoryDTO.swift
// Popcorn-iOS
//
// Created by 제민우 on 3/12/25.
//

struct InterestCategoryDTO: Codable, Hashable {
let category: String

init(from entity: InterestCategory) {
switch entity {
case .fashion:
category = "FASHION"
case .beauty:
category = "BEAUTY"
case .food:
category = "FOOD"
case .character:
category = "CHARACTERS"
case .dramaMovie:
category = "MOVIES"
case .lifeStyle:
category = "LIFESTYLE"
case .art:
category = "ART"
case .IT:
category = "IT"
case .sports:
category = "SPORTS"
case .celebrity:
category = "CELEBRITY"
case .pet:
category = "PETS"
}
}
}

extension InterestCategoryDTO {
func toEntity() -> InterestCategory? {
return switch category {
case "FASHION": InterestCategory.fashion
case "BEAUTY": InterestCategory.beauty
case "FOOD": InterestCategory.food
case "CHARACTERS": InterestCategory.character
case "MOVIES": InterestCategory.dramaMovie
case "LIFESTYLE": InterestCategory.lifeStyle
case "ART": InterestCategory.art
case "IT": InterestCategory.IT
case "SPORTS": InterestCategory.sports
case "CELEBRITY": InterestCategory.celebrity
case "PETS": InterestCategory.pet
default: nil
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Foundation

struct PopupMainListResponseDTO: Decodable {
let userPickPopups: [PopupPreviewResponseDTO]
let userInterestPopups: [String: [PopupPreviewResponseDTO]]
let userInterestPopups: [InterestCategoryDTO: [PopupPreviewResponseDTO]]
let closingSoonPopups: [PopupPreviewResponseDTO]

enum CodingKeys: String, CodingKey {
Expand All @@ -18,41 +18,3 @@ struct PopupMainListResponseDTO: Decodable {
case closingSoonPopups = "allPopups"
}
}

enum DtoInterestCategory: String {
case fashion = "패션"
case beauty = "뷰티"
case food = "음식"
case character = "캐릭터"
case dramaMovie = "영화 / 드라마"
case lifeStyle = "라이프스타일"
case art = "아트"
case IT = "IT"
case celebrity = "셀럽"
case pet = "반려동물"

var convert: InterestCategory {
switch self {
case .fashion:
return .fashion
case .beauty:
return .beauty
case .food:
return .food
case .character:
return .character
case .dramaMovie:
return .dramaMovie
case .lifeStyle:
return .lifeStyle
case .art:
return .art
case .IT:
return .IT
case .celebrity:
return .celebrity
case .pet:
return .pet
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//
// PopupOverviewResponseDTO.swift
// Popcorn-iOS
//
// Created by 제민우 on 3/12/25.
//

import Foundation

struct PopupOverviewResponseDTO: Decodable {
let popupId: Int
let popupImageUrl: String
let popupTitle: String
let startDate: String
let endDate: String
let address: String

enum CodingKeys: String, CodingKey {
case popupId
case popupImageUrl = "popupImage"
case popupTitle = "title"
case startDate = "startedAt"
case endDate = "endedAt"
case address = "location"
}
}

extension PopupOverviewResponseDTO {
func toEntity() -> PopupOverview {
let errorDate = DateFormatter.apiDateFormatter.date(from: "1900-01-01 00:00:00")!
let endDate = DateFormatter.apiDateFormatter.date(from: endDate) ?? errorDate
let startDate = DateFormatter.apiDateFormatter.date(from: startDate) ?? errorDate

return PopupOverview(
popupId: popupId,
popupImageUrl: popupImageUrl,
popupTitle: popupTitle,
startDate: startDate,
endDate: endDate,
address: address
)
}
}
6 changes: 2 additions & 4 deletions Popcorn-iOS/Source/Data/Network/APIConstant.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,8 @@ struct APIConstant {

// MARK: - Main Scene
static let mainScenePath = "/popups/home"
static let moreUserPickPath = "/popups/likes"
static let moreInterestPath = "/popups/interest"

static func moreUserPickPath(category: String) -> String {
static let userPickOverviewPath = "/popups/likes"
static func interestOverviewPath(category: String) -> String {
return "/popups/interests/\(category)"
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,60 @@ final class PopupListRepository: PopupListRepositoryProtocol {
}
}

func fetchCategorizedPopupList(completion: @escaping (Result<[PopupPreview], NetworkError>) -> Void) {
// TODO: API 나온 후 구현
func fetchPopupOverview(
category: PopupSectionCategory,
page: Int,
completion: @escaping (Result<[PopupOverview], Error>) -> Void
) {
guard let token = tokenRepository.fetchAccessToken() else {
// TODO: TokenRepository에서 access token 만료 시 자동으로 reissue 하는 로직 구현 후 리팩토링
completion(.failure(NSError(
domain: "PopupListRepository",
code: -1,
userInfo: [NSLocalizedDescriptionKey: "액세스 토큰 만료"]
)))
return
}

if case .userPick = category {
let userPickOverviewEndpoint = Endpoint<[PopupOverviewResponseDTO]>(
httpMethod: .get,
path: APIConstant.userPickOverviewPath,
queryItems: [URLQueryItem(name: "page", value: String(page))],
headers: ["Authorization": "Bearer \(token)"]
)

networkManager.request(endpoint: userPickOverviewEndpoint) { result in
switch result {
case .success(let endpoint):
completion(.success(endpoint.map { $0.toEntity() }))
case .failure(let error):
completion(.failure(error))
}
}
} else if case .userInterest(let interestCategory) = category {
guard let interestCategory else {
print("관심사 카테고리가 옵셔널 타입입니다.")
return
}

let interestCategoryString: String = InterestCategoryDTO(from: interestCategory).category
let interestOverviewEndpoint = Endpoint<[PopupOverviewResponseDTO]>(
httpMethod: .get,
path: APIConstant.interestOverviewPath(category: interestCategoryString),
queryItems: [URLQueryItem(name: "page", value: String(page))],
headers: ["Authorization": "Bearer \(token)"]
)

networkManager.request(endpoint: interestOverviewEndpoint) { result in
switch result {
case .success(let endpoint):
completion(.success(endpoint.map { $0.toEntity() }))
case .failure(let error):
completion(.failure(error))
}
}
}
}
}

Expand All @@ -125,7 +177,7 @@ extension PopupListRepository {
let userPickPopups = mainListResponseDTO.userPickPopups.map { $0.toEntity() }

let userInterestPopups: [UserInterestPopup] = mainListResponseDTO.userInterestPopups.compactMap { key, value in
guard let interestCategory = InterestCategory(serverValue: key) else { return nil }
guard let interestCategory = key.toEntity() else { return nil }
return UserInterestPopup(
interestCategory: interestCategory,
popups: value.map { $0.toEntity() }
Expand Down
22 changes: 22 additions & 0 deletions Popcorn-iOS/Source/Domain/Entities/Category/InterestCategory.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// InterestCategory.swift
// Popcorn-iOS
//
// Created by 김성훈 on 2/19/25.
//

import Foundation

enum InterestCategory {
case fashion
case beauty
case food
case character
case dramaMovie
case lifeStyle
case art
case IT
case sports
case celebrity
case pet
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// PopupSectionCategory.swift
// Popcorn-iOS
//
// Created by 제민우 on 3/12/25.
//

enum PopupSectionCategory {
case todayRecommend
case userPick
case userInterest(InterestCategory?)
case closingSoon
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// PopupOverview.swift
// Popcorn-iOS
//
// Created by 제민우 on 3/12/25.
//

import Foundation

struct PopupOverview {
let popupId: Int
let popupImageUrl: String
let popupTitle: String
let startDate: Date
let endDate: Date
let address: String
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,9 @@

protocol PopupListRepositoryProtocol {
func fetchPopupMainList(completion: @escaping (Result<PopupMainList, Error>) -> Void)
func fetchCategorizedPopupList(completion: @escaping (Result<[PopupPreview], NetworkError>) -> Void)
func fetchPopupOverview(
category: PopupSectionCategory,
page: Int,
completion: @escaping (Result<[PopupOverview], Error>) -> Void
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,18 @@ final class PopupFetchListUseCase: PopupFetchListUseCaseProtocol {
repository.fetchPopupMainList(completion: completion)
}

func fetchCategorizedPopupList(completion: @escaping (Result<[PopupPreview], NetworkError>) -> Void) {
// TODO: API 나온 후 구현
func fetchPopupOverview(
category: PopupSectionCategory,
page: Int,
completion: @escaping (Result<[PopupOverview], any Error>) -> Void
) {
repository.fetchPopupOverview(category: category, page: page) { result in
switch result {
case .success(let endpoint):
completion(.success(endpoint))
case .failure(let error):
completion(.failure(error))
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,9 @@

protocol PopupFetchListUseCaseProtocol {
func fetchPopupMainList(completion: @escaping (Result<PopupMainList, Error>) -> Void)
func fetchCategorizedPopupList(completion: @escaping (Result<[PopupPreview], NetworkError>) -> Void)
func fetchPopupOverview(
category: PopupSectionCategory,
page: Int,
completion: @escaping (Result<[PopupOverview], Error>) -> Void
)
}
Loading