diff --git a/Projects/CodestackApp/Derived/Sources/TuistAssets+CodestackApp.swift b/Projects/CodestackApp/Derived/Sources/TuistAssets+CodestackApp.swift new file mode 100644 index 00000000..fe82cb7f --- /dev/null +++ b/Projects/CodestackApp/Derived/Sources/TuistAssets+CodestackApp.swift @@ -0,0 +1,171 @@ +// swiftlint:disable all +// swift-format-ignore-file +// swiftformat:disable all +// Generated using tuist — https://github.com/tuist/tuist + +#if os(macOS) + import AppKit +#elseif os(iOS) + import UIKit +#elseif os(tvOS) || os(watchOS) + import UIKit +#endif +#if canImport(SwiftUI) + import SwiftUI +#endif + +// swiftlint:disable superfluous_disable_command file_length implicit_return + +// MARK: - Asset Catalogs + +// swiftlint:disable identifier_name line_length nesting type_body_length type_name +public enum CodestackAppAsset { + public static let accentColor = CodestackAppColors(name: "AccentColor") + public static let bell = CodestackAppImages(name: "bell") + public static let cftype = CodestackAppImages(name: "cftype") + public static let codeStack = CodestackAppImages(name: "codeStack") + public static let github = CodestackAppImages(name: "github") + public static let home = CodestackAppImages(name: "home") + public static let mainBanner = CodestackAppImages(name: "main-banner") + public static let my = CodestackAppImages(name: "my") + public static let onboarding1 = CodestackAppImages(name: "onboarding-1") + public static let onboarding2 = CodestackAppImages(name: "onboarding-2") + public static let problem = CodestackAppImages(name: "problem") + public static let submit = CodestackAppImages(name: "submit") +} +// swiftlint:enable identifier_name line_length nesting type_body_length type_name + +// MARK: - Implementation Details + +public final class CodestackAppColors { + public fileprivate(set) var name: String + + #if os(macOS) + public typealias Color = NSColor + #elseif os(iOS) || os(tvOS) || os(watchOS) + public typealias Color = UIColor + #endif + + @available(iOS 11.0, tvOS 11.0, watchOS 4.0, macOS 10.13, *) + public private(set) lazy var color: Color = { + guard let color = Color(asset: self) else { + fatalError("Unable to load color asset named \(name).") + } + return color + }() + + #if canImport(SwiftUI) + private var _swiftUIColor: Any? = nil + @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) + public private(set) var swiftUIColor: SwiftUI.Color { + get { + if self._swiftUIColor == nil { + self._swiftUIColor = SwiftUI.Color(asset: self) + } + + return self._swiftUIColor as! SwiftUI.Color + } + set { + self._swiftUIColor = newValue + } + } + #endif + + fileprivate init(name: String) { + self.name = name + } +} + +public extension CodestackAppColors.Color { + @available(iOS 11.0, tvOS 11.0, watchOS 4.0, macOS 10.13, *) + convenience init?(asset: CodestackAppColors) { + let bundle = CodestackAppResources.bundle + #if os(iOS) || os(tvOS) + self.init(named: asset.name, in: bundle, compatibleWith: nil) + #elseif os(macOS) + self.init(named: NSColor.Name(asset.name), bundle: bundle) + #elseif os(watchOS) + self.init(named: asset.name) + #endif + } +} + +#if canImport(SwiftUI) +@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) +public extension SwiftUI.Color { + init(asset: CodestackAppColors) { + let bundle = CodestackAppResources.bundle + self.init(asset.name, bundle: bundle) + } +} +#endif + +public struct CodestackAppImages { + public fileprivate(set) var name: String + + #if os(macOS) + public typealias Image = NSImage + #elseif os(iOS) || os(tvOS) || os(watchOS) + public typealias Image = UIImage + #endif + + public var image: Image { + let bundle = CodestackAppResources.bundle + #if os(iOS) || os(tvOS) + let image = Image(named: name, in: bundle, compatibleWith: nil) + #elseif os(macOS) + let image = bundle.image(forResource: NSImage.Name(name)) + #elseif os(watchOS) + let image = Image(named: name) + #endif + guard let result = image else { + fatalError("Unable to load image asset named \(name).") + } + return result + } + + #if canImport(SwiftUI) + @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) + public var swiftUIImage: SwiftUI.Image { + SwiftUI.Image(asset: self) + } + #endif +} + +public extension CodestackAppImages.Image { + @available(macOS, deprecated, + message: "This initializer is unsafe on macOS, please use the CodestackAppImages.image property") + convenience init?(asset: CodestackAppImages) { + #if os(iOS) || os(tvOS) + let bundle = CodestackAppResources.bundle + self.init(named: asset.name, in: bundle, compatibleWith: nil) + #elseif os(macOS) + self.init(named: NSImage.Name(asset.name)) + #elseif os(watchOS) + self.init(named: asset.name) + #endif + } +} + +#if canImport(SwiftUI) +@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) +public extension SwiftUI.Image { + init(asset: CodestackAppImages) { + let bundle = CodestackAppResources.bundle + self.init(asset.name, bundle: bundle) + } + + init(asset: CodestackAppImages, label: Text) { + let bundle = CodestackAppResources.bundle + self.init(asset.name, bundle: bundle, label: label) + } + + init(decorative asset: CodestackAppImages) { + let bundle = CodestackAppResources.bundle + self.init(decorative: asset.name, bundle: bundle) + } +} +#endif + +// swiftlint:enable all +// swiftformat:enable all diff --git a/Projects/CodestackApp/Project.swift b/Projects/CodestackApp/Project.swift index 60747a43..22dc2801 100644 --- a/Projects/CodestackApp/Project.swift +++ b/Projects/CodestackApp/Project.swift @@ -8,6 +8,14 @@ import ProjectDescription import ProjectDescriptionHelpers + +let dependencies: [TargetDependency] = [ + .PRO.data, + .PRO.domain +] + +let entitlementPath: ProjectDescription.Path = .relativeToCurrentFile("Resources/CodeStack.entitlements") + let resources: ResourceFileElements = [ "Resources/Apollo/codestackSchema.graphqls", @@ -26,85 +34,17 @@ let resources: ResourceFileElements "Resources/PLanguage/python3/*.txt", "Resources/PLanguage/swift/*.txt", "Resources/PLanguage/typescript/*.txt", - "Resources/Base.lproj/LaunchScreen.storyboard", - "Sources/Data/MO/db_model_v1.xcdatamodeld" + "Resources/Base.lproj/LaunchScreen.storyboard" ] +let project = Project.createModule(name: "CodestackApp", + product: .app, + bundleID: "kr.co.codestack.ios", + settings: true, + dependencies: dependencies, + configuration: true, + resources: resources, + entitlement: entitlementPath, + infoPlist: "Config/Info.plist") -let project = Project(name: "CodestackApp", - organizationName: "hyeong", - options: .options(automaticSchemesOptions: .disabled), - packages: [], - settings: .settings(configurations: [ - .debug(name: "Dev", - settings: [:], - xcconfig: "Config/BuildSetting.xcconfig"), - .debug(name: "Prod", - settings: [:], - xcconfig: "Config/BuildSetting.xcconfig"), - ]), - targets: [ - Project.target(name: "CodestackApp", - product: .app, - bundleID: "kr.co.codestack.ios", - infoPlist: "Config/Info.plist", - sources: "Sources/**", - resources: resources, - entitlement: .relativeToCurrentFile("Resources/CodeStack.entitlements"), - dependencies: [ - - .project(target: "Global", path: .relativeToRoot("Projects/Global")), - .project(target: "Data", path: .relativeToRoot("Projects/Data")), - .rxSwift, - .snapKit, - .then, - .apollo, - .richtextKit, - .rxdatasources, - .rxFlow, - .rxGesture, - .sqlite, - .swinject, - .codestackAPI, - .highlightr, - ], - configuration: [ - .debug(name: "Dev", - settings: [:], - xcconfig: "Config/Codestack.xcconfig"), - .debug(name: "Prod", - settings: [:], - xcconfig: "Config/Codestack.xcconfig") - ]), - ], - schemes: [ - Scheme(name: "CodestackApp-Dev", - shared: true, - buildAction: .buildAction(targets: ["CodestackApp"]), - testAction: .targets(["MyAppTests"], - configuration: .configuration("Dev"), - options: .options(coverage: true)), - runAction: .runAction(configuration: .configuration("Dev"), - arguments: .init(environment: ["OS_ACTIVITY_MODE" : "disable"], - launchArguments: [])), - archiveAction: .archiveAction(configuration: .configuration("Dev")), - profileAction: .profileAction(configuration: .configuration("Dev")), - analyzeAction: .analyzeAction(configuration: .configuration("Dev"))), - Scheme(name: "CodestackApp-Prod", - shared: true, - buildAction: .buildAction(targets: ["CodestackApp"]), - runAction: .runAction(configuration: .configuration("Prod")), - archiveAction: .archiveAction(configuration: .configuration("Prod")), - profileAction: .profileAction(configuration: .configuration("Prod")), - analyzeAction: .analyzeAction(configuration: .configuration("Prod"))) - ], - fileHeaderTemplate: nil, - additionalFiles: [], - resourceSynthesizers: []) -// Project.target(name: "MyAppTests", -// product: .unitTests, -// sources: "Tests/**", -// dependencies: [ -// .target(name: "MyApp"), -// ]) diff --git "a/Projects/CodestackApp/Resources/Assets.xcassets/main-banner.imageset/main-banner \353\263\265\354\202\254\353\263\270@2x.jpeg" "b/Projects/CodestackApp/Resources/Assets.xcassets/main-banner.imageset/main-banner \353\263\265\354\202\254\353\263\270@2x.jpeg" deleted file mode 100644 index e8f4d813..00000000 Binary files "a/Projects/CodestackApp/Resources/Assets.xcassets/main-banner.imageset/main-banner \353\263\265\354\202\254\353\263\270@2x.jpeg" and /dev/null differ diff --git "a/Projects/CodestackApp/Resources/Assets.xcassets/main-banner.imageset/main-banner \353\263\265\354\202\254\353\263\270@3x.jpeg" "b/Projects/CodestackApp/Resources/Assets.xcassets/main-banner.imageset/main-banner \353\263\265\354\202\254\353\263\270@3x.jpeg" deleted file mode 100644 index e3b7d651..00000000 Binary files "a/Projects/CodestackApp/Resources/Assets.xcassets/main-banner.imageset/main-banner \353\263\265\354\202\254\353\263\270@3x.jpeg" and /dev/null differ diff --git a/Projects/CodestackApp/Sources/APP/SceneDelegate.swift b/Projects/CodestackApp/Sources/APP/SceneDelegate.swift index eee6861c..a9752618 100644 --- a/Projects/CodestackApp/Sources/APP/SceneDelegate.swift +++ b/Projects/CodestackApp/Sources/APP/SceneDelegate.swift @@ -10,19 +10,15 @@ import RxSwift import RxFlow import Swinject import Global -import Data +import Domain class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? var coordinator: FlowCoordinator = FlowCoordinator() + private var gitAuthUsecase: AuthUsecase? var disposeBag: DisposeBag = DisposeBag() - - private let loginService: Auth = LoginService() - private let authService: RestAPI = DefaultRestAPI() -// private lazy var appleLoginManger: AppleLoginManager = AppleLoginManager(serviceManager: self.loginService as AppleAuthorization) - private let injector = DefaultInjector(container: Container()) func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { @@ -48,7 +44,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { let flow = AppFlow(dependency: appDependency) self.coordinator.coordinate(flow: flow, with: AppStepper()) - + self.gitAuthUsecase = injector.resolve(AuthUsecase.self) Flows.use(flow, when: .created, block: { root in window.rootViewController = root window.makeKeyAndVisible() @@ -56,8 +52,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { self.window = window } - - /// Git Auth 과정에서 redirect 되었을때 호출되는 함수 /// - Parameters: /// - scene: 현재의 scene @@ -71,7 +65,8 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { if let flag = component.first?.elementsEqual("codestackios://git/auth"), flag, let code = component.last?.components(separatedBy: "=").last{ - (loginService as GitAuth).gitOAuthComplete(code: code) + // TODO: LoginVIewModel Protocol을 어떻게든 해야댐 + gitAuthUsecase?.gitOAuthComplete(code: code) } } } diff --git a/Projects/CodestackApp/Sources/Common/SwiftUI/SubmissionViewModel.swift b/Projects/CodestackApp/Sources/Common/SwiftUI/SubmissionViewModel.swift index 53b09224..6633948b 100644 --- a/Projects/CodestackApp/Sources/Common/SwiftUI/SubmissionViewModel.swift +++ b/Projects/CodestackApp/Sources/Common/SwiftUI/SubmissionViewModel.swift @@ -10,6 +10,7 @@ import RxCocoa import RxFlow import RxSwift import Global +import Domain class ContributionViewModel: ObservableObject, RxFlow.Stepper { @@ -22,16 +23,13 @@ class ContributionViewModel: ObservableObject, RxFlow.Stepper { depth: .one)] struct Dependency { - let service: WebRepository let submissionUsecase: SubmissionUseCase } - private var service: WebRepository? private var submissionUsecase: SubmissionUseCase? static func create(depenedency: Dependency) -> ContributionViewModel { let viewModel = ContributionViewModel() - viewModel.service = depenedency.service viewModel.submissionUsecase = depenedency.submissionUsecase viewModel.binding() return viewModel @@ -66,7 +64,7 @@ class ContributionViewModel: ObservableObject, RxFlow.Stepper { } - private func caculateCurrentContribution(value: SubmissionCalendar) -> [SubmissionContribution] { + private func caculateCurrentContribution(value: SubmissionCalendarVO) -> [SubmissionContribution] { var dayArray: [String: SubmissionContribution] = self.getContributionDates(size: contribution_day_count) diff --git a/Projects/CodestackApp/Sources/Common/View/CommonViewModel.swift b/Projects/CodestackApp/Sources/Common/View/CommonViewModel.swift deleted file mode 100644 index fcfe15fd..00000000 --- a/Projects/CodestackApp/Sources/Common/View/CommonViewModel.swift +++ /dev/null @@ -1,8 +0,0 @@ -// -// CommonViewModel.swift -// CodeStack -// -// Created by 박형환 on 2023/08/16. -// - -import Foundation diff --git a/Projects/CodestackApp/Sources/Common/View/RichTextView.swift b/Projects/CodestackApp/Sources/Common/View/RichTextView.swift index a22923f5..2a5c71d5 100644 --- a/Projects/CodestackApp/Sources/Common/View/RichTextView.swift +++ b/Projects/CodestackApp/Sources/Common/View/RichTextView.swift @@ -9,6 +9,7 @@ import UIKit import SnapKit import RichTextKit import Global +import Domain class RichTextViewController: UIViewController{ diff --git a/Projects/CodestackApp/Sources/DI/CodeEditorAssembly.swift b/Projects/CodestackApp/Sources/DI/CodeEditorAssembly.swift index ad52bc9b..b66ec13a 100644 --- a/Projects/CodestackApp/Sources/DI/CodeEditorAssembly.swift +++ b/Projects/CodestackApp/Sources/DI/CodeEditorAssembly.swift @@ -8,6 +8,7 @@ import Swinject import Network +import Domain public struct CodeEditorAssembly: Assembly { diff --git a/Projects/CodestackApp/Sources/DI/CodeProblemAssembly.swift b/Projects/CodestackApp/Sources/DI/CodeProblemAssembly.swift index 3ad8b9b4..4b8d854d 100644 --- a/Projects/CodestackApp/Sources/DI/CodeProblemAssembly.swift +++ b/Projects/CodestackApp/Sources/DI/CodeProblemAssembly.swift @@ -7,14 +7,15 @@ // import Swinject +import Domain public struct CodeProblemAssembly: Assembly { public func assemble(container: Container) { container.register(CodeProblemViewModel.self) { resolver in - let web = resolver.resolve(WebRepository.self)! - return CodeProblemViewModel(DummyData(), web) + let usecase = resolver.resolve(ProblemUsecase.self)! + return CodeProblemViewModel(DummyData(), usecase) }.inObjectScope(.container) container.register(CodeProblemViewController.self) { resolver in diff --git a/Projects/CodestackApp/Sources/DI/DataAssembly.swift b/Projects/CodestackApp/Sources/DI/DataAssembly.swift index e16e8dc4..f7d72133 100644 --- a/Projects/CodestackApp/Sources/DI/DataAssembly.swift +++ b/Projects/CodestackApp/Sources/DI/DataAssembly.swift @@ -8,10 +8,19 @@ import Swinject import Data +import Domain public struct DataAssembly: Assembly { public func assemble(container: Container) { + + container.register(AuthRepository.self) { resolver in + let auth = resolver.resolve(Auth.self)! + let graph = resolver.resolve(GraphQLAPI.self)! + let rest = resolver.resolve(RestAPI.self)! + return DefaultAuthRepository(auth: auth, graph: graph, rest: rest) + } + container.register(WebRepository.self) { resolver in let graphAPI = resolver.resolve(GraphQLAPI.self)! let restAPI = resolver.resolve(RestAPI.self)! diff --git a/Projects/CodestackApp/Sources/DI/DomainAssembly.swift b/Projects/CodestackApp/Sources/DI/DomainAssembly.swift index 02c0d56e..596d1bc9 100644 --- a/Projects/CodestackApp/Sources/DI/DomainAssembly.swift +++ b/Projects/CodestackApp/Sources/DI/DomainAssembly.swift @@ -7,10 +7,38 @@ // import Swinject +import Domain public struct DomainAssembly: Assembly { public func assemble(container: Container) { + + container.register(AuthUsecase.self) { resolver in + let authRepo = resolver.resolve(AuthRepository.self)! + return AuthUsecase(authRepository: authRepo) + } + container.register(ProfileUsecase.self) { resolver in + let web = resolver.resolve(WebRepository.self)! + return ProfileUsecase(webRepository: web) + } + + container.register(ProblemUsecase.self) { resolver in + let web = resolver.resolve(WebRepository.self)! + return ProblemUsecase(webRepository: web) + } + + container.register(HistoryUsecase.self) { resolver in + let db = resolver.resolve(DBRepository.self)! + let web = resolver.resolve(WebRepository.self)! + return DefaultHistoryUsecase.init(webRepository: web, dbRepository: db) + } + + container.register(HomeUsecase.self) { resolver in + let db = resolver.resolve(DBRepository.self)! + let web = resolver.resolve(WebRepository.self)! + return HomeUsecase(webRepository: web, dbRepository: db) + } + container.register(SubmissionUseCase.self) { resolver in let db = resolver.resolve(DBRepository.self)! let web = resolver.resolve(WebRepository.self)! diff --git a/Projects/CodestackApp/Sources/DI/HistoryAssembly.swift b/Projects/CodestackApp/Sources/DI/HistoryAssembly.swift index 6216ff3f..f32fa3ee 100644 --- a/Projects/CodestackApp/Sources/DI/HistoryAssembly.swift +++ b/Projects/CodestackApp/Sources/DI/HistoryAssembly.swift @@ -7,15 +7,15 @@ // import Swinject +import Domain public struct HistoryAssembly: Assembly { public func assemble(container: Container) { container.register(HistoryViewModel.self) { resolver in - let webRepository = resolver.resolve(WebRepository.self)! - let useCase = resolver.resolve(SubmissionUseCase.self)! - let dp = HistoryViewModel.Dependency(service: webRepository, submisionUsecase: useCase) + let usecase = resolver.resolve(HistoryUsecase.self)! + let dp = HistoryViewModel.Dependency.init(submisionUsecase: usecase) return HistoryViewModel(dependency: dp) }.inObjectScope(.container) diff --git a/Projects/CodestackApp/Sources/DI/HomeAssembly.swift b/Projects/CodestackApp/Sources/DI/HomeAssembly.swift index 7b782a9a..53b390cb 100644 --- a/Projects/CodestackApp/Sources/DI/HomeAssembly.swift +++ b/Projects/CodestackApp/Sources/DI/HomeAssembly.swift @@ -7,6 +7,7 @@ // import Swinject +import Domain public struct HomeAssembly: Assembly { @@ -14,21 +15,17 @@ public struct HomeAssembly: Assembly { // MARK: Home ViewModel container.register(HomeViewModel.self) { resolver in - let dbRepository = resolver.resolve(DBRepository.self)! - let webRepository = resolver.resolve(WebRepository.self)! - let homeViewModel: HomeViewModel.Dependency = .init(repository: dbRepository, - service: webRepository) + let home = resolver.resolve(HomeUsecase.self)! + let homeViewModel: HomeViewModel.Dependency = .init(homeUsecase: home) return HomeViewModel(dependency: homeViewModel) }.inObjectScope(.container) // MARK: Contribution ViewModel container.register(ContributionViewModel.self) { resolver in - let dbRepository = resolver.resolve(WebRepository.self)! let submissionUseCase = resolver.resolve(SubmissionUseCase.self)! - let dependency = ContributionViewModel.Dependency(service: dbRepository, - submissionUsecase: submissionUseCase) + let dependency = ContributionViewModel.Dependency(submissionUsecase: submissionUseCase) return ContributionViewModel.create(depenedency: dependency) } diff --git a/Projects/CodestackApp/Sources/DI/LoginAssembly.swift b/Projects/CodestackApp/Sources/DI/LoginAssembly.swift index 2fe8df76..15d79513 100644 --- a/Projects/CodestackApp/Sources/DI/LoginAssembly.swift +++ b/Projects/CodestackApp/Sources/DI/LoginAssembly.swift @@ -7,33 +7,31 @@ // import Swinject -import Data +import Domain public struct LoginAssembly: Assembly { public func assemble(container: Container) { container.register(LoginStepper.self) { resolver in - let service = resolver.resolve(RestAPI.self)! - return LoginStepper(authService: service) +// let service = resolver.resolve(RestAPI.self)! + return LoginStepper() }.inObjectScope(.container) - container.register((any LoginViewModelProtocol).self) { resolver in - let request = resolver.resolve(Auth.self)! - let web = resolver.resolve(WebRepository.self)! + container.register(LoginViewModel.self) { resolver in let stepper = resolver.resolve(LoginStepper.self)! - let dp = LoginViewModel.Dependency(loginService: request, - apolloService: web, - stepper: stepper) + let useCase = resolver.resolve(AuthUsecase.self)! + + let dp = LoginViewModel.Dependency(authUsecase: useCase, stepper: stepper) return LoginViewModel(dependency: dp) }.inObjectScope(.container) container.register(LoginViewController.self) { resolver in - let loginViewModel = resolver.resolve((any LoginViewModelProtocol).self)! - let apple = resolver.resolve(AppleLoginManager.self)! - let dp = LoginViewController.Dependencies.init(viewModel: loginViewModel, - appleManager: apple) + let loginViewModel = resolver.resolve(LoginViewModel.self)! +// let apple = resolver.resolve(AppleLoginManager.self)! + let dp = LoginViewController.Dependencies.init(viewModel: loginViewModel + /*appleManager: apple*/) return LoginViewController.create(with: dp) } } diff --git a/Projects/CodestackApp/Sources/DI/MyPageAssembly.swift b/Projects/CodestackApp/Sources/DI/MyPageAssembly.swift index 8cef3319..188f1844 100644 --- a/Projects/CodestackApp/Sources/DI/MyPageAssembly.swift +++ b/Projects/CodestackApp/Sources/DI/MyPageAssembly.swift @@ -7,18 +7,15 @@ // import Swinject -import Data +import Domain public struct MyPageAssembly: Assembly { public func assemble(container: Container) { container.register(MyPageViewModel.self) { resolver in - let auth = resolver.resolve(RestAPI.self)! - let web = resolver.resolve(WebRepository.self)! - - let dp = MyPageViewModel.Dependency(authService: auth, - apolloService: web) + let useCase = resolver.resolve(ProfileUsecase.self)! + let dp = MyPageViewModel.Dependency(profileUsecase: useCase) return MyPageViewModel(dependency: dp) } diff --git a/Projects/CodestackApp/Sources/DI/NetworkAssembly.swift b/Projects/CodestackApp/Sources/DI/NetworkAssembly.swift index 9ac6bad9..5b9124cc 100644 --- a/Projects/CodestackApp/Sources/DI/NetworkAssembly.swift +++ b/Projects/CodestackApp/Sources/DI/NetworkAssembly.swift @@ -9,6 +9,7 @@ import Swinject import Foundation import Data +import Domain public struct NetworkAssembly: Assembly { diff --git a/Projects/CodestackApp/Sources/DI/RegisterAssembly.swift b/Projects/CodestackApp/Sources/DI/RegisterAssembly.swift index 3c384327..82508575 100644 --- a/Projects/CodestackApp/Sources/DI/RegisterAssembly.swift +++ b/Projects/CodestackApp/Sources/DI/RegisterAssembly.swift @@ -7,14 +7,14 @@ // import Swinject -import Data +import Domain public struct RegisterAssembly: Assembly { public func assemble(container: Container) { container.register(RegisterViewController.self) { resolver in - let service = resolver.resolve(RestAPI.self)! - return RegisterViewController.create(with: service) + let usecase = resolver.resolve(AuthUsecase.self)! + return RegisterViewController.create(with: usecase) } } } diff --git a/Projects/CodestackApp/Sources/Data/Interface/DBRepository.swift b/Projects/CodestackApp/Sources/Data/Interface/DBRepository.swift deleted file mode 100644 index a0605b89..00000000 --- a/Projects/CodestackApp/Sources/Data/Interface/DBRepository.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// DBRepository.swift -// CodestackApp -// -// Created by 박형환 on 12/14/23. -// Copyright © 2023 hyeong. All rights reserved. -// - -import Foundation -import RxSwift - -protocol DBRepository: AnyObject { - func fetch(_ requestType: SubmissionMO.RequestType) -> Single<[SubmissionVO]> - func store(submission: SubmissionVO) -> Single - func remove() -> Single - func remove(_ requestType: SubmissionMO.RequestType) -> Completable - func update(submission: SubmissionVO, type request: SubmissionMO.RequestType) -> Single - - func fetchProblemState() -> Single<[ProblemSubmissionState]> - func fetchSubmissionCalendar() -> Single<[SubmissionCalendar]> - - func store(favoriteProblem: FavoriteProblem) -> Single - func fetchFavoriteProblems() -> Single<[FavoriteProblem]> - func removeFavor(_ requestType: FavoritProblemMO.RequestType) -> Completable -} diff --git a/Projects/CodestackApp/Sources/Data/Interface/WebRepository.swift b/Projects/CodestackApp/Sources/Data/Interface/WebRepository.swift deleted file mode 100644 index e3c4f175..00000000 --- a/Projects/CodestackApp/Sources/Data/Interface/WebRepository.swift +++ /dev/null @@ -1,50 +0,0 @@ -// -// ApolloServiceType.swift -// CodeStack -// -// Created by 박형환 on 2023/09/08. -// - -import Foundation -import RxSwift -import CodestackAPI -import Apollo -import Data - -public protocol WebRepository: AnyObject { - - // MARK: GraphQL -// func getSolvedProblems(_ graph: GRAPH) -> Maybe<[ProblemIdentityFR]> - - func request(type: T.Type, graph: GRAPH) -> Maybe - - func getSolvedProblems(_ query: FetchSolvedProblemQuery) -> Maybe<[ProblemIdentityFR]> - - func getMe(_ graph: GRAPH) -> Maybe - func updateNickName(_ graph: GRAPH) -> Maybe - func perform(_ graph: GRAPH, max retry: Int) -> Maybe - func getProblemsQuery(_ graph: GRAPH) -> Maybe<([ProblemFR],PageInfoFR)> - func getProblemByID(_ graph: GRAPH) -> Maybe - func getAllTag(_ graph: GRAPH) -> Maybe<([TagFR],PageInfoFR)> - func getAllLanguage(_ graph: GRAPH) -> Maybe<[LanguageFR]> - func getMeSubmissions(_ graph: GRAPH) -> Maybe<[SubmissionFR]> - func getSubmission(_ graph: GRAPH, cache: CachePolicy?) -> Maybe<([SubmissionFR],PageInfoFR)> - - // MARK: REST - func signUp(member: MemberDTO) -> Maybe - func passwordChange(_ original: Pwd, new: Pwd) -> Maybe - func editProfile(_ image: Data) -> Maybe -} - - -public extension WebRepository { - - private static var endpoint: String { - return Bundle.main.infoDictionary!["graphql_endpoint"] as! String - } - - static var baseURL: URL { - return URL(string: endpoint)! - } - -} diff --git a/Projects/CodestackApp/Sources/Data/Persistence/Models-extension.swift b/Projects/CodestackApp/Sources/Data/Persistence/Models-extension.swift deleted file mode 100644 index 21670e09..00000000 --- a/Projects/CodestackApp/Sources/Data/Persistence/Models-extension.swift +++ /dev/null @@ -1,114 +0,0 @@ -// -// Models-extension.swift -// CodeStack -// -// Created by 박형환 on 11/22/23. -// - - -import CoreData -import Global -protocol ManagedEntity: NSFetchRequestResult { } - -extension SubmissionMO: ManagedEntity { } -extension CodeContextMO: ManagedEntity { } -extension LanguageMO: ManagedEntity { } -extension ProblemSubmissionStateMO: ManagedEntity { } -extension SubmissionCalendarMO: ManagedEntity { } -extension FavoritProblemMO: ManagedEntity { } - -protocol MapperbleProtocol { - associatedtype PersistenceType: NSFetchRequestResult - - @discardableResult - func store(in context: NSManagedObjectContext) -> PersistenceType? - - static func fetchRequest() -> NSFetchRequest -} - -extension FavoriteProblem: MapperbleProtocol { - - func store(in context: NSManagedObjectContext) -> FavoritProblemMO? { - guard let favoriteMO = FavoritProblemMO.insertNew(in: context) else { return nil } - favoriteMO.problemID = self.problemID - favoriteMO.problemTitle = self.problmeTitle - favoriteMO.createdAt = self.createdAt - return favoriteMO - } - - static func fetchRequest() -> NSFetchRequest { - FavoritProblemMO.fetchRequest() - } - - typealias PersistenceType = FavoritProblemMO - - init?(managedContext: FavoritProblemMO) { - guard - let createdAt = managedContext.createdAt, - let problemID = managedContext.problemID, - let problemTitle = managedContext.problemTitle - else { - return nil - } - self.init(problemID: problemID, problmeTitle: problemTitle, createdAt: createdAt) - } -} - -struct ProblemSubmissionState { - var submissions: [SubmissionVO] -} - -extension SubmissionCalendar: MapperbleProtocol { - typealias PersistenceType = SubmissionCalendarMO - - - init?(managedContext: SubmissionCalendarMO) { - guard let submissionSet = managedContext.submission else { return nil } - - let _submissions = - submissionSet - .toArray(of: SubmissionMO.self) - .compactMap { $0.toDomain() } - - self.dates = _submissions.map(\.createdAt) - } - - func store(in context: NSManagedObjectContext) -> SubmissionCalendarMO? { - return nil - } - - static func fetchRequest() -> NSFetchRequest { - SubmissionCalendarMO.newFetchRequest() - } -} - - - -extension ProblemSubmissionState: MapperbleProtocol { - - typealias PersistenceType = ProblemSubmissionStateMO - - static func fetchRequest() -> NSFetchRequest { - ProblemSubmissionStateMO.fetchRequest() - } - - init?(managedContext: ProblemSubmissionStateMO) { - guard let submissionSet = managedContext.submissions else { return nil } - - let _submissions = - submissionSet - .toArray(of: SubmissionMO.self) - .compactMap { $0.toDomain() } - //.compactMap { Submission(managedContext: $0) } - - self.submissions = _submissions - } - - func store(in context: NSManagedObjectContext) -> ProblemSubmissionStateMO? { - guard let problemSubmissionMO = ProblemSubmissionStateMO.insertNew(in: context) else { return nil } - - problemSubmissionMO.submissions = self.submissions.compactMap { $0.toMO(in:) } as? NSSet - - return problemSubmissionMO - } -} diff --git a/Projects/CodestackApp/Sources/Data/Repository/DefaultRepository.swift b/Projects/CodestackApp/Sources/Data/Repository/DefaultRepository.swift deleted file mode 100644 index 7ba7c15e..00000000 --- a/Projects/CodestackApp/Sources/Data/Repository/DefaultRepository.swift +++ /dev/null @@ -1,249 +0,0 @@ -// -// ApolloService.swift -// CodeStack -// -// Created by 박형환 on 2023/07/25. -// - -import Foundation -import RxSwift -import CodestackAPI -import Apollo -import Data -import Global - -//public typealias SolvedProblems = [GetSolvedProblemQuery.Data.MatchMember.SolvedProblem] -// -//public typealias GetSubmissions = GetSubmissionsQuery.Data.GetSubmissions -//public typealias GetMeSubmissions = [GetMeSubmissionsQuery.Data.GetMe.Submission] -// -//public typealias SubmissionProblem = CreateSubmissionMutation.Data.CreateSubmission.Problem -//public typealias SubmissionMutation = CreateSubmissionMutation.Data.CreateSubmission -// -//public typealias ProblemContent = GetProblemsQuery.Data.GetProblems.Content -//public typealias ProblemPageInfo = GetProblemsQuery.Data.GetProblems.PageInfo -// -//public typealias Member = GetMeQuery.Data.GetMe -//public typealias MemberSolvedProblem = GetMeQuery.Data.GetMe.SolvedProblem - - -public class DefaultRepository: WebRepository { - - private var tokenAcquizition: TokenAcquisitionService - private var graphAPI: GraphQLAPI - private var restAPI: RestAPI - - public struct Dependency { - var tokenAcquizition: TokenAcquisitionService - var graphAPI: GraphQLAPI - var restAPI: RestAPI - } - - public init(dependency: Dependency ) { - self.tokenAcquizition = dependency.tokenAcquizition - self.graphAPI = dependency.graphAPI - self.restAPI = dependency.restAPI - } - - func getSubmissionDate(api: GRAPH) -> Single { - // TODO: 현재 GraphQL API에 Submission Result Date 모델을 가져오는 API가 없다. - #if DEBUG - Single.create { single in - let calendars = SubmissionCalendar.generateMockCalendar() - single(.success(calendars)) - return Disposables.create { } - } - #endif -// api.fetch(query: query, -// cachePolicy: .fetchIgnoringCacheData, -// queue: .global(qos: .background)) -// .map { value in -// value.getSubmissions.content?.compactMap { SubmissionCalendar(date: $0.createdAt) } ?? [] -// } -// .asObservable() -// .asSingle() - } - - public func request(type: T.Type, graph: GRAPH) -> Maybe { - let query = API.GRAPH_QUERY(path: graph) - - switch query { - case let q as FetchSolvedProblemQuery: - return getSolvedProblems(q) as! Maybe -// case let q as FetchSolvedProblemQuery: -// break -// case let q as FetchMeQuery: -// break -// case let q as UpdateNickNameMutation: -// break -// case let q as SubmitSubmissionMutation: -// break -// case let q as FetchProblemsQuery: -// break -// case let q as FetchProblemByIdQuery: -// break -// case let q as FetchAllTagQuery: -// break -// case let q as FetchAllLanguageQuery: -// break -// case let q as FetchMeSubmissionsQuery: -// break -// case let q as FetchSubmissionsQuery: -// break - default: - break - } - return .empty() - } - - - typealias MatchMember = FetchSolvedProblemQuery.Data.MatchMember.SolvedProblem.Fragments - - public func getSolvedProblems(_ query: FetchSolvedProblemQuery) -> Maybe<[ProblemIdentityFR]> { -// let query = API.GRAPH_QUERY(path: graph) as! FetchSolvedProblemQuery - return graphAPI.fetch(query: query , cachePolicy: .default, queue: .main) - .map { item in - item.matchMember.solvedProblems.map { $0.fragments.problemIdentityFR } - } - } - - public func getMe(_ graph: GRAPH) -> Maybe { - let query = API.GRAPH_QUERY(path: graph) as! FetchMeQuery - return graphAPI.fetch(query: query, cachePolicy: .fetchIgnoringCacheData, queue: .main) - .map { item in - item.getMe.fragments.memberFR - } - .retry(when: { [weak self] errorObservable in - guard let self else { return Observable.never() } - return errorObservable.renewToken(with: self.tokenAcquizition) - }) - } - - public func updateNickName(_ graph: GRAPH) -> Maybe { - let mutation = API.GRAPH_MUTATION(path: graph) as! UpdateNickNameMutation - return graphAPI.perform(query: mutation, cachePolichy: .default, queue: .global(qos: .default)) - .map { item in - item.updateNickname.fragments.memberFR - } - .retry(when: { [weak self] errorObservable in - guard let self else { return Observable.never() } - return errorObservable.renewToken(with: self.tokenAcquizition) - }) - } - - public func perform(_ graph: GRAPH, max retry: Int = 2) -> Maybe { - let mutation = API.GRAPH_MUTATION(path: graph) as! SubmitSubmissionMutation - return graphAPI.perform(query: mutation, cachePolichy: .default, queue: .main) - .map { item in - item.createSubmission.fragments.submissionFR - } - .retry(when: { [weak self] errorObservable in - guard let self else { return Observable.never() } - return errorObservable.renewToken(with: self.tokenAcquizition) - }) // retry - } - - public func getProblemsQuery(_ graph: GRAPH) -> Maybe<([ProblemFR],PageInfoFR)> { - let query = API.GRAPH_QUERY(path: graph) as! FetchProblemsQuery - return graphAPI.fetch(query: query, cachePolicy: .default, queue: .main) - .map { item in - let content = item.getProblems.content! - let fproblems = content.compactMap { content in - content.fragments.problemFR - } - let fpageInfo = item.getProblems.pageInfo.fragments.pageInfoFR - return (fproblems, fpageInfo) - } - } - - public func getProblemByID(_ graph: GRAPH) -> Maybe { - let query = API.GRAPH_QUERY(path: graph) as! FetchProblemByIdQuery - return graphAPI.fetch(query: query, cachePolicy: .default, queue: .main) - .map { data in - data.getProblemById.fragments.problemFR - } - } - - public func getAllTag(_ graph: GRAPH) -> Maybe<([TagFR],PageInfoFR)> { - //tag: 완전 탐색 - //tag: 4 - //tag: 자료 구조 - let query = API.GRAPH_QUERY(path: graph) as! FetchAllTagQuery - return graphAPI.fetch(query: query, cachePolicy: .default, queue: .main) - .map { data in - let ftags: [TagFR] = data.getAllTag.content!.map { content in - content.fragments.tagFR - } - let pageInfo = data.getAllTag.pageInfo.fragments.pageInfoFR - return (ftags,pageInfo) - } - } - - public func getAllLanguage(_ graph: GRAPH) -> Maybe<[LanguageFR]> { - let query = API.GRAPH_QUERY(path: graph) as! FetchAllLanguageQuery - return graphAPI.fetch(query: query, cachePolicy: .default, queue: .main) - .map { data in - data.getAllLanguage.map { content in - content.fragments.languageFR - } - } - } - - public func getMeSubmissions(_ graph: GRAPH) -> Maybe<[SubmissionFR]> { - let query = API.GRAPH_QUERY(path: graph) as! FetchMeSubmissionsQuery - return graphAPI.fetch(query: query, cachePolicy: .default, queue: .main) - .map { data in - data.matchMember.submissions.map { submission in - submission.fragments.submissionFR - } - } - } - - public func getSubmission(_ graph: GRAPH, cache: CachePolicy? = nil) -> Maybe<([SubmissionFR],PageInfoFR)> { - let query = API.GRAPH_QUERY(path: graph) as! FetchSubmissionsQuery - return graphAPI.fetch(query: query, cachePolicy: cache ?? .default, queue: .main) - .map { data in - guard let contents = data.getSubmissions.content else { return ([],.init(_fieldData: nil)) } - let fsubmissions = contents.map { content in - content.fragments.submissionFR - } - let pageInfo = data.getSubmissions.pageInfo.fragments.pageInfoFR - return (fsubmissions, pageInfo) - } - } - - - public func signUp(member: MemberDTO) -> Maybe { - restAPI.request(.rest(.regitster(member))) { data in - return true - } - .map { $0 } - .retry(when: { [weak self] errorObservable in - guard let self else { return Observable.never() } - return errorObservable.renewToken(with: self.tokenAcquizition) - }) - } - - public func passwordChange(_ original: Pwd, new: Pwd) -> Maybe { - restAPI.request(.rest(.password(original, new))) { _ in - return true - } - .map { $0 } - .retry(when: { [weak self] errorObservable in - guard let self else { return Observable.never() } - return errorObservable.renewToken(with: self.tokenAcquizition) - }) - } - - public func editProfile(_ image: Data) -> Maybe { - restAPI.request(.rest(.profile(image))) { data in - String(data: data, encoding: .utf8) - } - .map { $0 } - .retry(when: { [weak self] errorObservable in - guard let self else { return Observable.never() } - return errorObservable.renewToken(with: self.tokenAcquizition) - }) - } -} - diff --git a/Projects/CodestackApp/Sources/Domain/Usecase/SubmissionHistoryUsecase.swift b/Projects/CodestackApp/Sources/Domain/Usecase/SubmissionHistoryUsecase.swift deleted file mode 100644 index 4d42bf35..00000000 --- a/Projects/CodestackApp/Sources/Domain/Usecase/SubmissionHistoryUsecase.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// SubmissionHistoryUsecase.swift -// CodeStack -// -// Created by 박형환 on 12/2/23. -// - -import Foundation -import RxSwift - -protocol SubmissionHistoryUsecase { - -} - -final class DefaultSubmissionHistoryUsecase: SubmissionHistoryUsecase { - -} diff --git a/Projects/CodestackApp/Sources/Flow/CodeProblemFlow.swift b/Projects/CodestackApp/Sources/Flow/CodeProblemFlow.swift index 5670a1cf..f42e2ffc 100644 --- a/Projects/CodestackApp/Sources/Flow/CodeProblemFlow.swift +++ b/Projects/CodestackApp/Sources/Flow/CodeProblemFlow.swift @@ -9,7 +9,7 @@ import UIKit import RxFlow import RxSwift import RxCocoa - +import Domain class CodeProblemFlow: Flow{ diff --git a/Projects/CodestackApp/Sources/Flow/CodestackStep.swift b/Projects/CodestackApp/Sources/Flow/CodestackStep.swift index babce775..5425ba00 100644 --- a/Projects/CodestackApp/Sources/Flow/CodestackStep.swift +++ b/Projects/CodestackApp/Sources/Flow/CodestackStep.swift @@ -7,7 +7,7 @@ import Foundation import RxFlow -import Data +import Domain enum ButtonType{ case today_problem(CodestackStep?) @@ -35,7 +35,7 @@ enum CodestackStep: Step, CaseIterable{ case registerPage case register case registerDissmiss - case userLoggedIn(ID?,Pwd?) + case userLoggedIn(String?,Pwd?) case logout diff --git a/Projects/CodestackApp/Sources/Flow/LoginFlow.swift b/Projects/CodestackApp/Sources/Flow/LoginFlow.swift index 7764e874..3a968215 100644 --- a/Projects/CodestackApp/Sources/Flow/LoginFlow.swift +++ b/Projects/CodestackApp/Sources/Flow/LoginFlow.swift @@ -9,7 +9,6 @@ import Foundation import RxFlow import RxSwift import RxCocoa -import Data import Global class LoginFlow: Flow{ @@ -44,9 +43,10 @@ class LoginFlow: Flow{ return .none case .logout: - UserManager.shared.logout { - KeychainItem.deleteToken() - } + // TODO: 바꿔야됌 +// UserManager.shared.logout { +// KeychainItem.deleteToken() +// } self.loginViewController.navigationController?.popViewController(animated: true) return .none @@ -94,11 +94,11 @@ class LoginFlow: Flow{ class LoginStepper: Stepper { var steps: PublishRelay = PublishRelay() + // TODO: Data Layer 의존성 제거해야됨 +// private var authService: RestAPI - private var authService: RestAPI - - init(authService: RestAPI) { - self.authService = authService + init(/*authService: RestAPI*/) { +// self.authService = authService } var initialStep: Step{ diff --git a/Projects/CodestackApp/Sources/Presentation/CodeEditor/VIew/CodeTextView.swift b/Projects/CodestackApp/Sources/Presentation/CodeEditor/VIew/CodeTextView.swift index 1c8b5563..f5bbfa94 100644 --- a/Projects/CodestackApp/Sources/Presentation/CodeEditor/VIew/CodeTextView.swift +++ b/Projects/CodestackApp/Sources/Presentation/CodeEditor/VIew/CodeTextView.swift @@ -8,6 +8,7 @@ import UIKit import Highlightr import RxSwift +import Domain class CodeUITextView: UITextView { diff --git a/Projects/CodestackApp/Sources/Presentation/CodeEditor/VIew/ProblemPopUpView.swift b/Projects/CodestackApp/Sources/Presentation/CodeEditor/VIew/ProblemPopUpView.swift index 6e974bca..c4f6af78 100644 --- a/Projects/CodestackApp/Sources/Presentation/CodeEditor/VIew/ProblemPopUpView.swift +++ b/Projects/CodestackApp/Sources/Presentation/CodeEditor/VIew/ProblemPopUpView.swift @@ -10,9 +10,8 @@ import RxCocoa import RxGesture import RxSwift import WebKit -import Data import Global - +import Domain //TODO: View -> UIViewCOntroller 로 변경해야됨 @@ -375,8 +374,7 @@ extension ProblemPopUpView: WKNavigationDelegate{ /// Load To WebView HTMLString /// - Parameter string: HTML String func loadHTMLToWebView(html string: String) { - - if string.isEmpty { + if string.isEmpty || string.count < 3 { wkWebView.addSubview(refreshLabel) wkWebView.addSubview(refreshButton) wkWebView.addSubview(activityIndicator) diff --git a/Projects/CodestackApp/Sources/Presentation/CodeEditor/VIew/SubmissionResultView.swift b/Projects/CodestackApp/Sources/Presentation/CodeEditor/VIew/SubmissionResultView.swift index e3705e51..f8f87650 100644 --- a/Projects/CodestackApp/Sources/Presentation/CodeEditor/VIew/SubmissionResultView.swift +++ b/Projects/CodestackApp/Sources/Presentation/CodeEditor/VIew/SubmissionResultView.swift @@ -7,6 +7,7 @@ import UIKit import RxSwift +import Domain class SubmissionResultView: UIView{ diff --git a/Projects/CodestackApp/Sources/Presentation/CodeEditor/ViewModel/CodeEditorViewModel.swift b/Projects/CodestackApp/Sources/Presentation/CodeEditor/ViewModel/CodeEditorViewModel.swift index 31c389f5..32aefb93 100644 --- a/Projects/CodestackApp/Sources/Presentation/CodeEditor/ViewModel/CodeEditorViewModel.swift +++ b/Projects/CodestackApp/Sources/Presentation/CodeEditor/ViewModel/CodeEditorViewModel.swift @@ -10,8 +10,8 @@ import RxFlow import RxSwift import RxCocoa import Apollo -import Data import Global +import Domain enum FavoriteError: Error { case error @@ -174,7 +174,7 @@ final class CodeEditorViewModel: CodeEditorViewModelType { } //MARK: - Action Binding FUNC - private func dissmissAction(action: Driver, problemID: Driver) { + private func dissmissAction(action: Driver, problemID: Driver) { action .asObservable() .withLatestFrom(sendProblemModel.asObservable()) { $1 } @@ -233,7 +233,7 @@ final class CodeEditorViewModel: CodeEditorViewModelType { }).disposed(by: disposeBag) } - private func loadSubmissionList(action: Driver, probelmID: Driver) { + private func loadSubmissionList(action: Driver, probelmID: Driver) { action .asObservable() .take(1) @@ -263,7 +263,7 @@ final class CodeEditorViewModel: CodeEditorViewModelType { /// 문제제출 메소드 /// - Parameter action: 문제 id를 관찰하고 있는 Driver - func sendAction(action: Signal) { + func sendAction(action: Signal) { action .skip(1) .withLatestFrom(sendProblemModel.asSignal(onErrorJustReturn: .default)) { $1 } @@ -316,20 +316,21 @@ final class CodeEditorViewModel: CodeEditorViewModelType { } private func viewErrorInteractorAction(_ error: Error) { - if let err = error as? TokenAcquisitionError { - if err == TokenAcquisitionError.unowned { - self.stepper?.steps.accept(CodestackStep.toastMessage("서버에 response가 오지 않습니다")) - } - - if err == TokenAcquisitionError.unauthorized { - self.stepper?.steps.accept(CodestackStep.loginNeeded) - return - } - } - if let _ = error as? SendError { - self.stepper?.steps.accept(CodestackStep.toastMessage("같은 내용입니다")) - return - } + // FIXME: TOKEN SERVICE ERROR 변경해야됌 +// if let err = error as? TokenAcquisitionError { +// if err == TokenAcquisitionError.unowned { +// self.stepper?.steps.accept(CodestackStep.toastMessage("서버에 response가 오지 않습니다")) +// } +// +// if err == TokenAcquisitionError.unauthorized { +// self.stepper?.steps.accept(CodestackStep.loginNeeded) +// return +// } +// } +// if let _ = error as? SendError { +// self.stepper?.steps.accept(CodestackStep.toastMessage("같은 내용입니다")) +// return +// } } } diff --git a/Projects/CodestackApp/Sources/Presentation/CodeEditor/ViewModel/SendProblemModel.swift b/Projects/CodestackApp/Sources/Presentation/CodeEditor/ViewModel/SendProblemModel.swift index abac7117..5ac6f479 100644 --- a/Projects/CodestackApp/Sources/Presentation/CodeEditor/ViewModel/SendProblemModel.swift +++ b/Projects/CodestackApp/Sources/Presentation/CodeEditor/ViewModel/SendProblemModel.swift @@ -6,17 +6,17 @@ // import Foundation -import Data +import Domain struct SendProblemModel { - let submissionID: ID - let problemID: ID + let submissionID: SubmissionID + let problemID: ProblemID let problemTitle: String let sourceCode: String let language: LanguageVO let userName: String = "" - init(submissionID: ID, problemID: ID, problemTitle: String = "", sourceCode: String, language: LanguageVO) { + init(submissionID: SubmissionID, problemID: ProblemID, problemTitle: String = "", sourceCode: String, language: LanguageVO) { self.submissionID = submissionID self.problemID = problemID self.problemTitle = problemTitle @@ -42,10 +42,10 @@ extension SendProblemModel { language: LanguageVO.default) } - func makeFavoirtProblem() -> FavoriteProblem { - FavoriteProblem(problemID: self.problemID, - problmeTitle: self.problemTitle, - createdAt: Date()) + func makeFavoirtProblem() -> FavoriteProblemVO { + FavoriteProblemVO(problemID: self.problemID, + problmeTitle: self.problemTitle, + createdAt: Date()) } func toProblemIdentity() -> ProblemIdentityVO { diff --git a/Projects/CodestackApp/Sources/Presentation/CodeEditor/ViewModel/SolvePageType.swift b/Projects/CodestackApp/Sources/Presentation/CodeEditor/ViewModel/SolvePageType.swift index d9ceb902..100c4287 100644 --- a/Projects/CodestackApp/Sources/Presentation/CodeEditor/ViewModel/SolvePageType.swift +++ b/Projects/CodestackApp/Sources/Presentation/CodeEditor/ViewModel/SolvePageType.swift @@ -6,7 +6,7 @@ // import Foundation - +import Domain enum SolveResultType { case problem diff --git a/Projects/CodestackApp/Sources/Presentation/CodeProblem/Cell/GraphCell/View/LanguageTagContainer.swift b/Projects/CodestackApp/Sources/Presentation/CodeProblem/Cell/GraphCell/View/LanguageTagContainer.swift index 7cd840be..300e282e 100644 --- a/Projects/CodestackApp/Sources/Presentation/CodeProblem/Cell/GraphCell/View/LanguageTagContainer.swift +++ b/Projects/CodestackApp/Sources/Presentation/CodeProblem/Cell/GraphCell/View/LanguageTagContainer.swift @@ -6,7 +6,7 @@ // import UIKit - +import Domain extension String{ func getColor() -> UIColor{ diff --git a/Projects/CodestackApp/Sources/Presentation/CodeProblem/Cell/ProblemCell.swift b/Projects/CodestackApp/Sources/Presentation/CodeProblem/Cell/ProblemCell.swift index 26dc9291..e7427a44 100644 --- a/Projects/CodestackApp/Sources/Presentation/CodeProblem/Cell/ProblemCell.swift +++ b/Projects/CodestackApp/Sources/Presentation/CodeProblem/Cell/ProblemCell.swift @@ -9,6 +9,7 @@ import UIKit import SnapKit import RxSwift import RxCocoa +import Domain class ProblemCell: UITableViewCell{ diff --git a/Projects/CodestackApp/Sources/Presentation/CodeProblem/DummyData.swift b/Projects/CodestackApp/Sources/Presentation/CodeProblem/DummyData.swift index fcc192c9..de7af1c2 100644 --- a/Projects/CodestackApp/Sources/Presentation/CodeProblem/DummyData.swift +++ b/Projects/CodestackApp/Sources/Presentation/CodeProblem/DummyData.swift @@ -6,6 +6,7 @@ // import Foundation +import Domain typealias DummyModel = (model: ProblemListItemModel, language: [LanguageVO], flag: Bool) diff --git a/Projects/CodestackApp/Sources/Model/Model+Mapping/ProblemVO+Mapping.swift b/Projects/CodestackApp/Sources/Presentation/CodeProblem/ProblemVO+Mapping.swift similarity index 98% rename from Projects/CodestackApp/Sources/Model/Model+Mapping/ProblemVO+Mapping.swift rename to Projects/CodestackApp/Sources/Presentation/CodeProblem/ProblemVO+Mapping.swift index df861fc3..08df2afa 100644 --- a/Projects/CodestackApp/Sources/Model/Model+Mapping/ProblemVO+Mapping.swift +++ b/Projects/CodestackApp/Sources/Presentation/CodeProblem/ProblemVO+Mapping.swift @@ -7,7 +7,7 @@ import Foundation import CodestackAPI - +import Domain //MARK: - To View Model extension ProblemVO { diff --git a/Projects/CodestackApp/Sources/Presentation/CodeProblem/VIewModel/CodeProblemViewModel.swift b/Projects/CodestackApp/Sources/Presentation/CodeProblem/VIewModel/CodeProblemViewModel.swift index ea305ab2..dc642f27 100644 --- a/Projects/CodestackApp/Sources/Presentation/CodeProblem/VIewModel/CodeProblemViewModel.swift +++ b/Projects/CodestackApp/Sources/Presentation/CodeProblem/VIewModel/CodeProblemViewModel.swift @@ -9,7 +9,7 @@ import Foundation import RxSwift import RxCocoa import RxFlow -import Data +import Domain protocol ProblemViewModelProtocol{ associatedtype Input = CodeProblemViewModel.Input @@ -22,7 +22,6 @@ protocol ProblemViewModelProtocol{ class CodeProblemViewModel: ProblemViewModelProtocol,Stepper { - var steps = PublishRelay() struct Input { @@ -34,7 +33,7 @@ class CodeProblemViewModel: ProblemViewModelProtocol,Stepper { var fetchProblemList: Signal } - struct Output{ + struct Output { var seg_list_model: Driver<[DummyModel]> var cell_temporary_content_update: Driver<(Int,Bool)> var refreshEndEvnet: Driver @@ -42,13 +41,13 @@ class CodeProblemViewModel: ProblemViewModelProtocol,Stepper { private var dummy: DummyData - private var apollo: WebRepository + private var useCase: ProblemUsecase var animationSelected: [String : Bool] = [:] - init(_ dummy: DummyData, _ service: WebRepository){ + init(_ dummy: DummyData, _ service: ProblemUsecase){ self.dummy = dummy - self.apollo = service + self.useCase = service } deinit{ @@ -81,6 +80,12 @@ class CodeProblemViewModel: ProblemViewModelProtocol,Stepper { fetchWhenViewDidLoad(load: input.viewDidLoad) + //MARK: View Will Disappear + _ = input.viewDissapear + .emit(with: self, onNext: { vm , value in + vm.currentPage = 0 + }).disposed(by: disposeBag) + //MARK: - Cell Select _ = input.cellSelect @@ -157,7 +162,10 @@ class CodeProblemViewModel: ProblemViewModelProtocol,Stepper { return true } .delay(.milliseconds(300)) - .flatMap { vm, _ in vm.requestProblem(offset: vm.currentPage) } + .flatMap { vm, _ in + vm.useCase.fetchProblems(offset: vm.currentPage) + .asSignal(onErrorJustReturn: []) + } .emit(with: self, onNext: { vm, problems in let dummyModel = problems.map { problem in let list1 = problem.toProblemList(.sample) @@ -188,7 +196,10 @@ class CodeProblemViewModel: ProblemViewModelProtocol,Stepper { private func fetchWhenViewDidLoad(load: Signal) { _ = load .withUnretained(self) - .flatMap { vm, _ in vm.requestProblem(offset: vm.currentPage) } + .flatMap { vm, _ in + vm.useCase.fetchProblems(offset: vm.currentPage) + .asSignal(onErrorJustReturn: []) + } .emit(with: self, onNext: { vm, problems in let dummyModel = problems.map { problem in let list = problem.toProblemList() @@ -205,16 +216,17 @@ class CodeProblemViewModel: ProblemViewModelProtocol,Stepper { .disposed(by: disposeBag) } - private func requestProblem(offset: Int, - sort: String = "id", - order: String = "asc") -> Signal<[ProblemVO]> { - apollo - // .getProblemsQuery(query: Query.getProblems(offset: offset, sort: sort, order: order)) - //TODO: 확인후 변경 - .getProblemsQuery(.PR_LIST(arg: GRAR(offset: offset))) - .map { frInfo in - frInfo.0.map { fr in fr.toDomain() } - } - .asSignal(onErrorJustReturn: []) - } +// private func requestProblem(offset: Int, +// sort: String = "id", +// order: String = "asc") -> Signal<[ProblemVO]> { +// apollo +// // .getProblemsQuery(query: Query.getProblems(offset: offset, sort: sort, order: order)) +// //TODO: 확인후 변경 +// .getProblemsQuery(.PR_LIST(arg: GRAR(offset: offset))) +// .map { $0.0 } +//// .map { frInfo in +//// frInfo.0.map { fr in fr.toDomain() } +//// } +// .asSignal(onErrorJustReturn: []) +// } } diff --git a/Projects/CodestackApp/Sources/Presentation/CodeProblem/VIewModel/ProblemItemModel.swift b/Projects/CodestackApp/Sources/Presentation/CodeProblem/VIewModel/ProblemItemModel.swift index 36881b2a..048f821d 100644 --- a/Projects/CodestackApp/Sources/Presentation/CodeProblem/VIewModel/ProblemItemModel.swift +++ b/Projects/CodestackApp/Sources/Presentation/CodeProblem/VIewModel/ProblemItemModel.swift @@ -6,6 +6,7 @@ // import Foundation +import Domain var contextExample = """ diff --git a/Projects/CodestackApp/Sources/Presentation/CodeProblem/ViewController/CodeProblemViewController.swift b/Projects/CodestackApp/Sources/Presentation/CodeProblem/ViewController/CodeProblemViewController.swift index 9e2fb7c0..7b713182 100644 --- a/Projects/CodestackApp/Sources/Presentation/CodeProblem/ViewController/CodeProblemViewController.swift +++ b/Projects/CodestackApp/Sources/Presentation/CodeProblem/ViewController/CodeProblemViewController.swift @@ -67,6 +67,7 @@ class CodeProblemViewController: UIViewController, UITableViewDelegate { if self.navigationController == nil { disposeBag = DisposeBag() + } } diff --git a/Projects/CodestackApp/Sources/Presentation/History/View/HistoryCell.swift b/Projects/CodestackApp/Sources/Presentation/History/View/HistoryCell.swift index 9c6f8aef..8d6fbbc8 100644 --- a/Projects/CodestackApp/Sources/Presentation/History/View/HistoryCell.swift +++ b/Projects/CodestackApp/Sources/Presentation/History/View/HistoryCell.swift @@ -10,6 +10,7 @@ import SnapKit import RxCocoa import RxSwift import Global +import Domain class HistoryCell: UITableViewCell { diff --git a/Projects/CodestackApp/Sources/Presentation/History/ViewModel/HistoryViewModel.swift b/Projects/CodestackApp/Sources/Presentation/History/ViewModel/HistoryViewModel.swift index a664051e..d7622a0c 100644 --- a/Projects/CodestackApp/Sources/Presentation/History/ViewModel/HistoryViewModel.swift +++ b/Projects/CodestackApp/Sources/Presentation/History/ViewModel/HistoryViewModel.swift @@ -10,7 +10,7 @@ import RxSwift import RxCocoa import RxRelay import Global -import Data +import Domain protocol HistoryViewModelType: ViewModelType, AnyObject { var sendSubmission: PublishRelay { get set } @@ -35,15 +35,12 @@ class HistoryViewModel: HistoryViewModelType { struct Dependency { - let service: WebRepository - let submisionUsecase: SubmissionUseCase + let submisionUsecase: HistoryUsecase } - private let service: WebRepository - private let submissionUsecase: SubmissionUseCase + private let historyUsecase: HistoryUsecase init(dependency: Dependency){ - self.service = dependency.service - self.submissionUsecase = dependency.submisionUsecase + self.historyUsecase = dependency.submisionUsecase } private var disposeBag = DisposeBag() @@ -95,7 +92,7 @@ class HistoryViewModel: HistoryViewModelType { return filteredSubmissions } - submissionUsecase.fetchFavoriteProblem() + historyUsecase.fetchFavoriteProblem() .subscribe(with: self, onNext: { vm, favoriteProblems in Log.debug("즐겨찾기 한 문제 : \(favoriteProblems)") }).disposed(by: disposeBag) @@ -126,16 +123,7 @@ class HistoryViewModel: HistoryViewModelType { viewDidLoad .asObservable() .withUnretained(self) - .flatMap { vm, _ in - let localTempSubmission - = vm.submissionUsecase - .fetchProblemHistoryEqualStatus(status: "temp") - .compactMap { try $0.get() } - - let fetchedSubmission = vm.requestSubmission().do(onNext: { _ in vm.currentPage += 1 }) - return Observable<[SubmissionVO]>.concat([localTempSubmission, fetchedSubmission]) - } - + .flatMap { vm, _ in vm.fetchSubmissionHistory() } .subscribe(with: self, onNext: { vm, data in vm.submissionModel.accept(data) vm.refreshEndEvent.accept(()) @@ -155,9 +143,10 @@ class HistoryViewModel: HistoryViewModelType { return true } .delay(.milliseconds(300)) - .flatMap {vm, _ in vm.requestSubmission().asSignal(onErrorJustReturn: []) } + .flatMap {vm, _ in + vm.historyUsecase.fetchSubmission(offset: 0).asSignal(onErrorJustReturn: []) + } .emit(with: self,onNext: { vm, datas in - if !datas.isEmpty { vm.submissionModel.accept(datas) vm.currentPage += 1 @@ -173,7 +162,7 @@ class HistoryViewModel: HistoryViewModelType { .throttle(.milliseconds(800)) .asObservable() .withUnretained(self) - .flatMapLatest { vm, _ in vm.requestSubmission() } + .flatMapLatest { vm, _ in vm.historyUsecase.fetchSubmission(offset: 0) } .subscribe(with: self, onNext: { vm, datas in vm.submissionModel.accept(datas) vm.refreshEndEvent.accept(()) @@ -182,6 +171,20 @@ class HistoryViewModel: HistoryViewModelType { .disposed(by: disposeBag) } + private func fetchSubmissionHistory() -> Observable<[SubmissionVO]> { + let localTempSubmission + = historyUsecase + .fetchProblemHistoryEqualStatus(status: "temp") + .compactMap { try $0.get() } + + let fetchedSubmission = + historyUsecase + .fetchSubmission(offset: self.currentPage) + .do(onNext: { [weak self] _ in self?.currentPage += 1 }) + + return Observable<[SubmissionVO]>.concat([localTempSubmission, fetchedSubmission]) + } + private lazy var retryHandler: (Observable) -> Observable = { error in error.enumerated().flatMap { [weak self] (attempt, error) -> Observable in if attempt >= 3 { @@ -192,18 +195,6 @@ class HistoryViewModel: HistoryViewModelType { } } - private func requestSubmission() -> Observable<[SubmissionVO]> { - return service - .getSubmission(.SUB_LIST(arg: GRAR.init(offset: self.currentPage)), cache: nil) - .map { frinfo in - frinfo.0.map { fr in fr.toDomain() } - } -// .getSubmission(query: Query.getSubmission(offest: self.currentPage), -// cache: nil) - .asObservable() - } - - // TODO: - "INTERNAL_SERVER_ERROR", // 현재 getMe 의 submission query Internal Server Error 발생 private func requestGetMeSubmissions(){ @@ -219,12 +210,14 @@ class HistoryViewModel: HistoryViewModelType { // Log.debug(me) // }) // _ = service.getSubmission(query: Query.getSubmission(),cache: .fetchIgnoringCacheCompletely) - _ = service.getMeSubmissions(.SUB_LIST(arg: GRAR.init(offset: 0))) - .map { $0.map { fr in fr.toDomain() }} - .subscribe(with: self, onSuccess: { vm, value in - vm.submissionModel.accept([]) - vm.historyData.accept([]) - vm.historyData.accept(value) - }) +// _ = service.getMeSubmissions(.SUB_LIST(arg: GRAR.init(offset: 0))) + //TODO: 확인 필요 +// .map { $0 } +// .map { $0.map { fr in fr.toDomain() }} +// .subscribe(with: self, onSuccess: { vm, value in +// vm.submissionModel.accept([]) +// vm.historyData.accept([]) +// vm.historyData.accept(value) +// }) } } diff --git a/Projects/CodestackApp/Sources/Presentation/LoginPage/View/LoginButtonType.swift b/Projects/CodestackApp/Sources/Presentation/LoginPage/View/LoginButtonType.swift index 9fdcefea..e0f195f5 100644 --- a/Projects/CodestackApp/Sources/Presentation/LoginPage/View/LoginButtonType.swift +++ b/Projects/CodestackApp/Sources/Presentation/LoginPage/View/LoginButtonType.swift @@ -6,11 +6,11 @@ // import Foundation -import Data +import Domain enum LoginButtonType{ case gitHub case apple - case email((ID,Pwd)) + case email((String, Pwd)) case none } diff --git a/Projects/CodestackApp/Sources/Presentation/LoginPage/ViewController/LoginViewController.swift b/Projects/CodestackApp/Sources/Presentation/LoginPage/ViewController/LoginViewController.swift index abee008b..1464e3a5 100644 --- a/Projects/CodestackApp/Sources/Presentation/LoginPage/ViewController/LoginViewController.swift +++ b/Projects/CodestackApp/Sources/Presentation/LoginPage/ViewController/LoginViewController.swift @@ -9,28 +9,27 @@ import UIKit import RxFlow import RxCocoa import RxSwift -import Data -class LoginViewController: UIViewController,Stepper, AppleLoginViewType { +class LoginViewController: UIViewController,Stepper /*AppleLoginViewType*/ { //MARK: - RXFLow let steps = PublishRelay() //MARK: -Dependency - private var loginViewModel: (any LoginViewModelProtocol)? - private var appleManager: AppleLoginManager? + private var loginViewModel: LoginViewModel? +// private var appleManager: AppleLoginManager? struct Dependencies{ - var viewModel: (any LoginViewModelProtocol)? - var appleManager: AppleLoginManager? + var viewModel: LoginViewModel? +// var appleManager: AppleLoginManager? } static func create(with dependencies: Dependencies) -> LoginViewController{ let vc = LoginViewController() vc.loginViewModel = dependencies.viewModel - vc.appleManager = dependencies.appleManager - vc.appleManager?.loginViewcontroller = vc +// vc.appleManager = dependencies.appleManager +// vc.appleManager?.loginViewcontroller = vc return vc } @@ -53,18 +52,20 @@ class LoginViewController: UIViewController,Stepper, AppleLoginViewType { layoutConfigure() // Apple login buton setting - appleManager?.settingLoginView() + // TODO: APPLE LOGIN MANAGER +// appleManager?.settingLoginView() let loginEvent = loginView.emitButtonEvents() let registerEvent = loginView.registerEvent() - let output = - (loginViewModel as! LoginViewModel) + let output = loginViewModel? .transform(input: LoginViewModel.Input(loginEvent: loginEvent, registerEvent: registerEvent)) - loginView.binding(loading: output.loading) + if let loading = output?.loading { + loginView.binding(loading: loading) + } DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: { diff --git a/Projects/CodestackApp/Sources/Presentation/LoginPage/ViewController/RegisterViewController.swift b/Projects/CodestackApp/Sources/Presentation/LoginPage/ViewController/RegisterViewController.swift index 050935eb..b2336eaf 100644 --- a/Projects/CodestackApp/Sources/Presentation/LoginPage/ViewController/RegisterViewController.swift +++ b/Projects/CodestackApp/Sources/Presentation/LoginPage/ViewController/RegisterViewController.swift @@ -4,7 +4,7 @@ import SnapKit import RxFlow import RxRelay import RxSwift -import Data +import Domain enum RError: Error{ case invalidID @@ -26,11 +26,11 @@ class RegisterViewController: UIViewController, Stepper{ case wrong } - private var service: RestAPI? - - static func create(with dependency: RestAPI) -> RegisterViewController { + private var authUsecase: AuthUsecase? + + static func create(with dependency: AuthUsecase) -> RegisterViewController { let vc = RegisterViewController() - vc.service = dependency + vc.authUsecase = dependency return vc } @@ -281,9 +281,16 @@ class RegisterViewController: UIViewController, Stepper{ return vc.isValid(id , password, isCorrect, email, nickname) } .filter{ $0 } + .withUnretained(self) + .compactMap { vc, _ in vc.makeUserInfo() } + .flatMap { [weak authUsecase] query -> Observable in + guard let authUsecase else { return .just(false) } + return authUsecase.signUpRequest(query: query) + } .subscribe(with: self, onNext: { vc, flag in - guard let userInfo = vc.makeUserInfo() else { return } - vc.request( userInfo ) + flag ? vc.toast(content: "실패하였습니당") : vc.steps.accept(CodestackStep.registerDissmiss) + // guard let userInfo = else { return } + // vc.request( userInfo ) }).disposed(by: disposeBag) } @@ -314,13 +321,13 @@ class RegisterViewController: UIViewController, Stepper{ //MARK: - Send to server register message - private func request(_ member: MemberDTO) { - guard let service else { return } - _ = service.signUp(member: member) - .subscribe(with: self, onSuccess: { vc, flag in - flag ? vc.toast(content: "실패하였습니당") : vc.steps.accept(CodestackStep.registerDissmiss) - }) - } +// private func request(_ member: MemberDTO) { +// guard let service else { return } +// _ = service.signUp(member: member) +// .subscribe(with: self, onSuccess: { vc, flag in +// +// }) +// } //MARK: - Util 함수 @@ -331,14 +338,14 @@ class RegisterViewController: UIViewController, Stepper{ boader: UIColor.black.cgColor) } - private func makeUserInfo() -> MemberDTO? { + private func makeUserInfo() -> RegisterQuery? { guard let id = idField.text else { return nil } guard let password = passwordField.text else { return nil } guard let email = emailField.text else { return nil } guard let nickname = nickNameField.text else { return nil } - return MemberDTO(id: id, password: password, email: email, nickName: nickname) + return RegisterQuery(id: id, password: password, email: email, nickname: nickname) } private func markColorWhenVaild(type: Valid, view: UIView) { diff --git a/Projects/CodestackApp/Sources/Presentation/LoginPage/ViewModel/LoginViewModel.swift b/Projects/CodestackApp/Sources/Presentation/LoginPage/ViewModel/LoginViewModel.swift index 1776dc72..712ecc03 100644 --- a/Projects/CodestackApp/Sources/Presentation/LoginPage/ViewModel/LoginViewModel.swift +++ b/Projects/CodestackApp/Sources/Presentation/LoginPage/ViewModel/LoginViewModel.swift @@ -10,15 +10,15 @@ import RxFlow import RxRelay import RxSwift import RxCocoa -import Data import Global +import Domain enum LoginError: Error{ case gitOAuthError case timeOut } -class LoginViewModel: LoginViewModelProtocol, Stepper{ +class LoginViewModel: /*LoginViewModelProtocol*/ Stepper{ var steps: PublishRelay @@ -32,21 +32,24 @@ class LoginViewModel: LoginViewModelProtocol, Stepper{ } struct Dependency { - let loginService: Auth - let apolloService: WebRepository +// let loginService: Auth +// let apolloService: WebRepository + let authUsecase: AuthUsecase let stepper: LoginStepper } - private var loginService: Auth - private var apolloService: WebRepository +// private var loginService: Auth +// private var apolloService: WebRepository private var disposeBag = DisposeBag() - + private let authUsecase: AuthUsecase init(dependency: Dependency){ - self.loginService = dependency.loginService - self.apolloService = dependency.apolloService +// self.loginService = dependency.loginService +// self.apolloService = dependency.apolloService + self.authUsecase = dependency.authUsecase self.steps = dependency.stepper.steps - (self.loginService as? LoginService)?.loginViewModel = self + // TODO: 해결 해야됌 + // (self.loginService as? LoginService)?.loginViewModel = self } private var loginFailAlert: PublishSubject = PublishSubject() @@ -72,7 +75,7 @@ class LoginViewModel: LoginViewModelProtocol, Stepper{ break case .gitHub: do{ - try vm.loginService.gitOAuthrization() + try vm.authUsecase.gitOAuthrization() }catch{ Log.error("github 로그인 실패 : \(error)") } @@ -99,63 +102,72 @@ class LoginViewModel: LoginViewModelProtocol, Stepper{ func requestOAuth() throws { - do { - try loginService.gitOAuthrization() - }catch{ - throw LoginError.gitOAuthError - } +// do { +// try loginService.gitOAuthrization() +// }catch{ +// throw LoginError.gitOAuthError +// } } - func oAuthComplte(apple token: AppleDTO) { - _ = loginService - .request(with: token) + func oAuthComplte(apple code: String, user: String) { + // _ = loginService + // .request(with: token) + _ = authUsecase + .request(apple: code , user: user ) .observe(on: ConcurrentDispatchQueueScheduler.init(qos: .default)) .do(onNext: { [weak self] _ in self?.loginLoadingEvent.accept(.success(false)) }) .do(onError: { [weak self] _ in self?.loginLoadingEvent.accept(.failure(LoginError.timeOut)) }) .flatMap { token in self.saveToken(token: token) } - .subscribe(with: self,onSuccess: { owner, user in owner.saveUserInfo(memeber: user) }, - onError: { owner , err in Log.error(err) }) + .subscribe(with: self, onNext: { owner, user in owner.saveUserInfo(memeber: user) }, + onError: { owner , err in Log.error(err) }) } /// gitHub OAuh 가 끝나고 다시 서버로 token 요청하는 함수 /// - Parameter code: github code func oAuthComplete(code: GitCode){ - _ = loginService +// _ = loginService +// .request(with: code) + _ = authUsecase .request(with: code) .observe(on: ConcurrentDispatchQueueScheduler.init(qos: .default)) .do(onNext: { [weak self] _ in self?.loginLoadingEvent.accept(.success(false))}) .do(onError: { [weak self] err in self?.loginLoadingEvent.accept(.failure(LoginError.timeOut)) }) .flatMap { token in self.saveToken(token: token) } - .subscribe(with: self,onSuccess: { owner, user in owner.saveUserInfo(memeber: user)}, + .subscribe(with: self,onNext: { owner, user in owner.saveUserInfo(memeber: user)}, onError: { owner , err in Log.error(err) }) } private func requestAuth(id: String, pwd: String){ - _ = loginService.request(name: id, password: pwd) +// _ = loginService.request(name: id, password: pwd) + _ = authUsecase + .request(name: id, password: pwd) .observe(on: ConcurrentDispatchQueueScheduler.init(qos: .default)) .do(onNext: { [weak self] _ in self?.loginLoadingEvent.accept(.success(false)) }) .do(onError: { [weak self] _ in self?.loginLoadingEvent.accept(.failure(LoginError.timeOut)) }) .flatMap { token in - Log.debug(KeychainItem.currentAccessToken) + // FIXME: KeyChain 계층 변경ㅇ해야됌 +// Log.debug(KeychainItem.currentAccessToken) return self.saveToken(token: token) } - .subscribe(with: self,onSuccess: { owner, user in owner.saveUserInfo(memeber: user) }, + .subscribe(with: self,onNext: { owner, user in owner.saveUserInfo(memeber: user) }, onError: { owner , err in Log.error(err) }) } - private func saveToken(token: CSTokenDTO) -> Maybe { - UserManager.shared.saveTokenInfo(with: TokenInfo(expiresIn: token.expiresIn, - tokenType: token.tokenType)) - - KeychainItem.saveTokens(access: token.accessToken, refresh: token.refreshToken) - - return apolloService.getMe(.ME) - .map { fr in fr.toDomain() } + private func saveToken(token: CSTokenVO) -> Observable { + // TODO: 바꿔야됌 +// UserManager.shared.saveTokenInfo(with: TokenInfo(expiresIn: token.expiresIn, +// tokenType: token.tokenType)) +// +// KeychainItem.saveTokens(access: token.accessToken, refresh: token.refreshToken) +// + return authUsecase.fetchMe() +// .map { fr in fr.toDomain() } } private func saveUserInfo(memeber: MemberVO){ - UserManager.shared.saveUser(with: memeber) + // TODO: 바꿔야됌 + //UserManager.shared.saveUser(with: memeber) steps.accept(CodestackStep.userLoggedIn(nil, nil)) } } diff --git a/Projects/CodestackApp/Sources/Presentation/Main/HomeView/CollectionViewCell/PRSubmissionHistoryCell.swift b/Projects/CodestackApp/Sources/Presentation/Main/HomeView/CollectionViewCell/PRSubmissionHistoryCell.swift index 6d6f15c5..779ca021 100644 --- a/Projects/CodestackApp/Sources/Presentation/Main/HomeView/CollectionViewCell/PRSubmissionHistoryCell.swift +++ b/Projects/CodestackApp/Sources/Presentation/Main/HomeView/CollectionViewCell/PRSubmissionHistoryCell.swift @@ -10,6 +10,7 @@ import UIKit import SnapKit import RxCocoa import RxSwift +import Domain import Global final class PRSubmissionHistoryCell: UICollectionViewCell { diff --git a/Projects/CodestackApp/Sources/Presentation/Main/SideBar-VC/SideMenu/SideMenuViewController.swift b/Projects/CodestackApp/Sources/Presentation/Main/SideBar-VC/SideMenu/SideMenuViewController.swift index 0ec9c503..a394d5cd 100644 --- a/Projects/CodestackApp/Sources/Presentation/Main/SideBar-VC/SideMenu/SideMenuViewController.swift +++ b/Projects/CodestackApp/Sources/Presentation/Main/SideBar-VC/SideMenu/SideMenuViewController.swift @@ -8,7 +8,6 @@ import UIKit import RxFlow import RxCocoa -import Data import Global struct SideMenuItem { @@ -242,8 +241,9 @@ extension SideMenuViewController: UITableViewDataSource, UITableViewDelegate { if item.presentation == .logout{ DispatchQueue.global().async { do{ - try KeychainItem(service: .bundle, account: .access).deleteItem() - try KeychainItem(service: .bundle, account: .refresh).deleteItem() + // TODO: KeyChain 변경해야됨 +// try KeychainItem(service: .bundle, account: .access).deleteItem() +// try KeychainItem(service: .bundle, account: .refresh).deleteItem() }catch{ Log.error("logout but KeychainItem deleteError") } diff --git a/Projects/CodestackApp/Sources/Presentation/Main/ViewController/ViewController.swift b/Projects/CodestackApp/Sources/Presentation/Main/ViewController/ViewController.swift index 7b6ee60f..7fb0a4b8 100644 --- a/Projects/CodestackApp/Sources/Presentation/Main/ViewController/ViewController.swift +++ b/Projects/CodestackApp/Sources/Presentation/Main/ViewController/ViewController.swift @@ -12,8 +12,7 @@ import RxSwift import RxRelay import RxGesture import RxDataSources - - +import Domain extension UIViewController { func adjustLargeTitleSize(title: String = "Codestack") { diff --git a/Projects/CodestackApp/Sources/Presentation/Main/ViewModel/HomeViewModel.swift b/Projects/CodestackApp/Sources/Presentation/Main/ViewModel/HomeViewModel.swift index 74a70ea8..6fca3025 100644 --- a/Projects/CodestackApp/Sources/Presentation/Main/ViewModel/HomeViewModel.swift +++ b/Projects/CodestackApp/Sources/Presentation/Main/ViewModel/HomeViewModel.swift @@ -12,7 +12,7 @@ import RxCocoa import RxFlow import CodestackAPI import Global -import Data +import Domain protocol HomeViewModelType: ViewModelType, Stepper, AnyObject { @@ -40,11 +40,9 @@ final class HomeViewModel: HomeViewModelType { var steps = PublishRelay() + private var homeUsecase: HomeUsecase private var disposeBag: DisposeBag = DisposeBag() - private var service: WebRepository - private var repository: DBRepository - //MARK: - Input var sendSubmission = PublishRelay() var updateSubmission = PublishRelay() @@ -53,13 +51,11 @@ final class HomeViewModel: HomeViewModelType { private var submissions = PublishRelay<[RecentSubmission]>() struct Dependency { - let repository: DBRepository - let service: WebRepository + let homeUsecase: HomeUsecase } init(dependency: Dependency){ - self.service = dependency.service - self.repository = dependency.repository + self.homeUsecase = dependency.homeUsecase sendSubmissionBinding() updateSubmissionBinding() } @@ -169,14 +165,7 @@ final class HomeViewModel: HomeViewModelType { } func fetchProblem(using submission: SubmissionVO) -> Signal { - service - .getProblemByID(.PR_BY_ID(submission.problem.id)) - .map { prFR in - let problemVO = prFR.toDomain() - var submission = submission - submission.problemVO = problemVO - return submission - } + homeUsecase.fetchProblem(using: submission) .asSignal(onErrorRecover: { error in Log.error("\(error)") return .just(SubmissionVO.sample) @@ -184,23 +173,8 @@ final class HomeViewModel: HomeViewModelType { } func fetchedSubmission() -> Signal<[SubmissionVO]> { - repository - .fetchProblemState() - .map { problemState in - if let state = problemState.first { - return state - .submissions - .sorted(by: { s1,s2 in - if let createdAt1 = s1.createdAt.toDateKST(), - let createdAt2 = s2.createdAt.toDateKST() { - return createdAt1 > createdAt2 - } - return false - }) - } else { - return [] - } - } + homeUsecase + .fetchSubmissionList() .asSignal(onErrorJustReturn: []) } @@ -216,13 +190,7 @@ final class HomeViewModel: HomeViewModelType { let newSubmissions: [SubmissionVO] = submissions.map { $0.problem.id == updateSubmission.problem.id ? updateSubmission : $0 } - let sortedSubmissions: [SubmissionVO] = newSubmissions.sorted(by: { first, second in - if let date1 = first.createdAt.toDateKST(), - let date2 = second.createdAt.toDateKST() { - return date1 > date2 - } - return true - }) + let sortedSubmissions: [SubmissionVO] = newSubmissions.sortByDate() return Array(sortedSubmissions.prefix(10)) } else { let array = [updateSubmission] + submissions diff --git a/Projects/CodestackApp/Sources/Presentation/Main/ViewModel/RecentPageModel.swift b/Projects/CodestackApp/Sources/Presentation/Main/ViewModel/RecentPageModel.swift index 593b6c56..30312c2e 100644 --- a/Projects/CodestackApp/Sources/Presentation/Main/ViewModel/RecentPageModel.swift +++ b/Projects/CodestackApp/Sources/Presentation/Main/ViewModel/RecentPageModel.swift @@ -7,7 +7,7 @@ import Foundation import RxDataSources - +import Domain //typealias RecentSubmissionSection = SectionModel // MARK: 확인후 제거 diff --git a/Projects/CodestackApp/Sources/Presentation/MyPage/View/ProfileView.swift b/Projects/CodestackApp/Sources/Presentation/MyPage/View/ProfileView.swift index 62749e06..a3ae2b49 100644 --- a/Projects/CodestackApp/Sources/Presentation/MyPage/View/ProfileView.swift +++ b/Projects/CodestackApp/Sources/Presentation/MyPage/View/ProfileView.swift @@ -10,6 +10,7 @@ import SnapKit import RxSwift import RxCocoa import Global +import Domain class ProfileView: UIView{ diff --git a/Projects/CodestackApp/Sources/Presentation/MyPage/ViewModel/MyPageViewModel.swift b/Projects/CodestackApp/Sources/Presentation/MyPage/ViewModel/MyPageViewModel.swift index 10c157cb..a65f77df 100644 --- a/Projects/CodestackApp/Sources/Presentation/MyPage/ViewModel/MyPageViewModel.swift +++ b/Projects/CodestackApp/Sources/Presentation/MyPage/ViewModel/MyPageViewModel.swift @@ -9,8 +9,8 @@ import Foundation import RxFlow import RxSwift import RxCocoa -import Data import Global +import Domain class MyPageViewModel: ViewModelType, Stepper{ @@ -28,22 +28,19 @@ class MyPageViewModel: ViewModelType, Stepper{ } struct Dependency { - let authService: RestAPI - let apolloService: WebRepository + let profileUsecase: ProfileUsecase } var steps = PublishRelay() private var disposeBag = DisposeBag() - private let authService: RestAPI - let apolloService: WebRepository + private let profileUsecase: ProfileUsecase init(dependency: Dependency) { - self.authService = dependency.authService - self.apolloService = dependency.apolloService + self.profileUsecase = dependency.profileUsecase } - private let userProfile = BehaviorRelay(value: UserManager.shared.profile) + private let userProfile = BehaviorRelay(value: MemberVO.sample) func transform(input: Input) -> Output { @@ -60,8 +57,10 @@ class MyPageViewModel: ViewModelType, Stepper{ .asObservable() .observe(on: ConcurrentDispatchQueueScheduler(qos: .background)) .withUnretained(self) - .flatMap{ vm, _ in vm.apolloService.getMe(.ME) } - .map { $0.toDomain() } + .flatMap{ vm, _ in vm.profileUsecase.fetchME() } + + //TODO: 확인 필요 +// .map { $0.toDomain() } .subscribe(with: self, onNext: { vm, member in vm.userProfile.accept(member) loading.onNext(.loaded(member.profileImage)) @@ -99,11 +98,13 @@ class MyPageViewModel: ViewModelType, Stepper{ .asObservable() .do(onNext: { _ in loading.onNext(.loading) }) .withUnretained(self) - .flatMap { vm,data in vm.authService.editProfile(data).asObservable() } + .flatMap { vm,data in vm.profileUsecase.editProfile(data: data) } .do(onNext: { image in loading.onNext(.loaded(image))}) .do(onError: {_ in loading.onNext(.loaded(nil))}) .subscribe(with: self, onNext: { vm, user in Log.debug("success: \(user)") + + // TODO: Fix 해야됩니다. // vm.userProfile.accept(user) }).disposed(by: disposeBag) diff --git a/Projects/Data/Derived/InfoPlists/Data_Data-Info.plist b/Projects/Data/Derived/InfoPlists/Data_Data-Info.plist new file mode 100644 index 00000000..04ffbaae --- /dev/null +++ b/Projects/Data/Derived/InfoPlists/Data_Data-Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Projects/Data/Derived/Sources/TuistBundle+Data.swift b/Projects/Data/Derived/Sources/TuistBundle+Data.swift new file mode 100644 index 00000000..1ca6291e --- /dev/null +++ b/Projects/Data/Derived/Sources/TuistBundle+Data.swift @@ -0,0 +1,40 @@ +// swiftlint:disable all +// swift-format-ignore-file +// swiftformat:disable all +import Foundation + +// MARK: - Swift Bundle Accessor + +private class BundleFinder {} + +extension Foundation.Bundle { + /// Since Data is a staticFramework, the bundle containing the resources is copied into the final product. + static let module: Bundle = { + let bundleName = "Data_Data" + + let candidates = [ + Bundle.main.resourceURL, + Bundle(for: BundleFinder.self).resourceURL, + Bundle.main.bundleURL, + ] + + for candidate in candidates { + let bundlePath = candidate?.appendingPathComponent(bundleName + ".bundle") + if let bundle = bundlePath.flatMap(Bundle.init(url:)) { + return bundle + } + } + fatalError("unable to find bundle named Data_Data") + }() +} + +// MARK: - Objective-C Bundle Accessor + +@objc +public class DataResources: NSObject { + @objc public class var bundle: Bundle { + return .module + } +} +// swiftlint:enable all +// swiftformat:enable all diff --git a/Projects/Data/Project.swift b/Projects/Data/Project.swift index cb67ea75..d076101e 100644 --- a/Projects/Data/Project.swift +++ b/Projects/Data/Project.swift @@ -9,57 +9,19 @@ import ProjectDescription import ProjectDescriptionHelpers -let project = Project(name: "Data", - organizationName: "hyeong", - options: .options(automaticSchemesOptions: .disabled), - packages: [], - settings: .settings(configurations: [ - .debug(name: "Dev"), - .debug(name: "Prod") - ]), - targets: [ - Project.target(name: "Data", - product: .framework, - sources: "Sources/**", - resources: "Resources/**", - dependencies: [ - .rxSwift, - .codestackAPI, - .project(target: "Global", path: .relativeToRoot("Projects/Global")), - ], - configuration: [ - .debug(name: "Dev"), - .debug(name: "Prod") - ]), - ], - schemes: [ - Scheme(name: "Data-Dev", - shared: true, - buildAction: .buildAction(targets: ["Data"]), - testAction: .targets(["DataTests"], - configuration: .configuration("Dev"), - options: .options(coverage: true)), - runAction: .runAction(configuration: .configuration("Dev"), - arguments: .init(environment: ["OS_ACTIVITY_MODE" : "disable"], - launchArguments: [])), - archiveAction: .archiveAction(configuration: .configuration("Dev")), - profileAction: .profileAction(configuration: .configuration("Dev")), - analyzeAction: .analyzeAction(configuration: .configuration("Dev"))), - Scheme(name: "Data-Prod", - shared: true, - buildAction: .buildAction(targets: ["Data"]), - runAction: .runAction(configuration: .configuration("Prod")), - archiveAction: .archiveAction(configuration: .configuration("Prod")), - profileAction: .profileAction(configuration: .configuration("Prod")), - analyzeAction: .analyzeAction(configuration: .configuration("Prod"))) - ], - fileHeaderTemplate: nil, - additionalFiles: [], - resourceSynthesizers: []) -// Project.target(name: "MyAppTests", -// product: .unitTests, -// sources: "Tests/**", -// dependencies: [ -// .target(name: "MyApp"), -// ]) +let targetDependecies: [TargetDependency] = [ + .PRO.global, + .PRO.domain + ] + +let resources: ResourceFileElements = [ + "Sources/Data/MO/db_model_v1.xcdatamodeld", + "Resources/**" +] + +let value = Project.createModule(name: "Data", + product: .staticFramework, + dependencies: targetDependecies, + resources: resources) + diff --git a/Projects/Data/Sources/DI/LoginAssembly.swift b/Projects/Data/Sources/DI/LoginAssembly.swift new file mode 100644 index 00000000..533e8dd8 --- /dev/null +++ b/Projects/Data/Sources/DI/LoginAssembly.swift @@ -0,0 +1,9 @@ +// +// LoginAssembly.swift +// Data +// +// Created by 박형환 on 12/16/23. +// Copyright © 2023 hyeong. All rights reserved. +// + +import Foundation diff --git a/Projects/Data/Sources/Network/API/Provider/GRAPH.swift b/Projects/Data/Sources/Data/GRAPH.swift similarity index 81% rename from Projects/Data/Sources/Network/API/Provider/GRAPH.swift rename to Projects/Data/Sources/Data/GRAPH.swift index f17da79b..8e8f5a8e 100644 --- a/Projects/Data/Sources/Network/API/Provider/GRAPH.swift +++ b/Projects/Data/Sources/Data/GRAPH.swift @@ -1,37 +1,64 @@ // -// GRAPH.swift -// Data +// Query.swift +// CodestackApp // -// Created by 박형환 on 12/14/23. +// Created by 박형환 on 12/16/23. // Copyright © 2023 hyeong. All rights reserved. // import Foundation +import Domain +/// Graph Argument +public struct GRAR { + public let limit: Int + public let offset: Int + public let sort: String + public let order: String + + public init(limit: Int = 20, offset: Int, sort: String = "id", order: String = "asc") { + self.limit = limit + self.offset = offset + self.sort = sort + self.order = order + } +} -public enum GRAPH { +/// Graph Argument +public struct GRSubmit { + public let languageId: String + public let problemId: String + public let sourceCode: String + public init(languageId: String, problemId: String, sourceCode: String) { + self.languageId = languageId + self.problemId = problemId + self.sourceCode = sourceCode + } +} + +public enum GRAPH { /// 특정 ID의 문제 - case PR_BY_ID(ID) + case PR_BY_ID(String) /// 문제 리스트 case PR_LIST(arg: GRAR) - + /// 문제 검색 리스트 case SER_PR_LIST(keyword: String, arg: GRAR) - + /// 제출 리스트 case SUB_LIST(arg: GRAR) /// 제출 By 문제 아이디 case SUB_BY_PR_ID(offset: Int, problemId: Double) /// 제출 By ID - case SUB_BY_ID(id: ID) + case SUB_BY_ID(id: String) /// 나의 제출 리스트/ case SUB_LIST_ME(username: String) /// 나의 제출 기록 (날짜와 아이디만) case ME_SUB_HISTORY(limit: Int, offset: Int) /// 멤버 조회 case ME - + /// solve한 문제 리스트 case SOLVE_PR_LIST(username: String) /// 언어 리스트/ @@ -41,34 +68,5 @@ public enum GRAPH { /// 닉네임 업데이트/ case UPDATE_NICKNAME(nickname: String) /// 코드 제출하기/ - case SUBMIT_SUB(submit: GRSubmit) -} - - -/// Graph Argument -public struct GRAR { - public let limit: Int - public let offset: Int - public let sort: String - public let order: String - - public init(limit: Int = 20, offset: Int, sort: String = "id", order: String = "asc") { - self.limit = limit - self.offset = offset - self.sort = sort - self.order = order - } -} - -/// Graph Argument -public struct GRSubmit { - public let languageId: ID - public let problemId: ID - public let sourceCode: String - - public init(languageId: ID, problemId: ID, sourceCode: String) { - self.languageId = languageId - self.problemId = problemId - self.sourceCode = sourceCode - } + case SUBMIT_SUB(submit: SubmitMutation) } diff --git a/Projects/CodestackApp/Sources/Data/MO/Class/CodeContextMO+CoreDataClass.swift b/Projects/Data/Sources/Data/MO/Class/CodeContextMO+CoreDataClass.swift similarity index 100% rename from Projects/CodestackApp/Sources/Data/MO/Class/CodeContextMO+CoreDataClass.swift rename to Projects/Data/Sources/Data/MO/Class/CodeContextMO+CoreDataClass.swift diff --git a/Projects/CodestackApp/Sources/Data/MO/Class/FavoritProblem+CoreDataClass.swift b/Projects/Data/Sources/Data/MO/Class/FavoritProblem+CoreDataClass.swift similarity index 100% rename from Projects/CodestackApp/Sources/Data/MO/Class/FavoritProblem+CoreDataClass.swift rename to Projects/Data/Sources/Data/MO/Class/FavoritProblem+CoreDataClass.swift diff --git a/Projects/CodestackApp/Sources/Data/MO/Class/LanguageMO+CoreDataClass.swift b/Projects/Data/Sources/Data/MO/Class/LanguageMO+CoreDataClass.swift similarity index 100% rename from Projects/CodestackApp/Sources/Data/MO/Class/LanguageMO+CoreDataClass.swift rename to Projects/Data/Sources/Data/MO/Class/LanguageMO+CoreDataClass.swift diff --git a/Projects/CodestackApp/Sources/Data/MO/Class/ProblemSubmissionStateMO+CoreDataClass.swift b/Projects/Data/Sources/Data/MO/Class/ProblemSubmissionStateMO+CoreDataClass.swift similarity index 100% rename from Projects/CodestackApp/Sources/Data/MO/Class/ProblemSubmissionStateMO+CoreDataClass.swift rename to Projects/Data/Sources/Data/MO/Class/ProblemSubmissionStateMO+CoreDataClass.swift diff --git a/Projects/CodestackApp/Sources/Data/MO/Class/SubmissionCalendarMO+CoreDataClass.swift b/Projects/Data/Sources/Data/MO/Class/SubmissionCalendarMO+CoreDataClass.swift similarity index 100% rename from Projects/CodestackApp/Sources/Data/MO/Class/SubmissionCalendarMO+CoreDataClass.swift rename to Projects/Data/Sources/Data/MO/Class/SubmissionCalendarMO+CoreDataClass.swift diff --git a/Projects/CodestackApp/Sources/Data/MO/Class/SubmissionMO+CoreDataClass.swift b/Projects/Data/Sources/Data/MO/Class/SubmissionMO+CoreDataClass.swift similarity index 100% rename from Projects/CodestackApp/Sources/Data/MO/Class/SubmissionMO+CoreDataClass.swift rename to Projects/Data/Sources/Data/MO/Class/SubmissionMO+CoreDataClass.swift diff --git a/Projects/CodestackApp/Sources/Data/MO/CodeContextMO+CoreDataProperties.swift b/Projects/Data/Sources/Data/MO/CodeContextMO+CoreDataProperties.swift similarity index 98% rename from Projects/CodestackApp/Sources/Data/MO/CodeContextMO+CoreDataProperties.swift rename to Projects/Data/Sources/Data/MO/CodeContextMO+CoreDataProperties.swift index fd34bc1f..3fcd59f1 100644 --- a/Projects/CodestackApp/Sources/Data/MO/CodeContextMO+CoreDataProperties.swift +++ b/Projects/Data/Sources/Data/MO/CodeContextMO+CoreDataProperties.swift @@ -8,7 +8,7 @@ import Foundation import CoreData - +import Domain extension CodeContextMO { diff --git a/Projects/CodestackApp/Sources/Data/MO/FavoritProblem+CoreDataProperties.swift b/Projects/Data/Sources/Data/MO/FavoritProblem+CoreDataProperties.swift similarity index 98% rename from Projects/CodestackApp/Sources/Data/MO/FavoritProblem+CoreDataProperties.swift rename to Projects/Data/Sources/Data/MO/FavoritProblem+CoreDataProperties.swift index 87cb9f8c..993a373e 100644 --- a/Projects/CodestackApp/Sources/Data/MO/FavoritProblem+CoreDataProperties.swift +++ b/Projects/Data/Sources/Data/MO/FavoritProblem+CoreDataProperties.swift @@ -8,7 +8,7 @@ import Foundation import CoreData - +import Domain extension FavoritProblemMO { diff --git a/Projects/CodestackApp/Sources/Data/MO/LanguageMO+CoreDataProperties.swift b/Projects/Data/Sources/Data/MO/LanguageMO+CoreDataProperties.swift similarity index 98% rename from Projects/CodestackApp/Sources/Data/MO/LanguageMO+CoreDataProperties.swift rename to Projects/Data/Sources/Data/MO/LanguageMO+CoreDataProperties.swift index f68cffcf..ccd773b1 100644 --- a/Projects/CodestackApp/Sources/Data/MO/LanguageMO+CoreDataProperties.swift +++ b/Projects/Data/Sources/Data/MO/LanguageMO+CoreDataProperties.swift @@ -8,7 +8,7 @@ import Foundation import CoreData - +import Domain extension LanguageMO { diff --git a/Projects/CodestackApp/Sources/Data/MO/ProblemSubmissionStateMO+CoreDataProperties.swift b/Projects/Data/Sources/Data/MO/ProblemSubmissionStateMO+CoreDataProperties.swift similarity index 99% rename from Projects/CodestackApp/Sources/Data/MO/ProblemSubmissionStateMO+CoreDataProperties.swift rename to Projects/Data/Sources/Data/MO/ProblemSubmissionStateMO+CoreDataProperties.swift index 7e62db18..0c1f86a1 100644 --- a/Projects/CodestackApp/Sources/Data/MO/ProblemSubmissionStateMO+CoreDataProperties.swift +++ b/Projects/Data/Sources/Data/MO/ProblemSubmissionStateMO+CoreDataProperties.swift @@ -8,7 +8,7 @@ import Foundation import CoreData - +import Domain extension ProblemSubmissionStateMO { diff --git a/Projects/CodestackApp/Sources/Data/MO/SubmissionCalendarMO+CoreDataProperties.swift b/Projects/Data/Sources/Data/MO/SubmissionCalendarMO+CoreDataProperties.swift similarity index 99% rename from Projects/CodestackApp/Sources/Data/MO/SubmissionCalendarMO+CoreDataProperties.swift rename to Projects/Data/Sources/Data/MO/SubmissionCalendarMO+CoreDataProperties.swift index fc58079b..4e2e414c 100644 --- a/Projects/CodestackApp/Sources/Data/MO/SubmissionCalendarMO+CoreDataProperties.swift +++ b/Projects/Data/Sources/Data/MO/SubmissionCalendarMO+CoreDataProperties.swift @@ -8,7 +8,7 @@ import Foundation import CoreData - +import Domain extension SubmissionCalendarMO { diff --git a/Projects/CodestackApp/Sources/Data/MO/SubmissionMO+CoreDataProperties.swift b/Projects/Data/Sources/Data/MO/SubmissionMO+CoreDataProperties.swift similarity index 99% rename from Projects/CodestackApp/Sources/Data/MO/SubmissionMO+CoreDataProperties.swift rename to Projects/Data/Sources/Data/MO/SubmissionMO+CoreDataProperties.swift index aba70911..3c35dbbe 100644 --- a/Projects/CodestackApp/Sources/Data/MO/SubmissionMO+CoreDataProperties.swift +++ b/Projects/Data/Sources/Data/MO/SubmissionMO+CoreDataProperties.swift @@ -9,6 +9,7 @@ import Foundation import CoreData import Global +import Domain extension SubmissionMO { diff --git a/Projects/CodestackApp/Sources/Data/MO/db_model_v1.xcdatamodeld/db_model_v1.xcdatamodel/contents b/Projects/Data/Sources/Data/MO/db_model_v1.xcdatamodeld/db_model_v1.xcdatamodel/contents similarity index 100% rename from Projects/CodestackApp/Sources/Data/MO/db_model_v1.xcdatamodeld/db_model_v1.xcdatamodel/contents rename to Projects/Data/Sources/Data/MO/db_model_v1.xcdatamodeld/db_model_v1.xcdatamodel/contents diff --git a/Projects/CodestackApp/Sources/Model/Model+Mapping/FR+Mapping.swift b/Projects/Data/Sources/Data/Model+Mapping/FR+Mapping.swift similarity index 99% rename from Projects/CodestackApp/Sources/Model/Model+Mapping/FR+Mapping.swift rename to Projects/Data/Sources/Data/Model+Mapping/FR+Mapping.swift index 0e00c539..2e0f2fde 100644 --- a/Projects/CodestackApp/Sources/Model/Model+Mapping/FR+Mapping.swift +++ b/Projects/Data/Sources/Data/Model+Mapping/FR+Mapping.swift @@ -7,7 +7,7 @@ import Foundation import CodestackAPI - +import Domain //[FProblemIdentity] // diff --git a/Projects/Data/Sources/Data/Model+Mapping/FavoriteProblem.swift b/Projects/Data/Sources/Data/Model+Mapping/FavoriteProblem.swift new file mode 100644 index 00000000..6733b6a0 --- /dev/null +++ b/Projects/Data/Sources/Data/Model+Mapping/FavoriteProblem.swift @@ -0,0 +1,16 @@ +// +// FavoriteSubmission.swift +// CodeStack +// +// Created by 박형환 on 11/22/23. +// + +import Foundation + +public struct FavoriteProblem { + let problemID: String + let problmeTitle: String + let createdAt: Date +} + + diff --git a/Projects/Data/Sources/Data/Model+Mapping/ProblemCalendar.swift b/Projects/Data/Sources/Data/Model+Mapping/ProblemCalendar.swift new file mode 100644 index 00000000..74667414 --- /dev/null +++ b/Projects/Data/Sources/Data/Model+Mapping/ProblemCalendar.swift @@ -0,0 +1,34 @@ +// +// ProblemCalendar.swift +// CodeStack +// +// Created by 박형환 on 2023/06/26. +// + +import Foundation +import Global + +struct ProblemCalendar: Codable { + let date: String + let solved: Int +} + +public struct SubmissionCalendar: Codable { + let dates: [String] +} + +extension SubmissionCalendar { + + public static func generateMockCalendar() -> Self { + let date = Date() + let dates = (0...200).map { num in + let randomNumber = (0...200).randomElement()! + + let dateString = date.adding(day: -randomNumber).toString(format: .FULL) + return dateString + } + return SubmissionCalendar(dates: dates) + } +} + + diff --git a/Projects/CodestackApp/Sources/Data/Persistence/CoreDataHelper.swift b/Projects/Data/Sources/Data/Persistence/CoreDataHelper.swift similarity index 100% rename from Projects/CodestackApp/Sources/Data/Persistence/CoreDataHelper.swift rename to Projects/Data/Sources/Data/Persistence/CoreDataHelper.swift diff --git a/Projects/CodestackApp/Sources/Data/Persistence/CoreDataStack.swift b/Projects/Data/Sources/Data/Persistence/CoreDataStack.swift similarity index 91% rename from Projects/CodestackApp/Sources/Data/Persistence/CoreDataStack.swift rename to Projects/Data/Sources/Data/Persistence/CoreDataStack.swift index 1dbdf81d..59fd3c8c 100644 --- a/Projects/CodestackApp/Sources/Data/Persistence/CoreDataStack.swift +++ b/Projects/Data/Sources/Data/Persistence/CoreDataStack.swift @@ -9,7 +9,7 @@ import CoreData import RxSwift import Global -protocol PersistentStore: AnyObject { +public protocol PersistentStore: AnyObject { typealias DBOperation = (NSManagedObjectContext) throws -> Result func count(_ fetchRequest: NSFetchRequest) -> Observable @@ -35,13 +35,13 @@ enum StoreType { } } -final class CoreDataStack: PersistentStore { +public final class CoreDataStack: PersistentStore { private let container: NSPersistentContainer private let isStoreLoaded = BehaviorSubject(value: false) private let bgQueue = DispatchQueue(label: "coredata") - init(directory: FileManager.SearchPathDirectory = .documentDirectory, + public init(directory: FileManager.SearchPathDirectory = .documentDirectory, domainMask: FileManager.SearchPathDomainMask = .userDomainMask, version vNumber: UInt) { let version = Version(vNumber) @@ -64,7 +64,7 @@ final class CoreDataStack: PersistentStore { } } - func count(_ fetchRequest: NSFetchRequest) -> Observable { + public func count(_ fetchRequest: NSFetchRequest) -> Observable { return onStoreIsReady .flatMap { [weak container] in Observable.create { observer in @@ -79,7 +79,7 @@ final class CoreDataStack: PersistentStore { } } - func fetch(_ fetchRequest: NSFetchRequest, + public func fetch(_ fetchRequest: NSFetchRequest, map: @escaping (T) throws -> V?) -> Observable> { assert(Thread.isMainThread) let fetch = Observable>.create { [weak container] observer in @@ -108,7 +108,7 @@ final class CoreDataStack: PersistentStore { .flatMap { fetch } } - func remove(request: NSFetchRequest) -> Observable { + public func remove(request: NSFetchRequest) -> Observable { Observable.create { [weak bgQueue, weak container] observer in bgQueue?.async { guard let context = container?.newBackgroundContext() else { return } @@ -136,7 +136,7 @@ final class CoreDataStack: PersistentStore { } - func update(_ operation: @escaping DBOperation) -> Observable> { + public func update(_ operation: @escaping DBOperation) -> Observable> { let updateObservable = Observable>.create { [weak bgQueue, weak container] observer in bgQueue?.async { @@ -174,15 +174,15 @@ final class CoreDataStack: PersistentStore { } // MARK: - Versioning -extension CoreDataStack.Version { +public extension CoreDataStack.Version { static var actual: UInt { 1 } } extension CoreDataStack { - struct Version { + public struct Version { private let number: UInt - init(_ number: UInt) { + public init(_ number: UInt) { self.number = number } diff --git a/Projects/CodestackApp/Sources/Data/Persistence/FavoriteProblemMO-extension.swift b/Projects/Data/Sources/Data/Persistence/FavoriteProblemMO-extension.swift similarity index 54% rename from Projects/CodestackApp/Sources/Data/Persistence/FavoriteProblemMO-extension.swift rename to Projects/Data/Sources/Data/Persistence/FavoriteProblemMO-extension.swift index 4d3d9e30..9465b9d6 100644 --- a/Projects/CodestackApp/Sources/Data/Persistence/FavoriteProblemMO-extension.swift +++ b/Projects/Data/Sources/Data/Persistence/FavoriteProblemMO-extension.swift @@ -6,24 +6,20 @@ // import CoreData -import Data +import Domain -extension FavoritProblemMO { - - enum RequestType { - case all - case isEqualID(ProblemID) - - func conditionRequest() -> NSFetchRequest { - switch self { - case .all: - return FavoriteProblem.fetchRequest() - case .isEqualID(let problemID): - return FavoritProblemMO.isEqualID(probelmID: problemID) - } +extension FAV_TYPE { + func conditionRequest() -> NSFetchRequest { + switch self { + case .all: + return FavoritProblemMO.fetchRequest() + case .isEqualID(let problemID): + return FavoritProblemMO.isEqualID(probelmID: problemID) } } - +} + +extension FavoritProblemMO { static func isEqualID(probelmID: ProblemID) -> NSFetchRequest { let request = newFetchRequest() let predicate1 = NSPredicate(format: "problemID == %@", "\(probelmID)") diff --git a/Projects/Data/Sources/Data/Persistence/Models-extension.swift b/Projects/Data/Sources/Data/Persistence/Models-extension.swift new file mode 100644 index 00000000..bfe781c1 --- /dev/null +++ b/Projects/Data/Sources/Data/Persistence/Models-extension.swift @@ -0,0 +1,116 @@ +// +// Models-extension.swift +// CodeStack +// +// Created by 박형환 on 11/22/23. +// + + +import CoreData +import Global +import Domain + +protocol ManagedEntity: NSFetchRequestResult { } + +extension SubmissionMO: ManagedEntity { } +extension CodeContextMO: ManagedEntity { } +extension LanguageMO: ManagedEntity { } +extension ProblemSubmissionStateMO: ManagedEntity { } +extension SubmissionCalendarMO: ManagedEntity { } +extension FavoritProblemMO: ManagedEntity { } + +protocol MapperbleProtocol { + associatedtype PersistenceType: NSFetchRequestResult + + @discardableResult + func store(in context: NSManagedObjectContext) -> PersistenceType? + + static func fetchRequest() -> NSFetchRequest +} + +//extension FavoriteProblem: MapperbleProtocol { +// +// func store(in context: NSManagedObjectContext) -> FavoritProblemMO? { +// guard let favoriteMO = FavoritProblemMO.insertNew(in: context) else { return nil } +// favoriteMO.problemID = self.problemID +// favoriteMO.problemTitle = self.problmeTitle +// favoriteMO.createdAt = self.createdAt +// return favoriteMO +// } +// +// static func fetchRequest() -> NSFetchRequest { +// FavoritProblemMO.fetchRequest() +// } +// +// typealias PersistenceType = FavoritProblemMO +// +// init?(managedContext: FavoritProblemMO) { +// guard +// let createdAt = managedContext.createdAt, +// let problemID = managedContext.problemID, +// let problemTitle = managedContext.problemTitle +// else { +// return nil +// } +// self.init(problemID: problemID, problmeTitle: problemTitle, createdAt: createdAt) +// } +//} +// +//struct ProblemSubmissionState { +// var submissions: [SubmissionVO] +//} +// +//extension SubmissionCalendar: MapperbleProtocol { +// typealias PersistenceType = SubmissionCalendarMO +// +// +// init?(managedContext: SubmissionCalendarMO) { +// guard let submissionSet = managedContext.submission else { return nil } +// +// let _submissions = +// submissionSet +// .toArray(of: SubmissionMO.self) +// .compactMap { $0.toDomain() } +// +// self.dates = _submissions.map(\.createdAt) +// } +// +// func store(in context: NSManagedObjectContext) -> SubmissionCalendarMO? { +// return nil +// } +// +// static func fetchRequest() -> NSFetchRequest { +// SubmissionCalendarMO.newFetchRequest() +// } +//} +// +// +// +//extension ProblemSubmissionState: MapperbleProtocol { +// +// typealias PersistenceType = ProblemSubmissionStateMO +// +// static func fetchRequest() -> NSFetchRequest { +// ProblemSubmissionStateMO.fetchRequest() +// } +// +// init?(managedContext: ProblemSubmissionStateMO) { +// guard let submissionSet = managedContext.submissions else { return nil } +// +// let _submissions = +// submissionSet +// .toArray(of: SubmissionMO.self) +// .compactMap { $0.toDomain() } +// //.compactMap { Submission(managedContext: $0) } +// +// self.submissions = _submissions +// } +// +// func store(in context: NSManagedObjectContext) -> ProblemSubmissionStateMO? { +// guard let problemSubmissionMO = ProblemSubmissionStateMO.insertNew(in: context) else { return nil } +// +// problemSubmissionMO.submissions = self.submissions.compactMap { $0.toMO(in:) } as? NSSet +// +// return problemSubmissionMO +// } +//} diff --git a/Projects/CodestackApp/Sources/Data/Persistence/ProblemSubmissionStateMO-extension.swift b/Projects/Data/Sources/Data/Persistence/ProblemSubmissionStateMO-extension.swift similarity index 81% rename from Projects/CodestackApp/Sources/Data/Persistence/ProblemSubmissionStateMO-extension.swift rename to Projects/Data/Sources/Data/Persistence/ProblemSubmissionStateMO-extension.swift index d8646d8f..ccaa7d87 100644 --- a/Projects/CodestackApp/Sources/Data/Persistence/ProblemSubmissionStateMO-extension.swift +++ b/Projects/Data/Sources/Data/Persistence/ProblemSubmissionStateMO-extension.swift @@ -6,24 +6,10 @@ // import CoreData -import Data +import Domain extension ProblemSubmissionStateMO { - enum RequestType { - case all - case isEqualID(ProblemID) - - func conditionRequest() -> NSFetchRequest { - switch self { - case .all: - return ProblemSubmissionStateMO.fetchRequest() - case .isEqualID(let problemID): - return ProblemSubmissionStateMO.fetchRequest() - } - } - } - // MARK: Probelm Submission StateMO 가 존재하면 fetch 후 리턴 // 존재하지 않으면 생성 후 리턴 static func fetchSubmissionStateMO(context: NSManagedObjectContext, @@ -60,3 +46,14 @@ extension ProblemSubmissionStateMO { return stateMO } } + +extension PR_SUB_ST_TYPE { + func conditionRequest() -> NSFetchRequest { + switch self { + case .all: + return ProblemSubmissionStateMO.fetchRequest() + case .isEqualID(let problemID): + return ProblemSubmissionStateMO.fetchRequest() + } + } +} diff --git a/Projects/CodestackApp/Sources/Data/Persistence/SubmissionCalendarsMO-extension.swift b/Projects/Data/Sources/Data/Persistence/SubmissionCalendarsMO-extension.swift similarity index 100% rename from Projects/CodestackApp/Sources/Data/Persistence/SubmissionCalendarsMO-extension.swift rename to Projects/Data/Sources/Data/Persistence/SubmissionCalendarsMO-extension.swift diff --git a/Projects/CodestackApp/Sources/Data/Persistence/SubmissionMO-extension.swift b/Projects/Data/Sources/Data/Persistence/SubmissionMO-extension.swift similarity index 58% rename from Projects/CodestackApp/Sources/Data/Persistence/SubmissionMO-extension.swift rename to Projects/Data/Sources/Data/Persistence/SubmissionMO-extension.swift index ab6bbd1d..9d365ba8 100644 --- a/Projects/CodestackApp/Sources/Data/Persistence/SubmissionMO-extension.swift +++ b/Projects/Data/Sources/Data/Persistence/SubmissionMO-extension.swift @@ -7,52 +7,9 @@ import CoreData import Global +import Domain + extension SubmissionMO { - - typealias ProblemID = String - typealias StatusCode = String - typealias SubmissionID = String - typealias LanguageName = String - enum RequestType { - case isExist(ProblemID) - case isNotTemp(ProblemID, StatusCode) - case isEqualStatusCode(StatusCode) - case update(SubmissionID, ProblemID) - case recent(ProblemID) - case `default` - case delete(LanguageName, ProblemID, StatusCode) - - func conditionRequest() -> NSFetchRequest { - switch self { - case .isExist(let problemID): - return SubmissionMO.isExistSubmission(problemID: problemID) - - case .isNotTemp(let problemID, let state): - return SubmissionMO.isNotTempSubmission(probelmID: problemID, status: state) - - case .isEqualStatusCode(let statusCode): - return SubmissionMO.isEqualStatusCode(statusCode) - - case .update(let submissionID, let problemID): - return SubmissionMO.isEqualID(id: submissionID, problemID) - - case .recent(let problemID): - return SubmissionMO.recent(probelm: problemID) - - case .default: - let request = SubmissionMO.newFetchRequest() - request.sortDescriptors = [NSSortDescriptor(key: "createdAt", ascending: true)] - request.fetchLimit = 10 - return request - - case .delete(let languageName, let problemID, let statusCode): - return SubmissionMO.isEqualStatus(languageName: languageName, - problemID: problemID, - statusCode: statusCode) - } - } - } - static func recent(probelm id: ProblemID) -> NSFetchRequest { let request = newFetchRequest() let predicate1 = NSPredicate(format: "codeContext.problemID == %@", "\(String(describing: id))") @@ -106,16 +63,36 @@ extension SubmissionMO { predicate3]) return request } - -// func update(submission: Submission) -> Self { -// self.codeContext?.code = submission.sourceCode -// self.codeContext?.problemID = submission.problem?.id -// self.codeContext?.problemTitle = submission.problem?.title -// self.language?.languageID = submission.language?.id -// self.language?.name = submission.language?.name -// self.language?.extension = submission.language?._extension -// self.statusCode = submission.statusCode -// self.createdAt = submission.createdAt?.toDateKST() -// return self -// } +} + +extension SUB_TYPE { + func conditionRequest() -> NSFetchRequest { + switch self { + case .isExist(let problemID): + return SubmissionMO.isExistSubmission(problemID: problemID) + + case .isNotTemp(let problemID, let state): + return SubmissionMO.isNotTempSubmission(probelmID: problemID, status: state) + + case .isEqualStatusCode(let statusCode): + return SubmissionMO.isEqualStatusCode(statusCode) + + case .update(let submissionID, let problemID): + return SubmissionMO.isEqualID(id: submissionID, problemID) + + case .recent(let problemID): + return SubmissionMO.recent(probelm: problemID) + + case .default: + let request = SubmissionMO.newFetchRequest() + request.sortDescriptors = [NSSortDescriptor(key: "createdAt", ascending: true)] + request.fetchLimit = 10 + return request + + case .delete(let languageName, let problemID, let statusCode): + return SubmissionMO.isEqualStatus(languageName: languageName, + problemID: problemID, + statusCode: statusCode) + } + } } diff --git a/Projects/Data/Sources/Network/API/Provider/REST.swift b/Projects/Data/Sources/Data/REST.swift similarity index 100% rename from Projects/Data/Sources/Network/API/Provider/REST.swift rename to Projects/Data/Sources/Data/REST.swift diff --git a/Projects/Data/Sources/Data/Repository/DefaultAuthRepository.swift b/Projects/Data/Sources/Data/Repository/DefaultAuthRepository.swift new file mode 100644 index 00000000..29456ae1 --- /dev/null +++ b/Projects/Data/Sources/Data/Repository/DefaultAuthRepository.swift @@ -0,0 +1,99 @@ +// +// AuthRepository.swift +// CodestackApp +// +// Created by 박형환 on 12/15/23. +// Copyright © 2023 hyeong. All rights reserved. +// + +import Foundation +import Domain +import RxSwift +import CodestackAPI + +public final class DefaultAuthRepository: AuthRepository { + + + private let auth: Auth + private let graph: GraphQLAPI + + private let rest: RestAPI + + public init(auth: Auth, graph: GraphQLAPI, rest: RestAPI) { + self.auth = auth + self.graph = graph + self.rest = rest + } + + public func signUp(query: RegisterQuery) -> Maybe { + rest.signUp(member: MemberDTO(id: query.id, + password: query.password, + email: query.email, + nickName: query.nickname)) + } + + public func getMe() -> Maybe { + graph.fetch(query: FetchMeQuery(), + cachePolicy: .fetchIgnoringCacheData, + queue: .main) + .map { $0.getMe.fragments.memberFR.toDomain() } + } + + public func gitOAuthComplete(code: String){ +// loginViewModel?.oAuthComplete(code: code) + auth.gitOAuthComplete(code: code) + } +// +// /// Git Login URL 오픈 -> 사파리 + public func gitOAuthrization() throws { + try auth.gitOAuthrization() +// guard let url = makeGitURL() else { throw APIError.badURLError} +// if UIApplication.shared.canOpenURL(url) { +// UIApplication.shared.open(url) +// }else{ +// throw APIError.canNotOpen +// } + } + + public func request(git serverCode: String) -> RxSwift.Maybe { + auth.request(git: serverCode) + .map { dto in dto.toDomain() } + } + + public func request(with code: String) -> RxSwift.Maybe { + auth.request(with: code) + .map { $0.toDomain() } + } + + public func request(apple code: String, user: String) -> RxSwift.Maybe { + auth + .request(with: AppleDTO(authorizationCode: code, user: user)) + .map { $0.toDomain() } + } + + public func oAuthComplte(apple code: String, user: String) { + auth.oAuthComplte(token: AppleDTO(authorizationCode: code, user: user)) + } + + public func request(name id: String, password: String) -> RxSwift.Maybe { + auth.request(name: id, password: password) + .map { $0.toDomain() } + } +} + +extension GitTokenDTO { + func toDomain() -> GitTokenVO { + GitTokenVO.init(accessToken: self.accessToken, + scope: self.scope, + tokenType: self.tokenType) + } +} + +extension CSTokenDTO { + func toDomain() -> CSTokenVO { + CSTokenVO(refreshToken: self.refreshToken, + accessToken: self.accessToken, + expiresIn: self.expiresIn, + tokenType: self.tokenType) + } +} diff --git a/Projects/CodestackApp/Sources/Data/Repository/DefaultDBRepository.swift b/Projects/Data/Sources/Data/Repository/DefaultDBRepository.swift similarity index 79% rename from Projects/CodestackApp/Sources/Data/Repository/DefaultDBRepository.swift rename to Projects/Data/Sources/Data/Repository/DefaultDBRepository.swift index e5ba474c..288beda3 100644 --- a/Projects/CodestackApp/Sources/Data/Repository/DefaultDBRepository.swift +++ b/Projects/Data/Sources/Data/Repository/DefaultDBRepository.swift @@ -9,17 +9,18 @@ import Foundation import CoreData import RxSwift import Global +import Domain - -final class DefaultDBRepository: DBRepository { +public final class DefaultDBRepository: DBRepository { + private var persistentStore: PersistentStore - init(persistenStore: PersistentStore = CoreDataStack(version: 1)) { + public init(persistenStore: PersistentStore = CoreDataStack(version: 1)) { self.persistentStore = persistenStore } - func fetch(_ requestType: SubmissionMO.RequestType = .default) -> Single<[SubmissionVO]> { + public func fetch(_ requestType: SUB_TYPE = .default) -> Single<[SubmissionVO]> { Single<[SubmissionVO]>.create { [weak persistentStore] single in let fetchRequest = requestType.conditionRequest() @@ -42,11 +43,12 @@ final class DefaultDBRepository: DBRepository { } } - func fetchProblemState() -> Single<[ProblemSubmissionState]> { - Single<[ProblemSubmissionState]>.create { [weak persistentStore] single in - let fetchRequest = ProblemSubmissionState.fetchRequest() + public func fetchProblemState() -> Single<[ProblemSubmissionStateVO]> { + Single<[ProblemSubmissionStateVO]>.create { [weak persistentStore] single in + let fetchRequest = ProblemSubmissionStateMO.fetchRequest() let observable = persistentStore?.fetch(fetchRequest, map: { value in - ProblemSubmissionState.init(managedContext: value) +// ProblemSubmissionState.init(managedContext: value) + value.toDomain() }).subscribe(onNext: { value in single(value) }) @@ -54,12 +56,13 @@ final class DefaultDBRepository: DBRepository { } } - func fetchSubmissionCalendar() -> Single<[SubmissionCalendar]> { - Single<[SubmissionCalendar]>.create { [weak persistentStore] single in + public func fetchSubmissionCalendar() -> Single<[SubmissionCalendarVO]> { + Single<[SubmissionCalendarVO]>.create { [weak persistentStore] single in let request = SubmissionCalendarMO.fetchRequest() let observable = persistentStore? .fetch(request, map: { value in - SubmissionCalendar(managedContext: value) +// SubmissionCalendar(managedContext: value) + value.toDomain() }).subscribe(onNext: { result in single(result) }) @@ -67,12 +70,13 @@ final class DefaultDBRepository: DBRepository { } } - func fetchFavoriteProblems() -> Single<[FavoriteProblem]> { - Single<[FavoriteProblem]>.create { [weak persistentStore] single in + public func fetchFavoriteProblems() -> Single<[FavoriteProblemVO]> { + Single<[FavoriteProblemVO]>.create { [weak persistentStore] single in let observable = persistentStore? .fetch(FavoritProblemMO.fetchRequest(), map: { context in - FavoriteProblem(managedContext: context) + context.toDomain() +// FavoriteProblem(managedContext: context) }) .subscribe(onNext: { result in single(result) @@ -81,7 +85,7 @@ final class DefaultDBRepository: DBRepository { } } - func remove(_ requestType: SubmissionMO.RequestType) -> Completable { + public func remove(_ requestType: SUB_TYPE) -> Completable { Completable.create { [weak persistentStore] complete in let request = requestType.conditionRequest() let observable = persistentStore? @@ -95,7 +99,7 @@ final class DefaultDBRepository: DBRepository { } } - func removeFavor(_ requestType: FavoritProblemMO.RequestType) -> Completable { + public func removeFavor(_ requestType: FAV_TYPE) -> Completable { Completable.create { [weak persistentStore] complete in let request = requestType.conditionRequest() let observable = persistentStore? @@ -109,7 +113,7 @@ final class DefaultDBRepository: DBRepository { } } - func remove() -> Single { + public func remove() -> Single { Single.create { [weak persistentStore] single in let observable = persistentStore?.remove(request: SubmissionMO.fetchRequest()) .subscribe { value in @@ -128,7 +132,7 @@ final class DefaultDBRepository: DBRepository { } } - func update(submission: SubmissionVO, type request: SubmissionMO.RequestType) -> Single { + public func update(submission: SubmissionVO, type request: SUB_TYPE) -> Single { Single.create { [weak persistentStore] single in let observable = persistentStore?.update { context in let request = request.conditionRequest() @@ -145,7 +149,7 @@ final class DefaultDBRepository: DBRepository { } } - func store(submission: SubmissionVO) -> Single { + public func store(submission: SubmissionVO) -> Single { Single.create { [weak persistentStore] single in let observable = persistentStore?.update { context in @@ -179,11 +183,12 @@ final class DefaultDBRepository: DBRepository { } } - func store(favoriteProblem: FavoriteProblem) -> Single { + public func store(favoriteProblem: FavoriteProblemVO) -> Single { Single.create { [weak persistentStore] complete in let observable = persistentStore? .update { context in - favoriteProblem.store(in: context) + // TODO: 변경 해야됨 +// favoriteProblem.store(in: context) } .subscribe(onNext: { _ in complete(.success(())) diff --git a/Projects/Data/Sources/Data/Repository/DefaultRepository.swift b/Projects/Data/Sources/Data/Repository/DefaultRepository.swift new file mode 100644 index 00000000..f9cbde0b --- /dev/null +++ b/Projects/Data/Sources/Data/Repository/DefaultRepository.swift @@ -0,0 +1,445 @@ +// +// ApolloService.swift +// CodeStack +// +// Created by 박형환 on 2023/07/25. +// + +import Foundation +import RxSwift +import CodestackAPI +import Apollo +import Global +import Domain + +//public typealias SolvedProblems = [GetSolvedProblemQuery.Data.MatchMember.SolvedProblem] +// +//public typealias GetSubmissions = GetSubmissionsQuery.Data.GetSubmissions +//public typealias GetMeSubmissions = [GetMeSubmissionsQuery.Data.GetMe.Submission] +// +//public typealias SubmissionProblem = CreateSubmissionMutation.Data.CreateSubmission.Problem +//public typealias SubmissionMutation = CreateSubmissionMutation.Data.CreateSubmission +// +//public typealias ProblemContent = GetProblemsQuery.Data.GetProblems.Content +//public typealias ProblemPageInfo = GetProblemsQuery.Data.GetProblems.PageInfo +// +//public typealias Member = GetMeQuery.Data.GetMe +//public typealias MemberSolvedProblem = GetMeQuery.Data.GetMe.SolvedProblem + + +public class DefaultRepository: WebRepository { + + private var tokenAcquizition: TokenAcquisitionService + private var graphAPI: GraphQLAPI + private var restAPI: RestAPI + + public struct Dependency { + public var tokenAcquizition: TokenAcquisitionService + public var graphAPI: GraphQLAPI + public var restAPI: RestAPI + + public init(tokenAcquizition: TokenAcquisitionService, graphAPI: GraphQLAPI, restAPI: RestAPI) { + self.tokenAcquizition = tokenAcquizition + self.graphAPI = graphAPI + self.restAPI = restAPI + } + } + + public init(dependency: Dependency) { + self.tokenAcquizition = dependency.tokenAcquizition + self.graphAPI = dependency.graphAPI + self.restAPI = dependency.restAPI + } + + public func signUp(member: MemberVO) -> Maybe { + let dto = MemberDTO(id: "id", password: "pwd", email: member.email, nickName: member.nickName) + return restAPI.request(.rest(.regitster(dto))) { data in + return true + } + .map { $0 } + .retry(when: { [weak self] errorObservable in + guard let self else { return Observable.never() } + return errorObservable.renewToken(with: self.tokenAcquizition) + }) + } + + public func passwordChange(_ original: Pwd, new: Pwd) -> Maybe { + restAPI.request(.rest(.password(original, new))) { _ in + return true + } + .map { $0 } + .retry(when: { [weak self] errorObservable in + guard let self else { return Observable.never() } + return errorObservable.renewToken(with: self.tokenAcquizition) + }) + } + + public func editProfile(_ image: Data) -> Maybe { + restAPI.request(.rest(.profile(image))) { data in + String(data: data, encoding: .utf8) + } + .map { $0 } + .retry(when: { [weak self] errorObservable in + guard let self else { return Observable.never() } + return errorObservable.renewToken(with: self.tokenAcquizition) + }) + } + + + // MARK: MayBe after solve Dependency Circular Problem +// public func getSolvedProblems(_ username: String) -> Maybe<[ProblemIdentityVO]> +// public func getMe() -> Maybe +// public func updateNickName(_ nickname: String) -> Maybe +// public func perform(_ mut: GraphMutation, max retry: Int = 2) -> Maybe +// public func getProblemsQuery(_ query: GraphQLQuery) -> Maybe<([ProblemVO], PageInfoVO)> +// public func getProblemByID(_ problemID: String) -> Maybe +// public func getAllTag() -> Maybe<([TagVO],PageInfoVO)> +// public func getAllLanguage() -> Maybe<[LanguageVO]> +// public func getSubmission(_ query: GraphQLQuery, cache: CachePolicy? = nil) -> Maybe<([SubmissionVO],PageInfoVO)> +// public func getMeSubmissions(_ name: String) -> Maybe<[SubmissionVO]> + + + public func getSolvedProblems(_ username: String) -> Maybe<[ProblemIdentityVO]> { + let query = API.GRAPH_QUERY(path: .SOLVE_PR_LIST(username: username)) as! FetchSolvedProblemQuery + return graphAPI.fetch(query: query , cachePolicy: .default, queue: .main) + .map { item in + item.matchMember + .solvedProblems + .map { $0.fragments.problemIdentityFR.toDomain() } + } + } + + public func getMe() -> Maybe { + let query = API.GRAPH_QUERY(path: .ME) as! FetchMeQuery + return graphAPI.fetch(query: query, cachePolicy: .fetchIgnoringCacheData, queue: .main) + .map { item in + item.getMe.fragments.memberFR.toDomain() + } + .retry(when: { [weak self] errorObservable in + guard let self else { return Observable.never() } + return errorObservable.renewToken(with: self.tokenAcquizition) + }) + } + + public func updateNickName(_ nickname: String) -> Maybe { + let mutation = API.GRAPH_MUTATION(path: .UPDATE_NICKNAME(nickname: nickname)) as! UpdateNickNameMutation + return graphAPI.perform(query: mutation, cachePolichy: .default, queue: .global(qos: .default)) + .map { item in + item.updateNickname.fragments.memberFR.toDomain() + } + .retry(when: { [weak self] errorObservable in + guard let self else { return Observable.never() } + return errorObservable.renewToken(with: self.tokenAcquizition) + }) + } + + public func perform(_ mutation: SubmitMutation, max retry: Int = 2) -> Maybe { + let submit = SubmitMutation(languageId: mutation.languageId, + problemId: mutation.problemId, + sourceCode: mutation.sourceCode) + let mutation = API.GRAPH_MUTATION(path: .SUBMIT_SUB(submit: submit)) as! SubmitSubmissionMutation + return graphAPI.perform(query: mutation, cachePolichy: .default, queue: .main) + .map { item in + item.createSubmission.fragments.submissionFR.toDomain() + } + .retry(when: { [weak self] errorObservable in + guard let self else { return Observable.never() } + return errorObservable.renewToken(with: self.tokenAcquizition) + }) // retry + } + + public func getProblemsQuery(_ query: GRQuery) -> Maybe<([ProblemVO], PageInfoVO)> { + let query = API.GRAPH_QUERY(path: .PR_LIST(arg: GRAR(limit: query.limit , + offset: query.offset, + sort: query.sort, + order: query.order))) as! FetchProblemsQuery + return graphAPI.fetch(query: query, cachePolicy: .default, queue: .main) + .map { item in + let content = item.getProblems.content! + let fproblems = content.compactMap { content in + content.fragments.problemFR.toDomain() + } + let fpageInfo = item.getProblems.pageInfo.fragments.pageInfoFR.toDomain() + return (fproblems, fpageInfo) + } + } + + public func getProblemByID(_ problemID: String) -> Maybe { + let query = API.GRAPH_QUERY(path: .PR_BY_ID(problemID)) as! FetchProblemByIdQuery + return graphAPI.fetch(query: query, cachePolicy: .default, queue: .main) + .map { data in + data.getProblemById.fragments.problemFR.toDomain() + } + } + + public func getAllTag() -> Maybe<([TagVO],PageInfoVO)> { + //tag: 완전 탐색 + //tag: 4 + //tag: 자료 구조 + let query = API.GRAPH_QUERY(path: .TAG_LIST) as! FetchAllTagQuery + return graphAPI.fetch(query: query, cachePolicy: .default, queue: .main) + .map { data in + let ftags: [TagVO] = data.getAllTag.content!.map { content in + content.fragments.tagFR.toDomain() + } + let pageInfo = data.getAllTag.pageInfo.fragments.pageInfoFR.toDomain() + return (ftags,pageInfo) + } + } + + public func getAllLanguage() -> Maybe<[LanguageVO]> { + let query = API.GRAPH_QUERY(path: .LANG_LIST(offset: 0)) as! FetchAllLanguageQuery + return graphAPI.fetch(query: query, cachePolicy: .default, queue: .main) + .map { data in + data.getAllLanguage.map { content in + content.fragments.languageFR.toDomain() + } + } + } + + public func getMeSubmissions(_ name: String) -> Maybe<[SubmissionVO]> { + let query = API.GRAPH_QUERY(path: .SUB_LIST_ME(username: name)) as! FetchMeSubmissionsQuery + return graphAPI.fetch(query: query, cachePolicy: .default, queue: .main) + .map { data in + data.matchMember.submissions.map { submission in + submission.fragments.submissionFR.toDomain() + } + } + } + + public func getSubmission(_ query: GRQuery) -> Maybe<([SubmissionVO],PageInfoVO)> { + let query = API.GRAPH_QUERY(path: .SUB_LIST(arg: GRAR(limit: query.limit, + offset: query.offset, + sort: query.sort, + order: query.order))) + as! FetchSubmissionsQuery + return graphAPI.fetch(query: query, cachePolicy: .default, queue: .main) + .map { data in + let contents = data.getSubmissions.content! + + let fsubmissions = contents.map { content in + content.fragments.submissionFR.toDomain() + } + let pageInfo = data.getSubmissions.pageInfo.fragments.pageInfoFR.toDomain() + return (fsubmissions, pageInfo) + } + } + + // MARK: 바꿔 야됨 +// public func signUp(member: MemberDTO) -> Maybe { +// restAPI.request(.rest(.regitster(member))) { data in +// return true +// } +// .map { $0 } +// .retry(when: { [weak self] errorObservable in +// guard let self else { return Observable.never() } +// return errorObservable.renewToken(with: self.tokenAcquizition) +// }) +// } +// +// public func passwordChange(_ original: Pwd, new: Pwd) -> Maybe { +// restAPI.request(.rest(.password(original, new))) { _ in +// return true +// } +// .map { $0 } +// .retry(when: { [weak self] errorObservable in +// guard let self else { return Observable.never() } +// return errorObservable.renewToken(with: self.tokenAcquizition) +// }) +// } +// +// public func editProfile(_ image: Data) -> Maybe { +// restAPI.request(.rest(.profile(image))) { data in +// String(data: data, encoding: .utf8) +// } +// .map { $0 } +// .retry(when: { [weak self] errorObservable in +// guard let self else { return Observable.never() } +// return errorObservable.renewToken(with: self.tokenAcquizition) +// }) +// } +} + +/// Graph Mutation Parameter +public struct GRMutation { + public let languageId: String + public let problemId: String + public let sourceCode: String + + public init(languageId: String, problemId: String, sourceCode: String) { + self.languageId = languageId + self.problemId = problemId + self.sourceCode = sourceCode + } +} + +// func getSubmissionDate(api: GRAPH) -> Single { +// // TODO: 현재 GraphQL API에 Submission Result Date 모델을 가져오는 API가 없다. +// #if DEBUG +// Single.create { single in +// let calendars = SubmissionCalendar.generateMockCalendar() +// single(.success(calendars)) +// return Disposables.create { } +// } +// #endif +//// api.fetch(query: query, +//// cachePolicy: .fetchIgnoringCacheData, +//// queue: .global(qos: .background)) +//// .map { value in +//// value.getSubmissions.content?.compactMap { SubmissionCalendar(date: $0.createdAt) } ?? [] +//// } +//// .asObservable() +//// .asSingle() +// } +// +// public func request(type: T.Type, graph: GRAPH) -> Maybe { +// let query = API.GRAPH_QUERY(path: graph) +// +// switch query { +//// case let q as FetchSolvedProblemQuery: +// return getSolvedProblems(q) as! Maybe +//// case let q as FetchSolvedProblemQuery: +//// break +//// case let q as FetchMeQuery: +//// break +//// case let q as UpdateNickNameMutation: +//// break +//// case let q as SubmitSubmissionMutation: +//// break +//// case let q as FetchProblemsQuery: +//// break +//// case let q as FetchProblemByIdQuery: +//// break +//// case let q as FetchAllTagQuery: +//// break +//// case let q as FetchAllLanguageQuery: +//// break +//// case let q as FetchMeSubmissionsQuery: +//// break +//// case let q as FetchSubmissionsQuery: +//// break +// default: +// break +// } +// return .empty() +// } + + +// typealias MatchMember = FetchSolvedProblemQuery.Data.MatchMember.SolvedProblem.Fragments +// +// public func getSolvedProblems(_ query: FetchSolvedProblemQuery) -> Maybe<[ProblemIdentityVO]> { +//// let query = API.GRAPH_QUERY(path: graph) as! FetchSolvedProblemQuery +// return graphAPI.fetch(query: query , cachePolicy: .default, queue: .main) +// .map { item in +// item.matchMember +// .solvedProblems +// .map { $0.fragments.problemIdentityFR.toDomain() } +// } +// } + +// public func getMe(_ graph: GRAPH) -> Maybe { +// let query = API.GRAPH_QUERY(path: graph) as! FetchMeQuery +// return graphAPI.fetch(query: query, cachePolicy: .fetchIgnoringCacheData, queue: .main) +// .map { item in +// item.getMe.fragments.memberFR.toDomain() +// } +// .retry(when: { [weak self] errorObservable in +// guard let self else { return Observable.never() } +// return errorObservable.renewToken(with: self.tokenAcquizition) +// }) +// } + +// public func updateNickName(_ graph: GRAPH) -> Maybe { +// let mutation = API.GRAPH_MUTATION(path: graph) as! UpdateNickNameMutation +// return graphAPI.perform(query: mutation, cachePolichy: .default, queue: .global(qos: .default)) +// .map { item in +// item.updateNickname.fragments.memberFR.toDomain() +// } +// .retry(when: { [weak self] errorObservable in +// guard let self else { return Observable.never() } +// return errorObservable.renewToken(with: self.tokenAcquizition) +// }) +// } + +// public func perform(_ graph: GRAPH, max retry: Int = 2) -> Maybe { +// let mutation = API.GRAPH_MUTATION(path: graph) as! SubmitSubmissionMutation +// return graphAPI.perform(query: mutation, cachePolichy: .default, queue: .main) +// .map { item in +// item.createSubmission.fragments.submissionFR.toDomain() +// } +// .retry(when: { [weak self] errorObservable in +// guard let self else { return Observable.never() } +// return errorObservable.renewToken(with: self.tokenAcquizition) +// }) // retry +// } + +// public func getProblemsQuery(_ graph: GRAPH) -> Maybe<([ProblemVO], PageInfoVO)> { +// let query = API.GRAPH_QUERY(path: graph) as! FetchProblemsQuery +// return graphAPI.fetch(query: query, cachePolicy: .default, queue: .main) +// .map { item in +// let content = item.getProblems.content! +// let fproblems = content.compactMap { content in +// content.fragments.problemFR.toDomain() +// } +// let fpageInfo = item.getProblems.pageInfo.fragments.pageInfoFR.toDomain() +// return (fproblems, fpageInfo) +// } +// } + +// public func getProblemByID(_ graph: GRAPH) -> Maybe { +// let query = API.GRAPH_QUERY(path: graph) as! FetchProblemByIdQuery +// return graphAPI.fetch(query: query, cachePolicy: .default, queue: .main) +// .map { data in +// data.getProblemById.fragments.problemFR.toDomain() +// } +// } + +// public func getAllTag(_ graph: GRAPH) -> Maybe<([TagVO],PageInfoVO)> { +// //tag: 완전 탐색 +// //tag: 4 +// //tag: 자료 구조 +// let query = API.GRAPH_QUERY(path: graph) as! FetchAllTagQuery +// return graphAPI.fetch(query: query, cachePolicy: .default, queue: .main) +// .map { data in +// let ftags: [TagVO] = data.getAllTag.content!.map { content in +// content.fragments.tagFR.toDomain() +// } +// let pageInfo = data.getAllTag.pageInfo.fragments.pageInfoFR.toDomain() +// return (ftags,pageInfo) +// } +// } + +// public func getAllLanguage(_ graph: GRAPH) -> Maybe<[LanguageVO]> { +// let query = API.GRAPH_QUERY(path: graph) as! FetchAllLanguageQuery +// return graphAPI.fetch(query: query, cachePolicy: .default, queue: .main) +// .map { data in +// data.getAllLanguage.map { content in +// content.fragments.languageFR.toDomain() +// } +// } +// } + +// public func getMeSubmissions(_ graph: GRAPH) -> Maybe<[SubmissionVO]> { +// let query = API.GRAPH_QUERY(path: graph) as! FetchMeSubmissionsQuery +// return graphAPI.fetch(query: query, cachePolicy: .default, queue: .main) +// .map { data in +// data.matchMember.submissions.map { submission in +// submission.fragments.submissionFR.toDomain() +// } +// } +// } + +// public func getSubmission(_ graph: GRAPH, cache: CachePolicy? = nil) -> Maybe<([SubmissionVO],PageInfoVO)> { +// let query = API.GRAPH_QUERY(path: graph) as! FetchSubmissionsQuery +// return graphAPI.fetch(query: query, cachePolicy: cache ?? .default, queue: .main) +// .map { data in +// let contents = data.getSubmissions.content! +// +// let fsubmissions = contents.map { content in +// content.fragments.submissionFR.toDomain() +// } +// let pageInfo = data.getSubmissions.pageInfo.fragments.pageInfoFR.toDomain() +// return (fsubmissions, pageInfo) +// } +// } +// diff --git a/Projects/CodestackApp/Sources/Data/Repository/UserManager.swift b/Projects/Data/Sources/Data/Repository/UserManager.swift similarity index 99% rename from Projects/CodestackApp/Sources/Data/Repository/UserManager.swift rename to Projects/Data/Sources/Data/Repository/UserManager.swift index cc68a0ed..49c96758 100644 --- a/Projects/CodestackApp/Sources/Data/Repository/UserManager.swift +++ b/Projects/Data/Sources/Data/Repository/UserManager.swift @@ -6,8 +6,8 @@ // import Foundation -import Data import Global +import Domain @propertyWrapper struct UserDefault { diff --git a/Projects/Data/Sources/Network/API/Auth/AppleAuth.swift b/Projects/Data/Sources/Network/API/Auth/AppleAuth.swift index 81955588..f9913fa7 100644 --- a/Projects/Data/Sources/Network/API/Auth/AppleAuth.swift +++ b/Projects/Data/Sources/Network/API/Auth/AppleAuth.swift @@ -13,7 +13,8 @@ import RxCocoa extension LoginService: AppleAuth { public func oAuthComplte(token: AppleDTO) { - loginViewModel?.oAuthComplte(apple: token) + // FIXME: FIX loginVIewModel +// loginViewModel?.oAuthComplte(apple: token) } //MARK: - codeStack APPle diff --git a/Projects/Data/Sources/Network/API/Auth/AppleLoginService.swift b/Projects/Data/Sources/Network/API/Auth/AppleLoginService.swift index ee4fb5da..811c6925 100644 --- a/Projects/Data/Sources/Network/API/Auth/AppleLoginService.swift +++ b/Projects/Data/Sources/Network/API/Auth/AppleLoginService.swift @@ -15,7 +15,7 @@ public protocol AppleLoginViewType: AnyObject { public class AppleLoginManager: NSObject, ASAuthorizationControllerDelegate { - public weak var loginViewcontroller: AppleLoginViewType? +// public weak var loginViewcontroller: AppleLoginViewType? weak var serviceManager: AppleAuth? var currentNonce: String? diff --git a/Projects/Data/Sources/Network/API/Auth/CSAuth.swift b/Projects/Data/Sources/Network/API/Auth/CSAuth.swift index 31270582..f335d21b 100644 --- a/Projects/Data/Sources/Network/API/Auth/CSAuth.swift +++ b/Projects/Data/Sources/Network/API/Auth/CSAuth.swift @@ -12,7 +12,7 @@ import RxCocoa //MARK: - Codestack login extension LoginService: CSAuth { - public func request(name id: ID,password: Pwd) -> Maybe { + public func request(name id: String, password: Pwd) -> Maybe { guard let request = try? API.rest(.auth(.email(CSDTO(id: id, password: password)))).urlRequest() diff --git a/Projects/Data/Sources/Network/API/Auth/GitAuth.swift b/Projects/Data/Sources/Network/API/Auth/GitAuth.swift index e75137af..7b03abf9 100644 --- a/Projects/Data/Sources/Network/API/Auth/GitAuth.swift +++ b/Projects/Data/Sources/Network/API/Auth/GitAuth.swift @@ -15,7 +15,8 @@ extension LoginService: GitAuth { /// LoginViewModel에 인증완료 이벤트 전달 /// - Parameter code: github code public func gitOAuthComplete(code: String){ - loginViewModel?.oAuthComplete(code: code) + // TODO: LoginVIewModel 제거 +// loginViewModel?.oAuthComplete(code: code) } /// Git Login URL 오픈 -> 사파리 diff --git a/Projects/Data/Sources/Network/API/Auth/LoginService.swift b/Projects/Data/Sources/Network/API/Auth/LoginService.swift index 6af8e7b1..b553c34b 100644 --- a/Projects/Data/Sources/Network/API/Auth/LoginService.swift +++ b/Projects/Data/Sources/Network/API/Auth/LoginService.swift @@ -9,6 +9,7 @@ import UIKit import RxSwift import RxCocoa +// TODO: DOmain layer로 옮겨야됨 public protocol LoginViewModelProtocol: AnyObject { func requestOAuth() throws @@ -20,11 +21,12 @@ public protocol LoginViewModelProtocol: AnyObject { } + public final class LoginService: NSObject { let urlSession: URLSession - public weak var loginViewModel: (any LoginViewModelProtocol)? + // public weak var loginViewModel: (any LoginViewModelProtocol)? var disposeBag = DisposeBag() diff --git a/Projects/Data/Sources/Network/API/Interface/OAuthorization.swift b/Projects/Data/Sources/Network/API/Interface/OAuthorization.swift index eb9e2f90..31a17bd9 100644 --- a/Projects/Data/Sources/Network/API/Interface/OAuthorization.swift +++ b/Projects/Data/Sources/Network/API/Interface/OAuthorization.swift @@ -24,7 +24,7 @@ public protocol AppleAuth: AnyObject{ } public protocol CSAuth { - func request(name id: ID,password: Pwd) -> Maybe + func request(name id: String, password: Pwd) -> Maybe } diff --git a/Projects/Data/Sources/Network/API/Provider/API.swift b/Projects/Data/Sources/Network/API/Provider/API.swift index aaf8c535..bde7f20d 100644 --- a/Projects/Data/Sources/Network/API/Provider/API.swift +++ b/Projects/Data/Sources/Network/API/Provider/API.swift @@ -8,9 +8,9 @@ import Foundation import CodestackAPI + public typealias Token = String public typealias GitCode = String -public typealias ID = String public typealias ProblemID = String public typealias SubmissionID = String public typealias Pwd = String diff --git a/Projects/Data/Sources/Network/API/RestAPI/DefaultRestAPI.swift b/Projects/Data/Sources/Network/API/RestAPI/DefaultRestAPI.swift index bfe1857b..73086861 100644 --- a/Projects/Data/Sources/Network/API/RestAPI/DefaultRestAPI.swift +++ b/Projects/Data/Sources/Network/API/RestAPI/DefaultRestAPI.swift @@ -11,7 +11,6 @@ import RxCocoa import Global - public typealias ImageURL = String public typealias Operation = (_ data: Data) throws -> T? diff --git a/Projects/Data/Sources/Network/DTO/AppleDTO.swift b/Projects/Data/Sources/Network/DTO/AppleDTO.swift index 1f210e7c..49157cad 100644 --- a/Projects/Data/Sources/Network/DTO/AppleDTO.swift +++ b/Projects/Data/Sources/Network/DTO/AppleDTO.swift @@ -10,6 +10,11 @@ import Foundation public struct AppleDTO { - let authorizationCode: String - let user: String? + public let authorizationCode: String + public let user: String? + + public init(authorizationCode: String, user: String?) { + self.authorizationCode = authorizationCode + self.user = user + } } diff --git a/Projects/Data/Sources/Network/DTO/CSDTO.swift b/Projects/Data/Sources/Network/DTO/CSDTO.swift index 43619478..b8d3994e 100644 --- a/Projects/Data/Sources/Network/DTO/CSDTO.swift +++ b/Projects/Data/Sources/Network/DTO/CSDTO.swift @@ -9,6 +9,11 @@ import Foundation public struct CSDTO { - let id: String - let password: String + public let id: String + public let password: String + + public init(id: String, password: String) { + self.id = id + self.password = password + } } diff --git a/Projects/Data/Sources/Network/DTO/CSTokenDTO.swift b/Projects/Data/Sources/Network/DTO/CSTokenDTO.swift index 8f5849ce..2a813d3e 100644 --- a/Projects/Data/Sources/Network/DTO/CSTokenDTO.swift +++ b/Projects/Data/Sources/Network/DTO/CSTokenDTO.swift @@ -14,7 +14,7 @@ public struct CSTokenDTO: Codable { public let expiresIn: TimeInterval public let tokenType: String - init(refreshToken: String, accessToken: String, expiresIn: TimeInterval, tokenType: String) { + public init(refreshToken: String, accessToken: String, expiresIn: TimeInterval, tokenType: String) { self.refreshToken = refreshToken self.accessToken = accessToken self.expiresIn = expiresIn diff --git a/Projects/Data/Sources/Network/DTO/GitTokenDTO.swift b/Projects/Data/Sources/Network/DTO/GitTokenDTO.swift index d0d36908..606568c3 100644 --- a/Projects/Data/Sources/Network/DTO/GitTokenDTO.swift +++ b/Projects/Data/Sources/Network/DTO/GitTokenDTO.swift @@ -7,11 +7,16 @@ import Foundation - public struct GitTokenDTO: Decodable { - let accessToken: String - let scope: String - let tokenType: String + public let accessToken: String + public let scope: String + public let tokenType: String + + public init(accessToken: String, scope: String, tokenType: String) { + self.accessToken = accessToken + self.scope = scope + self.tokenType = tokenType + } enum CodingKeys: String, CodingKey { case accessToken = "access_token" @@ -21,3 +26,5 @@ public struct GitTokenDTO: Decodable { } + + diff --git a/Projects/Data/Sources/Network/DTO/MemberDTO.swift b/Projects/Data/Sources/Network/DTO/MemberDTO.swift index b69e15dd..e5a5e1ef 100644 --- a/Projects/Data/Sources/Network/DTO/MemberDTO.swift +++ b/Projects/Data/Sources/Network/DTO/MemberDTO.swift @@ -6,16 +6,17 @@ // import Foundation +import Domain //MARK: REST API , 회원가입 모델로 사용 public struct MemberDTO: Codable { - public let id: ID + public let id: String public let password: Pwd public let email: String public let nickName: String public var imageURL: String? - public init(id: ID, password: Pwd, email: String, nickName: String, imageURL: String? = nil) { + public init(id: String, password: Pwd, email: String, nickName: String, imageURL: String? = nil) { self.id = id self.password = password self.email = email @@ -23,3 +24,13 @@ public struct MemberDTO: Codable { self.imageURL = imageURL } } + +extension MemberDTO { + public func toDomain() -> MemberVO { + MemberVO(email: self.email, + nickName: self.nickName, + username: "", + solvedProblems: [], + profileImage: self.imageURL ?? "codeStack") + } +} diff --git a/Projects/Data/Sources/Network/DTO/Token.swift b/Projects/Data/Sources/Network/DTO/Token.swift index 91cab587..0b9c6cb7 100644 --- a/Projects/Data/Sources/Network/DTO/Token.swift +++ b/Projects/Data/Sources/Network/DTO/Token.swift @@ -9,7 +9,7 @@ import Foundation public struct AsessToken: Codable { - let accessToken: Token + public let accessToken: Token public init(accessToken: Token) { self.accessToken = accessToken @@ -17,7 +17,7 @@ public struct AsessToken: Codable { } public struct RefreshToken: Codable { - let refresh: Token + public let refresh: Token public init(refresh: Token) { self.refresh = refresh diff --git a/Projects/Domain/Derived/InfoPlists/Domain-Info.plist b/Projects/Domain/Derived/InfoPlists/Domain-Info.plist new file mode 100644 index 00000000..323e5ecf --- /dev/null +++ b/Projects/Domain/Derived/InfoPlists/Domain-Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Projects/Domain/Derived/InfoPlists/Domain_Domain-Info.plist b/Projects/Domain/Derived/InfoPlists/Domain_Domain-Info.plist new file mode 100644 index 00000000..04ffbaae --- /dev/null +++ b/Projects/Domain/Derived/InfoPlists/Domain_Domain-Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Projects/Domain/Derived/Sources/TuistBundle+Domain.swift b/Projects/Domain/Derived/Sources/TuistBundle+Domain.swift new file mode 100644 index 00000000..a46e8393 --- /dev/null +++ b/Projects/Domain/Derived/Sources/TuistBundle+Domain.swift @@ -0,0 +1,40 @@ +// swiftlint:disable all +// swift-format-ignore-file +// swiftformat:disable all +import Foundation + +// MARK: - Swift Bundle Accessor + +private class BundleFinder {} + +extension Foundation.Bundle { + /// Since Domain is a staticFramework, the bundle containing the resources is copied into the final product. + static let module: Bundle = { + let bundleName = "Domain_Domain" + + let candidates = [ + Bundle.main.resourceURL, + Bundle(for: BundleFinder.self).resourceURL, + Bundle.main.bundleURL, + ] + + for candidate in candidates { + let bundlePath = candidate?.appendingPathComponent(bundleName + ".bundle") + if let bundle = bundlePath.flatMap(Bundle.init(url:)) { + return bundle + } + } + fatalError("unable to find bundle named Domain_Domain") + }() +} + +// MARK: - Objective-C Bundle Accessor + +@objc +public class DomainResources: NSObject { + @objc public class var bundle: Bundle { + return .module + } +} +// swiftlint:enable all +// swiftformat:enable all diff --git a/Projects/Domain/Project.swift b/Projects/Domain/Project.swift new file mode 100644 index 00000000..3d9ea9f4 --- /dev/null +++ b/Projects/Domain/Project.swift @@ -0,0 +1,16 @@ +// +// Project.swift +// ProjectDescriptionHelpers +// +// Created by 박형환 on 12/14/23. +// + +import ProjectDescription +import ProjectDescriptionHelpers + + +let project = Project.createModule(name: "Domain", + product: .staticFramework, + dependencies: [ .PRO.global ], + resources: "Resources/**") + diff --git a/Projects/Domain/Resources/Temp.swift b/Projects/Domain/Resources/Temp.swift new file mode 100644 index 00000000..6e768620 --- /dev/null +++ b/Projects/Domain/Resources/Temp.swift @@ -0,0 +1,8 @@ +// +// Temp.swift +// ProjectDescriptionHelpers +// +// Created by 박형환 on 12/14/23. +// + +import Foundation diff --git a/Projects/CodestackApp/Sources/Domain/Extension/Array+Extension.swift b/Projects/Domain/Sources/Domain/Extension/Array+Extension.swift similarity index 100% rename from Projects/CodestackApp/Sources/Domain/Extension/Array+Extension.swift rename to Projects/Domain/Sources/Domain/Extension/Array+Extension.swift diff --git a/Projects/CodestackApp/Sources/Domain/Extension/SubmissionVO-Extension.swift b/Projects/Domain/Sources/Domain/Extension/SubmissionVO-Extension.swift similarity index 59% rename from Projects/CodestackApp/Sources/Domain/Extension/SubmissionVO-Extension.swift rename to Projects/Domain/Sources/Domain/Extension/SubmissionVO-Extension.swift index 379a841d..998a09ed 100644 --- a/Projects/CodestackApp/Sources/Domain/Extension/SubmissionVO-Extension.swift +++ b/Projects/Domain/Sources/Domain/Extension/SubmissionVO-Extension.swift @@ -9,7 +9,7 @@ import Foundation -extension SubmissionVO { +public extension SubmissionVO { /// 제출 같은 language와 source코드 여부 /// - Parameter other: 다른 제출 모델 /// - Returns: 같은 내용인지 flag @@ -22,3 +22,15 @@ extension SubmissionVO { } } } + +public extension Array where Element == SubmissionVO { + func sortByDate() -> Self { + self.sorted(by: { s1,s2 in + if let createdAt1 = s1.createdAt.toDateKST(), + let createdAt2 = s2.createdAt.toDateKST() { + return createdAt1 > createdAt2 + } + return false + }) + } +} diff --git a/Projects/Domain/Sources/Domain/Usecase/AuthUsecase.swift b/Projects/Domain/Sources/Domain/Usecase/AuthUsecase.swift new file mode 100644 index 00000000..8e5fe058 --- /dev/null +++ b/Projects/Domain/Sources/Domain/Usecase/AuthUsecase.swift @@ -0,0 +1,57 @@ +// +// AuthUsecase.swift +// Domain +// +// Created by 박형환 on 12/15/23. +// Copyright © 2023 hyeong. All rights reserved. +// + +import Foundation +import RxSwift + + +public final class AuthUsecase { + private let authRepository: AuthRepository + + public init(authRepository: AuthRepository) { + self.authRepository = authRepository + } + + + public func signUpRequest(query: RegisterQuery) -> Observable { + authRepository.signUp(query: query) + .asObservable() + } + + public func fetchMe() -> Observable { + authRepository.getMe().asObservable() + } + + public func gitOAuthrization() throws { + try authRepository.gitOAuthrization() + } + + public func gitOAuthComplete(code: String) { + authRepository.gitOAuthComplete(code: code) + } + + public func request(git serverCode: String) -> Observable { + authRepository.request(git: serverCode).asObservable() + } + + public func request(with code: String) -> Observable { + authRepository.request(with: code).asObservable() + } + + public func request(apple code: String, user: String) -> Observable { + authRepository.request(apple: code, user: user).asObservable() + } + + public func oAuthComplte(apple code: String, user: String) { + authRepository.oAuthComplte(apple: code, user: user) + } + + public func request(name id: String, password: String) -> Observable { + authRepository.request(name: id, password: password).asObservable() + } +} diff --git a/Projects/Domain/Sources/Domain/Usecase/HomeUsecase.swift b/Projects/Domain/Sources/Domain/Usecase/HomeUsecase.swift new file mode 100644 index 00000000..65e71da7 --- /dev/null +++ b/Projects/Domain/Sources/Domain/Usecase/HomeUsecase.swift @@ -0,0 +1,45 @@ +// +// HomeUsecase.swift +// Domain +// +// Created by 박형환 on 12/15/23. +// Copyright © 2023 hyeong. All rights reserved. +// + +import Foundation +import RxSwift +import Global + +public final class HomeUsecase { + + private let webRepository: WebRepository + private let dbRepository: DBRepository + + public init(webRepository: WebRepository, dbRepository: DBRepository) { + self.webRepository = webRepository + self.dbRepository = dbRepository + } + + public func fetchSubmissionList() -> Observable<[SubmissionVO]> { + dbRepository + .fetchProblemState() + .map(\.first) + .map { problemState in + if let problemState { return problemState.submissions.sortByDate() } + else { return [] } + } + .asObservable() + } + + public func fetchProblem(using subVO: SubmissionVO) -> Observable { + webRepository + .getProblemByID(subVO.problem.id) + .map { /*prFR*/ prVo in + let problemVO = prVo /*.toDomain()*/ + var submission = subVO + submission.problemVO = problemVO + return submission + } + .asObservable() + } +} diff --git a/Projects/Domain/Sources/Domain/Usecase/ProblemUsecase.swift b/Projects/Domain/Sources/Domain/Usecase/ProblemUsecase.swift new file mode 100644 index 00000000..87292825 --- /dev/null +++ b/Projects/Domain/Sources/Domain/Usecase/ProblemUsecase.swift @@ -0,0 +1,30 @@ +// +// ProblemUsecase.swift +// Domain +// +// Created by 박형환 on 12/15/23. +// Copyright © 2023 hyeong. All rights reserved. +// + +import Foundation +import RxSwift + +public final class ProblemUsecase { + + private let webRepository: WebRepository + + public init(webRepository: WebRepository) { + self.webRepository = webRepository + } + + public func fetchProblems(offset: Int) -> Observable<[ProblemVO]> { + webRepository + .getProblemsQuery(GRQuery(offset: offset)) +// .getProblemsQuery(.PR_LIST(arg: GRAR(offset: offset))) + .map { $0.0 } + .asObservable() + // TODO: Page Info 확인하기 + // .asSignal(onErrorJustReturn: []) + } + +} diff --git a/Projects/Domain/Sources/Domain/Usecase/ProfileUsecase.swift b/Projects/Domain/Sources/Domain/Usecase/ProfileUsecase.swift new file mode 100644 index 00000000..8e71da1e --- /dev/null +++ b/Projects/Domain/Sources/Domain/Usecase/ProfileUsecase.swift @@ -0,0 +1,29 @@ +// +// ProfileUsecase.swift +// Domain +// +// Created by 박형환 on 12/15/23. +// Copyright © 2023 hyeong. All rights reserved. +// + +import Foundation +import RxSwift + +public typealias ImageURL = String + +public final class ProfileUsecase { + + private let webRepository: WebRepository + + public init(webRepository: WebRepository) { + self.webRepository = webRepository + } + + public func fetchME() -> Observable { + webRepository.getMe().asObservable() + } + + public func editProfile(data: Data) -> Observable { + webRepository.editProfile(data).asObservable() + } +} diff --git a/Projects/Domain/Sources/Domain/Usecase/SubmissionHistoryUsecase.swift b/Projects/Domain/Sources/Domain/Usecase/SubmissionHistoryUsecase.swift new file mode 100644 index 00000000..de4e7d6d --- /dev/null +++ b/Projects/Domain/Sources/Domain/Usecase/SubmissionHistoryUsecase.swift @@ -0,0 +1,61 @@ +// +// SubmissionHistoryUsecase.swift +// CodeStack +// +// Created by 박형환 on 12/2/23. +// + +import Foundation +import RxSwift + +public protocol HistoryUsecase { + func fetchSubmission(offset: Int) -> Observable<[SubmissionVO]> + func fetchMe(name: String) -> Observable<[SubmissionVO]> + func fetchFavoriteProblem() -> Observable> + func fetchProblemHistoryEqualStatus(status code: String) -> Observable> +} + +public final class DefaultHistoryUsecase: HistoryUsecase { + + private let webRepository: WebRepository + private let dbRepository: DBRepository + + public init(webRepository: WebRepository, dbRepository: DBRepository) { + self.webRepository = webRepository + self.dbRepository = dbRepository + } + + public func fetchSubmission(offset: Int) -> Observable<[SubmissionVO]> { + return webRepository + .getSubmission(GRQuery(offset: offset)) +// .getSubmission(.SUB_LIST(arg: GRAR.init(offset: offset)), cache: nil) + .map { frinfo in + frinfo.0 + // TODO: 확인 -> Page INfo VO + } + .asObservable() + } + + public func fetchMe(name: String) -> Observable<[SubmissionVO]> { + webRepository + .getMeSubmissions(name) +// .getMeSubmissions(.SUB_LIST(arg: GRAR.init(offset: offset))) + //TODO: 확인 필요 + .map { $0 } + .asObservable() + } + + public func fetchFavoriteProblem() -> Observable> { + dbRepository + .fetchFavoriteProblems() + .asObservable() + .map { .success($0) } + } + + public func fetchProblemHistoryEqualStatus(status code: String) -> Observable> { + dbRepository.fetch(.isEqualStatusCode(code)) + .asObservable() + .map { .success($0) } + + } +} diff --git a/Projects/CodestackApp/Sources/Domain/Usecase/SubmissionUseCase.swift b/Projects/Domain/Sources/Domain/Usecase/SubmissionUseCase.swift similarity index 79% rename from Projects/CodestackApp/Sources/Domain/Usecase/SubmissionUseCase.swift rename to Projects/Domain/Sources/Domain/Usecase/SubmissionUseCase.swift index d7ab6acb..d72dcfc0 100644 --- a/Projects/CodestackApp/Sources/Domain/Usecase/SubmissionUseCase.swift +++ b/Projects/Domain/Sources/Domain/Usecase/SubmissionUseCase.swift @@ -8,28 +8,22 @@ import Foundation import RxSwift import RxCocoa -import Data import Global -public typealias State = Result - public protocol SubmissionUseCase: AnyObject { func submitSubmissionAction(model: SubmissionVO) -> Observable> func updateSubmissionAction(model: SubmissionVO) -> Observable func fetchProblemSubmissionHistory(id: ProblemID, state code: String) -> Observable> - func updateFavoritProblem(model: FavoriteProblem, flag: Bool) -> Observable> - - func fetchFavoriteProblem() -> Observable> + func updateFavoritProblem(model: FavoriteProblemVO, flag: Bool) -> Observable> func fetchProblem(id: ProblemID) -> Observable> - - func fetchProblemHistoryEqualStatus(status code: String) -> Observable> - func fetchSubmissionCalendar() -> Observable> + + func fetchSubmissionCalendar() -> Observable> } -enum SendError: Error { +public enum SendError: Error { case isEqualSubmission case isNotStoreState case fetchFailProblem @@ -37,27 +31,27 @@ enum SendError: Error { } -final class DefaultSubmissionUseCase: SubmissionUseCase { +public final class DefaultSubmissionUseCase: SubmissionUseCase { private let dbRepository: DBRepository private let webRepository: WebRepository - init(dbRepository: DBRepository, - webRepository: WebRepository) { + public init(dbRepository: DBRepository, + webRepository: WebRepository) { self.dbRepository = dbRepository self.webRepository = webRepository } - - func fetchProblem(id: ProblemID) -> Observable> { + public func fetchProblem(id: ProblemID) -> Observable> { webRepository - .getProblemByID(.PR_BY_ID(id)) + .getProblemByID(id) +// .getProblemByID(.PR_BY_ID(id)) .asObservable() - .map { problem in .success(problem.toDomain())} + .map { problem in .success(problem)} .catchAndReturn(.failure(SendError.fetchFailProblem)) } - func submitSubmissionAction(model: SubmissionVO) -> Observable> { + public func submitSubmissionAction(model: SubmissionVO) -> Observable> { dbRepository.fetch(.recent(model.problem.id)) .asObservable() .subscribe(on: MainScheduler.instance) @@ -69,35 +63,33 @@ final class DefaultSubmissionUseCase: SubmissionUseCase { } .observe(on: ConcurrentDispatchQueueScheduler.init(queue: .global())) .map { _ in - GRSubmit(languageId: model.language.id, - problemId: model.problem.id, - sourceCode: model.sourceCode) + SubmitMutation(languageId: model.language.id, + problemId: model.problem.id, + sourceCode: model.sourceCode) +// GRSubmit(languageId: model.language.id, +// problemId: model.problem.id, +// sourceCode: model.sourceCode) } .flatMap { [weak webRepository] model -> Maybe in guard let webRepository else { return .empty() } return webRepository - .perform(.SUBMIT_SUB(submit: model), max: 2) - .map { submitFR in submitFR.toDomain() } + .perform(model, max: 2) +// .perform(.SUBMIT_SUB(submit: model), max: 2) + // .map { submitFR in submitFR.toDomain() } } .do(onNext: { [weak self] in self?.storeInRepo(submission: $0) }) .map { .success($0) } .catch { .just(.failure($0)) } } - func fetchProblemSubmissionHistory(id: ProblemID, state code: String) -> Observable> { + public func fetchProblemSubmissionHistory(id: ProblemID, state code: String) -> Observable> { dbRepository.fetch(.isNotTemp(id, code)) .asObservable() .map { .success($0) } } - func fetchProblemHistoryEqualStatus(status code: String) -> Observable> { - dbRepository.fetch(.isEqualStatusCode(code)) - .asObservable() - .map { .success($0) } - } - - func updateSubmissionAction(model: SubmissionVO) -> Observable { + public func updateSubmissionAction(model: SubmissionVO) -> Observable { // MARK: 이미 임시저장이 되어있을 경우 무시 // 최근 기록이 같을때 dbRepository @@ -136,7 +128,7 @@ final class DefaultSubmissionUseCase: SubmissionUseCase { } } - func fetchSubmissionCalendar() -> Observable> { + public func fetchSubmissionCalendar() -> Observable> { dbRepository .fetchSubmissionCalendar() .compactMap { @@ -144,9 +136,9 @@ final class DefaultSubmissionUseCase: SubmissionUseCase { return calendar } else { #if DEBUG - return SubmissionCalendar.generateMockCalendar() + return SubmissionCalendarVO.generateMockCalendar() #else - return SubmissionCalendar.init(dates: []) + return SubmissionCalendarVO.init(dates: []) #endif } } @@ -156,14 +148,7 @@ final class DefaultSubmissionUseCase: SubmissionUseCase { // .observe(on: MainScheduler.instance) } - func fetchFavoriteProblem() -> Observable> { - dbRepository - .fetchFavoriteProblems() - .asObservable() - .map { .success($0) } - } - - func updateFavoritProblem(model: FavoriteProblem, flag: Bool) -> Observable> { + public func updateFavoritProblem(model: FavoriteProblemVO, flag: Bool) -> Observable> { if flag { return dbRepository .store(favoriteProblem: model) diff --git a/Projects/CodestackApp/Sources/Domain/VO/CodeContextVO.swift b/Projects/Domain/Sources/Domain/VO/CodeContextVO.swift similarity index 100% rename from Projects/CodestackApp/Sources/Domain/VO/CodeContextVO.swift rename to Projects/Domain/Sources/Domain/VO/CodeContextVO.swift diff --git a/Projects/CodestackApp/Sources/Domain/VO/FavoriteProblemVO.swift b/Projects/Domain/Sources/Domain/VO/FavoriteProblemVO.swift similarity index 100% rename from Projects/CodestackApp/Sources/Domain/VO/FavoriteProblemVO.swift rename to Projects/Domain/Sources/Domain/VO/FavoriteProblemVO.swift diff --git a/Projects/CodestackApp/Sources/Domain/VO/LanguageVO.swift b/Projects/Domain/Sources/Domain/VO/LanguageVO.swift similarity index 97% rename from Projects/CodestackApp/Sources/Domain/VO/LanguageVO.swift rename to Projects/Domain/Sources/Domain/VO/LanguageVO.swift index 86b59ce4..4f00ab81 100644 --- a/Projects/CodestackApp/Sources/Domain/VO/LanguageVO.swift +++ b/Projects/Domain/Sources/Domain/VO/LanguageVO.swift @@ -21,7 +21,7 @@ public struct LanguageVO: Codable, Equatable { } } -extension LanguageVO { +public extension LanguageVO { static var `default`: Self { LanguageVO(id: "1", name: "C", diff --git a/Projects/CodestackApp/Sources/Domain/VO/MemberVO.swift b/Projects/Domain/Sources/Domain/VO/MemberVO.swift similarity index 64% rename from Projects/CodestackApp/Sources/Domain/VO/MemberVO.swift rename to Projects/Domain/Sources/Domain/VO/MemberVO.swift index b6d82d84..a638e530 100644 --- a/Projects/CodestackApp/Sources/Domain/VO/MemberVO.swift +++ b/Projects/Domain/Sources/Domain/VO/MemberVO.swift @@ -7,7 +7,6 @@ // import Foundation -import Data public struct MemberNameVO: Codable, Equatable { public let username: String @@ -24,7 +23,7 @@ public struct MemberVO: Codable { public let solvedProblems: [ProblemIdentityVO] public let profileImage: String - init(email: String, nickName: String, username: String, solvedProblems: [ProblemIdentityVO], profileImage: String) { + public init(email: String, nickName: String, username: String, solvedProblems: [ProblemIdentityVO], profileImage: String) { self.email = email self.nickName = nickName self.username = username @@ -33,13 +32,8 @@ public struct MemberVO: Codable { } } - -extension MemberDTO { - func toDomain() -> MemberVO { - MemberVO(email: self.email, - nickName: self.nickName, - username: "", - solvedProblems: [], - profileImage: self.imageURL ?? "codeStack") +public extension MemberVO { + static var sample: Self { + .init(email: "GUT", nickName: "환", username: "형환", solvedProblems: [], profileImage: "codeStack") } } diff --git a/Projects/CodestackApp/Sources/Domain/VO/PageInfoVO.swift b/Projects/Domain/Sources/Domain/VO/PageInfoVO.swift similarity index 100% rename from Projects/CodestackApp/Sources/Domain/VO/PageInfoVO.swift rename to Projects/Domain/Sources/Domain/VO/PageInfoVO.swift diff --git a/Projects/CodestackApp/Sources/Domain/VO/ProblemCalendarVO.swift b/Projects/Domain/Sources/Domain/VO/ProblemCalendarVO.swift similarity index 100% rename from Projects/CodestackApp/Sources/Domain/VO/ProblemCalendarVO.swift rename to Projects/Domain/Sources/Domain/VO/ProblemCalendarVO.swift diff --git a/Projects/CodestackApp/Sources/Domain/VO/ProblemIdentityVO.swift b/Projects/Domain/Sources/Domain/VO/ProblemIdentityVO.swift similarity index 100% rename from Projects/CodestackApp/Sources/Domain/VO/ProblemIdentityVO.swift rename to Projects/Domain/Sources/Domain/VO/ProblemIdentityVO.swift diff --git a/Projects/CodestackApp/Sources/Domain/VO/ProblemSubmissionStateVO.swift b/Projects/Domain/Sources/Domain/VO/ProblemSubmissionStateVO.swift similarity index 100% rename from Projects/CodestackApp/Sources/Domain/VO/ProblemSubmissionStateVO.swift rename to Projects/Domain/Sources/Domain/VO/ProblemSubmissionStateVO.swift diff --git a/Projects/CodestackApp/Sources/Domain/VO/ProblemVO.swift b/Projects/Domain/Sources/Domain/VO/ProblemVO.swift similarity index 100% rename from Projects/CodestackApp/Sources/Domain/VO/ProblemVO.swift rename to Projects/Domain/Sources/Domain/VO/ProblemVO.swift diff --git a/Projects/CodestackApp/Sources/Domain/VO/SubmissionCalendarVO.swift b/Projects/Domain/Sources/Domain/VO/SubmissionCalendarVO.swift similarity index 100% rename from Projects/CodestackApp/Sources/Domain/VO/SubmissionCalendarVO.swift rename to Projects/Domain/Sources/Domain/VO/SubmissionCalendarVO.swift diff --git a/Projects/CodestackApp/Sources/Domain/VO/SubmissionVO.swift b/Projects/Domain/Sources/Domain/VO/SubmissionVO.swift similarity index 98% rename from Projects/CodestackApp/Sources/Domain/VO/SubmissionVO.swift rename to Projects/Domain/Sources/Domain/VO/SubmissionVO.swift index b3be9ac2..5229e85e 100644 --- a/Projects/CodestackApp/Sources/Domain/VO/SubmissionVO.swift +++ b/Projects/Domain/Sources/Domain/VO/SubmissionVO.swift @@ -49,7 +49,7 @@ public struct SubmissionVO: Codable, Equatable { } } -extension SubmissionVO { +public extension SubmissionVO { static var sample: Self { .init(id: "", sourceCode: "", diff --git a/Projects/CodestackApp/Sources/Domain/VO/TagVO.swift b/Projects/Domain/Sources/Domain/VO/TagVO.swift similarity index 100% rename from Projects/CodestackApp/Sources/Domain/VO/TagVO.swift rename to Projects/Domain/Sources/Domain/VO/TagVO.swift diff --git a/Projects/CodestackApp/Sources/Domain/VO/TempSubmissionVO.swift b/Projects/Domain/Sources/Domain/VO/TempSubmissionVO.swift similarity index 100% rename from Projects/CodestackApp/Sources/Domain/VO/TempSubmissionVO.swift rename to Projects/Domain/Sources/Domain/VO/TempSubmissionVO.swift diff --git a/Projects/Domain/Sources/Interface/AuthRepository.swift b/Projects/Domain/Sources/Interface/AuthRepository.swift new file mode 100644 index 00000000..bee60368 --- /dev/null +++ b/Projects/Domain/Sources/Interface/AuthRepository.swift @@ -0,0 +1,66 @@ +// +// AuthRepository.swift +// Domain +// +// Created by 박형환 on 12/15/23. +// Copyright © 2023 hyeong. All rights reserved. +// + +import Foundation +import RxSwift + +//public protocol GitAuth: AnyObject { +// func gitOAuthrization() throws +// func gitOAuthComplete(code: String) +// +// func request(git serverCode: String) -> Maybe +// func request(with code: GitCode) -> Maybe +//} +// +//public protocol AppleAuth: AnyObject{ +// func request(with token: AppleDTO) -> Maybe +// func oAuthComplte(token: AppleDTO) +//} +// +//public protocol CSAuth { +// func request(name id: ID,password: Pwd) -> Maybe +//} + +public protocol AuthRepository { + func getMe() -> Maybe + func gitOAuthrization() throws + func gitOAuthComplete(code: String) + func request(git serverCode: String) -> Maybe + func request(with code: String) -> Maybe + func request(apple code: String, user: String) -> Maybe + func oAuthComplte(apple code: String, user: String) + func request(name id: String, password: String) -> Maybe + + func signUp(query: RegisterQuery) -> Maybe +} + +public struct GitTokenVO { + public let accessToken: String + public let scope: String + public let tokenType: String + + public init(accessToken: String, scope: String, tokenType: String) { + self.accessToken = accessToken + self.scope = scope + self.tokenType = tokenType + } +} + +public struct CSTokenVO { + public let refreshToken: String + public let accessToken: String + public let expiresIn: TimeInterval + public let tokenType: String + + public init(refreshToken: String, accessToken: String, expiresIn: TimeInterval, tokenType: String) { + self.refreshToken = refreshToken + self.accessToken = accessToken + self.expiresIn = expiresIn + self.tokenType = tokenType + } +} diff --git a/Projects/Domain/Sources/Interface/DBRepository.swift b/Projects/Domain/Sources/Interface/DBRepository.swift new file mode 100644 index 00000000..cd6fd264 --- /dev/null +++ b/Projects/Domain/Sources/Interface/DBRepository.swift @@ -0,0 +1,60 @@ +// +// DBRepository.swift +// CodestackApp +// +// Created by 박형환 on 12/14/23. +// Copyright © 2023 hyeong. All rights reserved. +// + +import Foundation +import RxSwift + + +public protocol DBRepository: AnyObject { + func fetch(_ requestType: SUB_TYPE) -> Single<[SubmissionVO]> + func store(submission: SubmissionVO) -> Single + func remove() -> Single + func remove(_ requestType: SUB_TYPE) -> Completable + func update(submission: SubmissionVO, type request: SUB_TYPE) -> Single + + func fetchProblemState() -> Single<[ProblemSubmissionStateVO]> + func fetchSubmissionCalendar() -> Single<[SubmissionCalendarVO]> + + func store(favoriteProblem: FavoriteProblemVO) -> Single + func fetchFavoriteProblems() -> Single<[FavoriteProblemVO]> + func removeFavor(_ requestType: FAV_TYPE) -> Completable +} + +public typealias StatusCode = String +public typealias SubmissionID = String +public typealias LanguageName = String +public typealias State = Result +public typealias Token = String +public typealias GitCode = String +public typealias ProblemID = String +public typealias Pwd = String +public typealias NewPwd = String +public typealias ImageData = Data + +/// SubmissionMO - Request Type +public enum SUB_TYPE { + case isExist(ProblemID) + case isNotTemp(ProblemID, StatusCode) + case isEqualStatusCode(StatusCode) + case update(SubmissionID, ProblemID) + case recent(ProblemID) + case `default` + case delete(LanguageName, ProblemID, StatusCode) +} + +/// ProblemSubmissionStateMO - Request Type +public enum PR_SUB_ST_TYPE { + case all + case isEqualID(ProblemID) +} + +/// FavoritProblemMO - Request Type +public enum FAV_TYPE { + case all + case isEqualID(ProblemID) +} diff --git a/Projects/Domain/Sources/Interface/Parameters/DefaultParameter.swift b/Projects/Domain/Sources/Interface/Parameters/DefaultParameter.swift new file mode 100644 index 00000000..2ffefa07 --- /dev/null +++ b/Projects/Domain/Sources/Interface/Parameters/DefaultParameter.swift @@ -0,0 +1,71 @@ +// +// DefaultParameter.swift +// Domain +// +// Created by 박형환 on 12/15/23. +// Copyright © 2023 hyeong. All rights reserved. +// + +import Foundation + +///// Graph Query Parameter +//public struct GraphQLQuery { +// public let limit: Int +// public let offset: Int +// public let sort: String +// public let order: String +// +// public init(limit: Int = 20, offset: Int, sort: String = "id", order: String = "asc") { +// self.limit = limit +// self.offset = offset +// self.sort = sort +// self.order = order +// } +//} +// +///// Graph Mutation Parameter +//public struct GraphMutation { +// public let languageId: String +// public let problemId: String +// public let sourceCode: String +// +// public init(languageId: String, problemId: String, sourceCode: String) { +// self.languageId = languageId +// self.problemId = problemId +// self.sourceCode = sourceCode +// } +//} + +//public enum GRAPH { +// /// 특정 ID의 문제 +// case PR_BY_ID(ID) +// /// 문제 리스트 +// case PR_LIST(arg: GRAR) +// +// /// 문제 검색 리스트 +// case SER_PR_LIST(keyword: String, arg: GRAR) +// +// /// 제출 리스트 +// case SUB_LIST(arg: GRAR) +// /// 제출 By 문제 아이디 +// case SUB_BY_PR_ID(offset: Int, problemId: Double) +// /// 제출 By ID +// case SUB_BY_ID(id: ID) +// /// 나의 제출 리스트/ +// case SUB_LIST_ME(username: String) +// /// 나의 제출 기록 (날짜와 아이디만) +// case ME_SUB_HISTORY(limit: Int, offset: Int) +// /// 멤버 조회 +// case ME +// +// /// solve한 문제 리스트 +// case SOLVE_PR_LIST(username: String) +// /// 언어 리스트/ +// case LANG_LIST(offset: Int) +// /// Tag 리스트 +// case TAG_LIST +// /// 닉네임 업데이트/ +// case UPDATE_NICKNAME(nickname: String) +// /// 코드 제출하기/ +// case SUBMIT_SUB(submit: GRSubmit) +//} diff --git a/Projects/Domain/Sources/Interface/Parameters/RegisterQuery.swift b/Projects/Domain/Sources/Interface/Parameters/RegisterQuery.swift new file mode 100644 index 00000000..aaac1a88 --- /dev/null +++ b/Projects/Domain/Sources/Interface/Parameters/RegisterQuery.swift @@ -0,0 +1,24 @@ +// +// RegisterQuery.swift +// Domain +// +// Created by 박형환 on 12/16/23. +// Copyright © 2023 hyeong. All rights reserved. +// + +import Foundation + + +public struct RegisterQuery { + public let id: String + public let password: String + public let email: String + public let nickname: String + + public init(id: String, password: String, email: String, nickname: String) { + self.id = id + self.password = password + self.email = email + self.nickname = nickname + } +} diff --git a/Projects/Domain/Sources/Interface/Parameters/SubmitQuery.swift b/Projects/Domain/Sources/Interface/Parameters/SubmitQuery.swift new file mode 100644 index 00000000..153ab4b1 --- /dev/null +++ b/Projects/Domain/Sources/Interface/Parameters/SubmitQuery.swift @@ -0,0 +1,73 @@ +// +// SubmitQuery.swift +// Domain +// +// Created by 박형환 on 12/16/23. +// Copyright © 2023 hyeong. All rights reserved. +// + +import Foundation + + +public struct SubmitMutation { + public let languageId: String + public let problemId: String + public let sourceCode: String + + public init(languageId: String, problemId: String, sourceCode: String) { + self.languageId = languageId + self.problemId = problemId + self.sourceCode = sourceCode + } +} + + +// MARK: Migration To -> Domain Parameter +/// Graph Query Parameter +public struct GRQuery { + public let limit: Int + public let offset: Int + public let sort: String + public let order: String + + public init(limit: Int = 20, offset: Int, sort: String = "id", order: String = "asc") { + self.limit = limit + self.offset = offset + self.sort = sort + self.order = order + } +} + +//public enum GRAPH { +// /// 특정 ID의 문제 +// case PR_BY_ID(String) +// /// 문제 리스트 +// case PR_LIST(arg: GRAR) +// +// /// 문제 검색 리스트 +// case SER_PR_LIST(keyword: String, arg: GRAR) +// +// /// 제출 리스트 +// case SUB_LIST(arg: GRAR) +// /// 제출 By 문제 아이디 +// case SUB_BY_PR_ID(offset: Int, problemId: Double) +// /// 제출 By ID +// case SUB_BY_ID(id: String) +// /// 나의 제출 리스트/ +// case SUB_LIST_ME(username: String) +// /// 나의 제출 기록 (날짜와 아이디만) +// case ME_SUB_HISTORY(limit: Int, offset: Int) +// /// 멤버 조회 +// case ME +// +// /// solve한 문제 리스트 +// case SOLVE_PR_LIST(username: String) +// /// 언어 리스트/ +// case LANG_LIST(offset: Int) +// /// Tag 리스트 +// case TAG_LIST +// /// 닉네임 업데이트/ +// case UPDATE_NICKNAME(nickname: String) +// /// 코드 제출하기/ +// case SUBMIT_SUB(submit: GRSubmit) +//} diff --git a/Projects/Domain/Sources/Interface/WebRepository.swift b/Projects/Domain/Sources/Interface/WebRepository.swift new file mode 100644 index 00000000..0ce9f340 --- /dev/null +++ b/Projects/Domain/Sources/Interface/WebRepository.swift @@ -0,0 +1,62 @@ +// +// ApolloServiceType.swift +// CodeStack +// +// Created by 박형환 on 2023/09/08. +// + +import Foundation +import RxSwift +import Apollo + +public protocol WebRepository: AnyObject { + // MARK: GraphQL + // func getSolvedProblems(_ graph: GRAPH) -> Maybe<[ProblemIdentityFR]> + + // func request(type: T.Type, graph: GRAPH) -> Maybe + + // func getSolvedProblems(_ query: FetchSolvedProblemQuery) -> Maybe<[ProblemIdentityVO]> + // func getMe(_ graph: GRAPH) -> Maybe + // func updateNickName(_ graph: GRAPH) -> Maybe + // func getProblemByID(_ graph: GRAPH) -> Maybe + // func getAllTag(_ graph: GRAPH) -> Maybe<([TagVO],PageInfoVO)> + // func getAllLanguage(_ graph: GRAPH) -> Maybe<[LanguageVO]> + // func getMeSubmissions(_ graph: GRAPH) -> Maybe<[SubmissionVO]> + + func perform(_ mutation: SubmitMutation, max retry: Int) -> Maybe + func getProblemsQuery(_ query: GRQuery) -> Maybe<([ProblemVO],PageInfoVO)> + + func getSubmission(_ query: GRQuery) -> Maybe<([SubmissionVO],PageInfoVO)> + + // MARK: REST + func signUp(member: MemberVO) -> Maybe + + /// Password + func passwordChange(_ original: String, new: String) -> Maybe + func editProfile(_ image: Data) -> Maybe + + + // MARK: 지금도 사용가능 + func getSolvedProblems(_ username: String) -> Maybe<[ProblemIdentityVO]> + func getMe() -> Maybe + func updateNickName(_ nickname: String) -> Maybe + + func getProblemByID(_ problemID: String) -> Maybe + func getAllTag() -> Maybe<([TagVO],PageInfoVO)> + func getAllLanguage() -> Maybe<[LanguageVO]> + + func getMeSubmissions(_ name: String) -> Maybe<[SubmissionVO]> +} + + +public extension WebRepository { + + private static var endpoint: String { + return Bundle.main.infoDictionary!["graphql_endpoint"] as! String + } + + static var baseURL: URL { + return URL(string: endpoint)! + } + +} diff --git a/Projects/Domain/Sources/Temp.swift b/Projects/Domain/Sources/Temp.swift new file mode 100644 index 00000000..6e768620 --- /dev/null +++ b/Projects/Domain/Sources/Temp.swift @@ -0,0 +1,8 @@ +// +// Temp.swift +// ProjectDescriptionHelpers +// +// Created by 박형환 on 12/14/23. +// + +import Foundation diff --git a/Projects/Global/Derived/InfoPlists/Global_Global-Info.plist b/Projects/Global/Derived/InfoPlists/Global_Global-Info.plist new file mode 100644 index 00000000..04ffbaae --- /dev/null +++ b/Projects/Global/Derived/InfoPlists/Global_Global-Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Projects/Global/Derived/Sources/TuistBundle+Global.swift b/Projects/Global/Derived/Sources/TuistBundle+Global.swift index 07ece8ad..c22de8fa 100644 --- a/Projects/Global/Derived/Sources/TuistBundle+Global.swift +++ b/Projects/Global/Derived/Sources/TuistBundle+Global.swift @@ -8,8 +8,24 @@ import Foundation private class BundleFinder {} extension Foundation.Bundle { - /// Since Global is a framework, the bundle for classes within this module can be used directly. - static let module = Bundle(for: BundleFinder.self) + /// Since Global is a staticFramework, the bundle containing the resources is copied into the final product. + static let module: Bundle = { + let bundleName = "Global_Global" + + let candidates = [ + Bundle.main.resourceURL, + Bundle(for: BundleFinder.self).resourceURL, + Bundle.main.bundleURL, + ] + + for candidate in candidates { + let bundlePath = candidate?.appendingPathComponent(bundleName + ".bundle") + if let bundle = bundlePath.flatMap(Bundle.init(url:)) { + return bundle + } + } + fatalError("unable to find bundle named Global_Global") + }() } // MARK: - Objective-C Bundle Accessor diff --git a/Projects/Global/Project.swift b/Projects/Global/Project.swift index 04700c01..d6e71eb2 100644 --- a/Projects/Global/Project.swift +++ b/Projects/Global/Project.swift @@ -8,56 +8,8 @@ import ProjectDescription import ProjectDescriptionHelpers -let project = Project(name: "Global", - organizationName: "hyeong", - options: .options(automaticSchemesOptions: .disabled), - packages: [], - settings: .settings(configurations: [ - .debug(name: "Dev"), - .debug(name: "Prod") - ]), - targets: [ - Project.target(name: "Global", - product: .framework, - sources: "Sources/**", - resources: "Resources/**", - dependencies: [ - ], - configuration: [ - .debug(name: "Dev", - settings: ["SWIFT_ACTIVE_COMPILATION_CONDITIONS": .string("DEBUG")]), - .debug(name: "Prod", - settings: ["SWIFT_ACTIVE_COMPILATION_CONDITIONS": .string("PROD")]) - ]), - ], - schemes: [ - Scheme(name: "Global-Dev", - shared: true, - buildAction: .buildAction(targets: ["Global"]), - testAction: .targets(["GlobalTests"], - configuration: .configuration("Dev"), - options: .options(coverage: true)), - runAction: .runAction(configuration: .configuration("Dev"), - arguments: .init(environment: ["OS_ACTIVITY_MODE" : "disable"], - launchArguments: [])), - archiveAction: .archiveAction(configuration: .configuration("Dev")), - profileAction: .profileAction(configuration: .configuration("Dev")), - analyzeAction: .analyzeAction(configuration: .configuration("Dev"))), - Scheme(name: "Global-Prod", - shared: true, - buildAction: .buildAction(targets: ["Global"]), - runAction: .runAction(configuration: .configuration("Prod")), - archiveAction: .archiveAction(configuration: .configuration("Prod")), - profileAction: .profileAction(configuration: .configuration("Prod")), - analyzeAction: .analyzeAction(configuration: .configuration("Prod"))) - ], - fileHeaderTemplate: nil, - additionalFiles: [], - resourceSynthesizers: []) -// Project.target(name: "MyAppTests", -// product: .unitTests, -// sources: "Tests/**", -// dependencies: [ -// .target(name: "MyApp"), -// ]) +let project = Project.createModule(name: "Global", + product: .staticFramework, + dependencies: [ .PRO.thridParty ], + resources: "Resources/**") diff --git a/Projects/Module/ThirdParty/Derived/InfoPlists/ThirdParty-Info.plist b/Projects/Module/ThirdParty/Derived/InfoPlists/ThirdParty-Info.plist new file mode 100644 index 00000000..323e5ecf --- /dev/null +++ b/Projects/Module/ThirdParty/Derived/InfoPlists/ThirdParty-Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Projects/Module/ThirdParty/Project.swift b/Projects/Module/ThirdParty/Project.swift new file mode 100644 index 00000000..07267106 --- /dev/null +++ b/Projects/Module/ThirdParty/Project.swift @@ -0,0 +1,25 @@ +// +// Project.swift +// ProjectDescriptionHelpers +// +// Created by 박형환 on 12/16/23. +// + +import ProjectDescription +import ProjectDescriptionHelpers + +let project = Project.createModule(name: "ThirdParty", + product: .framework, + dependencies: [.SPM.rxSwift, + .SPM.snapKit, + .SPM.then, + .SPM.apollo, + .SPM.richtextKit, + .SPM.rxdatasources, + .SPM.rxFlow, + .SPM.rxGesture, + .SPM.sqlite, + .SPM.swinject, + .SPM.codestackAPI, + .SPM.highlightr]) + diff --git a/Projects/Module/ThirdParty/Sources/.gitkeep b/Projects/Module/ThirdParty/Sources/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/Tuist/ProjectDescriptionHelpers/Project+Templates.swift b/Tuist/ProjectDescriptionHelpers/Project+Templates.swift index 7e527ac3..e1cb5670 100644 --- a/Tuist/ProjectDescriptionHelpers/Project+Templates.swift +++ b/Tuist/ProjectDescriptionHelpers/Project+Templates.swift @@ -1,8 +1,89 @@ import ProjectDescription + +extension Project { + public static func createModule( + name: String, + platform: Platform = .iOS, + product: Product, + bundleID: String? = nil, + packages: [Package] = [], + settings: Bool = false, + dependencies: [TargetDependency] = [], + configuration: Bool = false, + sources: SourceFilesList = ["Sources/**"], + resources: ResourceFileElements? = nil, + entitlement: ProjectDescription.Path? = nil, + infoPlist: InfoPlist = .default, + resourceSynthesizers: [ResourceSynthesizer] = .default + ) -> Project { + return project( + name: name, + platform: platform, + product: product, + bundleID: bundleID, + packages: packages, + settings: settings, + dependencies: dependencies, + configuration: configuration, + sources: sources, + resources: resources, + entitlement: entitlement, + infoPlist: infoPlist, + resourceSynthesizers: resourceSynthesizers + ) + } +} + +public extension Project { + static func project( + name: String, + platform: Platform, + product: Product, + bundleID: String? = nil, + organizationName: String = env.organizationName, + packages: [Package], + settings: Bool, + deploymentTarget: DeploymentTarget? = env.deploymentTarget, + dependencies: [TargetDependency] = [], + configuration: Bool, + sources: SourceFilesList, + resources: ResourceFileElements? = nil, + entitlement: ProjectDescription.Path? = nil, + infoPlist: InfoPlist = .default, + resourceSynthesizers: [ResourceSynthesizer] = .default + ) -> Project { + + let appTarget = Project.target(name: name, + product: product, + bundleID: bundleID, + infoPlist: infoPlist, + sources: sources, + resources: resources, + entitlement: entitlement, + scripts: [], + dependencies: dependencies, + configuration: configuration ? targetSetting : defaultTargetSetting , + coreDataModels: []) + + return Project( + name: name, + organizationName: organizationName, + options: .options(automaticSchemesOptions: .disabled), + packages: packages, + settings: settings ? appBuildSetting : defalutBuildSetting, + targets: [appTarget], + schemes: generateSchemes(name), + resourceSynthesizers: resourceSynthesizers + ) + } +} + + + + public extension Project { - static func target( name: String, product: Product, diff --git a/Tuist/ProjectDescriptionHelpers/Project+env.swift b/Tuist/ProjectDescriptionHelpers/Project+env.swift new file mode 100644 index 00000000..b8b853ef --- /dev/null +++ b/Tuist/ProjectDescriptionHelpers/Project+env.swift @@ -0,0 +1,22 @@ +// +// Env.swift +// ProjectDescriptionHelpers +// +// Created by 박형환 on 12/16/23. +// + +import ProjectDescription + +public struct Environment { + public let organizationName: String + public let deploymentTarget: DeploymentTarget + public let platform: Platform +} + +public let env = Environment( + organizationName: "hyeong", + deploymentTarget: .iOS(targetVersion: "15.0", + devices: .iphone, + supportsMacDesignedForIOS: true), + platform: .iOS +) diff --git a/Tuist/ProjectDescriptionHelpers/Setting+Variable.swift b/Tuist/ProjectDescriptionHelpers/Setting+Variable.swift new file mode 100644 index 00000000..f49940ab --- /dev/null +++ b/Tuist/ProjectDescriptionHelpers/Setting+Variable.swift @@ -0,0 +1,64 @@ +// +// Setting.swift +// ProjectDescriptionHelpers +// +// Created by 박형환 on 12/16/23. +// + +import ProjectDescription + +let appBuildSetting: Settings = .settings(configurations: [ + .debug(name: "Dev", + settings: [:], + xcconfig: "Config/BuildSetting.xcconfig"), + .debug(name: "Prod", + settings: [:], + xcconfig: "Config/BuildSetting.xcconfig"), + ]) + +let targetSetting: [ProjectDescription.Configuration] = [ + .debug(name: "Dev", + settings: [:], + xcconfig: "Config/Codestack.xcconfig"), + .debug(name: "Prod", + settings: [:], + xcconfig: "Config/Codestack.xcconfig") +] + +let defalutBuildSetting: Settings = .settings(configurations: [ + .debug(name: "Dev"), + .debug(name: "Prod") +]) + + +let defaultTargetSetting: [ProjectDescription.Configuration] = [ + .debug(name: "Dev", + settings: ["SWIFT_ACTIVE_COMPILATION_CONDITIONS": .string("DEBUG")]), + .debug(name: "Prod", + settings: ["SWIFT_ACTIVE_COMPILATION_CONDITIONS": .string("PROD")]) +] + + +func generateSchemes(_ name: String) -> [Scheme] { + [ + Scheme(name: "\(name)-Dev", + shared: true, + buildAction: .buildAction(targets: ["\(name)"]), + testAction: .targets(["\(name)Tests"], + configuration: .configuration("Dev"), + options: .options(coverage: true)), + runAction: .runAction(configuration: .configuration("Dev"), + arguments: .init(environment: ["OS_ACTIVITY_MODE" : "disable"], + launchArguments: [])), + archiveAction: .archiveAction(configuration: .configuration("Dev")), + profileAction: .profileAction(configuration: .configuration("Dev")), + analyzeAction: .analyzeAction(configuration: .configuration("Dev"))), + Scheme(name: "\(name)-Prod", + shared: true, + buildAction: .buildAction(targets: ["\(name)"]), + runAction: .runAction(configuration: .configuration("Prod")), + archiveAction: .archiveAction(configuration: .configuration("Prod")), + profileAction: .profileAction(configuration: .configuration("Prod")), + analyzeAction: .analyzeAction(configuration: .configuration("Prod"))) + ] +} diff --git a/Tuist/ProjectDescriptionHelpers/TargetDependencies.swift b/Tuist/ProjectDescriptionHelpers/Spm+Project+Dependency.swift similarity index 50% rename from Tuist/ProjectDescriptionHelpers/TargetDependencies.swift rename to Tuist/ProjectDescriptionHelpers/Spm+Project+Dependency.swift index 1d344494..03b0608e 100644 --- a/Tuist/ProjectDescriptionHelpers/TargetDependencies.swift +++ b/Tuist/ProjectDescriptionHelpers/Spm+Project+Dependency.swift @@ -2,7 +2,14 @@ import ProjectDescription + public extension TargetDependency { + enum PRO {} + enum MOD {} + enum SPM {} +} + +public extension TargetDependency.SPM { static let rxSwift: TargetDependency = .external(name: "RxSwift") static let snapKit: TargetDependency = .external(name: "SnapKit") static let then: TargetDependency = .external(name: "Then") @@ -17,3 +24,30 @@ public extension TargetDependency { static let highlightr: TargetDependency = .external(name: "Highlightr") } +public extension TargetDependency.PRO { + static let global = TargetDependency.project( + target: "Global", + path: .relativeToRoot("Projects/Global") + ) + static let data = TargetDependency.project( + target: "Data", + path: .relativeToRoot("Projects/Data") + ) + static let domain = TargetDependency.project( + target: "Domain", + path: .relativeToRoot("Projects/Domain") + ) + static let flow = TargetDependency.project( + target: "Flow", + path: .relativeToRoot("Projects/Flow") + ) + + static let thridParty = TargetDependency.project( + target: "ThirdParty", + path: .relativeToRoot("Projects/Module/ThirdParty") + ) + static let presentation = TargetDependency.project( + target: "Presentation", + path: .relativeToRoot("Projects/Presentation") + ) +} diff --git a/Workspace.swift b/Workspace.swift index cd144b9d..2f03b820 100644 --- a/Workspace.swift +++ b/Workspace.swift @@ -25,7 +25,9 @@ let workspace = Workspace( projects: [ "Projects/CodestackApp", "Projects/Global", - "Projects/Data" + "Projects/Data", + "Projects/Domain", + "Projects/Module/ThirdParty" ]) //let project = Project.app(name: "MyApp", // platform: .iOS, diff --git a/graph.png b/graph.png index da030fe9..7cdf8241 100644 Binary files a/graph.png and b/graph.png differ