diff --git a/Runnect-iOS/Runnect-iOS/Global/UIComponents/CustomAlertVC.swift b/Runnect-iOS/Runnect-iOS/Global/UIComponents/CustomAlertVC.swift index 4fd70b1d..2084fced 100644 --- a/Runnect-iOS/Runnect-iOS/Global/UIComponents/CustomAlertVC.swift +++ b/Runnect-iOS/Runnect-iOS/Global/UIComponents/CustomAlertVC.swift @@ -37,7 +37,7 @@ final class CustomAlertVC: UIViewController { } private let contentsLabel: UILabel = UILabel().then { $0.text = "코스를 만들었어요!\n지정한 코스는 보관함에서 볼 수 있어요." - $0.font = .h5 + $0.font = .b4 $0.textColor = .g2 $0.textAlignment = .center $0.numberOfLines = 2 @@ -55,7 +55,6 @@ final class CustomAlertVC: UIViewController { } // MARK: - View Life Cycle - override func viewDidLoad() { super.viewDidLoad() self.setUI() @@ -119,6 +118,7 @@ extension CustomAlertVC { private func setLayout() { view.addSubviews(alertView) + alertView.addSubviews(alertImageView, contentsLabel, buttonStackView) alertView.snp.makeConstraints { make in @@ -138,7 +138,6 @@ extension CustomAlertVC { make.leading.trailing.equalToSuperview().inset(10) make.centerX.equalToSuperview() } - buttonStackView.snp.makeConstraints { make in make.top.equalTo(contentsLabel.snp.bottom).offset(26) make.leading.trailing.equalToSuperview().inset(14) diff --git a/Runnect-iOS/Runnect-iOS/Global/UIComponents/RNAlertVC.swift b/Runnect-iOS/Runnect-iOS/Global/UIComponents/RNAlertVC.swift index ea377387..f159ed5c 100644 --- a/Runnect-iOS/Runnect-iOS/Global/UIComponents/RNAlertVC.swift +++ b/Runnect-iOS/Runnect-iOS/Global/UIComponents/RNAlertVC.swift @@ -10,11 +10,17 @@ import UIKit import SnapKit import Then +enum AlertType { + case defaultType + case custom +} + final class RNAlertVC: UIViewController { // MARK: - Properties - + var leftButtonTapAction: (()-> Void)? var rightButtonTapAction: (() -> Void)? + var alertType: AlertType = .defaultType // MARK: - UI Components @@ -69,8 +75,10 @@ final class RNAlertVC: UIViewController { extension RNAlertVC { override func touchesBegan(_ touches: Set, with event: UIEvent?) { super.touchesBegan(touches, with: event) - if let touch = touches.first, touch.view == self.view { - dismiss(animated: false) + if alertType == .defaultType { + if let touch = touches.first, touch.view == self.view { + dismiss(animated: false) + } } } @@ -91,7 +99,8 @@ extension RNAlertVC { extension RNAlertVC { @objc private func touchUpNoButton() { - dismiss(animated: false) + alertType == .defaultType + ? dismiss(animated: false) : self.leftButtonTapAction? () } @objc private func touchYesButton() { diff --git a/Runnect-iOS/Runnect-iOS/Global/Utils/RNUtils/convertLocationObject.swift b/Runnect-iOS/Runnect-iOS/Global/Utils/RNUtils/convertLocationObject.swift index e06f2fe6..9d3be648 100644 --- a/Runnect-iOS/Runnect-iOS/Global/Utils/RNUtils/convertLocationObject.swift +++ b/Runnect-iOS/Runnect-iOS/Global/Utils/RNUtils/convertLocationObject.swift @@ -28,7 +28,7 @@ extension NMGLatLng { } func toRNLocationModel() -> RNLocationModel { - return RNLocationModel(lat: self.lat, long: self.lng) + return RNLocationModel(latitude: self.lat, longitude: self.lng) } } diff --git a/Runnect-iOS/Runnect-iOS/Network/Dto/CourseDrawingDto/RequestDto/CourseDrawingRequestDto.swift b/Runnect-iOS/Runnect-iOS/Network/Dto/CourseDrawingDto/RequestDto/CourseDrawingRequestDto.swift index c339f79c..e869f93a 100644 --- a/Runnect-iOS/Runnect-iOS/Network/Dto/CourseDrawingDto/RequestDto/CourseDrawingRequestDto.swift +++ b/Runnect-iOS/Runnect-iOS/Network/Dto/CourseDrawingDto/RequestDto/CourseDrawingRequestDto.swift @@ -11,9 +11,19 @@ import Foundation struct CourseDrawingRequestDto: Codable { let image: Data - let data: CourseDrawingRequestData + let path: [RNLocationModel] + let title: String + let distance: Float + let departureAddress, departureName: String } +//"image" : 이미지 파일, +// "path" : [{lat : 실수(double), long : 실수},{lat : 실수(double), long : 실수},{lat : 실수(double), long : 실수} ], +// "title" : "한강 공원 한 바퀴", +// "distance" : 4.4, +// "departureAddress" : "전북 익산시 삼성동 100", +// "departureName" : "보리의 집" + // MARK: - CourseDrawingRequestData struct CourseDrawingRequestData: Codable { diff --git a/Runnect-iOS/Runnect-iOS/Network/Dto/CourseDrawingDto/ResponseDto/CourseDrawingResponseData.swift b/Runnect-iOS/Runnect-iOS/Network/Dto/CourseDrawingDto/ResponseDto/CourseDrawingResponseData.swift index 66ec9ee9..a521079c 100644 --- a/Runnect-iOS/Runnect-iOS/Network/Dto/CourseDrawingDto/ResponseDto/CourseDrawingResponseData.swift +++ b/Runnect-iOS/Runnect-iOS/Network/Dto/CourseDrawingDto/ResponseDto/CourseDrawingResponseData.swift @@ -10,12 +10,6 @@ import Foundation // MARK: - DataClass struct CourseDrawingResponseData: Codable { - let course: CourseDrawingResponse -} - -// MARK: - Course - -struct CourseDrawingResponse: Codable { let id: Int let createdAt: String } diff --git a/Runnect-iOS/Runnect-iOS/Network/Model/CourseDrawingModel/RNLocationModel.swift b/Runnect-iOS/Runnect-iOS/Network/Model/CourseDrawingModel/RNLocationModel.swift index 736e2712..449ef137 100644 --- a/Runnect-iOS/Runnect-iOS/Network/Model/CourseDrawingModel/RNLocationModel.swift +++ b/Runnect-iOS/Runnect-iOS/Network/Model/CourseDrawingModel/RNLocationModel.swift @@ -8,6 +8,6 @@ import Foundation struct RNLocationModel: Codable { - let lat: Double - let long: Double + let latitude: Double + let longitude: Double } diff --git a/Runnect-iOS/Runnect-iOS/Network/Router/CourseRouter.swift b/Runnect-iOS/Runnect-iOS/Network/Router/CourseRouter.swift index 3153a5b7..8080844e 100644 --- a/Runnect-iOS/Runnect-iOS/Network/Router/CourseRouter.swift +++ b/Runnect-iOS/Runnect-iOS/Network/Router/CourseRouter.swift @@ -29,7 +29,7 @@ extension CourseRouter: TargetType { var path: String { switch self { case .uploadCourseDrawing: - return "/course" + return "/course/v2" case .getAllPrivateCourse: return "/course/user" case .getPrivateCourseNotUploaded: @@ -66,20 +66,22 @@ extension CourseRouter: TargetType { var content = [String: Any]() var path = [[String: Any]]() + var tempPath = String() do { - for location in param.data.path { + for location in param.path { let locationData = try location.asParameter() path.append(locationData) } - + content["path"] = path - content["distance"] = param.data.distance - content["departureAddress"] = param.data.departureAddress - content["departureName"] = param.data.departureName + content["title"] = param.title + content["distance"] = param.distance + content["departureAddress"] = param.departureAddress + content["departureName"] = param.departureName let jsonData = try JSONSerialization.data(withJSONObject: content) - let formData = MultipartFormData(provider: .data(jsonData), name: "data", mimeType: "application/json") + let formData = MultipartFormData(provider: .data(jsonData), name: "courseCreateRequestDto", mimeType: "application/json") multipartFormData.append(formData) } catch { print(error.localizedDescription) diff --git a/Runnect-iOS/Runnect-iOS/Network/Service/NetworkProvider.swift b/Runnect-iOS/Runnect-iOS/Network/Service/NetworkProvider.swift index 86beae44..77c7a813 100644 --- a/Runnect-iOS/Runnect-iOS/Network/Service/NetworkProvider.swift +++ b/Runnect-iOS/Runnect-iOS/Network/Service/NetworkProvider.swift @@ -7,19 +7,15 @@ import Moya -enum ResponseResult { - case success(T) -} - class NetworkProvider : MoyaProvider { - func request(target : Provider, instance : Model.Type , vc: UIViewController, completion : @escaping(ResponseResult) -> ()){ + func request(target : Provider, instance : Model.Type , vc: UIViewController, completion : @escaping(Model) -> ()){ self.request(target) { result in switch result { /// 서버 통신 성공 case .success(let response): if (200..<300).contains(response.statusCode) { if let decodeData = try? JSONDecoder().decode(instance, from: response.data) { - completion(.success(decodeData)) + completion(decodeData) } else{ /// decoding error vc.showNetworkFailureToast() diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDrawing/VC/CourseDrawingVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDrawing/VC/CourseDrawingVC.swift index c5c413b1..f27edc7d 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDrawing/VC/CourseDrawingVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDrawing/VC/CourseDrawingVC.swift @@ -13,11 +13,8 @@ import Moya final class CourseDrawingVC: UIViewController { // MARK: - Properties - - private let courseProvider = Providers.courseProvider - private let departureSearchingProvider = Providers.departureSearchingProvider - - private let networkProvider = NetworkProvider(withAuth: false) + private let departureSearchingProvider = NetworkProvider(withAuth: false) + private let courseProvider = NetworkProvider(withAuth: true) private var departureLocationModel: DepartureLocationModel? @@ -196,8 +193,8 @@ extension CourseDrawingVC { mapView.eventSubject.sink { [weak self] arr in guard let self = self else { return } -// self.searchLocationTmapAddress(latitude: arr[0], longitude: arr[1]) - self.searchTest(latitude: arr[0], longitude: arr[1]) + self.searchLocationTmapAddress(latitude: arr[0], longitude: arr[1]) +// self.searchTest(latitude: arr[0], longitude: arr[1]) }.store(in: cancelBag) } @@ -434,13 +431,14 @@ extension CourseDrawingVC { guard let imageData = image.jpegData(compressionQuality: 1.0) else { return nil } guard let departureLocationModel = self.departureLocationModel else { return nil } let path = mapView.getMarkersLatLng().map { $0.toRNLocationModel() } - let courseDrawingRequestData = CourseDrawingRequestData(path: path, - // title : self.courseName, - distance: self.distance, - departureAddress: departureLocationModel.departureAddress, - departureName: departureLocationModel.departureName) - let courseDrawingRequestDto = CourseDrawingRequestDto(image: imageData, data: courseDrawingRequestData) + let courseDrawingRequestDto = CourseDrawingRequestDto( + image: imageData, + path: path, + title: self.courseName, + distance: self.distance, + departureAddress: departureLocationModel.departureAddress, + departureName: departureLocationModel.departureName) return courseDrawingRequestDto } @@ -449,63 +447,16 @@ extension CourseDrawingVC { guard let requestDto = makecourseDrawingRequestDto() else { return } LoadingIndicator.showLoading() - courseProvider.request(.uploadCourseDrawing(param: requestDto)) {[weak self] response in - guard let self = self else { return } + courseProvider.request(target: .uploadCourseDrawing(param: requestDto), instance: BaseResponse.self, vc: self) { response in LoadingIndicator.hideLoading() - switch response { - case .success(let result): - let status = result.statusCode - if 200..<300 ~= status { - do { - let responseDto = try result.map(BaseResponse.self) - guard let data = responseDto.data else { return } - self.presentAlertVC(courseId: data.course.id) - } catch { - print(error.localizedDescription) - } - } - if status >= 400 { - print("400 error") - self.showNetworkFailureToast() - } - case .failure(let error): - print(error.localizedDescription) - self.showNetworkFailureToast() - } + guard let data = response.data else { return } + self.presentAlertVC(courseId: data.id) } } private func searchLocationTmapAddress(latitude: Double, longitude: Double) { - departureSearchingProvider - .request(.getLocationTmapAddress(latitude: latitude, longitude: longitude)) { [weak self] response in - guard let self = self else { return } - switch response { - case .success(let result): - let status = result.statusCode - if 200..<300 ~= status { - do { - let responseDto = try result.map(TmapAddressSearchingResponseDto.self) - self.updateData(model: responseDto.toDepartureLocationModel(latitude: latitude, longitude: longitude)) - } catch { - print(error.localizedDescription) - } - } - if status >= 400 { - print("400 error") - } - case .failure(let error): - print(error.localizedDescription) - self.showToast(message: "네트워크 통신 실패") - } - } - } - - private func searchTest(latitude: Double, longitude: Double) { - networkProvider.request(target: .getLocationTmapAddress(latitude: latitude, longitude: longitude), instance: TmapAddressSearchingResponseDto.self, vc: self) { result in - switch result { - case .success(let data): + departureSearchingProvider.request(target: .getLocationTmapAddress(latitude: latitude, longitude: longitude), instance: TmapAddressSearchingResponseDto.self, vc: self) { data in self.updateData(model: data.toDepartureLocationModel(latitude: latitude, longitude: longitude)) - } } } } diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDrawing/VC/DepartureSearchVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDrawing/VC/DepartureSearchVC.swift index 5313442a..93afa451 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDrawing/VC/DepartureSearchVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDrawing/VC/DepartureSearchVC.swift @@ -25,7 +25,6 @@ final class DepartureSearchVC: UIViewController, CLLocationManagerDelegate { private lazy var naviBar = CustomNavigationBar(self, type: .search) .setTextFieldPlaceholder(placeholder: "출발지를 설정해주세요") - .showKeyboard() private let dividerView = UIView().then { $0.backgroundColor = .g5 @@ -79,6 +78,7 @@ final class DepartureSearchVC: UIViewController, CLLocationManagerDelegate { self.setDelegate() self.registerCell() self.setBinding() + self.setAuthorization() } } @@ -89,6 +89,7 @@ extension DepartureSearchVC { self.naviBar.delegate = self self.locationTableView.delegate = self self.locationTableView.dataSource = self + self.locationManager.delegate = self } private func registerCell() { @@ -103,12 +104,14 @@ extension DepartureSearchVC { } private func setBinding() { - selectDirectionView.gesture().sink { _ in + selectDirectionView.gesture().sink { [weak self] _ in + guard let self = self else { return } SelectedInfo.shared.type = .other self.setLocation() }.store(in: cancelBag) - selectMapView.gesture().sink { _ in + selectMapView.gesture().sink { [weak self] _ in + guard let self = self else { return } SelectedInfo.shared.type = .map self.setLocation() }.store(in: cancelBag) @@ -120,7 +123,6 @@ extension DepartureSearchVC { extension DepartureSearchVC { /// 현재 위도, 경도에 따른 주소 받아오는 함수 private func setLocation() { - locationManager.delegate = self locationManager.desiredAccuracy = kCLLocationAccuracyBest DispatchQueue.global().async { [self] in @@ -136,6 +138,69 @@ extension DepartureSearchVC { } } } + + private func setAuthorization() { + let authorizationStatus: CLAuthorizationStatus + if #available(iOS 14.0, *) { + authorizationStatus = locationManager.authorizationStatus + }else { + authorizationStatus = CLLocationManager.authorizationStatus() + } + + DispatchQueue.global().async { [weak self] in + guard let self = self else { return } + + guard CLLocationManager.locationServicesEnabled() else { + /// 전체 위치 서비스가 꺼진 경우 + DispatchQueue.main.async { + self.presentServiceAlertVC() + } + return + } + + switch authorizationStatus { + case .notDetermined, .restricted, .denied: + /// 러넥트 위치 서비스가 꺼진 경우 + DispatchQueue.main.async { + self.presentAlertVC() + } + default: + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.55) { + self.naviBar.showKeyboard() + } + } + } + } + + private func presentServiceAlertVC() { + let requestLocationServiceAlert = UIAlertController(title: "위치 정보 이용", message: "'위치 서비스'를 사용할 수 없습니다.\n디바이스의 '설정 > 개인정보 보호'에서\n위치 서비스를 먼저 켜주세요.", preferredStyle: .alert) + let cancel = UIAlertAction(title: "확인", style: .default) { [weak self] _ in + self?.navigationController?.popViewController(animated: true) + } + requestLocationServiceAlert.addAction(cancel) + + present(requestLocationServiceAlert, animated: true) + } + + private func presentAlertVC() { + let alertVC = RNAlertVC(description: "위치 접근을 허용해야 사용할 수 있어요.\n[설정] - [애플리케이션] - [위치접근]을\n허용해 주세요.") + alertVC.modalPresentationStyle = .overFullScreen + alertVC.alertType = .custom + + alertVC.leftButtonTapAction = { + alertVC.dismiss(animated: false) + self.navigationController?.popViewController(animated: true) + } + alertVC.rightButtonTapAction = { + if let url = URL(string: UIApplication.openSettingsURLString) { + UIApplication.shared.open(url) + alertVC.dismiss(animated: false) + } + self.navigationController?.popViewController(animated: true) + } + + self.present(alertVC, animated: false) + } } // MARK: - UI & Layout