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
4 changes: 4 additions & 0 deletions GAMSS/Config/Debug.xcconfig
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@ APP_NAME =

// Network Defines
BASE_URL =

// Web
TERMS_OF_SERVICE_URL =
PRIVACY_POLICY_URL =
4 changes: 4 additions & 0 deletions GAMSS/Config/Release.xcconfig
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@ APP_NAME =

// Network Defines
BASE_URL =

// Web
TERMS_OF_SERVICE_URL =
PRIVACY_POLICY_URL =
4 changes: 4 additions & 0 deletions GAMSS/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
<string>$(APP_NAME)</string>
<key>BASE_URL</key>
<string>$(BASE_URL)</string>
<key>TERMS_OF_SERVICE_URL</key>
<string>$(TERMS_OF_SERVICE_URL)</string>
<key>PRIVACY_POLICY_URL</key>
<string>$(PRIVACY_POLICY_URL)</string>
<key>FIREBASE_CRASHLYTICS_COLLECTION_ENABLED</key>
<string>NO</string>
<key>UIDesignRequiresCompatibility</key>
Expand Down
23 changes: 23 additions & 0 deletions GAMSS/Resources/Assets.xcassets/emotions.imageset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "emotions.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "emotions@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "emotions@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
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.
4 changes: 2 additions & 2 deletions GAMSS/Sources/App/GAMSSApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ struct GAMSSApp: App {

var body: some Scene {
WindowGroup {
MainTabView()
.preferredColorScheme(.light)
RootView()
.preferredColorScheme(.light)
}
// 디자인 토큰이 다크모드 값을 갖고 있지만 디자이너가 아직 다크모드 화면을 설계하지
// 않았다 — 시스템 다크모드를 그대로 따라가면 색상이 라이트/다크 목적과 반대로
Expand Down
60 changes: 60 additions & 0 deletions GAMSS/Sources/Core/Components/List/MenuListItemView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//
// MenuListItemView.swift
// GAMSS
//
// Created by LEEGEONJUN on 8/13/26.
//

import SwiftUI

struct MenuListItemView: View {
let title: String
var titleColor: Color = .colorGray950
var badgeText: String?
var trailingText: String?
var trailingColor: Color = .colorGray500

var body: some View {
HStack(spacing: Spacing.spacing100) {
Text(title)
.typography(.body3Medium)
.foregroundStyle(titleColor)

if let badgeText {
badge(badgeText)
}

Spacer(minLength: 0)

if let trailingText {
Text(trailingText)
.typography(.body3Medium)
.foregroundStyle(trailingColor)
}
}
.frame(height: 40)
.padding(.vertical, Spacing.spacing200)
.frame(maxWidth: .infinity, alignment: .leading)
.contentShape(Rectangle())
}

private func badge(_ text: String) -> some View {
Text(text)
.typography(.body6Medium)
.foregroundStyle(Color.colorGray900)
.padding(.horizontal, Spacing.spacing075)
.padding(.vertical, Spacing.spacing025)
.background(Color.colorGray200)
.clipShape(Capsule())
}
}

#Preview {
VStack(spacing: 0) {
MenuListItemView(title: "계정 정보")
MenuListItemView(title: "알림 설정", badgeText: "OFF")
MenuListItemView(title: "앱 버전", trailingText: "0.0.1")
MenuListItemView(title: "회원탈퇴", titleColor: .colorRed)
}
.padding(.horizontal, 18)
}
22 changes: 22 additions & 0 deletions GAMSS/Sources/Core/Components/Safari/SafariView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// SafariView.swift
// GAMSS
//
// Created by 이건준 on 8/12/26.
//

import SafariServices
import SwiftUI

struct SafariView: UIViewControllerRepresentable {
let url: URL

func makeUIViewController(context: Context) -> SFSafariViewController {
SFSafariViewController(url: url)
}

func updateUIViewController(
_ uiViewController: SFSafariViewController,
context: Context
) {}
}
20 changes: 20 additions & 0 deletions GAMSS/Sources/Core/Environment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,30 @@ public enum Environment {
return value(key: "APP_NAME")
}()

/// 앱 버전 (`MARKETING_VERSION` → `CFBundleShortVersionString`)
static let appVersion: String = {
guard let version = infoDictionary["CFBundleShortVersionString"] as? String,
!version.isEmpty
else {
return "-"
}
return version
}()

/// 서버 Base URL
static let baseURL: String = {
return value(key: "BASE_URL")
}()

/// 서비스 이용약관 URL
static let termsOfServiceURL: String = {
return value(key: "TERMS_OF_SERVICE_URL")
}()

/// 개인정보 처리방침 URL
static let privacyPolicyURL: String = {
return value(key: "PRIVACY_POLICY_URL")
}()
}

// MARK: I/O 관련 코드
Expand Down
1 change: 1 addition & 0 deletions GAMSS/Sources/Core/Network/NetworkError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ enum NetworkError: Error {
case invalidResponse
case httpError(statusCode: Int)
case decodingError
case expiredToken
}
10 changes: 10 additions & 0 deletions GAMSS/Sources/Core/Network/NetworkManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,16 @@ final class NetworkManager: NetworkRequesting {
from: data
).error

