Skip to content

Commit da2ae77

Browse files
authored
[Feat-T3-203] 제보 히스토리 API 연동 (#72)
* Feat: 제보 기록 조회 네트워트 구성 요소 구현 (#T3-203) * Feat: 제보 히스토리 Domain - Presentation 연결 (#T3-203)
1 parent ef1fed3 commit da2ae77

File tree

8 files changed

+120
-19
lines changed

8 files changed

+120
-19
lines changed

Projects/DataSource/Sources/DTO/ReportDTO.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,20 @@ struct ReportDTO: Decodable {
3535
address: reportLocation),
3636
photoUrls: reportImageUrls ?? [])
3737
}
38+
39+
func toReportEntity(date: String) throws -> ReportEntity {
40+
guard let reportId else { throw NetworkError.decodingError }
41+
return ReportEntity(
42+
id: reportId,
43+
title: reportTitle,
44+
date: date,
45+
type: ReportType(rawValue: reportCategory) ?? .transportation,
46+
progress: ReportProgress(rawValue: reportStatus) ?? .received,
47+
content: reportContent,
48+
location: LocationEntity(
49+
longitude: longitude,
50+
latitude: latitude,
51+
address: reportLocation),
52+
photoUrls: reportImageUrls ?? [])
53+
}
3854
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//
2+
// ReportDictonaryDTO.swift
3+
// DataSource
4+
//
5+
// Created by 최정인 on 11/21/25.
6+
//
7+
8+
struct ReportDictonaryDTO: Decodable {
9+
let reportInfos: [String: [ReportDTO]]
10+
}

Projects/DataSource/Sources/Endpoint/ReportEndpoint.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,30 @@
66
//
77

88
enum ReportEndpoint {
9+
case fetchReports
910
case fetchReportDetail(reportId: Int)
1011
}
1112

1213
extension ReportEndpoint: Endpoint {
1314
var baseURL: String {
1415
switch self {
15-
case .fetchReportDetail:
16+
case .fetchReports, .fetchReportDetail:
1617
return AppProperties.baseURL + "/api/v2/reports"
1718
}
1819
}
1920

2021
var path: String {
2122
switch self {
23+
case .fetchReports:
24+
return baseURL
2225
case .fetchReportDetail(let reportId):
23-
"\(baseURL)/\(reportId)"
26+
return "\(baseURL)/\(reportId)"
2427
}
2528
}
2629

2730
var method: HTTPMethod {
2831
switch self {
29-
case .fetchReportDetail:
32+
case .fetchReports, .fetchReportDetail:
3033
.get
3134
}
3235
}

Projects/DataSource/Sources/Repository/ReportRepository.swift

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,25 @@ final class ReportRepository: ReportRepositoryProtocol {
1414

1515
}
1616

17+
func fetchReports() async throws -> [ReportEntity] {
18+
let endpoint = ReportEndpoint.fetchReports
19+
guard let response = try await networkService.request(endpoint: endpoint, type: ReportDictonaryDTO.self)
20+
else { return [] }
21+
22+
var reportEntities: [ReportEntity] = []
23+
for (date, reports) in response.reportInfos {
24+
let reportHistories = reports.compactMap({ try? $0.toReportEntity(date: date) })
25+
reportEntities += reportHistories
26+
}
27+
28+
return reportEntities
29+
}
30+
1731
func fetchReportDetail(reportId: Int) async throws -> ReportEntity? {
1832
let endpoint = ReportEndpoint.fetchReportDetail(reportId: reportId)
19-
guard let response = try await networkService.request(endpoint: endpoint, type: ReportDTO.self) else { return nil }
33+
guard let response = try await networkService.request(endpoint: endpoint, type: ReportDTO.self)
34+
else { return nil }
35+
2036
return try response.toReportEntity()
2137
}
2238
}

