Skip to content

Commit

Permalink
Merge pull request #45 from TeamHY2/Feature/#40-WeeklyStudy
Browse files Browse the repository at this point in the history
[Feature] WeeklyStudy API 구현 및 연동
  • Loading branch information
owllwo98 authored Oct 25, 2024
2 parents dc22c5b + 8a56758 commit 7a2dbf6
Show file tree
Hide file tree
Showing 28 changed files with 538 additions and 116 deletions.
118 changes: 99 additions & 19 deletions HongikYeolgong2.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

81 changes: 64 additions & 17 deletions HongikYeolgong2/Core/AppEnviroment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,15 @@ struct AppEnviroment {
}

extension AppEnviroment {

static func bootstrap() -> AppEnviroment {
let appState = Store<AppState>(AppState())
let authRepository = AuthRepositoryImpl()

let services: DIContainer.Services = .init(
authenticationService: AuthenticationServiceImpl()
)

let interactors: DIContainer.Interactors = .init(
userDataInteractor: UserDataInteractorImpl(
appState: appState,
authRepository: authRepository,
authService: services.authenticationService
),
userPermissionsInteractor: RealUserPermissionsInteractor(appState: appState, openAppSetting: {
if let url = URL(string: UIApplication.openNotificationSettingsURLString) {
UIApplication.shared.open(url)
}
})
let services = configuredServices()
let remoteRepositories = configuredRemoteRepositories()
let interactors = configuredInteractors(
appState: appState,
remoteRepository: remoteRepositories,
services: services
)

let diContainer = DIContainer(
Expand All @@ -42,4 +32,61 @@ extension AppEnviroment {

return .init(container: diContainer)
}

/// 외부 데이터소스에 접근하는 레포지토리 의존성을 설정하고 관련 인스턴스를 반환합니다.
/// - Returns:외부 데이터소스에 접근하기위한 컨테이너
static func configuredRemoteRepositories() -> DIContainer.RemoteRepositories {
.init(
authRepository: AuthRepositoryImpl(),
studySessionRepository: StudySessionRepositoryImpl()
)
}

/// 앱의 인터랙터를 구성하고 의존성을 설정합니다.
/// - Parameters:
/// - appState: 앱의 상태를 관리하는 글로벌 인스턴스
/// - remoteRepository: 외부 데이터소스 접근 레포지토리
/// - services: 앱을 구성하는 서비스 인스턴스
/// - Returns:앱의 인터랙터를 구성하는 컨테이너
static func configuredInteractors(
appState: Store<AppState>,
remoteRepository: DIContainer.RemoteRepositories,
services: DIContainer.Services
) -> DIContainer.Interactors {
.init(
userDataInteractor: UserDataInteractorImpl(
appState: appState,
authRepository: remoteRepository.authRepository,
authService: services.appleAuthService
),
studySessionInteractor: StudySessionInteractorImpl(
studySessionRepository: remoteRepository.studySessionRepository
),
userPermissionsInteractor: RealUserPermissionsInteractor(
appState: appState,
openAppSetting: {
if let url = URL(string: UIApplication.openNotificationSettingsURLString) {
UIApplication.shared.open(url)
}
}
)
)
}

/// 앱의 서비스를 구성하는 컨테이너를 반환합니다.
/// - Returns: 앱의 서비스를 구성하는 컨테이너
static func configuredServices() -> DIContainer.Services {
.init(appleAuthService: AuthenticationServiceImpl())
}
}

extension DIContainer {
struct RemoteRepositories {
let authRepository: AuthRepository
let studySessionRepository: StudySessionRepository
}

struct Services {
let appleAuthService: AuthenticationService
}
}
4 changes: 2 additions & 2 deletions HongikYeolgong2/Data/DTO/BaseResponse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ struct BaseResponse<T: Decodable>: Decodable {
let code: Int
let status: String
let message: String
let data: T?
let data: T

enum CodingKeys: CodingKey {
case code
Expand All @@ -27,6 +27,6 @@ struct BaseResponse<T: Decodable>: Decodable {
self.code = try container.decode(Int.self, forKey: BaseResponse<T>.CodingKeys.code)
self.status = try container.decode(String.self, forKey: BaseResponse<T>.CodingKeys.status)
self.message = try container.decode(String.self, forKey: BaseResponse<T>.CodingKeys.message)
self.data = try container.decodeIfPresent(T.self, forKey: BaseResponse<T>.CodingKeys.data)
self.data = try container.decodeIfPresent(T.self, forKey: BaseResponse<T>.CodingKeys.data)!
}
}
13 changes: 13 additions & 0 deletions HongikYeolgong2/Data/DTO/StudySession/WeeklyStudySessionDTO.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// WeelyStudyResonseDTO.swift
// HongikYeolgong2
//
// Created by 권석기 on 10/24/24.
//

import Foundation

struct WeeklyStudySessionDTO: Decodable {
let date: String
let studyCount: Int
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ final class AuthRepositoryImpl: AuthRepository {
Task {
do {
let response: BaseResponse<LoginResponseDTO> = try await NetworkService.shared.request(endpoint: AuthEndpoint.login(loginReqDto: loginReqDto))
promise(.success(response.data!))
promise(.success(response.data))
} catch let error as NetworkError {
promise(.failure(error))
}
Expand All @@ -34,7 +34,7 @@ final class AuthRepositoryImpl: AuthRepository {
Task {
do {
let response: BaseResponse<NicknameCheckDTO> = try await NetworkService.shared.request(endpoint: UserEndpoint.checkUserNickname(nickname: nickname))
promise(.success(response.data?.duplicate ?? false))
promise(.success(response.data.duplicate))
} catch let error as NetworkError {
promise(.failure(error))
}
Expand All @@ -50,7 +50,7 @@ final class AuthRepositoryImpl: AuthRepository {
Task {
do {
let response: BaseResponse<SignUpResponseDTO> = try await NetworkService.shared.request(endpoint: UserEndpoint.signUp(signUpReqDto: signUpReqDto))
promise(.success(response.data!))
promise(.success(response.data))
} catch let error as NetworkError {
promise(.failure(error))
}
Expand All @@ -65,7 +65,7 @@ final class AuthRepositoryImpl: AuthRepository {
Task {
do {
let response: BaseResponse<SignUpResponseDTO> = try await NetworkService.shared.request(endpoint: UserEndpoint.getUser)
promise(.success(response.data!))
promise(.success(response.data))
} catch let error as NetworkError {
promise(.failure(error))

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//
// WeeklyStudyRepositoryImpl.swift
// HongikYeolgong2
//
// Created by 권석기 on 10/24/24.
//

import Foundation
import Combine

final class StudySessionRepositoryImpl: StudySessionRepository {
func getWeeklyStudyRecords() -> AnyPublisher<[WeeklyStudySessionDTO], NetworkError> {
return Future<[WeeklyStudySessionDTO], NetworkError> { promise in
Task {
do {
let response: BaseResponse<[WeeklyStudySessionDTO]> = try await NetworkService.shared.request(endpoint: WeeklyEndpoint.getWeeklyStudy)
promise(.success(response.data))
} catch let error as NetworkError {
promise(.failure(error))
}
}
}.eraseToAnyPublisher()
}
}
8 changes: 0 additions & 8 deletions HongikYeolgong2/Domain/Entities/StudyRoomUsage.swift

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// WeeklyStudyRecord.swift
// HongikYeolgong2
//
// Created by 권석기 on 10/25/24.
//

import Foundation

struct WeeklyStudyRecord {
let monthOfDay: String
let dayOfWeek: String
let studyCount: Int
let isUpcomming: Bool

var imageName: String {
switch studyCount {
case 0:
"shineCount00"
case 1:
"shineCount01"
case 2:
"shineCount02"
case 3:
"shineCount03"
default:
"shineCount00"
}
}
}
33 changes: 33 additions & 0 deletions HongikYeolgong2/Domain/Interactors/StudySessionInteractor.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// WeeklyStudyInteractor.swift
// HongikYeolgong2
//
// Created by 권석기 on 10/24/24.
//

import SwiftUI

protocol StudySessionInteractor {
func getWeekyStudy(studyRecords: Binding<[WeeklyStudyRecord]>)
}

final class StudySessionInteractorImpl: StudySessionInteractor {
private let cancleBag = CancelBag()
private let studySessionRepository: StudySessionRepository

init(studySessionRepository: StudySessionRepository) {
self.studySessionRepository = studySessionRepository
}

/// 한 주에 대한 공부 횟수를 가져옵니다.
/// - Parameter studyRecords: 공부 기록(월 - 일)
func getWeekyStudy(studyRecords: Binding<[WeeklyStudyRecord]>) {
studySessionRepository
.getWeeklyStudyRecords()
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { _ in }) {
studyRecords.wrappedValue = $0.map { $0.toEntity() }
}
.store(in: cancleBag)
}
}
13 changes: 13 additions & 0 deletions HongikYeolgong2/Domain/Interfaces/StudySessionRepository.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// WeeklyStudyRepository.swift
// HongikYeolgong2
//
// Created by 권석기 on 10/24/24.
//

import Foundation
import Combine

protocol StudySessionRepository {
func getWeeklyStudyRecords() -> AnyPublisher<[WeeklyStudySessionDTO], NetworkError>
}
18 changes: 18 additions & 0 deletions HongikYeolgong2/Domain/Mapper/StudySessionMapper.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// StudySessionMapper.swift
// HongikYeolgong2
//
// Created by 권석기 on 10/24/24.
//

import Foundation

// StudySession에 대한 Mapper
extension WeeklyStudySessionDTO {
func toEntity() -> WeeklyStudyRecord {
.init(monthOfDay: date.toMonthOfDay(),
dayOfWeek: date.toDayOfWeek(),
studyCount: studyCount,
isUpcomming: date.toDate() ?? .now <= .now)
}
}
6 changes: 4 additions & 2 deletions HongikYeolgong2/Injected/DependencyInjector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ struct DIContainer: EnvironmentKey {
let interactors: Interactors
let services: Services

init(appState: Store<AppState>, interactors: Interactors, services: Services) {
init(appState: Store<AppState>,
interactors: Interactors,
services: Services) {
self.appState = appState
self.interactors = interactors
self.services = services
Expand All @@ -24,7 +26,7 @@ struct DIContainer: EnvironmentKey {

private static let `default` = Self(appState: .init(AppState()),
interactors: .default,
services: .default)
services: .init(appleAuthService: AuthenticationServiceImpl()))
}

extension EnvironmentValues {
Expand Down
9 changes: 8 additions & 1 deletion HongikYeolgong2/Injected/InteractorsContainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,15 @@ import Foundation
extension DIContainer {
struct Interactors {
let userDataInteractor: UserDataInteractor
let studySessionInteractor: StudySessionInteractor
let userPermissionsInteractor: UserPermissionsInteractor

init(userDataInteractor: UserDataInteractor, userPermissionsInteractor: UserPermissionsInteractor) {
init(userDataInteractor: UserDataInteractor,
studySessionInteractor: StudySessionInteractor,
userPermissionsInteractor: UserPermissionsInteractor) {

self.userDataInteractor = userDataInteractor
self.studySessionInteractor = studySessionInteractor
self.userPermissionsInteractor = userPermissionsInteractor
}

Expand All @@ -23,7 +28,9 @@ extension DIContainer {
authRepository: AuthRepositoryImpl(),
authService: AuthenticationServiceImpl()
),
studySessionInteractor: StudySessionInteractorImpl(studySessionRepository: StudySessionRepositoryImpl()),
userPermissionsInteractor: RealUserPermissionsInteractor(appState: Store<AppState>(AppState()), openAppSetting: {})
)
}
}

20 changes: 0 additions & 20 deletions HongikYeolgong2/Injected/ServicesContainer.swift

This file was deleted.

File renamed without changes.
Loading

0 comments on commit 7a2dbf6

Please sign in to comment.