Skip to content

Commit 3caa139

Browse files
authored
[#21] Store 프로토콜을 채택한 새로운 SearchViewModel을 구현하고 뷰에 연결한다 (#52)
* refactor: 유저 uid 의존성 추가 * refactor: 필요한 데이터만을 받도록 개선 * refactor: 순수 도메인 모델 재구성 * refactor: 순수하게 받아오는 형태로 변경 * fix: 데이터 저장 필드명 통일 * feat: 각 레이어에 필요한 DTO / 모델 구현 * feat: 웹페이지의 메타 데이터를 받아오는 서비스 객체 구현 * feat: 웹페이지의 메타 데이터를 받아오는 데이터 레이어 구현 * feat: 웹페이지 데이터를 추가, 삭제 및 불러오는 도메인 레이어 구현 * feat: SearchViewModel 구현 및 적용 * fix: 비동기 호출에 따른 append로 뷰에 표시될 때 순서가 달라지는 현상 해결 * fix: LoadingView 중일 때 하단에 컨텐츠가 비치는 현상 제거 * refactor: url의 host 및 url에 포함된 특수문자 처리 * design: 주변 패딩 제거 * ui: padding 추가 * feat: webInfoCard에 내비게이션 추가 * refactor: 저장 원 url 그대로 불러오도록 개선 * fix: 스와이프 액션으로 웹페이지 리스트 요소가 제거되지 않는 현상 수정 * feat: 이미지를 로드하지 못했을 때의 이미지를 밖에서 받아올 수 있도록 추가 * feat: WebPageView의 내비게이션 타이틀 설정 * feat: 로컬 파일 URL도 가능하도록 구현
1 parent ff49a36 commit 3caa139

24 files changed

Lines changed: 787 additions & 324 deletions

DevLog/App/Assembler/DataAssembler.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,5 +44,12 @@ final class DataAssembler: Assembler {
4444
container.register(PushNotificationRepository.self) {
4545
PushNotificationRepositoryImpl(pushNotificationService: container.resolve(PushNotificationService.self))
4646
}
47+
48+
container.register(WebPageRepository.self) {
49+
WebPageRepositoryImpl(
50+
webPageService: container.resolve(WebPageService.self),
51+
metadataService: container.resolve(WebPageMetadataService.self)
52+
)
53+
}
4754
}
4855
}

DevLog/App/Assembler/DomainAssembler.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,17 @@ final class DomainAssembler: Assembler {
5050
container.register(FetchTodosByKindUseCase.self) {
5151
FetchTodosByKindUseCaseImpl(container.resolve(TodoRepository.self))
5252
}
53+
54+
container.register(FetchWebPagesUseCase.self) {
55+
FetchWebPagesUseCaseImpl(container.resolve(WebPageRepository.self))
56+
}
57+
58+
container.register(AddWebPageUseCase.self) {
59+
AddWebPageUseCaseImpl(container.resolve(WebPageRepository.self))
60+
}
61+
62+
container.register(DeleteWebPageUseCase.self) {
63+
DeleteWebPageUseCaseImpl(container.resolve(WebPageRepository.self))
64+
}
5365
}
5466
}

