Skip to content

Commit

Permalink
πŸ”— :: (#199) μ•Œλ¦Ό api 연동
Browse files Browse the repository at this point in the history
  • Loading branch information
juyeong525 authored Mar 28, 2024
2 parents 248e430 + 8f9da5a commit a94af62
Show file tree
Hide file tree
Showing 16 changed files with 335 additions and 56 deletions.
4 changes: 4 additions & 0 deletions Projects/Data/Sources/DI/DataSourceAssembly.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,9 @@ public final class DataSourceAssembly: Assembly {
container.register(RemoteNoticesDataSource.self) { resolver in
RemoteNoticesDataSourceDataSourceImpl(keychain: self.keychain(resolver))
}

container.register(RemoteNotificationsDataSource.self) { resolver in
RemoteNotificationsDataSourceImpl(keychain: self.keychain(resolver))
}
}
}
6 changes: 6 additions & 0 deletions Projects/Data/Sources/DI/RepositoryAssembly.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ public final class RepositoryAssembly: Assembly {
remoteNoticesDataSource: resolver.resolve(RemoteNoticesDataSource.self)!
)
}

container.register(NotificationsRepository.self) { resolver in
NotificationsRepositoryImpl(
remoteNotificationsDataSource: resolver.resolve(RemoteNotificationsDataSource.self)!
)
}
}
// swiftlint:enable function_body_length
}
12 changes: 12 additions & 0 deletions Projects/Data/Sources/DI/UseCaseAssembly.swift
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,18 @@ public final class UseCaseAssembly: Assembly {
noticesRepository: resolver.resolve(NoticesRepository.self)!
)
}

// Notifications
container.register(FetchNotificationListUseCase.self) { resolver in
FetchNotificationListUseCase(
notificationsRepository: resolver.resolve(NotificationsRepository.self)!
)
}
container.register(ReadNotificationUseCase.self) { resolver in
ReadNotificationUseCase(
notificationsRepository: resolver.resolve(NotificationsRepository.self)!
)
}
}
// swiftlint:enable function_body_length
}
38 changes: 38 additions & 0 deletions Projects/Data/Sources/DTO/Notifications/NotificationListDTO.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import Foundation
import Domain

struct NotificationListResponseDTO: Codable {
let notifications: [NotificationResponseDTO]
}

public struct NotificationResponseDTO: Codable {
public let notificationID: Int
public let title, content, topic: String
public let detailID: Int
public let createdAt: String
public let new: Bool

enum CodingKeys: String, CodingKey {
case notificationID = "notification_id"
case title, content, topic
case detailID = "detail_id"
case createdAt = "created_at"
case new
}
}

extension NotificationListResponseDTO {
func toDomain() -> [NotificationEntity] {
notifications.map {
NotificationEntity(
notificationID: $0.notificationID,
title: $0.title,
content: $0.content,
topic: $0.topic,
detailID: $0.detailID,
createdAt: $0.createdAt.toJobisDate().toStringFormat("yyyy.MM.dd"),
new: $0.new
)
}
}
}
60 changes: 60 additions & 0 deletions Projects/Data/Sources/DataSource/API/NotificationsAPI.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import Moya
import AppNetwork
import Domain

public enum NotificationsAPI {
case fetchNotificationList
case patchReadNotification(id: Int)
}

