Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
CE17F0382961BF8B00E1DED0 /* FontLiterals.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE17F0372961BF8B00E1DED0 /* FontLiterals.swift */; };
CE29D582296402B500F47542 /* CourseDrawingVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE29D581296402B500F47542 /* CourseDrawingVC.swift */; };
CE29D584296416D800F47542 /* caculateStatusBarHeight.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE29D583296416D800F47542 /* caculateStatusBarHeight.swift */; };
CE3A53C5296C6017003D518C /* KeychainManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE3A53C4296C6017003D518C /* KeychainManager.swift */; };
CE40BB1C2967E4910030ABCA /* RunningWaitingVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE40BB1B2967E4910030ABCA /* RunningWaitingVC.swift */; };
CE40BB1E2968054F0030ABCA /* BaseResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE40BB1D2968054F0030ABCA /* BaseResponse.swift */; };
CE40BB20296805F70030ABCA /* NetworkResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE40BB1F296805F70030ABCA /* NetworkResult.swift */; };
Expand Down Expand Up @@ -107,6 +108,7 @@
CEC2A68E2962AF2C00160BF7 /* RNMarker.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEC2A68D2962AF2C00160BF7 /* RNMarker.swift */; };
CEC2A6902962B06C00160BF7 /* convertLocationObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEC2A68F2962B06C00160BF7 /* convertLocationObject.swift */; };
CEC2A6922962BE2900160BF7 /* DepartureSearchVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEC2A6912962BE2900160BF7 /* DepartureSearchVC.swift */; };
CECBAD2F296C2F3C00AC8976 /* SignInRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = CECBAD2E296C2F3C00AC8976 /* SignInRouter.swift */; };
CEEC6B3A2961C4F300D00E1E /* CourseDrawingHomeVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEEC6B392961C4F300D00E1E /* CourseDrawingHomeVC.swift */; };
CEEC6B3C2961C51A00D00E1E /* CourseStorageVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEEC6B3B2961C51A00D00E1E /* CourseStorageVC.swift */; };
CEEC6B3E2961C53700D00E1E /* CourseDiscoveryVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEEC6B3D2961C53700D00E1E /* CourseDiscoveryVC.swift */; };
Expand Down Expand Up @@ -172,6 +174,7 @@
CE17F0372961BF8B00E1DED0 /* FontLiterals.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontLiterals.swift; sourceTree = "<group>"; };
CE29D581296402B500F47542 /* CourseDrawingVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseDrawingVC.swift; sourceTree = "<group>"; };
CE29D583296416D800F47542 /* caculateStatusBarHeight.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = caculateStatusBarHeight.swift; sourceTree = "<group>"; };
CE3A53C4296C6017003D518C /* KeychainManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainManager.swift; sourceTree = "<group>"; };
CE40BB1B2967E4910030ABCA /* RunningWaitingVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunningWaitingVC.swift; sourceTree = "<group>"; };
CE40BB1D2968054F0030ABCA /* BaseResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseResponse.swift; sourceTree = "<group>"; };
CE40BB1F296805F70030ABCA /* NetworkResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkResult.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -247,6 +250,7 @@
CEC2A68D2962AF2C00160BF7 /* RNMarker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RNMarker.swift; sourceTree = "<group>"; };
CEC2A68F2962B06C00160BF7 /* convertLocationObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = convertLocationObject.swift; sourceTree = "<group>"; };
CEC2A6912962BE2900160BF7 /* DepartureSearchVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DepartureSearchVC.swift; sourceTree = "<group>"; };
CECBAD2E296C2F3C00AC8976 /* SignInRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInRouter.swift; sourceTree = "<group>"; };
CEEC6B392961C4F300D00E1E /* CourseDrawingHomeVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseDrawingHomeVC.swift; sourceTree = "<group>"; };
CEEC6B3B2961C51A00D00E1E /* CourseStorageVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseStorageVC.swift; sourceTree = "<group>"; };
CEEC6B3D2961C53700D00E1E /* CourseDiscoveryVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseDiscoveryVC.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -660,6 +664,7 @@
isa = PBXGroup;
children = (
CE10064029680C9F00FD31FB /* .gitkeep */,
CECBAD2E296C2F3C00AC8976 /* SignInRouter.swift */,
);
path = SignInRouter;
sourceTree = "<group>";
Expand Down Expand Up @@ -880,6 +885,7 @@
CE29D583296416D800F47542 /* caculateStatusBarHeight.swift */,
CE14677729658C7200DCEA1B /* Stopwatch.swift */,
CE9291282965E01D0010959C /* RNTimeFormatter.swift */,
CE3A53C4296C6017003D518C /* KeychainManager.swift */,
);
path = Utils;
sourceTree = "<group>";
Expand Down Expand Up @@ -1264,6 +1270,7 @@
DA20D84E2966A9B300F1581F /* SearchVC.swift in Sources */,
CE1006572968230800FD31FB /* DepartureLocationModel.swift in Sources */,
CE6655EC295D88D000C64E12 /* UITableView+.swift in Sources */,
CECBAD2F296C2F3C00AC8976 /* SignInRouter.swift in Sources */,
CEEC6B3A2961C4F300D00E1E /* CourseDrawingHomeVC.swift in Sources */,
CEC2A6902962B06C00160BF7 /* convertLocationObject.swift in Sources */,
CEC2A6852961F92C00160BF7 /* CustomButton.swift in Sources */,
Expand All @@ -1286,6 +1293,7 @@
CE5875A4296015D2005D967E /* Encodable+.swift in Sources */,
A3BC2F4129667A0D00198261 /* NicknameEditorVC.swift in Sources */,
CE0C23742966D62A00B45063 /* PagedView.swift in Sources */,
CE3A53C5296C6017003D518C /* KeychainManager.swift in Sources */,
CE14677A2965A80700DCEA1B /* CustomBottomSheetVC.swift in Sources */,
CEEC6B4B2961D89700D00E1E /* CustomNavigationBar.swift in Sources */,
CE40BB2D296808B00030ABCA /* DepartureSearchingRouter.swift in Sources */,
Expand Down
3 changes: 2 additions & 1 deletion Runnect-iOS/Runnect-iOS/Global/Supports/SceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
guard let windowScene = (scene as? UIWindowScene) else { return }

