Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@
import Foundation

struct PopupInformationResponseDTO: Decodable {
let popupId: Int
let popupImagesUrl: [String]
let popupTitle: String
let id: Int
let imageUrls: [String]
let title: String
let startDate: String
let endDate: String
let isPick: Bool
let hashtag: String

let address: String
let officialLink: String
Expand All @@ -22,18 +23,19 @@ struct PopupInformationResponseDTO: Decodable {
let reservationUrl: String

enum CodingKeys: String, CodingKey {
case popupId
case popupImagesUrl = "popupImage"
case popupTitle = "title"
case id = "popupId"
case imageUrls = "popupImage"
case title
case startDate = "startedAt"
case endDate = "endedAt"
case isPick = "isLiked"
case hashtag = "interest"

case address = "location"
case officialLink = "organizerUrl"
case businesesHours = "hours"
case introduce = "contents"
case reservationUrl
case businesesHours = "business_hours"
case introduce = "content"
case reservationUrl = "reservationUrl"
}
}

Expand All @@ -44,13 +46,13 @@ extension PopupInformationResponseDTO {
let endDate = DateFormatter.apiDateFormatter.date(from: endDate) ?? errorDate

return PopupInformation(
id: popupId,
imageUrls: popupImagesUrl,
title: popupTitle,
id: id,
imageUrls: imageUrls,
title: title,
startDate: startDate,
endDate: endDate,
isPick: isPick,
hashTags: [],
hashTags: [hashtag],
address: address,
organizationUrl: officialLink,
businesesHours: businesesHours,
Expand Down
4 changes: 2 additions & 2 deletions Popcorn-iOS/Source/Data/Network/APIConstant.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ struct APIConstant {
}

static func popupRatingPath(popupId: String) -> String {
return "/popups/reviewrating/\(popupId)"
return "/api/popups/reviewrating/\(popupId)"
}

static func popupReviewPath(popupId: String) -> String {
return "/popups/reviews/\(popupId)"
return "/api/reviews/popups/\(popupId)"
}

static func popupReviewToggleLike(popupId: String) -> String {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,186 +17,98 @@ final class PopupDetailRepository: PopupDetailRepositoryProtocol {
}

func fetchPopupAllData(
popupId: Int,
completion: @escaping (Result<(PopupInformation, PopupRatingDistribution, PopupReviewList), any Error>
) -> Void) {
for popupId: Int
) async throws -> (PopupInformation, PopupRatingDistribution, PopupReviewList) {
// TODO: TokenRepository에서 access token 만료 시 자동으로 reissue 하는 로직 구현 후 리팩토링
guard let token = tokenRepository.fetchAccessToken() else {
completion(.failure(NSError(
throw NSError(
domain: "PopupDetailRepository",
code: -1,
userInfo: [NSLocalizedDescriptionKey: "액세스 토큰 만료"]
)))
return
)
}

let dispatchGroup = DispatchGroup()
var capturedErrors = [NetworkError]()
let lock = NSLock()
async let information = fetchInformation(popupId: popupId, token: token)
async let ratingDistribution = fetchRatingDistribution(popupId: popupId, token: token)
async let reviewList = fetchReviewList(popupId: popupId, page: 1)

var popupInformationResponse: PopupInformationResponseDTO?
var popupRatingDistributionResponse: PopupRatingDistributionResponseDTO?
var popupReviewListResponse: PopupReviewListResponseDTO?
return try await (information, ratingDistribution, reviewList)
}

let popupInformationEndpoint = Endpoint<PopupInformationResponseDTO>(
httpMethod: .get,
path: APIConstant.popupDetailPath(popupId: APIConstant.popupDetailPath(popupId: String(popupId))),
func togglePopupPick(popupId: Int) async throws -> Bool {
// TODO: TokenRepository에서 access token 만료 시 자동으로 reissue 하는 로직 구현 후 리팩토링
guard let token = tokenRepository.fetchAccessToken() else {
throw NSError(
domain: "PopupDetailRepository",
code: -1,
userInfo: [NSLocalizedDescriptionKey: "액세스 토큰 만료"]
)
}

let endpoint = Endpoint<DefaultResponseDTO<Bool>>(
httpMethod: .post,
path: APIConstant.popupTogglePick(popupId: String(popupId)),
headers: ["Authorization": "Bearer \(token)"]
)

let popupRatingDistributionEndpoint = Endpoint<PopupRatingDistributionResponseDTO>(
httpMethod: .get,
path: APIConstant.popupRatingPath(popupId: String(popupId))
)
let isPick = try await networkManager.request(endpoint: endpoint).data
return isPick
}
}

let popupReviewListEndpoint = Endpoint<PopupReviewListResponseDTO>(
extension PopupDetailRepository {
func fetchInformation(popupId: Int, token: String) async throws -> PopupInformation {
print(popupId)
let endpoint = Endpoint<PopupInformationResponseDTO>(
httpMethod: .get,
path: APIConstant.popupReviewPath(popupId: String(popupId)),
queryItems: [URLQueryItem(name: "page", value: "1")],
path: APIConstant.popupDetailPath(popupId: String(1)), // TODO: - 서버 데이터 변경 후 1을 popupId로 변경
headers: ["Authorization": "Bearer \(token)"]
)

dispatchGroup.enter()
networkManager.request(endpoint: popupInformationEndpoint) { result in
lock.lock()
defer {
lock.unlock()
dispatchGroup.leave()
}

if !capturedErrors.isEmpty { return }

switch result {
case .success(let response):
popupInformationResponse = response
case .failure(let error):
capturedErrors.append(error)
}
}

dispatchGroup.enter()
networkManager.request(endpoint: popupRatingDistributionEndpoint) { result in
lock.lock()
defer {
lock.unlock()
dispatchGroup.leave()
}

if !capturedErrors.isEmpty { return }

switch result {
case .success(let response):
popupRatingDistributionResponse = response
case .failure(let error):
capturedErrors.append(error)
}
}

dispatchGroup.enter()
networkManager.request(endpoint: popupReviewListEndpoint) { result in
lock.lock()
defer {
lock.unlock()
dispatchGroup.leave()
}

if !capturedErrors.isEmpty { return }

switch result {
case .success(let response):
popupReviewListResponse = response
case .failure(let error):
capturedErrors.append(error)
}
}

dispatchGroup.notify(queue: .main) {
if !capturedErrors.isEmpty {
let combinedError = NSError(
domain: "PopupDetailRepsitory",
code: -2,
userInfo: [
NSLocalizedDescriptionKey: "상세화면 데이터 요청 실패",
"error": capturedErrors
]
)

completion(.failure(combinedError))
return
}

guard let popupInformationResponse,
let popupRatingDistributionResponse,
let popupReviewListResponse else { return }

let popupReviewList = PopupReviewList(reviews: popupReviewListResponse.reviews.map { $0.toEntity() })

completion(.success((
popupInformationResponse.toEntity(),
popupRatingDistributionResponse.toEntity(),
popupReviewList
)))
do {
return try await networkManager.request(endpoint: endpoint).toEntity()
} catch {
print(#function, error)
throw error
}
}

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

let endpoint = Endpoint<PopupReviewListResponseDTO>(
func fetchRatingDistribution(popupId: Int, token: String) async throws -> PopupRatingDistribution {
let endpoint = Endpoint<DefaultResponseDTO<PopupRatingDistributionResponseDTO>>(
httpMethod: .get,
path: APIConstant.popupReviewPath(popupId: String(popupId)),
queryItems: [URLQueryItem(name: "page", value: String(page))],
headers: ["Authorization": "Bearer \(token)"]
path: APIConstant.popupRatingPath(popupId: String(popupId))
)

networkManager.request(endpoint: endpoint) { result in
switch result {
case .success(let response):
let reviewList = response.reviews.map { $0.toEntity() }
completion(.success(PopupReviewList(reviews: reviewList)))
case .failure(let error):
completion(.failure(error))
}
do {
return try await networkManager.request(endpoint: endpoint).data.toEntity()
} catch {
print(#function, error)
throw error
}
}

func togglePopupPick(popupId: Int, completion: @escaping (Result<Bool, any Error>) -> Void) {
// TODO: TokenRepository에서 access token 만료 시 자동으로 reissue 하는 로직 구현 후 리팩토링
func fetchReviewList(popupId: Int, page: Int) async throws -> PopupReviewList {
guard let token = tokenRepository.fetchAccessToken() else {
completion(.failure(NSError(
throw NSError(
domain: "PopupDetailRepository",
code: -1,
userInfo: [NSLocalizedDescriptionKey: "액세스 토큰 만료"]
)))
return
)
}

let endpoint = Endpoint<DefaultResponseDTO<Bool>>(
httpMethod: .post,
path: APIConstant.popupTogglePick(popupId: String(popupId)),
let endpoint = Endpoint<DefaultResponseDTO<PopupReviewListResponseDTO>>(
httpMethod: .get,
path: APIConstant.popupReviewPath(popupId: String(popupId)),
queryItems: [URLQueryItem(name: "page", value: String(page))],
headers: ["Authorization": "Bearer \(token)"]
)

networkManager.request(endpoint: endpoint) { result in
switch result {
case .success(let response):
let isPick = response.data
completion(.success(isPick))
case .failure(let error):
completion(.failure(error))
}
do {
let dto = try await networkManager.request(endpoint: endpoint).data
return PopupReviewList(reviews: dto.reviews.map { $0.toEntity() })
} catch {
print(#function, error)
throw error
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@ import Foundation

protocol PopupDetailRepositoryProtocol {
func fetchPopupAllData(
popupId: Int,
completion: @escaping (Result<(PopupInformation, PopupRatingDistribution, PopupReviewList), Error>) -> Void
)
for popupId: Int
) async throws -> (PopupInformation, PopupRatingDistribution, PopupReviewList)

func fetchPopupReviews(popupId: Int, page: Int, completion: @escaping (Result<PopupReviewList, Error>) -> Void)
func fetchReviewList(popupId: Int, page: Int) async throws -> PopupReviewList

func togglePopupPick(popupId: Int, completion: @escaping (Result<Bool, Error>) -> Void)
func togglePopupPick(popupId: Int) async throws -> Bool
// 리뷰 좋아요 토글, 리뷰 작성 추가
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,41 +9,32 @@ import Foundation

final class PopupDetailUseCase: PopupDetailUseCaseProtocol {
private let repository: PopupDetailRepositoryProtocol

init(repository: PopupDetailRepositoryProtocol) {
self.repository = repository
}

func fetchPopupAllData(
popupId: Int,
completion: @escaping (Result<(PopupInformation, PopupRatingDistribution, PopupReviewList), Error>
) -> Void) {
repository.fetchPopupAllData(popupId: popupId) { [weak self] result in
guard let self else { return }
switch result {
case .success(let (popupInfo, popupRatingDistribution, popupReviewList)):
var popupInfo = popupInfo
popupInfo.hashTags = self.extractHashTag(from: popupInfo)
completion(.success((popupInfo, popupRatingDistribution, popupReviewList)))
case .failure(let error):
completion(.failure(error))
}
}
for popupId: Int
) async throws -> (PopupInformation, PopupRatingDistribution, PopupReviewList) {
var (information, ratingDistribution, reviewList) = try await repository.fetchPopupAllData(for: popupId)
information.hashTags = extractHashTag(from: information)
return (information, ratingDistribution, reviewList)
}

func fetchPopupReviews(
popupId: Int,
page: Int,
completion: @escaping (Result<PopupReviewList, any Error>
) -> Void
) {
repository.fetchPopupReviews(popupId: popupId, page: page, completion: completion)
page: Int
) async throws -> PopupReviewList {
return try await repository.fetchReviewList(popupId: popupId, page: page)
}

func togglePopupPick(popupId: Int, completion: @escaping (Result<Bool, Error>) -> Void) {
repository.togglePopupPick(popupId: popupId, completion: completion)
func togglePopupPick(popupId: Int) async throws -> Bool {
return try await repository.togglePopupPick(popupId: popupId)
}
}

extension PopupDetailUseCase {
func extractHashTag(from popupInformation: PopupInformation) -> [String] {
let address = popupInformation.address
let dDay = PopupDateFormatter.calculateDDay(from: popupInformation.endDate)
Expand Down
Loading