extension NotificationsAPI: JobisAPI {
public typealias ErrorType = JobisError

public var domain: JobisDomain {
return .notifications
}

public var urlPath: String {
switch self {
case .fetchNotificationList:
return ""

case let .patchReadNotification(id):
return "/\(id)"
}
}

public var method: Method {
switch self {
case .fetchNotificationList:
return .get

case .patchReadNotification:
return .patch
}
}

public var task: Task {
switch self {
case .fetchNotificationList:
return .requestParameters(
parameters:
// // TODO: μΆ”ν›„ μ½μŒμ™€ μ•ˆμ½μŒ λΆ„κΈ°μ²˜λ¦¬ ν•„μš”
["is_new": ""],
encoding: URLEncoding.queryString)
default:
return .requestPlain
}
}

public var jwtTokenType: JwtTokenType {
switch self {
default:
return .accessToken
}
}

public var errorMap: [Int: ErrorType]? {
return [:]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import RxSwift
import RxCocoa
import Domain

public protocol RemoteNotificationsDataSource {
func fetchNotificationList() -> Single<[NotificationEntity]>
func patchReadNotification(id: Int) -> Completable
}

final class RemoteNotificationsDataSourceImpl: RemoteBaseDataSource<NotificationsAPI>, RemoteNotificationsDataSource {
public func fetchNotificationList() -> Single<[NotificationEntity]> {
request(.fetchNotificationList)
.map(NotificationListResponseDTO.self)
.map { $0.toDomain() }
}

public func patchReadNotification(id: Int) -> Completable {
request(.patchReadNotification(id: id))
.asCompletable()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import RxSwift
import Domain

struct NotificationsRepositoryImpl: NotificationsRepository {
private let remoteNotificationsDataSource: any RemoteNotificationsDataSource

init(
remoteNotificationsDataSource: any RemoteNotificationsDataSource
) {
self.remoteNotificationsDataSource = remoteNotificationsDataSource
}

func fetchNotificationsList() -> Single<[NotificationEntity]> {
remoteNotificationsDataSource.fetchNotificationList()
}

func patchReadNotification(id: Int) -> Completable {
remoteNotificationsDataSource.patchReadNotification(id: id)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import Foundation

public struct NotificationEntity: Equatable, Hashable {
public let notificationID: Int
public let title, content, topic: String
public let detailID: Int
public let createdAt: String
public let new: Bool

public init(
notificationID: Int,
title: String,
content: String,
topic: String,
detailID: Int,
createdAt: String,
new: Bool
) {
self.notificationID = notificationID
self.title = title
self.content = content
self.topic = topic
self.detailID = detailID
self.createdAt = createdAt
self.new = new
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import RxSwift

public protocol NotificationsRepository {
func fetchNotificationsList() -> Single<[NotificationEntity]>
func patchReadNotification(id: Int) -> Completable
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import RxSwift

public struct FetchNotificationListUseCase {
private let notificationsRepository: any NotificationsRepository

public init(notificationsRepository: any NotificationsRepository) {
self.notificationsRepository = notificationsRepository
}

public func execute() -> Single<[NotificationEntity]> {
notificationsRepository.fetchNotificationsList()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import RxSwift

public struct ReadNotificationUseCase {
private let notificationsRepository: any NotificationsRepository

public init(notificationsRepository: any NotificationsRepository) {
self.notificationsRepository = notificationsRepository
}

public func execute(id: Int) -> Completable {
notificationsRepository.patchReadNotification(id: id)
}
}
1 change: 1 addition & 0 deletions Projects/Modules/AppNetwork/Sources/JobisAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public enum JobisDomain: String {
case presignedURL = ""
case banners
case notices
case notifications
}

extension JobisDomain {
Expand Down
5 changes: 3 additions & 2 deletions Projects/Presentation/Sources/DI/PresentationAssembly.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ public final class PresentationAssembly: Assembly {
container.register(AlarmViewController.self) { resolver in
AlarmViewController(resolver.resolve(AlarmViewModel.self)!)
}
container.register(AlarmViewModel.self) { _ in
container.register(AlarmViewModel.self) { resolver in
AlarmViewModel(
// fetchStudentInfoUseCase: resolver.resolve(FetchStudentInfoUseCase.self)!
fetchNotificationListUseCase: resolver.resolve(FetchNotificationListUseCase.self)!,
readNotificationUseCase: resolver.resolve(ReadNotificationUseCase.self)!
)
}

Expand Down
45 changes: 21 additions & 24 deletions Projects/Presentation/Sources/Home/Alarm/AlarmViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,28 @@ public final class AlarmViewController: BaseViewController<AlarmViewModel> {
}
}

public override func configureViewController() {
alarmTableView.dataSource = self
alarmTableView.delegate = self
public override func bind() {
let input = AlarmViewModel.Input(
viewAppear: viewDidAppearPublisher,
readNotification:
Observable.zip(
alarmTableView.rx.modelSelected(NotificationEntity.self),
alarmTableView.rx.itemSelected
)
)
let output = viewModel.transform(input)

output.notificationList.asObservable()
.bind(to: alarmTableView.rx.items(
cellIdentifier: AlarmTableViewCell.identifier,
cellType: AlarmTableViewCell.self
)) { _, element, cell in
cell.adapt(model: element)
}
.disposed(by: disposeBag)
}

public override func configureViewController() {
self.viewWillDisappearPublisher.asObservable()
.subscribe(onNext: { [weak self] in
self?.showTabbar()
Expand All @@ -41,24 +59,3 @@ public final class AlarmViewController: BaseViewController<AlarmViewModel> {
setSmallTitle(title: "μ•Œλ¦Ό")
}
}

extension AlarmViewController: UITableViewDelegate, UITableViewDataSource {
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 30
}

public func tableView(
_ tableView: UITableView,
cellForRowAt indexPath: IndexPath
) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(
withIdentifier: AlarmTableViewCell.identifier,
for: indexPath
) as? AlarmTableViewCell else { return UITableViewCell() }

cell.setCell()
cell.layoutIfNeeded()

return cell
}
}
53 changes: 44 additions & 9 deletions Projects/Presentation/Sources/Home/Alarm/AlarmViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,56 @@ public final class AlarmViewModel: BaseViewModel, Stepper {
public let steps = PublishRelay<Step>()
private let disposeBag = DisposeBag()

// private let fetchStudentInfoUseCase: FetchStudentInfoUseCase
//
// init(
// fetchStudentInfoUseCase: FetchStudentInfoUseCase
// ) {
// self.fetchStudentInfoUseCase = fetchStudentInfoUseCase
// }
private let fetchNotificationListUseCase: FetchNotificationListUseCase
private let readNotificationUseCase: ReadNotificationUseCase

init(
fetchNotificationListUseCase: FetchNotificationListUseCase,
readNotificationUseCase: ReadNotificationUseCase
) {
self.readNotificationUseCase = readNotificationUseCase
self.fetchNotificationListUseCase = fetchNotificationListUseCase
}

public struct Input {
let viewAppear: PublishRelay<Void>
let readNotification: Observable<(NotificationEntity, IndexPath)>
}

public struct Output {}
public struct Output {
let notificationList: BehaviorRelay<[NotificationEntity]>
}

public func transform(_ input: Input) -> Output {
return Output()
let notificationList = BehaviorRelay<[NotificationEntity]>(value: [])

input.viewAppear.asObservable()
.flatMap {
self.fetchNotificationListUseCase.execute()
}
.bind(to: notificationList)
.disposed(by: disposeBag)

input.readNotification.asObservable()
.do(onNext: {
var oldList = notificationList.value
oldList[$0.1.row] = .init(
notificationID: $0.0.notificationID,
title: $0.0.title,
content: $0.0.content,
topic: $0.0.topic,
detailID: $0.0.detailID,
createdAt: $0.0.createdAt,
new: false
)
notificationList.accept(oldList)
})
.flatMap {
self.readNotificationUseCase.execute(id: $0.0.notificationID)
}
.subscribe()
.disposed(by: disposeBag)

return Output(notificationList: notificationList)
}
}
Loading

0 comments on commit a94af62

Please sign in to comment.