let window = UIWindow(windowScene: windowScene)
window.rootViewController = TabBarController()
let nav = UINavigationController(rootViewController: SplashVC())
window.rootViewController = nav
self.window = window
window.makeKeyAndVisible()
}
Expand Down
105 changes: 105 additions & 0 deletions Runnect-iOS/Runnect-iOS/Global/Utils/KeychainManager.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
//
// KeychainManager.swift
// Runnect-iOS
//
// Created by sejin on 2023/01/09.
//

import UIKit

struct KeychainManager {

static let shared = KeychainManager()

private let service = Bundle.main.bundleIdentifier
private let deviceId = "deviceId"

private init() {}

func storeDeviceId() -> Bool {
guard let service = service else {
print("Keychain >> addItem Fail with no service")
return false
}

let query: [CFString: Any] = [kSecClass: kSecClassGenericPassword,
kSecAttrService: service,
kSecAttrAccount: deviceId,
kSecValueData: setDeviceID().data(using: .utf8, allowLossyConversion: false)!]

// keychain 에 저장을 수행한 결과 값 반환 (true / false)
let status: OSStatus = SecItemAdd(query as CFDictionary, nil)
if status == errSecSuccess {
print("")
print("Keychain >> addItem() : Success Status : \(status)]")
print("")
return true
} else {
print("")
print("[Keychain >> addItem() : Fail Status : \(status)]")
print("")
return false
}
}

func getDeviceId() -> String {
guard let service = service else {
print("Keychain >> getDeviceId Fail with no service")
return ""
}

let query: [CFString: Any] = [kSecClass: kSecClassGenericPassword, // 보안 데이터 저장
kSecAttrService: service,
kSecAttrAccount: deviceId,
kSecReturnData: true,
kSecReturnAttributes: true,
kSecMatchLimit: kSecMatchLimitOne]

var dataTypeRef: CFTypeRef?
let status: OSStatus = SecItemCopyMatching(query as CFDictionary, &dataTypeRef)

if status == errSecSuccess {
guard let existingItem = dataTypeRef as? [String: Any]
else {
print("Keychain >> getDeviceID() : Type Check : false")
return ""
}
let deviceData = existingItem[kSecValueData as String] as? Data
let uuidData = String(data: deviceData!, encoding: .utf8)!

print("Keychain >> getDeviceID() : Success Status : \(status)")
return uuidData
} else if status == errSecItemNotFound || status == -25300 {
print("Keychain >> getDeviceID() : Fail Status : 저장된 데이터가 없습니다")
return ""
} else {
print("Keychain >> getDeviceID() : Fail Status : \(status)")
return ""
}
}

func deleteDeviceID() -> Bool {
guard let service = self.service
else {
print("Keychain >> deleteDeviceID() : Service Check : false")
return false
}

let query: [CFString: Any] = [kSecClass: kSecClassGenericPassword,
kSecAttrService: service,
kSecAttrAccount: deviceId]

let status: OSStatus = SecItemDelete(query as CFDictionary)
if status == errSecSuccess {
print("Keychain >> deleteDeviceID() : Success Status : \(status)")
return true
} else {
print("Keychain >> deleteDeviceID() : Fail Status : \(status)")
return false
}
}

func setDeviceID() -> String {
return UIDevice.current.identifierForVendor!.uuidString
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ import Foundation

struct UserDefaultKeyList {
struct Auth {
@UserDefaultWrapper<String>(key: "deviceId") public static var deviceId
@UserDefaultWrapper<Bool>(key: "didSignIn") public static var didSignIn
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//
// SignInRouter.swift
// Runnect-iOS
//
// Created by sejin on 2023/01/09.
//

import Foundation

import Moya

enum SignInRouter {
case signUp(nickname: String)
}

extension SignInRouter: TargetType {
var baseURL: URL {
guard let url = URL(string: Config.baseURL) else {
fatalError("baseURL could not be configured")
}

return url
}

var path: String {
switch self {
case .signUp:
return "/user"
}
}

var method: Moya.Method {
switch self {
case .signUp:
return .post
}
}

var task: Moya.Task {
switch self {
case .signUp(let nickname):
return .requestParameters(parameters: ["nickname": nickname], encoding: JSONEncoding.default)
}
}

var headers: [String: String]? {
switch self {
case .signUp:
return Config.headerWithDeviceId
}
}
}
56 changes: 56 additions & 0 deletions Runnect-iOS/Runnect-iOS/Presentation/SignIn/VC/SignInVC.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,16 @@

import UIKit

import Moya

final class SignInVC: UIViewController {

// MARK: - Properties

private var signInProvider = MoyaProvider<SignInRouter>(
plugins: [NetworkLoggerPlugin(verbose: true)]
)

private let nicknameMaxLength = 7

// MARK: - UI Components
Expand Down Expand Up @@ -50,6 +56,7 @@ final class SignInVC: UIViewController {
self.setUI()
self.setLayout()
self.setDelegate()
self.setAddTarget()
}
}

Expand All @@ -60,9 +67,19 @@ extension SignInVC {
self.nicknameTextField.delegate = self
}

private func setAddTarget() {
self.startButton.addTarget(self, action: #selector(startButtonDidTap), for: .touchUpInside)
}

private func changeTextFieldLayerColor(_ isEditing: Bool) {
nicknameTextField.layer.borderColor = isEditing ? UIColor.m1.cgColor : UIColor.g3.cgColor
}

private func pushToTabBarController() {
let tabBarController = TabBarController()
guard let window = self.view.window else { return }
ViewControllerUtils.setRootViewController(window: window, viewController: tabBarController, withAnimation: true)
}
}

// MARK: - @objc Function
Expand All @@ -80,6 +97,11 @@ extension SignInVC {
self.nicknameTextField.text = String(newString)
}
}

@objc func startButtonDidTap() {
guard let nickname = nicknameTextField.text else { return }
self.signIn(nickname: nickname)
}
}

// MARK: - UI & Layout
Expand Down Expand Up @@ -124,3 +146,37 @@ extension SignInVC: UITextFieldDelegate {
return true
}
}

// MARK: - Network

extension SignInVC {
func signIn(nickname: String) {
LoadingIndicator.showLoading()
signInProvider.request(.signUp(nickname: nickname)) { [weak self] response in
LoadingIndicator.hideLoading()
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(BaseResponse<BlankData>.self)
if responseDto.status == 200 {
self.pushToTabBarController()
} else {
self.showToast(message: responseDto.message)
}
} catch {
print(error.localizedDescription)
}
}
if status >= 400 {
print("400 error")
}
case .failure(let error):
print(error.localizedDescription)
self.showToast(message: "네트워크 통신 실패")
}
}
}
}
29 changes: 24 additions & 5 deletions Runnect-iOS/Runnect-iOS/Presentation/Splash/VC/SplashVC.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,38 @@ final class SplashVC: UIViewController {
self.setUI()
self.setNavigationBar()
self.setLayout()
self.pushToSignInView()
self.checkDidSignIn()
}
}

// MARK: - Methods

extension SplashVC {
private func pushToSignInView() {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
let signInVC = SignInVC()
self.navigationController?.pushViewController(signInVC, animated: true)
private func checkDidSignIn() {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
let deviceId = KeychainManager.shared.getDeviceId()

if deviceId.isEmpty {
let deviceIdStoreSuccess = KeychainManager.shared.storeDeviceId()
guard deviceIdStoreSuccess else { return }
self.pushToSignInView()
return
}

self.pushToTabBarController()
}
}

private func pushToSignInView() {
let signInVC = SignInVC()
self.navigationController?.pushViewController(signInVC, animated: true)
}

private func pushToTabBarController() {
let tabBarController = TabBarController()
guard let window = self.view.window else { return }
ViewControllerUtils.setRootViewController(window: window, viewController: tabBarController, withAnimation: true)
}
}

// MARK: - UI & Layout
Expand Down