if apiError?.code == "EXPIRED_TOKEN" {
do {
try await TokenStorage.shared.reissueToken()
} catch {
Log.error("Token reissue failed: \(error)")
}

throw NetworkError.expiredToken
}

Log.error("""
❌ API Error
StatusCode: \(response.statusCode)
Expand Down
17 changes: 17 additions & 0 deletions GAMSS/Sources/Core/UserManager.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// UserManager.swift
// GAMSS
//
// Created by 이건준 on 8/13/26.
//

import Foundation

@Observable
final class UserManager {
static let shared = UserManager()

var user: User?

private init() {}
}
13 changes: 13 additions & 0 deletions GAMSS/Sources/Data/DTO/Auth/Response/ReissueTokenResponseDTO.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// ReissueTokenResponseDTO.swift
// GAMSS
//
// Created by 이건준 on 8/8/26.
//

import Foundation

struct ReissueTokenResponseDTO: Decodable {
let accessToken: String
let refreshToken: String
}
5 changes: 5 additions & 0 deletions GAMSS/Sources/Data/Repository/DefaultAuthRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ final class DefaultAuthRepository: AuthRepository {
self.tokenStorage = tokenStorage
}

func logout() async throws {
_ = try await networkManager.request(AuthEndpoint.logout, responseType: APIResponse<EmptyResponseDTO>.self)
try tokenStorage.deleteTokens()
}

func login(
with socialType: SocialType,
credential: ASAuthorizationAppleIDCredential,
Expand Down
7 changes: 4 additions & 3 deletions GAMSS/Sources/Data/Storage/TokenStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@ final class TokenStorage {

private init() {}

func reissueToken() throws {
guard LoginState.current != .notLoggedIn else {
func reissueToken() async throws {
guard let refreshToken = readToken(.refreshToken) else {
try TokenStorage.shared.deleteTokens()
return
}

/// TODO: - accessToken, refreshToken 재발행 로직 필요
let response = try await NetworkManager.shared.request(AuthEndpoint.reissueToken(.init(refreshToken: refreshToken)), responseType: APIResponse<ReissueTokenResponseDTO>.self).data
try createTokens(accessToken: response.accessToken, refreshToken: response.refreshToken)
}

func createTokens(accessToken: String, refreshToken: String) throws {
Expand Down
15 changes: 15 additions & 0 deletions GAMSS/Sources/Domain/Entity/User.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//
// User.swift
// GAMSS
//
// Created by 이건준 on 8/13/26.
//

import Foundation

struct User {
let id: Int
let email: String
let name: String
let nickname: String
}
5 changes: 3 additions & 2 deletions GAMSS/Sources/Domain/Repository/AuthRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@

import AuthenticationServices

// FIXME: - 로그인 시 올바른 응답값으로 수정 필요
protocol AuthRepository {
func login(
with socialType: SocialType,
credential: ASAuthorizationAppleIDCredential,
nonce: String
) async throws
) async throws

func logout() async throws
}
20 changes: 20 additions & 0 deletions GAMSS/Sources/Domain/UseCase/Auth/DefaultLogoutUseCase.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// DefaultLogoutUseCase.swift
// GAMSS
//
// Created by 이건준 on 8/13/26.
//

import Foundation

final class DefaultLogoutUseCase: LogoutUseCase {
private let authRepository: AuthRepository

init(authRepository: AuthRepository) {
self.authRepository = authRepository
}

func logout() async throws {
try await authRepository.logout()
}
}
12 changes: 12 additions & 0 deletions GAMSS/Sources/Domain/UseCase/Auth/LogoutUseCase.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//
// LogoutUseCase.swift
// GAMSS
//
// Created by 이건준 on 8/13/26.
//

import Foundation

protocol LogoutUseCase {
func logout() async throws
}
12 changes: 10 additions & 2 deletions GAMSS/Sources/Presentation/Home/HomeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import SwiftUI
struct HomeView: View {
@StateObject private var viewModel: HomeViewModel
@FocusState private var isInputFocused: Bool
@State private var isSettingPresented = false

init(viewModel: HomeViewModel) {
_viewModel = StateObject(wrappedValue: viewModel)
Expand Down Expand Up @@ -57,6 +58,9 @@ struct HomeView: View {
)
.toolbar(.hidden, for: .tabBar)
}
.navigationDestination(isPresented: $isSettingPresented) {
SettingView()
}
}
.alert(viewModel.alertMessage ?? "", isPresented: Binding(
get: { viewModel.alertMessage != nil },
Expand All @@ -75,8 +79,12 @@ struct HomeView: View {

Spacer()

Image(systemName: "gearshape")
.foregroundStyle(Color.colorGray500)
Button {
isSettingPresented = true
} label: {
Image(systemName: "gearshape")
.foregroundStyle(Color.colorGray500)
}
}
}

Expand Down
Loading
Loading