Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1600,11 +1600,12 @@
CODE_SIGN_ENTITLEMENTS = "Runnect-iOS/Runnect-iOS.entitlements";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 1;
CURRENT_PROJECT_VERSION = 23051501;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 9K86FQHDLU;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "Runnect-iOS/Info.plist";
INFOPLIST_KEY_CFBundleDisplayName = Runnect;
INFOPLIST_KEY_NSLocationAlwaysAndWhenInUseUsageDescription = "위치 정보 권한이 필요합니다.";
INFOPLIST_KEY_NSLocationAlwaysUsageDescription = "위치 정보 권한이 필요합니다.";
INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "위치 정보 권한이 필요합니다.";
Expand Down Expand Up @@ -1637,11 +1638,12 @@
CODE_SIGN_ENTITLEMENTS = "Runnect-iOS/Runnect-iOS.entitlements";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 1;
CURRENT_PROJECT_VERSION = 23051501;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 9K86FQHDLU;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "Runnect-iOS/Info.plist";
INFOPLIST_KEY_CFBundleDisplayName = Runnect;
INFOPLIST_KEY_NSLocationAlwaysAndWhenInUseUsageDescription = "위치 정보 권한이 필요합니다.";
INFOPLIST_KEY_NSLocationAlwaysUsageDescription = "위치 정보 권한이 필요합니다.";
INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "위치 정보 권한이 필요합니다.";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,59 @@ extension UIViewController {
func hideTabBar(wantsToHide: Bool) {
self.tabBarController?.tabBar.isHidden = wantsToHide
}

/// 인증 과정을 다시 거치도록 SplashVC로 보내기
func showSplashVC() {
let splashVC = SplashVC()
let navigationController = UINavigationController(rootViewController: splashVC)
guard let window = self.view.window else { return }
ViewControllerUtils.setRootViewController(window: window, viewController: navigationController, withAnimation: true)
}

func presentSignInRequestAlertVC() {
let alertVC = CustomAlertVC()
.setTitle("가입 후 로그인 시 코스를 저장하고 달릴 수 있어요!")
.setLeftButtonTitle(NSAttributedString(string: "닫기", attributes: [.font: UIFont.h5, .foregroundColor: UIColor.m1]))
.setRightButtonTitle(NSAttributedString(string: "가입하기", attributes: [.font: UIFont.h5, .foregroundColor: UIColor.w1]))
alertVC.modalPresentationStyle = .overFullScreen

alertVC.leftButtonTapAction = {
alertVC.dismiss(animated: false)
}

alertVC.rightButtonTapAction = {
self.showSplashVC()
}

self.present(alertVC, animated: false)
alertVC.setImage(ImageLiterals.imgSpaceship, size: CGSize(width: 229, height: 136))
}

func handleVisitor() -> Bool {
guard UserManager.shared.userType == .visitor else { return true }
self.presentSignInRequestAlertVC()
return false
}

func showSignInRequestEmptyView() {
let emptyView = ListEmptyView(description: "러넥트에 가입하면 내가 그린 코스와\n 스크랩 코스를 관리할 수 있어요!",
buttonTitle: "가입하기")
emptyView.buttonTapAction = {
self.showSplashVC()
}

emptyView.setImage(ImageLiterals.imgSpaceship, size: CGSize(width: 229, height: 136))

self.view.addSubview(emptyView)

emptyView.snp.makeConstraints { make in
make.center.equalTo(view.safeAreaLayoutGuide)
make.leading.trailing.equalTo(view.safeAreaLayoutGuide).inset(80)
}
}

func showToastOnWindow(text: String) {
let window = self.view.window!
Toast.show(message: text, view: window, safeAreaBottomInset: self.safeAreaBottomInset())
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
{
"images" : [
{
"filename" : "Property 1=Component 77, Property 2=btn_heart2.png",
"filename" : "Component 91.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Property 1=Component 77, Property 2=btn_heart2@2x.png",
"filename" : "Component 91@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Property 1=Component 77, Property 2=btn_heart2@3x.png",
"filename" : "Component 91@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
10 changes: 10 additions & 0 deletions Runnect-iOS/Runnect-iOS/Global/Supports/SceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
//

import UIKit
import KakaoSDKAuth
import KakaoSDKCommon

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

Expand All @@ -21,6 +23,14 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
self.window = window
window.makeKeyAndVisible()
}

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
if let url = URLContexts.first?.url {
if (AuthApi.isKakaoTalkLoginUrl(url)) {
_ = AuthController.handleOpenUrl(url: url)
}
}
}

func sceneDidDisconnect(_ scene: UIScene) {
// Called as the scene is being released by the system.
Expand Down
31 changes: 29 additions & 2 deletions Runnect-iOS/Runnect-iOS/Global/UIComponents/CustomAlertVC.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,23 @@ final class CustomAlertVC: UIViewController {
.asDriver()
}

var leftButtonTapAction: (() -> Void)?
var rightButtonTapAction: (() -> Void)?

private var cancelBag = CancelBag()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cancleBag은 combine을 써서 할당된 액션을 다 취소해주려고 선언하신 거죠..? 넘 신기한 기능이네요.. 나중에 콤바인 공부하고 다시 살펴볼게요

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Combine은 스트림을 생성하여 비동기적으로 액션을 처리하는 (반응형이라는 명칭이 있음) 프레임워크에요!
cancelBag은 RxSwift처럼 이 스트림을 관리하기 위해 한번 감싼 객체입니다. 원래는 Set<AnyCancellable> 로 선언해야해요!
클래스의 deInit 시점에 연결해놓은 스트림을 취소하기 위해 선언한 거에요~!
Combine을 공부하기 전에 클로저를 사용한 비동기처리를 먼저 공부하고 도전해보면 좋아요! 매우 편리합니다


// MARK: - UI Components

private let alertView = UIView()
private let alertImageView = UIImageView().then {
$0.image = ImageLiterals.imgPaper
}
private let contentsLabel = UILabel().then {
$0.text = "코스를 만들었어요!"
private let contentsLabel: UILabel = UILabel().then {
$0.text = "코스를 만들었어요!\n지정한 코스는 보관함에서 볼 수 있어요."
$0.font = .h5
$0.textColor = .g2
$0.textAlignment = .center
$0.numberOfLines = 2
}

private let leftButton = CustomButton(title: "보관함 가기")
Expand All @@ -54,12 +60,22 @@ final class CustomAlertVC: UIViewController {
super.viewDidLoad()
self.setUI()
self.setLayout()
self.bindViews()
}
}

// MARK: - Methods

extension CustomAlertVC {
/// 이미지 변경
public func setImage(_ image: UIImage, size: CGSize) {
self.alertImageView.image = image
self.alertImageView.snp.updateConstraints { make in
make.width.equalTo(size.width)
make.height.equalTo(size.height)
}
}

/// conentsLabel의 텍스트 변경
@discardableResult
public func setTitle(_ title: String) -> Self {
Expand All @@ -80,6 +96,16 @@ extension CustomAlertVC {
self.rightButton.changeTitle(attributedString: title)
return self
}

private func bindViews() {
self.leftButtonTapped.sink { _ in
self.leftButtonTapAction?()
}.store(in: cancelBag)

self.rightButtonTapped.sink { _ in
self.rightButtonTapAction?()
}.store(in: cancelBag)
}
}

// MARK: - UI & Layout
Expand Down Expand Up @@ -109,6 +135,7 @@ extension CustomAlertVC {

contentsLabel.snp.makeConstraints { make in
make.top.equalTo(alertImageView.snp.bottom).offset(24)
make.leading.trailing.equalToSuperview().inset(10)
make.centerX.equalToSuperview()
}

Expand Down
11 changes: 11 additions & 0 deletions Runnect-iOS/Runnect-iOS/Global/UIComponents/ListEmptyView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ final class ListEmptyView: UIView {

weak var delegate: ListEmptyViewDelegate?

var buttonTapAction: (() -> Void)?

// MARK: - UI Components

private let mainImageView = UIImageView().then {
Expand Down Expand Up @@ -61,13 +63,22 @@ extension ListEmptyView {
private func setAddTarget() {
bottomButton.addTarget(self, action: #selector(bottomButtonDidTap), for: .touchUpInside)
}

public func setImage(_ image: UIImage, size: CGSize) {
self.mainImageView.image = image
self.mainImageView.snp.updateConstraints { make in
make.width.equalTo(size.width)
make.height.equalTo(size.height)
}
}
}

// MARK: - @objc Function

extension ListEmptyView {
@objc private func bottomButtonDidTap() {
delegate?.emptyViewButtonTapped()
self.buttonTapAction?()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ enum RNError: Error {
case etc
}

enum UserType {
case visitor
case registered
}

final class UserManager {
static let shared = UserManager()

Expand All @@ -23,6 +28,7 @@ final class UserManager {
@UserDefaultWrapper<String>(key: "refreshToken") public var refreshToken
@UserDefaultWrapper<Bool>(key: "isKakao") public var isKakao
var hasAccessToken: Bool { return self.accessToken != nil }
var userType: UserType = .registered

private init() {}

Expand All @@ -45,6 +51,7 @@ final class UserManager {
self.accessToken = data.accessToken
self.refreshToken = data.refreshToken
self.isKakao = provider == "KAKAO" ? true : false
UserManager.shared.userType = .registered
completion(.success(data.type)) // 로그인인지 회원가입인지 전달
} catch {
print(error.localizedDescription)
Expand Down
21 changes: 12 additions & 9 deletions Runnect-iOS/Runnect-iOS/Global/Utils/Toast.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,14 @@ public class Toast {
let toastContainer = UIView()
let toastLabel = UILabel()

toastContainer.backgroundColor = .lightGray
toastContainer.alpha = 1
toastContainer.layer.cornerRadius = 9
toastContainer.backgroundColor = UIColor.g2.withAlphaComponent(0.7)
toastContainer.alpha = 1.0
toastContainer.layer.cornerRadius = 15
toastContainer.clipsToBounds = true
toastContainer.isUserInteractionEnabled = false

toastLabel.textColor = .white
toastLabel.textColor = .m4
toastLabel.font = .b4
toastLabel.textAlignment = .center
toastLabel.text = message
toastLabel.clipsToBounds = true
Expand All @@ -41,21 +42,23 @@ public class Toast {
toastContainer.addSubview(toastLabel)
view.addSubview(toastContainer)

let toastConatinerWidth = toastLabel.intrinsicContentSize.width + 40.0

toastContainer.snp.makeConstraints {
$0.centerX.equalToSuperview()
$0.bottom.equalToSuperview().inset(safeAreaBottomInset+80)
$0.width.equalTo(200)
$0.height.equalTo(44)
$0.bottom.equalToSuperview().inset(safeAreaBottomInset+160)
$0.width.equalTo(toastConatinerWidth)
$0.height.equalTo(31)
}

toastLabel.snp.makeConstraints {
$0.center.equalToSuperview()
}

UIView.animate(withDuration: 0.4, delay: 0.0, options: .curveEaseIn, animations: {
UIView.animate(withDuration: 1.0, delay: 0.0, options: .curveEaseIn, animations: {
toastContainer.alpha = 1.0
}, completion: { _ in
UIView.animate(withDuration: 0.4, delay: 1.0, options: .curveEaseOut, animations: {
UIView.animate(withDuration: 1.0, delay: 2.0, options: .curveEaseOut, animations: {
toastContainer.alpha = 0.0
}, completion: {_ in
toastContainer.removeFromSuperview()
Expand Down
12 changes: 11 additions & 1 deletion Runnect-iOS/Runnect-iOS/Network/Service/AuthInterceptor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ final class AuthInterceptor: RequestInterceptor {
private init() {}

func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
var urlRequest = urlRequest

// 방문자일 경우
if UserManager.shared.userType == .visitor && urlRequest.url?.absoluteString.hasPrefix(Config.baseURL) == true {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

urlRequest의 앞부분이 저희 baseurl과 같은지 확인하는 이유가 따로 있나요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

코스 출발지 검색 api는 카카오 주소 api를 사용하고 있기 때문에 저희 서버 baseUrl과 달라요! 그래서 이 api 호출에서는 방문자를 분기처리할 필요가 없습니다.
사실 이 api 호출시에는 AuthInterCeptor가 실행되지 않도록 했지만 혹시 모르니 한번 더 안전장치로 걸러주고 있습니다.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아 그렇쿤요!1 이해했습니다 감사합니다!

urlRequest.setValue("visitor", forHTTPHeaderField: "accessToken")
urlRequest.setValue("null", forHTTPHeaderField: "refreshToken")
completion(.success(urlRequest))
return
}

guard urlRequest.url?.absoluteString.hasPrefix(Config.baseURL) == true,
let accessToken = UserManager.shared.accessToken,
let refreshToken = UserManager.shared.refreshToken
Expand All @@ -26,7 +36,6 @@ final class AuthInterceptor: RequestInterceptor {
return
}

var urlRequest = urlRequest
urlRequest.setValue(accessToken, forHTTPHeaderField: "accessToken")
urlRequest.setValue(refreshToken, forHTTPHeaderField: "refreshToken")
print("adator 적용 \(urlRequest.headers)")
Expand All @@ -38,6 +47,7 @@ final class AuthInterceptor: RequestInterceptor {
guard let response = request.task?.response as? HTTPURLResponse, response.statusCode == 401, let pathComponents = request.request?.url?.pathComponents,
!pathComponents.contains("getNewToken")
else {
dump(error)
completion(.doNotRetryWithError(error))
return
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,11 @@ final class CourseDetailVC: UIViewController {

extension CourseDetailVC {
@objc func likeButtonDidTap(_ sender: UIButton) {
guard UserManager.shared.userType != .visitor else {
showToastOnWindow(text: "러넥트에 가입하면 코스를 스크랩할 수 있어요")
return
}

scrapCourse(scrapTF: !sender.isSelected)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,13 @@ extension CourseDiscoveryVC {
let nextVC = CourseSearchVC()
self.navigationController?.pushViewController(nextVC, animated: true)
}

@objc private func pushToDiscoveryVC() {
guard UserManager.shared.userType != .visitor else {
self.showToastOnWindow(text: "러넥트에 가입하면 코스를 업로드할 수 있어요.")
return
}

let nextVC = MyCourseSelectVC()
self.navigationController?.pushViewController(nextVC, animated: true)
}
Expand Down Expand Up @@ -242,6 +248,11 @@ extension CourseDiscoveryVC: UICollectionViewDelegateFlowLayout {

extension CourseDiscoveryVC: CourseListCVCDeleagte {
func likeButtonTapped(wantsTolike: Bool, index: Int) {
guard UserManager.shared.userType != .visitor else {
showToastOnWindow(text: "러넥트에 가입하면 코스를 스크랩할 수 있어요")
return
}

let publicCourseId = courseList[index].id
scrapCourse(publicCourseId: publicCourseId, scrapTF: wantsTolike)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,11 @@ extension CourseSearchVC: CustomNavigationBarDelegate {

extension CourseSearchVC: CourseListCVCDeleagte {
func likeButtonTapped(wantsTolike: Bool, index: Int) {
guard UserManager.shared.userType != .visitor else {
showToastOnWindow(text: "러넥트에 가입하면 코스를 스크랩할 수 있어요")
return
}

let pubilcCourseId = courseList[index].id
scrapCourse(publicCourseId: pubilcCourseId, scrapTF: wantsTolike)
}
Expand Down
Loading