diff --git a/Runnect-iOS/.swiftlint.yml b/Runnect-iOS/.swiftlint.yml index 05d158e0..bbe4a629 100644 --- a/Runnect-iOS/.swiftlint.yml +++ b/Runnect-iOS/.swiftlint.yml @@ -6,6 +6,7 @@ disabled_rules: - legacy_constructor - unused_setter_value - file_length + - void_function_in_ternary included: - Runnect-iOS diff --git a/Runnect-iOS/Runnect-iOS.xcodeproj/xcshareddata/xcschemes/Runnect-iOS.xcscheme b/Runnect-iOS/Runnect-iOS.xcodeproj/xcshareddata/xcschemes/Runnect-iOS.xcscheme index 1ee1ed34..03f5e3ec 100644 --- a/Runnect-iOS/Runnect-iOS.xcodeproj/xcshareddata/xcschemes/Runnect-iOS.xcscheme +++ b/Runnect-iOS/Runnect-iOS.xcodeproj/xcshareddata/xcschemes/Runnect-iOS.xcscheme @@ -54,7 +54,7 @@ + isEnabled = "NO"> diff --git a/Runnect-iOS/Runnect-iOS/Global/Analytics/GAEvent.swift b/Runnect-iOS/Runnect-iOS/Global/Analytics/GAEvent.swift index e18bfe74..b97ef3e3 100644 --- a/Runnect-iOS/Runnect-iOS/Global/Analytics/GAEvent.swift +++ b/Runnect-iOS/Runnect-iOS/Global/Analytics/GAEvent.swift @@ -9,8 +9,20 @@ import Foundation struct GAEvent { struct View { + + // 진입 화면 static let viewHome = "view_home" // 앱 실행 static let viewSocialLogin = "view_social_login" + + // 코스발견 + static let viewCourseSearch = "view_course_search" + static let viewCourseDetail = "view_course_detail" + static let viewUserProfile = "view_user_profile" + static let viewCourseUpload = "view_course_upload" + + // 마이페이지 + static let viewSuccessLogout = "view_success_logout" + static let viewSuccessWithdraw = "view_success_withdraw" } struct Button { @@ -43,15 +55,13 @@ struct GAEvent { static let clickUploadButton = "click_upload_button" static let clickTrySearchCourse = "click_try_search_course" static let clickTryBanner = "click_try_banner" - static let clickSearchCourse = "click_search_course" - static let clickCourseDetail = "click_course_detail" static let clickShare = "click_share" static let clickUserProfile = "click_user_profile" static let clickScrapPageStartCourse = "click_scrap_page_start_course" - static let clickUploadCourse = "click_upload_course" + static let clickCourseUpload = "click_course_upload" /// 보관함 - static let clickMyStorageCourseStart = "click_my_storage_course_start" + static let clickMyStorageCourseDrawingStart = "click_my_storage_course_drawing_start" static let clickMyStorageTryModify = "click_my_storage_try_modify" static let clickMyStorageTryRemove = "click_my_storage_try_remove" static let clickScrapCourse = "click_scrap_course" @@ -64,8 +74,6 @@ struct GAEvent { static let clickCourseUploadInUploadedCourse = "click_course_upload_in_uploaded_course" static let clickTryLogout = "click_try_logout" static let clickTryWithdraw = "click_try_withdraw" - static let clickSuccessLogout = "click_success_logout" - static let clickSuccessWithdraw = "click_success_withdraw" /// 방문자 모드 static let clickJoinInCourseDrawing = "click_join_in_course_drawing" diff --git a/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UIGestureRecognizer+.swift b/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UIGestureRecognizer+.swift index 027a8bda..a9ae72da 100644 --- a/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UIGestureRecognizer+.swift +++ b/Runnect-iOS/Runnect-iOS/Global/Extension/UIKit+/UIGestureRecognizer+.swift @@ -15,6 +15,7 @@ enum GestureType { case pan(UIPanGestureRecognizer = .init()) case pinch(UIPinchGestureRecognizer = .init()) case edge(UIScreenEdgePanGestureRecognizer = .init()) + func get() -> UIGestureRecognizer { switch self { case let .tap(tapGesture): @@ -33,51 +34,55 @@ enum GestureType { } } -extension UIGestureRecognizer { - struct GesturePublisher: Publisher { - typealias Output = GestureType - typealias Failure = Never - private let view: UIView - private let gestureType: GestureType - init(view: UIView, gestureType: GestureType) { - self.view = view - self.gestureType = gestureType - } - - func receive(subscriber: S) where S: Subscriber, - GesturePublisher.Failure == S.Failure, - GesturePublisher.Output == S.Input { - let subscription = GestureSubscription( - subscriber: subscriber, - view: view, - gestureType: gestureType - ) - subscriber.receive(subscription: subscription) - } +struct GesturePublisher: Publisher { + typealias Output = GestureType + typealias Failure = Never + + private let view: UIView + private let gestureType: GestureType + + init(view: UIView, gestureType: GestureType) { + self.view = view + self.gestureType = gestureType } - class GestureSubscription: Subscription where S.Input == GestureType, S.Failure == Never { - private var subscriber: S? - private var gestureType: GestureType - private var view: UIView - init(subscriber: S, view: UIView, gestureType: GestureType) { - self.subscriber = subscriber - self.view = view - self.gestureType = gestureType - configureGesture(gestureType) - } - private func configureGesture(_ gestureType: GestureType) { - let gesture = gestureType.get() - gesture.addTarget(self, action: #selector(handler)) - view.addGestureRecognizer(gesture) - } - func request(_ demand: Subscribers.Demand) { } - func cancel() { - subscriber = nil - } - @objc - private func handler() { - _ = subscriber?.receive(gestureType) - } + func receive(subscriber: S) where S: Subscriber, + Failure == S.Failure, + Output == S.Input { + let subscription = GestureSubscription( + subscriber: subscriber, + view: view, + gestureType: gestureType + ) + subscriber.receive(subscription: subscription) + } +} + +class GestureSubscription: Subscription where S.Input == GestureType, S.Failure == Never { + private var subscriber: S? + private var gestureType: GestureType + private var view: UIView + + init(subscriber: S, view: UIView, gestureType: GestureType) { + self.subscriber = subscriber + self.view = view + self.gestureType = gestureType + configureGesture(gestureType) + } + + func request(_ demand: Subscribers.Demand) { } + + func cancel() { + subscriber = nil + } + + private func configureGesture(_ gestureType: GestureType) { + let gesture = gestureType.get() + gesture.addTarget(self, action: #selector(handler)) + view.addGestureRecognizer(gesture) + } + + @objc private func handler() { + _ = subscriber?.receive(gestureType) } } diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_banner1.imageset/Contents.json b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_banner1.imageset/Contents.json index bf0d6034..334c5c52 100644 --- a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_banner1.imageset/Contents.json +++ b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_banner1.imageset/Contents.json @@ -1,17 +1,17 @@ { "images" : [ { - "filename" : "ios 앱 배너.png", + "filename" : "ios배너x1.png", "idiom" : "universal", "scale" : "1x" }, { - "filename" : "ios 앱 배너@2x.png", + "filename" : "ios배너x2.png", "idiom" : "universal", "scale" : "2x" }, { - "filename" : "ios 앱 배너@3x.png", + "filename" : "ios배너x3.png", "idiom" : "universal", "scale" : "3x" } diff --git "a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_banner1.imageset/ios \354\225\261 \353\260\260\353\204\210.png" "b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_banner1.imageset/ios\353\260\260\353\204\210x1.png" similarity index 100% rename from "Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_banner1.imageset/ios \354\225\261 \353\260\260\353\204\210.png" rename to "Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_banner1.imageset/ios\353\260\260\353\204\210x1.png" diff --git "a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_banner1.imageset/ios \354\225\261 \353\260\260\353\204\210@2x.png" "b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_banner1.imageset/ios\353\260\260\353\204\210x2.png" similarity index 100% rename from "Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_banner1.imageset/ios \354\225\261 \353\260\260\353\204\210@2x.png" rename to "Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_banner1.imageset/ios\353\260\260\353\204\210x2.png" diff --git "a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_banner1.imageset/ios \354\225\261 \353\260\260\353\204\210@3x.png" "b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_banner1.imageset/ios\353\260\260\353\204\210x3.png" similarity index 100% rename from "Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_banner1.imageset/ios \354\225\261 \353\260\260\353\204\210@3x.png" rename to "Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_banner1.imageset/ios\353\260\260\353\204\210x3.png" diff --git a/Runnect-iOS/Runnect-iOS/Global/UIComponents/MapView/RNMapView.swift b/Runnect-iOS/Runnect-iOS/Global/UIComponents/MapView/RNMapView.swift index 1f72ab51..f9862441 100644 --- a/Runnect-iOS/Runnect-iOS/Global/UIComponents/MapView/RNMapView.swift +++ b/Runnect-iOS/Runnect-iOS/Global/UIComponents/MapView/RNMapView.swift @@ -19,7 +19,7 @@ final class RNMapView: UIView { @Published var pathDistance: Double = 0 @Published var markerCount = 0 - var eventSubject = PassthroughSubject, Never>() + var eventSubject = PassthroughSubject<[Double], Never>() private let screenWidth = UIScreen.main.bounds.width private let screenHeight = UIScreen.main.bounds.height diff --git a/Runnect-iOS/Runnect-iOS/Network/Service/NetworkProvider.swift b/Runnect-iOS/Runnect-iOS/Network/Service/NetworkProvider.swift index d98f8cba..b3718175 100644 --- a/Runnect-iOS/Runnect-iOS/Network/Service/NetworkProvider.swift +++ b/Runnect-iOS/Runnect-iOS/Network/Service/NetworkProvider.swift @@ -7,8 +7,8 @@ import Moya -class NetworkProvider : MoyaProvider { - func request(target: Provider, instance: Model.Type , vc: UIViewController, completion: @escaping(Model) -> ()){ +class NetworkProvider: MoyaProvider { + func request(target: Provider, instance: Model.Type, vc: UIViewController, completion: @escaping(Model) -> Void) { self.request(target) { result in switch result { /// 서버 통신 성공 diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDetail/VC/CourseDetailVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDetail/VC/CourseDetailVC.swift index 0c743b2f..856d7150 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDetail/VC/CourseDetailVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDetail/VC/CourseDetailVC.swift @@ -144,7 +144,7 @@ final class CourseDetailVC: UIViewController { setLayout() setAddTarget() setRefreshControl() - analyze(screenName: "코스 상세 페이지") + analyze(screenName: GAEvent.View.viewCourseDetail) self.hideTabBar(wantsToHide: true) } @@ -177,6 +177,8 @@ extension CourseDetailVC { return } + analyze(buttonName: GAEvent.Button.clickShare) + let publicCourse = model.publicCourse let title = publicCourse.title let courseId = publicCourse.id // primaryKey @@ -231,6 +233,9 @@ extension CourseDetailVC { self.showToast(message: "회원만 조회 가능 합니다.") return } + + analyze(screenName: GAEvent.Button.clickUserProfile) + guard let userId = self.userId else {return} let userProfile = UserProfileVC() userProfile.setUserId(userId: userId) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonMapCollectionViewCell.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonMapCollectionViewCell.swift index e67307e3..d371faa9 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonMapCollectionViewCell.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MarathonMapCollectionViewCell.swift @@ -205,16 +205,20 @@ extension MarathonMapCollectionViewCell { private func scrapCourse(publicCourseId: Int, scrapTF: Bool) { LoadingIndicator.showLoading() + scrapProvider.request(.createAndDeleteScrap(publicCourseId: publicCourseId, scrapTF: scrapTF)) { [weak self] response in - LoadingIndicator.hideLoading() + defer { + LoadingIndicator.hideLoading() + } + guard let self = self else { return } + switch response { case .success(let result): let status = result.statusCode if 200..<300 ~= status { print("스크랩 성공") - } - if status >= 400 { + } else if status >= 400 { print("400 error") } case .failure(let error): diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift index 0d7c9f6f..a048ff5a 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift @@ -165,6 +165,8 @@ extension CourseDiscoveryVC { return } + analyze(buttonName: GAEvent.Button.clickUploadButton) + let nextVC = MyCourseSelectVC() nextVC.delegate = self self.navigationController?.pushViewController(nextVC, animated: true) @@ -552,5 +554,14 @@ extension CourseDiscoveryVC: TitleCollectionViewCellDelegate { sort = ordering self.courseList.removeAll() getCourseData(pageNo: pageNo) + + switch ordering { + case "date": + analyze(buttonName: GAEvent.Button.clickDate) + case "scrap": + analyze(buttonName: GAEvent.Button.clickScrap) + default: + break + } } } diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseSearchVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseSearchVC.swift index ac5879b2..d340141d 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseSearchVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseSearchVC.swift @@ -73,6 +73,7 @@ final class CourseSearchVC: UIViewController { setDelegate() layout() setTabBar() + analyze(screenName: GAEvent.View.viewCourseSearch) } override func viewWillAppear(_ animated: Bool) { @@ -228,6 +229,7 @@ extension CourseSearchVC { do { let responseDto = try result.map(BaseResponse.self) guard let data = responseDto.data else { return } + self.analyze(buttonName: GAEvent.Button.clickTrySearchCourse) self.setData(data: data.publicCourses) } catch { self.setData(data: []) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseUploadVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseUploadVC.swift index 92eba72f..b69a82da 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseUploadVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseUploadVC.swift @@ -66,6 +66,7 @@ class CourseUploadVC: UIViewController { setKeyboardNotification() setTapGesture() addKeyboardObserver() + analyze(screenName: GAEvent.View.viewCourseUpload) } override func viewWillDisappear(_ animated: Bool) { @@ -95,8 +96,8 @@ extension CourseUploadVC { courseModel.departure.town, courseModel.departure.name ] - .compactMap { $0 } - .joined(separator: " ") + .compactMap { $0 } + .joined(separator: " ") self.departureInfoView.setDescriptionText(description: departureString) } @@ -192,6 +193,8 @@ extension CourseUploadVC { @objc func uploadButtonDidTap() { self.uploadCourse() + + analyze(buttonName: GAEvent.Button.clickCourseUpload) } } @@ -264,7 +267,7 @@ extension CourseUploadVC { make.leading.trailing.equalTo(view.safeAreaLayoutGuide).inset(16) make.height.equalTo(35) } - + dividerView.snp.makeConstraints { make in make.top.equalTo(courseTitleTextField.snp.bottom).offset(0) make.leading.trailing.equalTo(view.safeAreaLayoutGuide).inset(16) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDrawing/VC/CourseDrawingHomeVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDrawing/VC/CourseDrawingHomeVC.swift index 6e421753..af6c4282 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDrawing/VC/CourseDrawingHomeVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDrawing/VC/CourseDrawingHomeVC.swift @@ -50,6 +50,9 @@ extension CourseDrawingHomeVC { // MARK: - @objc Function extension CourseDrawingHomeVC { @objc private func pushToDepartureSearchVC() { + + analyze(buttonName: GAEvent.Button.clickCourseDrawing) + let departureSearchVC = DepartureSearchVC() departureSearchVC.hidesBottomBarWhenPushed = true self.navigationController?.pushViewController(departureSearchVC, animated: true) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDrawing/VC/CourseDrawingVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDrawing/VC/CourseDrawingVC.swift index e7234907..e61af314 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDrawing/VC/CourseDrawingVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDrawing/VC/CourseDrawingVC.swift @@ -194,7 +194,7 @@ 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.searchTest(latitude: arr[0], longitude: arr[1]) }.store(in: cancelBag) } @@ -206,15 +206,21 @@ extension CourseDrawingVC { let alertVC = CustomAlertVC() alertVC.modalPresentationStyle = .overFullScreen + // 보관함 가기 alertVC.leftButtonTapped.sink { [weak self] _ in guard let self = self else { return } + analyze(buttonName: GAEvent.Button.clickStoredAfterCourseComplete) + self.tabBarController?.selectedIndex = 1 self.navigationController?.popToRootViewController(animated: true) alertVC.dismiss(animated: true) }.store(in: cancelBag) + // 바로 달리기 alertVC.rightButtonTapped.sink { [weak self] _ in guard let self = self else { return } + analyze(buttonName: GAEvent.Button.clickRunAfterCourseComplete) + let countDownVC = CountDownVC() let runningModel = RunningModel(courseId: courseId, locations: self.mapView.getMarkersLatLng(), @@ -283,6 +289,77 @@ extension CourseDrawingVC { self.departureInfoContainerView.addSubviews(departureLocationLabel, departureDetailLocationLabel, decideDepartureButton) view.bringSubviewToFront(naviBarContainerStackView) + setNotchCoverViewLayout() + setNaviBarLayout() + setMapViewLayout() + setStartMarkStackViewLayout() + setDepartureInfoContainerViewLayout() + setAboutMapNoticeViewLayout() + } + + private func setHiddenViewsLayout() { + view.addSubviews(naviBarForEditing, guideView, distanceContainerView, completeButton, undoButton) + view.sendSubviewToBack(naviBarForEditing) + + naviBarForEditing.snp.makeConstraints { make in + make.leading.top.trailing.equalTo(view.safeAreaLayoutGuide) + make.height.equalTo(48) + } + + guideView.snp.makeConstraints { make in + make.centerY.equalTo(naviBarForEditing.snp.centerY) + make.leading.equalTo(view.safeAreaLayoutGuide).inset(55) + make.trailing.equalTo(view.safeAreaLayoutGuide).inset(27) + } + + distanceContainerView.snp.makeConstraints { make in + make.width.equalTo(96) + make.height.equalTo(44) + make.leading.equalTo(view.safeAreaLayoutGuide).inset(16) + make.top.equalTo(view.snp.bottom) + } + + distanceContainerView.addSubviews(distanceStackView) + + distanceStackView.snp.makeConstraints { make in + make.center.equalToSuperview() + } + + undoButton.snp.makeConstraints { make in + make.trailing.equalTo(view.safeAreaLayoutGuide) + make.top.equalTo(view.snp.bottom) + } + + completeButton.snp.makeConstraints { make in + make.leading.trailing.equalTo(view.safeAreaLayoutGuide).inset(16) + make.height.equalTo(44) + make.top.equalTo(view.snp.bottom).offset(34) + } + } + + private func showHiddenViews(withDuration: TimeInterval = 0) { + [naviBarForEditing, guideView, distanceContainerView, completeButton, undoButton].forEach { subView in + view.bringSubviewToFront(subView) + } + + UIView.animate(withDuration: withDuration) { + let naviBarContainerStackViewHeight = self.naviBarContainerStackView.frame.height + self.naviBarContainerStackView.transform = CGAffineTransform(translationX: 0, y: -naviBarContainerStackViewHeight) + self.departureInfoContainerView.transform = CGAffineTransform(translationX: 0, y: 172) + } + + self.guideView.transform = CGAffineTransform(translationX: 0, y: -100) + + UIView.animate(withDuration: withDuration) { + self.naviBarForEditing.alpha = 1 + self.guideView.transform = .identity + self.distanceContainerView.transform = CGAffineTransform(translationX: 0, y: -151) + self.completeButton.transform = CGAffineTransform(translationX: 0, y: -112) + self.undoButton.transform = CGAffineTransform(translationX: 0, y: -(self.undoButton.frame.height+95)) + } + } + + private func setNotchCoverViewLayout() { notchCoverView.snp.makeConstraints { make in var notchHeight = calculateTopInset() if notchHeight == -44 { @@ -291,7 +368,9 @@ extension CourseDrawingVC { } make.height.equalTo(-notchHeight) } - + } + + private func setNaviBarLayout() { naviBar.snp.makeConstraints { make in make.height.equalTo(56) } @@ -299,15 +378,20 @@ extension CourseDrawingVC { naviBarContainerStackView.snp.makeConstraints { make in make.leading.top.trailing.equalToSuperview() } - + } + + private func setMapViewLayout() { mapView.snp.makeConstraints { make in make.edges.equalToSuperview() } - + } + + private func setStartMarkStackViewLayout() { startLabelUIImage.snp.makeConstraints { make in make.height.equalTo(34) make.width.equalTo(58) } + startMarkUIImage.snp.makeConstraints { make in make.height.width.equalTo(65) } @@ -318,7 +402,9 @@ extension CourseDrawingVC { make.centerX.equalToSuperview() make.centerY.equalToSuperview().offset(-17) } - + } + + private func setDepartureInfoContainerViewLayout() { departureInfoContainerView.snp.makeConstraints { make in make.leading.bottom.trailing.equalToSuperview() make.height.equalTo(172) @@ -339,10 +425,12 @@ extension CourseDrawingVC { make.leading.trailing.equalToSuperview().inset(16) make.height.equalTo(44) } - + } + + private func setAboutMapNoticeViewLayout() { if SelectedInfo.shared.type == .map { - self.aboutMapNoticeView.addSubview(aboutMapNoticeLabel) - self.naviBarContainerStackView.addArrangedSubviews(underlineView, aboutMapNoticeView) + aboutMapNoticeView.addSubview(aboutMapNoticeLabel) + naviBarContainerStackView.addArrangedSubviews(underlineView, aboutMapNoticeView) underlineView.snp.makeConstraints { $0.leading.trailing.equalToSuperview() @@ -359,68 +447,6 @@ extension CourseDrawingVC { } } } - - private func setHiddenViewsLayout() { - view.addSubviews(naviBarForEditing, guideView, distanceContainerView, completeButton, undoButton) - view.sendSubviewToBack(naviBarForEditing) - - naviBarForEditing.snp.makeConstraints { make in - make.leading.top.trailing.equalTo(view.safeAreaLayoutGuide) - make.height.equalTo(48) - } - - guideView.snp.makeConstraints { make in - make.centerY.equalTo(naviBarForEditing.snp.centerY) - make.leading.equalTo(view.safeAreaLayoutGuide).inset(55) - make.trailing.equalTo(view.safeAreaLayoutGuide).inset(27) - } - - distanceContainerView.snp.makeConstraints { make in - make.width.equalTo(96) - make.height.equalTo(44) - make.leading.equalTo(view.safeAreaLayoutGuide).inset(16) - make.top.equalTo(view.snp.bottom) - } - - distanceContainerView.addSubviews(distanceStackView) - - distanceStackView.snp.makeConstraints { make in - make.center.equalToSuperview() - } - - undoButton.snp.makeConstraints { make in - make.trailing.equalTo(view.safeAreaLayoutGuide) - make.top.equalTo(view.snp.bottom) - } - - completeButton.snp.makeConstraints { make in - make.leading.trailing.equalTo(view.safeAreaLayoutGuide).inset(16) - make.height.equalTo(44) - make.top.equalTo(view.snp.bottom).offset(34) - } - } - - private func showHiddenViews(withDuration: TimeInterval = 0) { - [naviBarForEditing, guideView, distanceContainerView, completeButton, undoButton].forEach { subView in - view.bringSubviewToFront(subView) - } - - UIView.animate(withDuration: withDuration) { - let naviBarContainerStackViewHeight = self.naviBarContainerStackView.frame.height - self.naviBarContainerStackView.transform = CGAffineTransform(translationX: 0, y: -naviBarContainerStackViewHeight) - self.departureInfoContainerView.transform = CGAffineTransform(translationX: 0, y: 172) - } - - self.guideView.transform = CGAffineTransform(translationX: 0, y: -100) - - UIView.animate(withDuration: withDuration) { - self.naviBarForEditing.alpha = 1 - self.guideView.transform = .identity - self.distanceContainerView.transform = CGAffineTransform(translationX: 0, y: -151) - self.completeButton.transform = CGAffineTransform(translationX: 0, y: -112) - self.undoButton.transform = CGAffineTransform(translationX: 0, y: -(self.undoButton.frame.height+95)) - } - } } // MARK: - Network @@ -456,7 +482,7 @@ extension CourseDrawingVC { private func searchLocationTmapAddress(latitude: Double, longitude: Double) { departureSearchingProvider.request(target: .getLocationTmapAddress(latitude: latitude, longitude: longitude), instance: TmapAddressSearchingResponseDto.self, vc: self) { data in - self.updateData(model: data.toDepartureLocationModel(latitude: latitude, longitude: longitude)) + self.updateData(model: data.toDepartureLocationModel(latitude: latitude, longitude: longitude)) } } } diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/VC/CourseStorageVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/VC/CourseStorageVC.swift index e332277c..996a3489 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/VC/CourseStorageVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/VC/CourseStorageVC.swift @@ -91,6 +91,8 @@ extension CourseStorageVC { privateCourseListView.courseDrawButtonTapped.sink { [weak self] in guard let self = self else { return } + + analyze(buttonName: GAEvent.Button.clickMyStorageCourseDrawingStart) // 내가 그린 코스 코스 그리기 진입 self.tabBarController?.selectedIndex = 0 }.store(in: cancelBag) @@ -101,11 +103,13 @@ extension CourseStorageVC { privateCourseListView.cellDidTapped.sink { [weak self] index in guard let self = self else { return } + analyze(buttonName: GAEvent.Button.clickScrapPageStartCourse) // 코스 발견_스크랩코스 상세페이지 시작하기 Event + let title = self.privateCourseList[index].title let runningWaitingVC = RunningWaitingVC() runningWaitingVC.setData(courseId: self.privateCourseList[index].id, publicCourseId: nil, courseTitle: title) - /// 코스 이름을 여기서 가져오는 로직 + /// 코스 이름을 여기서 가져오는 로직 runningWaitingVC.hidesBottomBarWhenPushed = true self.navigationController?.pushViewController(runningWaitingVC, animated: true) }.store(in: cancelBag) @@ -204,12 +208,12 @@ extension CourseStorageVC { private func setLayout() { view.addSubviews(naviBar) - + naviBar.snp.makeConstraints { make in make.leading.top.trailing.equalTo(view.safeAreaLayoutGuide) make.height.equalTo(56) } - + guard UserManager.shared.userType != .visitor else { self.showSignInRequestEmptyView() return diff --git a/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/ActivityRecordInfoVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/ActivityRecordInfoVC.swift index 148d1a3d..90afa4ea 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/ActivityRecordInfoVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/ActivityRecordInfoVC.swift @@ -293,6 +293,8 @@ extension ActivityRecordInfoVC: UITableViewDataSource { extension ActivityRecordInfoVC: ListEmptyViewDelegate { func emptyViewButtonTapped() { + analyze(buttonName: GAEvent.Button.clickCourseDrawingInRunningRecord) + self.tabBarController?.selectedIndex = 0 } } diff --git a/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/UploadedCourseInfoVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/UploadedCourseInfoVC.swift index 3c69fa85..80ded91c 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/UploadedCourseInfoVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/UploadedCourseInfoVC.swift @@ -68,8 +68,8 @@ final class UploadedCourseInfoVC: UIViewController { $0.titleLabel?.font = .b5 $0.layer.backgroundColor = UIColor.m3.cgColor $0.layer.cornerRadius = 11 -// $0.layer.borderColor = UIColor.m1.cgColor -// $0.layer.borderWidth = 1 + // $0.layer.borderColor = UIColor.m1.cgColor + // $0.layer.borderWidth = 1 } private lazy var deleteCourseButton = CustomButton(title: "삭제하기").then { @@ -283,7 +283,7 @@ extension UploadedCourseInfoVC: UICollectionViewDelegate, UICollectionViewDataSo } else { cell.selectCell(didSelect: false) } - + let model = uploadedCourseList[indexPath.item] let cellTitle = "\(model.departure.region) \(model.departure.city)" cell.setData(imageURL: model.image, title: cellTitle, location: nil, didLike: nil, isEditMode: isEditMode) @@ -389,6 +389,8 @@ extension UploadedCourseInfoVC { extension UploadedCourseInfoVC: ListEmptyViewDelegate { func emptyViewButtonTapped() { + analyze(buttonName: GAEvent.Button.clickCourseUploadInUploadedCourse) + let myCourseSelectVC = MyCourseSelectVC() self.navigationController?.pushViewController(myCourseSelectVC, animated: true) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/MyPageVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/MyPageVC.swift index b4918a8b..5f9ad7a5 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/MyPageVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/MyPageVC.swift @@ -14,16 +14,16 @@ import KakaoSDKTalk import KakaoSDKUser final class MyPageVC: UIViewController { - + // MARK: - Properties private var userProvider = Providers.userProvider let stampNameImageDictionary: [String: UIImage] = GoalRewardInfoModel.stampNameImageDictionary - + var sendEmail = String() var sendNickname = String() - + // MARK: - UI Components private lazy var navibar = CustomNavigationBar(self, type: .title).setTitle("마이페이지") @@ -88,7 +88,7 @@ final class MyPageVC: UIViewController { let tap = UITapGestureRecognizer(target: self, action: #selector(self.touchUpUploadedCourseRecordInfoView)) $0.addGestureRecognizer(tap) } - + private lazy var settingView = makeInfoView(title: "설정").then { let tap = UITapGestureRecognizer(target: self, action: #selector(self.touchUpSettingView)) $0.addGestureRecognizer(tap) @@ -231,16 +231,19 @@ extension MyPageVC { extension MyPageVC { @objc private func touchUpActivityRecordInfoView() { + analyze(buttonName: GAEvent.Button.clickRunningRecord) pushToActivityRecordInfoVC() } @objc private func touchUpGoalRewardInfoView() { + analyze(buttonName: GAEvent.Button.clickGoalReward) pushToGoalRewardInfoVC() } @objc private func touchUpUploadedCourseRecordInfoView() { + analyze(buttonName: GAEvent.Button.clickUploadedCourse) pushToUploadedCourseInfoVC() } @@ -256,7 +259,7 @@ extension MyPageVC { @objc private func touchUpkakaoChannelAsk() { - if let url = TalkApi.shared.makeUrlForChannelChat(channelPublicId: "_hXduG") { + if let url = TalkApi.shared.makeUrlForChatChannel(channelPublicId: "_hXduG") { UIApplication.shared.open(url) } } diff --git a/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/SettingVC/PersonalInfoVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/SettingVC/PersonalInfoVC.swift index 9aaab093..20fc0680 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/SettingVC/PersonalInfoVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/SettingVC/PersonalInfoVC.swift @@ -12,13 +12,13 @@ import SnapKit import Then final class PersonalInfoVC: UIViewController { - + // MARK: - Properties private let userProvider = Providers.userProvider var email = String() - + // MARK: - UI Components private lazy var navibar = CustomNavigationBar(self, type: .titleWithLeftButton).setTitle("계정 정보") @@ -63,11 +63,15 @@ final class PersonalInfoVC: UIViewController { extension PersonalInfoVC { @objc func touchUpLogoutView() { + analyze(buttonName: GAEvent.Button.clickTryLogout) + pushToLogoutVC() } @objc func touchUpDeleteAccountView() { + analyze(buttonName: GAEvent.Button.clickTryWithdraw) + pushToDeleteAccountVC() } } @@ -130,17 +134,21 @@ extension PersonalInfoVC { private func logout() { UserManager.shared.logout() + analyze(screenName: GAEvent.View.viewSuccessLogout) + self.showSplashVC() } private func deleteUserDidComplete() { + analyze(screenName: GAEvent.View.viewSuccessWithdraw) + self.logout() } private func requestAppleToken() { let appleIDProvider = ASAuthorizationAppleIDProvider() let request = appleIDProvider.createRequest() - + let authorizationController = ASAuthorizationController(authorizationRequests: [request]) authorizationController.delegate = self authorizationController.presentationContextProvider = self @@ -287,21 +295,21 @@ extension PersonalInfoVC: ASAuthorizationControllerPresentationContextProviding, /// Apple ID 연동 성공 시 func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) { - switch authorization.credential { - /// Apple ID - case let appleIDCredential as ASAuthorizationAppleIDCredential: - - /// 계정 정보 가져오기 - let userIdentifier = appleIDCredential.user - let idToken = appleIDCredential.identityToken! - guard let tokenStr = String(data: idToken, encoding: .utf8) else { return } - - print("User ID : \(userIdentifier)") - print("token : \(String(describing: tokenStr))") - - self.deleteUser(appleToken: tokenStr) - default: - break + switch authorization.credential { + /// Apple ID + case let appleIDCredential as ASAuthorizationAppleIDCredential: + + /// 계정 정보 가져오기 + let userIdentifier = appleIDCredential.user + let idToken = appleIDCredential.identityToken! + guard let tokenStr = String(data: idToken, encoding: .utf8) else { return } + + print("User ID : \(userIdentifier)") + print("token : \(String(describing: tokenStr))") + + self.deleteUser(appleToken: tokenStr) + default: + break } } diff --git a/Runnect-iOS/Runnect-iOS/Presentation/Running/VC/RunningRecordVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/Running/VC/RunningRecordVC.swift index 2cf56c18..552ce207 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/Running/VC/RunningRecordVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/Running/VC/RunningRecordVC.swift @@ -305,6 +305,7 @@ extension RunningRecordVC { case .success(let result): let status = result.statusCode if 200..<300 ~= status { + analyze(buttonName: GAEvent.Button.clickStoreRunningTracking) self.showToastOnWindow(text: "저장한 러닝 기록은 마이페이지에서 볼 수 있어요.") self.navigationController?.popToRootViewController(animated: true) } diff --git a/Runnect-iOS/Runnect-iOS/Presentation/Running/VC/RunningWaitingVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/Running/VC/RunningWaitingVC.swift index c712e77c..2aba81c8 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/Running/VC/RunningWaitingVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/Running/VC/RunningWaitingVC.swift @@ -323,8 +323,10 @@ extension RunningWaitingVC { switch item { case "수정하기": + analyze(buttonName: GAEvent.Button.clickMyStorageTryModify) ModifyCourseTitle() case "삭제하기": + analyze(buttonName: GAEvent.Button.clickMyStorageTryRemove) let deleteAlertVC = RNAlertVC(description: "러닝 기록을 정말로 삭제하시겠어요?").setButtonTitle("취소", "삭제하기") deleteAlertVC.modalPresentationStyle = .overFullScreen deleteAlertVC.rightButtonTapAction = { diff --git a/Runnect-iOS/Runnect-iOS/Presentation/SignIn/VC/SignInSocialLoginVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/SignIn/VC/SignInSocialLoginVC.swift index d5c69d92..b6511707 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/SignIn/VC/SignInSocialLoginVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/SignIn/VC/SignInSocialLoginVC.swift @@ -218,7 +218,11 @@ extension SignInSocialLoginVC: ASAuthorizationControllerPresentationContextProvi switch result { case .success(let type): self?.analyze(buttonName: GAEvent.Button.clickAppleLogin) - type == "Signup" ? self?.pushToNickNameSetUpVC() : self?.pushToTabBarController() + if type == "Signup" { + self?.pushToNickNameSetUpVC() + } else { + self?.pushToTabBarController() + } case .failure(let error): print(error) self?.showNetworkFailureToast() diff --git a/Runnect-iOS/Runnect-iOS/Presentation/TabBar/TaBarController.swift b/Runnect-iOS/Runnect-iOS/Presentation/TabBar/TaBarController.swift index 208d54cf..bf72afe2 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/TabBar/TaBarController.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/TabBar/TaBarController.swift @@ -13,6 +13,7 @@ final class TabBarController: UITabBarController { override func viewDidLoad() { super.viewDidLoad() + delegate = self setUI() setTabBarControllers() } @@ -32,21 +33,21 @@ extension TabBarController { private func setTabBarControllers() { let courseDrawingNVC = templateNavigationController(title: "코스 그리기", - unselectedImage: ImageLiterals.icCourseDraw, - selectedImage: ImageLiterals.icCourseDrawFill, - rootViewController: CourseDrawingHomeVC()) + unselectedImage: ImageLiterals.icCourseDraw, + selectedImage: ImageLiterals.icCourseDrawFill, + rootViewController: CourseDrawingHomeVC()) let courseStorageNVC = templateNavigationController(title: "보관함", - unselectedImage: ImageLiterals.icStorage, - selectedImage: ImageLiterals.icStorageFill, - rootViewController: CourseStorageVC()) + unselectedImage: ImageLiterals.icStorage, + selectedImage: ImageLiterals.icStorageFill, + rootViewController: CourseStorageVC()) let courseDiscoveryNVC = templateNavigationController(title: "코스 발견", - unselectedImage: ImageLiterals.icCourseDiscover, - selectedImage: ImageLiterals.icCourseDiscoverFill, - rootViewController: CourseDiscoveryVC()) + unselectedImage: ImageLiterals.icCourseDiscover, + selectedImage: ImageLiterals.icCourseDiscoverFill, + rootViewController: CourseDiscoveryVC()) let myPageNVC = templateNavigationController(title: "마이페이지", - unselectedImage: ImageLiterals.icMypage, - selectedImage: ImageLiterals.icMypageFill, - rootViewController: MyPageVC()) + unselectedImage: ImageLiterals.icMypage, + selectedImage: ImageLiterals.icMypageFill, + rootViewController: MyPageVC()) viewControllers = [courseDrawingNVC, courseStorageNVC, courseDiscoveryNVC, myPageNVC] } @@ -59,5 +60,23 @@ extension TabBarController { nav.navigationBar.isHidden = true return nav } - +} + +extension TabBarController: UITabBarControllerDelegate { + func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) { + guard let title = viewController.title else { return } + + switch title { + case "코스 그리기": + analyze(buttonName: GAEvent.Button.clickCourseDrawingTabBar) + case "보관함": + analyze(buttonName: GAEvent.Button.clickStorageTabBar) + case "코스 발견": + analyze(buttonName: GAEvent.Button.clickCourseDiscoveryTabBar) + case "마이페이지": + analyze(buttonName: GAEvent.Button.clickMyPageTabBar) + default: + break + } + } } diff --git a/Runnect-iOS/Runnect-iOS/Presentation/UserProfile/UserProfileVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/UserProfile/UserProfileVC.swift index 6b25dc48..881c264d 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/UserProfile/UserProfileVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/UserProfile/UserProfileVC.swift @@ -45,6 +45,7 @@ final class UserProfileVC: UIViewController { setNavigationBar() setDelegate() setLayout() + analyze(screenName: GAEvent.View.viewUserProfile) } override func viewWillAppear(_ animated: Bool) { diff --git a/Runnect-iOS/fastlane/Appfile b/Runnect-iOS/fastlane/Appfile index 82aa07e8..83428cd7 100644 --- a/Runnect-iOS/fastlane/Appfile +++ b/Runnect-iOS/fastlane/Appfile @@ -2,7 +2,7 @@ app_identifier("com.runnect.Runnect-iOS") # The bundle identifier of your app apple_id(ENV["APPLE_ID"]) # Your Apple Developer Portal username itc_team_id("125287287") # App Store Connect Team ID -team_id("9K86FQHDLU") # Developer Portal Team ID +team_id("8Q4H7X3Q58") # Developer Portal Team ID # For more information about the Appfile, see: # https://docs.fastlane.tools/advanced/#appfile