Projects/Domain/Sources/Protocol/Repository/ReportRepositoryProtocol.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
public protocol ReportRepositoryProtocol {
99
func report(reportEntity: ReportEntity) async
1010

11+
/// 제보 목록을 조회합니다.
12+
/// - Returns: 조회된 제보 목록
13+
func fetchReports() async throws -> [ReportEntity]
14+
1115
/// 제보 상세 기록을 조회합니다.
1216
/// - Parameter reportId: 조회할 제보의 ID
1317
/// - Returns: 조회된 제보

Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,11 @@ public struct PresentationDependencyAssembler: DependencyAssemblerProtocol {
128128
return ReportRegistrationViewModel(reportUseCase: reportUseCase)
129129
}
130130

131-
DIContainer.shared
132-
.register(type: ReportHistoryViewModel.self) { container in
133-
return ReportHistoryViewModel()
131+
DIContainer.shared.register(type: ReportHistoryViewModel.self) { container in
132+
guard let reportRepository = container.resolve(type: ReportRepositoryProtocol.self)
133+
else { fatalError("reportRepository 의존성이 등록되지 않았습니다.") }
134+
135+
return ReportHistoryViewModel(reportRepository: reportRepository)
134136
}
135137

136138
DIContainer.shared.register(type: ReportDetailViewModel.self) { container in

Projects/Presentation/Sources/Report/View/ReportHistoryViewController.swift

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import Combine
99
import Domain
10+
import Shared
1011
import SnapKit
1112
import UIKit
1213

@@ -262,7 +263,6 @@ final class ReportHistoryViewController: BaseViewController<ReportHistoryViewMod
262263
var snapshot = NSDiffableDataSourceSnapshot<ProgressSection, ReportProgressItem>()
263264
snapshot.appendSections([.main])
264265
snapshot.appendItems(items, toSection: .main)
265-
print(items)
266266
progressDataSource?.apply(snapshot, animatingDifferences: true)
267267
}
268268

@@ -316,6 +316,28 @@ extension ReportHistoryViewController: UITableViewDelegate {
316316

317317
return header
318318
}
319+
320+
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
321+
guard let snapshot = historyDataSource?.snapshot()
322+
else { return }
323+
324+
let sectionID = snapshot.sectionIdentifiers[indexPath.section]
325+
let items = snapshot.itemIdentifiers(inSection: sectionID)
326+
327+
guard indexPath.row < items.count
328+
else { return }
329+
330+
let item = items[indexPath.row]
331+
332+
guard let reportDetailViewModel = DIContainer.shared.resolve(type: ReportDetailViewModel.self)
333+
else { fatalError("reportDetailViewModel 의존성이 등록되지 않았습니다.") }
334+
335+
let reportDetailViewController = ReportDetailViewController(viewModel: reportDetailViewModel, reportId: item.id)
336+
reportDetailViewController.hidesBottomBarWhenPushed = true
337+
338+
self.navigationController?.pushViewController(reportDetailViewController, animated: true)
339+
tableView.deselectRow(at: indexPath, animated: true)
340+
}
319341
}
320342