DevLog/App/Assembler/InfraAssembler.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,13 @@ final class InfraAssembler: Assembler {
4343
container.register(PushNotificationService.self) {
4444
PushNotificationService()
4545
}
46+
47+
container.register(WebPageService.self) {
48+
WebPageService()
49+
}
50+
51+
container.register(WebPageMetadataService.self) {
52+
WebPageMetadataService()
53+
}
4654
}
4755
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//
2+
// WebPageMetadata.swift
3+
// DevLog
4+
//
5+
// Created by 최윤진 on 2/9/26.
6+
//
7+
8+
import Foundation
9+
10+
struct WebPageMetadata: Hashable {
11+
let title: String?
12+
let url: URL
13+
let displayURL: URL
14+
let imageURL: URL?
15+
16+
func toDomain() -> WebPage {
17+
WebPage(
18+
title: title,
19+
url: url,
20+
displayURL: displayURL,
21+
imageURL: imageURL
22+
)
23+
}
24+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//
2+
// WebPageRepositoryImpl.swift
3+
// DevLog
4+
//
5+
// Created by 최윤진 on 2/8/26.
6+
//
7+
8+
final class WebPageRepositoryImpl: WebPageRepository {
9+
private let webPageService: WebPageService
10+
private let metadataService: WebPageMetadataService
11+
12+
init(
13+
webPageService: WebPageService,
14+
metadataService: WebPageMetadataService
15+
) {
16+
self.webPageService = webPageService
17+
self.metadataService = metadataService
18+
}
19+
20+
func fetch() async throws -> [WebPageMetadata] {
21+
let responses = try await webPageService.fetchWebPages()
22+
let indexedResponses = responses.enumerated().map { ($0.offset, $0.element) }
23+
24+
return try await withThrowingTaskGroup(of: (Int, WebPageMetadata?).self) { group in
25+
for (index, response) in indexedResponses {
26+
group.addTask {
27+
let metadata = try? await self.metadataService.fetchMetadata(from: response)
28+
return (index, metadata)
29+
}
30+
}
31+
32+
var results: [WebPageMetadata?] = Array(repeating: nil, count: responses.count)
33+
for try await (index, metadata) in group {
34+
results[index] = metadata
35+
}
36+
37+
return results.compactMap { $0 }
38+
}
39+
}
40+
41+
func upsert(_ urlString: String) async throws -> WebPageMetadata {
42+
try await webPageService.upsertWebPage(urlString)
43+
let response = WebPageResponse(urlString: urlString)
44+
return try await metadataService.fetchMetadata(from: response)
45+
}
46+
47+
func delete(_ urlString: String) async throws {
48+
try await webPageService.deleteWebPage(urlString)
49+
}
50+
}

DevLog/Domain/Entity/WebPage.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//
2+
// WebPage.swift
3+
// DevLog
4+
//
5+
// Created by 최윤진 on 2/9/26.
6+
//
7+
8+
import Foundation
9+
10+
struct WebPage {
11+
let title: String?
12+
let url: URL
13+
let displayURL: URL
14+
let imageURL: URL?
15+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//
2+
// WebPageRepository.swift
3+
// DevLog
4+
//
5+
// Created by 최윤진 on 2/8/26.
6+
//
7+
8+
protocol WebPageRepository {
9+
func fetch() async throws -> [WebPageMetadata]
10+
func upsert(_ urlString: String) async throws -> WebPageMetadata
11+
func delete(_ urlString: String) async throws
12+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
//
2+
// FetchWebPagesUseCase.swift
3+
// DevLog
4+
//
5+
// Created by 최윤진 on 2/9/26.
6+
//
7+
8+
protocol FetchWebPagesUseCase {
9+
var repository: WebPageRepository { get }
10+
func execute() async throws -> [WebPage]
11+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//
2+
// FetchWebUseCaseImpl.swift
3+
// DevLog
4+
//
5+
// Created by 최윤진 on 2/9/26.
6+
//
7+
8+
final class FetchWebPagesUseCaseImpl: FetchWebPagesUseCase {
9+
let repository: WebPageRepository
10+
11+
init(_ repository: WebPageRepository) {
12+
self.repository = repository
13+
}
14+
15+
func execute() async throws -> [WebPage] {
16+
return try await repository.fetch().map { $0.toDomain() }
17+
}
18+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
//
2+
// AddWebPageUseCase.swift
3+
// DevLog
4+
//
5+
// Created by 최윤진 on 2/8/26.
6+
//
7+
8+
protocol AddWebPageUseCase {
9+
var repository: WebPageRepository { get }
10+
func execute(_ urlString: String) async throws -> WebPage
11+
}

0 commit comments

Comments
 (0)