diff --git a/Projects/Data/Sources/API/AuthAPI.swift b/Projects/Data/Sources/API/AuthAPI.swift index 2f8f8f6..97c8f49 100644 --- a/Projects/Data/Sources/API/AuthAPI.swift +++ b/Projects/Data/Sources/API/AuthAPI.swift @@ -15,7 +15,7 @@ extension AuthAPI: PiCKAPI { public typealias ErrorType = PiCKError public var urlType: PiCKURL { - .user + return .user } public var urlPath: String { diff --git a/Projects/Data/Sources/API/BugAPI.swift b/Projects/Data/Sources/API/BugAPI.swift index 0ac12e2..6c718ba 100644 --- a/Projects/Data/Sources/API/BugAPI.swift +++ b/Projects/Data/Sources/API/BugAPI.swift @@ -15,7 +15,7 @@ extension BugAPI: PiCKAPI { public typealias ErrorType = PiCKError public var urlType: PiCKURL { - .bug + return .bug } public var urlPath: String { diff --git a/Projects/Data/Sources/API/ProfileAPI.swift b/Projects/Data/Sources/API/ProfileAPI.swift index 35d787f..d60cfc3 100644 --- a/Projects/Data/Sources/API/ProfileAPI.swift +++ b/Projects/Data/Sources/API/ProfileAPI.swift @@ -9,6 +9,7 @@ import AppNetwork public enum ProfileAPI { case fetchSimpleProfile case fetchDetailProfile + case uploadProfileImage(image: Data) } extension ProfileAPI: PiCKAPI { @@ -24,15 +25,35 @@ extension ProfileAPI: PiCKAPI { return "/simple" case .fetchDetailProfile: return "/details" + case .uploadProfileImage: + return "/profile" } } public var method: Moya.Method { - return .get + switch self { + case .uploadProfileImage: + return .patch + default: + return .get + } } public var task: Moya.Task { - return .requestPlain + switch self { + case .uploadProfileImage(let image): + var multiformData: [MultipartFormData] = [] + multiformData.append(.init( + provider: .data(image), + name: "file", + fileName: "file.jpg", + mimeType: "file/jpg" + )) + + return .uploadMultipart(multiformData) + default: + return .requestPlain + } } public var pickHeader: TokenType { diff --git a/Projects/Data/Sources/DI/UseCaseAssembly.swift b/Projects/Data/Sources/DI/UseCaseAssembly.swift index f1f8965..424d7c4 100644 --- a/Projects/Data/Sources/DI/UseCaseAssembly.swift +++ b/Projects/Data/Sources/DI/UseCaseAssembly.swift @@ -77,6 +77,9 @@ public final class UseCaseAssembly: Assembly { container.register(FetchDetailProfileUseCase.self) { resolver in FetchDetailProfileUseCase(repository: resolver.resolve(ProfileRepository.self)!) } + container.register(UploadProfileImageUseCase.self) { resolver in + UploadProfileImageUseCase(repository: resolver.resolve(ProfileRepository.self)!) + } // bug container.register(BugImageUploadUseCase.self) { resolver in BugImageUploadUseCase(repository: resolver.resolve(BugRepository.self)!) diff --git a/Projects/Data/Sources/DataSource/ProfileDataSource.swift b/Projects/Data/Sources/DataSource/ProfileDataSource.swift index d94228a..adce27c 100644 --- a/Projects/Data/Sources/DataSource/ProfileDataSource.swift +++ b/Projects/Data/Sources/DataSource/ProfileDataSource.swift @@ -10,6 +10,7 @@ import Domain protocol ProfileDataSource { func fetchSimpleProfile() -> Single func fetchDetailProfile() -> Single + func uploadProfileImage(image: Data) -> Completable } class ProfileDataSourceImpl: BaseDataSource, ProfileDataSource { @@ -23,4 +24,10 @@ class ProfileDataSourceImpl: BaseDataSource, ProfileDataSource { .filterSuccessfulStatusCodes() } + func uploadProfileImage(image: Data) -> Completable { + return request(.uploadProfileImage(image: image)) + .filterSuccessfulStatusCodes() + .asCompletable() + } + } diff --git a/Projects/Data/Sources/Repository/ProfileRepositoryImpl.swift b/Projects/Data/Sources/Repository/ProfileRepositoryImpl.swift index 4d2f2a3..cebac91 100644 --- a/Projects/Data/Sources/Repository/ProfileRepositoryImpl.swift +++ b/Projects/Data/Sources/Repository/ProfileRepositoryImpl.swift @@ -23,4 +23,8 @@ class ProfileRepositoryImpl: ProfileRepository { .map { $0.toDomain() } } + func uploadProfileImage(image: Data) -> Completable { + return remoteDataSource.uploadProfileImage(image: image) + } + } diff --git a/Projects/Domain/Sources/Repository/ProfileRepository.swift b/Projects/Domain/Sources/Repository/ProfileRepository.swift index 254e401..6a1f796 100644 --- a/Projects/Domain/Sources/Repository/ProfileRepository.swift +++ b/Projects/Domain/Sources/Repository/ProfileRepository.swift @@ -5,4 +5,5 @@ import RxSwift public protocol ProfileRepository { func fetchSimpleProfile() -> Single func fetchDetailProfile() -> Single + func uploadProfileImage(image: Data) -> Completable } diff --git a/Projects/Domain/Sources/UseCase/Profile/UploadProfileImageUseCase.swift b/Projects/Domain/Sources/UseCase/Profile/UploadProfileImageUseCase.swift new file mode 100644 index 0000000..2627394 --- /dev/null +++ b/Projects/Domain/Sources/UseCase/Profile/UploadProfileImageUseCase.swift @@ -0,0 +1,16 @@ +import Foundation + +import RxSwift + +public class UploadProfileImageUseCase { + let repository: ProfileRepository + + public init(repository: ProfileRepository) { + self.repository = repository + } + + public func execute(image: Data) -> Completable { + return repository.uploadProfileImage(image: image) + } + +} diff --git a/Projects/Flow/Project.swift b/Projects/Flow/Project.swift index e457eae..7ad14e5 100644 --- a/Projects/Flow/Project.swift +++ b/Projects/Flow/Project.swift @@ -7,8 +7,8 @@ let project = Project.makeModule( platform: .iOS, product: .staticLibrary, dependencies: [ - .Projects.presentation, .Projects.data, + .Projects.presentation, .SPM.FCM ] ) diff --git a/Projects/Modules/DesignSystem/Sources/Component/View/PiCKProfileView.swift b/Projects/Modules/DesignSystem/Sources/Component/View/PiCKProfileView.swift index e278ca1..3eccc02 100644 --- a/Projects/Modules/DesignSystem/Sources/Component/View/PiCKProfileView.swift +++ b/Projects/Modules/DesignSystem/Sources/Component/View/PiCKProfileView.swift @@ -7,10 +7,16 @@ import RxSwift import RxCocoa import RxGesture +import Kingfisher + import Core public class PiCKProfileView: BaseView { - private let profileImageView = UIImageView(image: .profile) + private let profileImageView = UIImageView().then { + $0.contentMode = .scaleToFill + $0.layer.cornerRadius = 30 + $0.clipsToBounds = true + } private let userInfoLabel = PiCKLabel( textColor: .modeBlack, font: .label1, @@ -18,10 +24,13 @@ public class PiCKProfileView: BaseView { ) public func setup( - image: UIImage, + image: String, info: String ) { - self.profileImageView.image = image + self.profileImageView.kf.setImage( + with: URL(string: image), + placeholder: UIImage.profile + ) self.userInfoLabel.text = "대덕소프트웨어마이스터고\n\(info)" } diff --git a/Projects/Presentation/Sources/DI/PresentationAssembly.swift b/Projects/Presentation/Sources/DI/PresentationAssembly.swift index 710012d..94ab877 100644 --- a/Projects/Presentation/Sources/DI/PresentationAssembly.swift +++ b/Projects/Presentation/Sources/DI/PresentationAssembly.swift @@ -125,7 +125,9 @@ public final class PresentationAssembly: Assembly { } container.register(AllTabViewModel.self) { resolver in AllTabViewModel( - logoutUseCase: resolver.resolve(LogoutUseCase.self)!) + fetchProfileUsecase: resolver.resolve(FetchSimpleProfileUseCase.self)!, + logoutUseCase: resolver.resolve(LogoutUseCase.self)! + ) } // Notice container.register(NoticeListViewController.self) { resolver in @@ -182,7 +184,8 @@ public final class PresentationAssembly: Assembly { } container.register(MyPageViewModel.self) { resolver in MyPageViewModel( - profileUsecase: resolver.resolve(FetchDetailProfileUseCase.self)! + profileUsecase: resolver.resolve(FetchDetailProfileUseCase.self)!, + uploadProfileImageUseCase: resolver.resolve(UploadProfileImageUseCase.self)! ) } } diff --git a/Projects/Presentation/Sources/Scene/AllTab/AllTabViewController.swift b/Projects/Presentation/Sources/Scene/AllTab/AllTabViewController.swift index 11b2154..ba5a621 100644 --- a/Projects/Presentation/Sources/Scene/AllTab/AllTabViewController.swift +++ b/Projects/Presentation/Sources/Scene/AllTab/AllTabViewController.swift @@ -44,6 +44,7 @@ public class AllTabViewController: BaseViewController { } public override func bind() { let input = AllTabViewModel.Input( + viewWillAppear: viewWillAppearRelay.asObservable(), clickSelfStudyTab: helpSectionView.getSelectedItem(type: .selfStudy).asObservable(), clickNoticeTab: helpSectionView.getSelectedItem(type: .notice).asObservable(), clickBugReportTab: helpSectionView.getSelectedItem(type: .bugReport).asObservable(), @@ -53,7 +54,18 @@ public class AllTabViewController: BaseViewController { clickLogOutTab: logoutRelay.asObservable() ) - _ = viewModel.transform(input: input) + let output = viewModel.transform(input: input) + + output.profileData.asObservable() + .withUnretained(self) + .bind { owner, profileData in + let userInfoData = UserDefaultStorage.shared.get(forKey: .userInfoData) as? String + + owner.profileView.setup( + image: profileData.profile ?? "", + info: userInfoData ?? "정보가 없는 사용자" + ) + }.disposed(by: disposeBag) accountSectionView .getSelectedItem(type: .logOut) @@ -103,9 +115,4 @@ public class AllTabViewController: BaseViewController { } } - public override func setLayoutData() { - let userInfoData = UserDefaultStorage.shared.get(forKey: .userInfoData) as? String - self.profileView.setup(image: .profile, info: userInfoData ?? "정보가 없는 사용자") - } - } diff --git a/Projects/Presentation/Sources/Scene/AllTab/AllTabViewModel.swift b/Projects/Presentation/Sources/Scene/AllTab/AllTabViewModel.swift index a3957c9..5bd1b69 100644 --- a/Projects/Presentation/Sources/Scene/AllTab/AllTabViewModel.swift +++ b/Projects/Presentation/Sources/Scene/AllTab/AllTabViewModel.swift @@ -10,13 +10,20 @@ import Domain public class AllTabViewModel: BaseViewModel, Stepper { private let disposeBag = DisposeBag() public var steps = PublishRelay() + + private let fetchProfileUsecase: FetchSimpleProfileUseCase private let logoutUseCase: LogoutUseCase - public init(logoutUseCase: LogoutUseCase) { + public init( + fetchProfileUsecase: FetchSimpleProfileUseCase, + logoutUseCase: LogoutUseCase + ) { + self.fetchProfileUsecase = fetchProfileUsecase self.logoutUseCase = logoutUseCase } public struct Input { + let viewWillAppear: Observable let clickSelfStudyTab: Observable let clickNoticeTab: Observable let clickBugReportTab: Observable @@ -25,9 +32,24 @@ public class AllTabViewModel: BaseViewModel, Stepper { let clickMyPageTab: Observable let clickLogOutTab: Observable } - public struct Output {} + public struct Output { + let profileData: Signal + } + + private let profileData = PublishRelay() public func transform(input: Input) -> Output { + input.viewWillAppear + .flatMap { + self.fetchProfileUsecase.execute() + .catch { + print($0.localizedDescription) + return .never() + } + } + .bind(to: profileData) + .disposed(by: disposeBag) + input.clickSelfStudyTab .map { _ in PiCKStep.selfStudyIsRequired } .bind(to: steps) @@ -66,7 +88,7 @@ public class AllTabViewModel: BaseViewModel, Stepper { .bind(to: steps) .disposed(by: disposeBag) - return Output() + return Output(profileData: profileData.asSignal()) } } diff --git a/Projects/Presentation/Sources/Scene/AllTab/MyPage/MyPageViewController.swift b/Projects/Presentation/Sources/Scene/AllTab/MyPage/MyPageViewController.swift index 8586a45..c46dea2 100644 --- a/Projects/Presentation/Sources/Scene/AllTab/MyPage/MyPageViewController.swift +++ b/Projects/Presentation/Sources/Scene/AllTab/MyPage/MyPageViewController.swift @@ -2,17 +2,25 @@ import UIKit import SnapKit import Then + import RxSwift import RxCocoa +import Kingfisher + import Core import DesignSystem public class MyPageViewController: BaseViewController { - private let profileImageView = UIImageView(image: .profile) + private var profileImageData = PublishRelay() + + private let profileImageView = UIImageView().then { + $0.layer.cornerRadius = 40 + $0.clipsToBounds = true + $0.contentMode = .scaleAspectFill + } private let changeButton = UIButton(type: .system).then { -// $0.setTitle("변경하기", for: .normal) - $0.setTitle("", for: .normal) + $0.setTitle("변경하기", for: .normal) $0.setTitleColor(.gray500, for: .normal) $0.titleLabel?.font = .body1 } @@ -47,17 +55,33 @@ public class MyPageViewController: BaseViewController { } public override func bind() { let input = MyPageViewModel.Input( - viewWillAppear: viewWillAppearRelay.asObservable() + viewWillAppear: viewWillAppearRelay.asObservable(), + profileImage: profileImageData.asObservable() ) let output = viewModel.transform(input: input) output.profileData.asObservable() - .bind(onNext: { [weak self] profileData in - self?.userNameLabel.text = profileData.name - self?.userBirthDayLabel.text = "\(profileData.birthDay.toDate(type: .fullDate).toString(type: .fullDateKorForCalendar))" - self?.userSchoolIDLabel.text = "\(profileData.grade)학년 \(profileData.classNum)반 \(profileData.num)번" - self?.userIDLabel.text = profileData.accountID - }).disposed(by: disposeBag) + .withUnretained(self) + .bind { owner, profileData in + owner.profileImageView.kf.setImage( + with: URL(string: profileData.profile ?? ""), + placeholder: UIImage.profile + ) + owner.userNameLabel.text = profileData.name + owner.userBirthDayLabel.text = "\(profileData.birthDay.toDate(type: .fullDate).toString(type: .fullDateKorForCalendar))" + owner.userSchoolIDLabel.text = "\(profileData.grade)학년 \(profileData.classNum)반 \(profileData.num)번" + owner.userIDLabel.text = profileData.accountID + }.disposed(by: disposeBag) + } + public override func bindAction() { + changeButton.rx.tap + .bind { [weak self] in + let picker = UIImagePickerController() + picker.sourceType = .photoLibrary + picker.allowsEditing = true + picker.delegate = self + self?.present(picker, animated: true) + }.disposed(by: disposeBag) } public override func addView() { @@ -72,6 +96,7 @@ public class MyPageViewController: BaseViewController { profileImageView.snp.makeConstraints { $0.centerX.equalToSuperview() $0.top.equalTo(view.safeAreaLayoutGuide).offset(40) + $0.size.equalTo(80) } changeButton.snp.makeConstraints { $0.centerX.equalToSuperview() @@ -99,3 +124,16 @@ public class MyPageViewController: BaseViewController { } } + +extension MyPageViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate { + public func imagePickerController( + _ picker: UIImagePickerController, + didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any] + ) { + picker.dismiss(animated: true) { [weak self] in + let image = info[UIImagePickerController.InfoKey.editedImage] as? UIImage + self?.profileImageView.image = image + self?.profileImageData.accept(image?.jpegData(compressionQuality: 0.1) ?? Data()) + } + } +} diff --git a/Projects/Presentation/Sources/Scene/AllTab/MyPage/MyPageViewModel.swift b/Projects/Presentation/Sources/Scene/AllTab/MyPage/MyPageViewModel.swift index 40332c6..d6f1fba 100644 --- a/Projects/Presentation/Sources/Scene/AllTab/MyPage/MyPageViewModel.swift +++ b/Projects/Presentation/Sources/Scene/AllTab/MyPage/MyPageViewModel.swift @@ -11,14 +11,20 @@ public class MyPageViewModel: BaseViewModel, Stepper { private let disposeBag = DisposeBag() public var steps = PublishRelay() - private let profileUsecase: FetchDetailProfileUseCase - - public init(profileUsecase: FetchDetailProfileUseCase) { - self.profileUsecase = profileUsecase + private let fetchProfileUsecase: FetchDetailProfileUseCase + private let uploadProfileImageUseCase: UploadProfileImageUseCase + + public init( + profileUsecase: FetchDetailProfileUseCase, + uploadProfileImageUseCase: UploadProfileImageUseCase + ) { + self.fetchProfileUsecase = profileUsecase + self.uploadProfileImageUseCase = uploadProfileImageUseCase } public struct Input { let viewWillAppear: Observable + let profileImage: Observable } public struct Output { let profileData: Signal @@ -29,7 +35,7 @@ public class MyPageViewModel: BaseViewModel, Stepper { public func transform(input: Input) -> Output { input.viewWillAppear .flatMap { - self.profileUsecase.execute() + self.fetchProfileUsecase.execute() .catch { print($0.localizedDescription) return .never() @@ -38,6 +44,17 @@ public class MyPageViewModel: BaseViewModel, Stepper { .bind(to: profileData) .disposed(by: disposeBag) + input.profileImage + .flatMap { image in + self.uploadProfileImageUseCase.execute(image: image) + .catch { + print($0.localizedDescription) + return .never() + } + } + .subscribe() + .disposed(by: disposeBag) + return Output(profileData: profileData.asSignal()) } diff --git a/Projects/Presentation/Sources/Scene/Home/HomeViewController.swift b/Projects/Presentation/Sources/Scene/Home/HomeViewController.swift index 89a173f..4e72f4a 100644 --- a/Projects/Presentation/Sources/Scene/Home/HomeViewController.swift +++ b/Projects/Presentation/Sources/Scene/Home/HomeViewController.swift @@ -136,6 +136,17 @@ public class HomeViewController: BaseViewController { owner.homeViewType = data }.disposed(by: disposeBag) + output.profileData.asObservable() + .withUnretained(self) + .bind { owner, data in + let userInfoData = UserDefaultStorage.shared.get(forKey: .userInfoData) as? String + + owner.profileView.setup( + image: data.profile ?? "", + info: userInfoData ?? "정보가 없는 사용자" + ) + }.disposed(by: disposeBag) + output.applyStatusData.asObservable() .withUnretained(self) .bind { owner, data in @@ -296,11 +307,6 @@ public class HomeViewController: BaseViewController { } } - public override func setLayoutData() { - let userInfoData = UserDefaultStorage.shared.get(forKey: .userInfoData) as? String - self.profileView.setup(image: .profile, info: userInfoData ?? "정보가 없는 사용자") - } - private func setupViewType(type: HomeViewType) { switch type { case .timeTable: diff --git a/Projects/Presentation/Sources/Scene/Home/HomeViewModel.swift b/Projects/Presentation/Sources/Scene/Home/HomeViewModel.swift index d5cceaf..c37eb68 100644 --- a/Projects/Presentation/Sources/Scene/Home/HomeViewModel.swift +++ b/Projects/Presentation/Sources/Scene/Home/HomeViewModel.swift @@ -58,6 +58,7 @@ public class HomeViewModel: BaseViewModel, Stepper { } public struct Output { let viewMode: Signal + let profileData: Signal let applyStatusData: Signal let weekendMealPeriodData: Signal let timetableData: Driver<[TimeTableEntityElement]> @@ -71,6 +72,7 @@ public class HomeViewModel: BaseViewModel, Stepper { } private let viewModeData = PublishRelay() + private let profileData = PublishRelay() private let applyStatusData = PublishRelay() private let weekendMealPeriodData = PublishRelay() private let timetableData = BehaviorRelay<[TimeTableEntityElement]>(value: []) @@ -93,6 +95,17 @@ public class HomeViewModel: BaseViewModel, Stepper { } }).disposed(by: disposeBag) + input.viewWillAppear.flatMap { + self.fetchProfileUseCase.execute() + .catch { + print($0.localizedDescription) + return .never() + } + } + .bind(to: profileData) + .disposed(by: disposeBag) + + input.viewWillAppear .flatMap { self.fetchWeekendMealPeriodUseCase.execute() @@ -113,8 +126,9 @@ public class HomeViewModel: BaseViewModel, Stepper { } } .subscribe(onNext: { data in - let value = "\(data.grade)학년 \(data.classNum)반 \(data.num)번 \(data.name)" - self.userDefaultStorage.set(to: value, forKey: .userInfoData) + let infoValue = "\(data.grade)학년 \(data.classNum)반 \(data.num)번 \(data.name)" + + self.userDefaultStorage.set(to: infoValue, forKey: .userInfoData) self.userDefaultStorage.set(to: data.name, forKey: .userNameData) }).disposed(by: disposeBag) @@ -237,6 +251,7 @@ public class HomeViewModel: BaseViewModel, Stepper { return Output( viewMode: viewModeData.asSignal(), + profileData: profileData.asSignal(), applyStatusData: applyStatusData.asSignal(), weekendMealPeriodData: weekendMealPeriodData.asSignal(), timetableData: timetableData.asDriver(), diff --git a/Projects/Presentation/Sources/Scene/Schedule/AcademicSchedule/View/AcademicScheduleView.swift b/Projects/Presentation/Sources/Scene/Schedule/AcademicSchedule/View/AcademicScheduleView.swift index c54d842..fbc4705 100644 --- a/Projects/Presentation/Sources/Scene/Schedule/AcademicSchedule/View/AcademicScheduleView.swift +++ b/Projects/Presentation/Sources/Scene/Schedule/AcademicSchedule/View/AcademicScheduleView.swift @@ -39,7 +39,10 @@ public class AcademicScheduleView: BaseView { ).then { $0.changePointColor(targetString: "오늘", color: .main500) } - private let scheduleLabel = PiCKLabel(textColor: .gray800, font: .caption2) + private let scheduleLabel = PiCKLabel( + textColor: .gray800, + font: .caption2 + ) private lazy var labelStackView = UIStackView(arrangedSubviews: [ dateLabel, scheduleLabel @@ -60,7 +63,8 @@ public class AcademicScheduleView: BaseView { $0.showsVerticalScrollIndicator = false $0.register( AcademicScheduleCell.self, - forCellWithReuseIdentifier: AcademicScheduleCell.identifier) + forCellWithReuseIdentifier: AcademicScheduleCell.identifier + ) } private lazy var emptyScheduleLabel = PiCKLabel( text: "일정이 없습니다.", diff --git a/Projects/Presentation/Sources/Scene/Schedule/Component/AcademicScheduleCalneder.swift b/Projects/Presentation/Sources/Scene/Schedule/Component/AcademicScheduleCalneder.swift index 7f902d1..ff93859 100644 --- a/Projects/Presentation/Sources/Scene/Schedule/Component/AcademicScheduleCalneder.swift +++ b/Projects/Presentation/Sources/Scene/Schedule/Component/AcademicScheduleCalneder.swift @@ -24,8 +24,14 @@ public class AcademicScheduleCalneder: BaseView, FSCalendarDelegate, FSCalendarD textColor: .modeBlack, font: .label1 ) - private let previousButton = PiCKImageButton(image: .leftArrow, imageColor: .modeBlack) - private let nextButton = PiCKImageButton(image: .rightArrow, imageColor: .modeBlack) + private let previousButton = PiCKImageButton( + image: .leftArrow, + imageColor: .modeBlack + ) + private let nextButton = PiCKImageButton( + image: .rightArrow, + imageColor: .modeBlack + ) private lazy var headerStackView = UIStackView(arrangedSubviews: [ previousButton, calendarHeaderLabel, @@ -74,6 +80,9 @@ public class AcademicScheduleCalneder: BaseView, FSCalendarDelegate, FSCalendarD self.clickYearAndMonth = clickYearAndMonth self.clickDate = clickDate super.init(frame: .zero) + + let todayDate = Date() + clickDate(todayDate) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") diff --git a/Projects/Presentation/Sources/Scene/Schedule/ScheduleViewController.swift b/Projects/Presentation/Sources/Scene/Schedule/ScheduleViewController.swift index a93b8b8..1b8c8cf 100644 --- a/Projects/Presentation/Sources/Scene/Schedule/ScheduleViewController.swift +++ b/Projects/Presentation/Sources/Scene/Schedule/ScheduleViewController.swift @@ -31,7 +31,9 @@ public class ScheduleViewController: BaseViewController { private lazy var navigationBar = PiCKMainNavigationBar(view: self) - private let segmentedControl = ScheduleSegmentedControl(items: ["시간표", "학사일정"]) + private let segmentedControl = ScheduleSegmentedControl( + items: ["시간표", "학사일정"] + ) private lazy var timeTableView = TimeTableView(frame: viewSize) private lazy var academicScheduleView = AcademicScheduleView( frame: viewSize, @@ -60,25 +62,28 @@ public class ScheduleViewController: BaseViewController { let output = viewModel.transform(input: input) output.timeTableData.asObservable() - .subscribe(onNext: { [weak self] data in - self?.timeTableView.timeTableSetup( + .withUnretained(self) + .bind { owner, data in + owner.timeTableView.timeTableSetup( timeTableData: data ) - }).disposed(by: disposeBag) + }.disposed(by: disposeBag) output.monthAcademicScheduleData.asObservable() - .subscribe(onNext: { [weak self] data in - self?.academicScheduleView.monthAcademicScheduleSetup( + .withUnretained(self) + .bind { owner, data in + owner.academicScheduleView.monthAcademicScheduleSetup( monthAcademicSchedule: data ) - }).disposed(by: disposeBag) + }.disposed(by: disposeBag) output.academicScheduleData.asObservable() - .subscribe(onNext: { [weak self] data in - self?.academicScheduleView.academicScheduleSetup( + .withUnretained(self) + .bind { owner, data in + owner.academicScheduleView.academicScheduleSetup( academicSchedule: data - ) - }).disposed(by: disposeBag) + ) + }.disposed(by: disposeBag) segmentedControl.rx.selectedSegmentIndex .map { $0 != 0 } @@ -86,13 +91,11 @@ public class ScheduleViewController: BaseViewController { .disposed(by: disposeBag) shouldHideFirstView - .subscribe( - onNext: { [weak self] shouldHide in - self?.timeTableView.isHidden = shouldHide - self?.academicScheduleView.isHidden = !shouldHide - } - ) - .disposed(by: disposeBag) + .withUnretained(self) + .bind { owner, shouldHide in + owner.timeTableView.isHidden = shouldHide + owner.academicScheduleView.isHidden = !shouldHide + }.disposed(by: disposeBag) } public override func addView() { [ diff --git a/Tuist/ProjectDescriptionHelpers/Project+Module.swift b/Tuist/ProjectDescriptionHelpers/Project+Module.swift index 67df83a..5f960bf 100644 --- a/Tuist/ProjectDescriptionHelpers/Project+Module.swift +++ b/Tuist/ProjectDescriptionHelpers/Project+Module.swift @@ -29,13 +29,15 @@ extension Project { ["OTHER_LDFLAGS": .string("$(inherited) -all_load")] : ["OTHER_LDFLAGS": .string("$(inherited)")] - var configurations = configurations - if configurations.isEmpty { - configurations = [ - .debug(name: .stage, xcconfig: .relativeToXCConfig(type: .stage, name: name)), - .release(name: .prod, xcconfig: .relativeToXCConfig(type: .prod, name: name)) - ] - } + let configurations: [Configuration] = isCI ? + [ + .debug(name: .stage), + .release(name: .prod) + ] : + [ + .debug(name: .stage, xcconfig: .relativeToXCConfig(type: .stage, name: name)), + .release(name: .prod, xcconfig: .relativeToXCConfig(type: .prod, name: name)) + ] let settings: Settings = .settings( base: env.baseSetting