321343
extension ReportHistoryViewController: ReportCategoryTableViewControllerDelegate {

Projects/Presentation/Sources/Report/ViewModel/ReportHistoryViewModel.swift

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ import Foundation
1212
final class ReportHistoryViewModel: ViewModel {
1313
enum Input {
1414
case fetchReports
15-
case fetchReport(index: Int)
1615
case filterCategory(type: ReportType)
1716
case filterProgress(progress: ReportProgress)
1817
}
1918

2019
struct Output {
2120
let progressPublisher: AnyPublisher<[ReportProgressItem], Never>
21+
let selectedProgressPublisher: AnyPublisher<ReportProgress?, Never>
2222
let categoryPublisher: AnyPublisher<[ReportType], Never>
2323
let selectedCategoryPublisher: AnyPublisher<ReportType?, Never>
2424
let reportsPublisher: AnyPublisher<[ReportHistoryItem], Never>
@@ -27,14 +27,17 @@ final class ReportHistoryViewModel: ViewModel {
2727

2828
private(set) var output: Output
2929
private let progressSubject = CurrentValueSubject<[ReportProgressItem], Never>([])
30+
private let selectedProgressSubject = CurrentValueSubject<ReportProgress?, Never>(nil)
3031
private let categorySubject = CurrentValueSubject<[ReportType], Never>([])
3132
private let selectedCategorySubject = CurrentValueSubject<ReportType?, Never>(nil)
3233
private let reportSubject = CurrentValueSubject<[ReportHistoryItem], Never>([])
3334
private let selectedReportSubject = PassthroughSubject<Int?, Never>()
3435
private(set) var selectedReportCategory: ReportType?
3536
private var reports: [ReportHistoryItem] = []
37+
private let reportRepository: ReportRepositoryProtocol
3638

37-
init() {
39+
init(reportRepository: ReportRepositoryProtocol) {
40+
self.reportRepository = reportRepository
3841
progressSubject
3942
.send(
4043
ReportProgress.allCases.map { ReportProgressItem(
@@ -46,6 +49,7 @@ final class ReportHistoryViewModel: ViewModel {
4649

4750
output = Output(
4851
progressPublisher: progressSubject.eraseToAnyPublisher(),
52+
selectedProgressPublisher: selectedProgressSubject.eraseToAnyPublisher(),
4953
categoryPublisher: categorySubject.eraseToAnyPublisher(),
5054
selectedCategoryPublisher: selectedCategorySubject.eraseToAnyPublisher(),
5155
reportsPublisher: reportSubject.eraseToAnyPublisher(),
@@ -56,8 +60,6 @@ final class ReportHistoryViewModel: ViewModel {
5660
switch input {
5761
case .fetchReports:
5862
fetchReports()
59-
case .fetchReport(let index):
60-
fetchReport(index: index)
6163
case .filterCategory(let type):
6264
filterCategory(reportType: type)
6365
case .filterProgress(let progress):
@@ -99,16 +101,42 @@ final class ReportHistoryViewModel: ViewModel {
99101
}
100102

101103
private func filterReports() {
104+
let selectedProgress = selectedProgressSubject.value
105+
let selectedCategory = selectedCategorySubject.value
102106

107+
var filteredReports = reports
108+
if let selectedProgress {
109+
filteredReports = filteredReports.filter({ $0.progress == selectedProgress })
110+
}
111+
if let selectedCategory {
112+
filteredReports = filteredReports.filter({ $0.type == selectedCategory })
113+
}
114+
reportSubject.send(filteredReports)
103115
}
104116

105117
private func fetchReports() {
106-
//reportSubject.send(ReportHistoryItem.dummyData)
107-
}
108-
109-
private func fetchReport(index: Int) {
110-
guard index < reports.count else { return }
111-
112-
selectedReportSubject.send(reports[index].id)
118+
Task {
119+
do {
120+
let reportEntities = try await reportRepository.fetchReports()
121+
122+
var reportHistoryItems: [ReportHistoryItem] = []
123+
for reportEntity in reportEntities {
124+
let reportHistoryItem = ReportHistoryItem(
125+
id: reportEntity.id,
126+
title: reportEntity.title,
127+
thumbnailUrl: reportEntity.photoUrls.first ?? "",
128+
date: reportEntity.date ?? "",
129+
type: reportEntity.type,
130+
progress: reportEntity.progress,
131+
location: reportEntity.location.address ?? "")
132+
reportHistoryItems.append(reportHistoryItem)
133+
}
134+
135+
reports = reportHistoryItems
136+
reportSubject.send(reportHistoryItems)
137+
} catch {
138+
// TODO: 에러 처리
139+
}
140+
}
113141
}
114142
}

0 commit comments

Comments
 (0)