Skip to content
Open
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
36 changes: 24 additions & 12 deletions ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
15304FC22E33C57A00EFCDEF /* CafeFeatureSelectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15304FC12E33C57A00EFCDEF /* CafeFeatureSelectionViewController.swift */; };
15304FC82E33EFA600EFCDEF /* SpotUploadSizeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15304FC72E33EFA600EFCDEF /* SpotUploadSizeType.swift */; };
1530CC792DDFC0D100EB4AEC /* SpotNoImageContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1530CC782DDFC0D100EB4AEC /* SpotNoImageContentView.swift */; };
153B78252E74090700B772F9 /* UserDefaultsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 153B78242E74090700B772F9 /* UserDefaultsManager.swift */; };
153B78252E74090700B772F9 /* UserDefaultsUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 153B78242E74090700B772F9 /* UserDefaultsUtils.swift */; };
15432E7D2E41ECCA0005DB90 /* PostSpotUploadRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15432E7C2E41ECCA0005DB90 /* PostSpotUploadRequest.swift */; };
15432E802E41ECDD0005DB90 /* SpotUploadTargetType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15432E7F2E41ECDD0005DB90 /* SpotUploadTargetType.swift */; };
15432E822E41ECF40005DB90 /* SpotUploadService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15432E812E41ECF40005DB90 /* SpotUploadService.swift */; };
Expand Down Expand Up @@ -137,6 +137,9 @@
15E71E422DF5A44B0020689D /* OpeningTimeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15E71E412DF5A44A0020689D /* OpeningTimeView.swift */; };
15F15E832DBFD17A002F81E2 /* Pretendard-Light.otf in Resources */ = {isa = PBXBuildFile; fileRef = 15F15E822DBFD17A002F81E2 /* Pretendard-Light.otf */; };
15F15E852DBFD206002F81E2 /* LanguageType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15F15E842DBFD206002F81E2 /* LanguageType.swift */; };
15F1F6C42E9A5AB30014669E /* TokenLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15F1F6C32E9A5AB30014669E /* TokenLogger.swift */; };
15F1F6C62E9A5ED80014669E /* TokenLogViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15F1F6C52E9A5ED80014669E /* TokenLogViewController.swift */; };
15F1F6C82E9D8C530014669E /* BuildConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15F1F6C72E9D8C530014669E /* BuildConfig.swift */; };
15F45F842D6664C900BD6B9C /* LoginLockOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15F45F832D6664C900BD6B9C /* LoginLockOverlayView.swift */; };
15F45F862D66877200BD6B9C /* LoadingAnimatedButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15F45F852D66877200BD6B9C /* LoadingAnimatedButton.swift */; };
4C6B2F332D65A15D0089BCB6 /* WithdrawalTargetType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C6B2F312D65A15D0089BCB6 /* WithdrawalTargetType.swift */; };
Expand Down Expand Up @@ -374,7 +377,7 @@
15304FC12E33C57A00EFCDEF /* CafeFeatureSelectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CafeFeatureSelectionViewController.swift; sourceTree = "<group>"; };
15304FC72E33EFA600EFCDEF /* SpotUploadSizeType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpotUploadSizeType.swift; sourceTree = "<group>"; };
1530CC782DDFC0D100EB4AEC /* SpotNoImageContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpotNoImageContentView.swift; sourceTree = "<group>"; };
153B78242E74090700B772F9 /* UserDefaultsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsManager.swift; sourceTree = "<group>"; };
153B78242E74090700B772F9 /* UserDefaultsUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsUtils.swift; sourceTree = "<group>"; };
15432E7C2E41ECCA0005DB90 /* PostSpotUploadRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostSpotUploadRequest.swift; sourceTree = "<group>"; };
15432E7F2E41ECDD0005DB90 /* SpotUploadTargetType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpotUploadTargetType.swift; sourceTree = "<group>"; };
15432E812E41ECF40005DB90 /* SpotUploadService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpotUploadService.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -462,6 +465,9 @@
15E71E412DF5A44A0020689D /* OpeningTimeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpeningTimeView.swift; sourceTree = "<group>"; };
15F15E822DBFD17A002F81E2 /* Pretendard-Light.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Pretendard-Light.otf"; sourceTree = "<group>"; };
15F15E842DBFD206002F81E2 /* LanguageType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguageType.swift; sourceTree = "<group>"; };
15F1F6C32E9A5AB30014669E /* TokenLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokenLogger.swift; sourceTree = "<group>"; };
15F1F6C52E9A5ED80014669E /* TokenLogViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokenLogViewController.swift; sourceTree = "<group>"; };
15F1F6C72E9D8C530014669E /* BuildConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuildConfig.swift; sourceTree = "<group>"; };
15F45F832D6664C900BD6B9C /* LoginLockOverlayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginLockOverlayView.swift; sourceTree = "<group>"; };
15F45F852D66877200BD6B9C /* LoadingAnimatedButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingAnimatedButton.swift; sourceTree = "<group>"; };
4C6B2F2E2D65A15D0089BCB6 /* WithdrawalRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WithdrawalRequest.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1225,6 +1231,7 @@
15BFF8B52D665A5B00909C4F /* Debug.xcconfig */,
15BFF8B62D665A5B00909C4F /* Release.xcconfig */,
74B25C312D2FF022008BDCB7 /* Shared.xcconfig */,
15F1F6C72E9D8C530014669E /* BuildConfig.swift */,
74B25C2B2D2FEE8C008BDCB7 /* Config.swift */,
);
path = Config;
Expand Down Expand Up @@ -1340,7 +1347,6 @@
741429662D5D550400B69528 /* AppVersionManager.swift */,
7462D0242D4262EF00580464 /* AuthManager.swift */,
1515CE0C2E00823B00A559A2 /* DeepLinkManager.swift */,
153B78242E74090700B772F9 /* UserDefaultsManager.swift */,
150B4B292E7EE4BB00C34C76 /* PhotoManager.swift */,
);
path = Service;
Expand Down Expand Up @@ -1597,15 +1603,17 @@
748D6F792D2BCCEF007690B4 /* Utils */ = {
isa = PBXGroup;
children = (
74C782182E01504B0053AA86 /* NavigatonUtils.swift */,
152F8D162DF252560064022B /* MapRedirect */,
74054ECD2D32549800D1CDE4 /* ACLocationManager.swift */,
746F5BF12DE00CE90081569B /* GlassBorderAttributes.swift */,
74E38D332D69B468009449D4 /* LocationUtils.swift */,
74054ECD2D32549800D1CDE4 /* ACLocationManager.swift */,
152F8D162DF252560064022B /* MapRedirect */,
74054EC92D32533800D1CDE4 /* MultitaskDelegate.swift */,
74C782182E01504B0053AA86 /* NavigatonUtils.swift */,
748D6F7F2D2BCD8F007690B4 /* ObservablePattern.swift */,
748D6F812D2BCDB0007690B4 /* ScreenUtils.swift */,
155BC2912DF09EB200E1744E /* ShadowColorCache.swift */,
748D6F7F2D2BCD8F007690B4 /* ObservablePattern.swift */,
15F1F6C32E9A5AB30014669E /* TokenLogger.swift */,
153B78242E74090700B772F9 /* UserDefaultsUtils.swift */,
748D6F7B2D2BCCFF007690B4 /* Enums */,
);
path = Utils;
Expand Down Expand Up @@ -1694,6 +1702,7 @@
748ECA6D2D3196ED00BBC981 /* LoginViewController.swift */,
15CD25832D40364F00320006 /* LoginModalView.swift */,
15CD25812D40364400320006 /* LoginModalViewController.swift */,
15F1F6C52E9A5ED80014669E /* TokenLogViewController.swift */,
);
path = View;
sourceTree = "<group>";
Expand Down Expand Up @@ -2219,6 +2228,7 @@
74DDBA8B2DC45E3B00BF9824 /* ACButtonType.swift in Sources */,
150BA5812E56F8580058A06B /* LimitedSpotsTutorialViewController.swift in Sources */,
156AA72A2D6510E1005B8DCE /* GetNicknameValidityRequest.swift in Sources */,
15F1F6C82E9D8C530014669E /* BuildConfig.swift in Sources */,
74D297F82D63467900DDEE31 /* ImageService.swift in Sources */,
15A147212D5B256D003793EE /* LocalVerificationFlowType.swift in Sources */,
15A167E22E3D722B00062C49 /* AlbumFlowType.swift in Sources */,
Expand Down Expand Up @@ -2328,11 +2338,12 @@
1576806E2DFA06A70079B255 /* SpotListSkeletonHeader.swift in Sources */,
74BF92122D391FFE00B923E3 /* LocalMapViewController.swift in Sources */,
D6E8168E2D6228F5001E4EBF /* WithdrawalViewController.swift in Sources */,
15F1F6C42E9A5AB30014669E /* TokenLogger.swift in Sources */,
740968B72DEC3CA700AFF624 /* VerifiedAreasCollectionViewCell.swift in Sources */,
740EE8282E059875007A5DCF /* GetAppUpdateRequest.swift in Sources */,
15304FC02E33BAA900EFCDEF /* ValueRatingViewController.swift in Sources */,
15F45F842D6664C900BD6B9C /* LoginLockOverlayView.swift in Sources */,
153B78252E74090700B772F9 /* UserDefaultsManager.swift in Sources */,
153B78252E74090700B772F9 /* UserDefaultsUtils.swift in Sources */,
746261692D3EA33300A4E84F /* PostReviewRequest.swift in Sources */,
15CD257E2D3FF4F200320006 /* SpotListTargetType.swift in Sources */,
15A246142DE79D7E00469272 /* NoMatchingSpotType.swift in Sources */,
Expand All @@ -2355,6 +2366,7 @@
74C914D12D64DBBB00BC13E1 /* ImageType.swift in Sources */,
74BF92152D391FFE00B923E3 /* LocalVerificationView.swift in Sources */,
151FBFBA2DBD2F1B005F4D0D /* ACFontType.swift in Sources */,
15F1F6C62E9A5ED80014669E /* TokenLogViewController.swift in Sources */,
15AA6D1A2D68C4AD008021C6 /* ReviewVerificationErrorType.swift in Sources */,
741429652D5D470200B69528 /* SettingViewModel.swift in Sources */,
15CD256B2D3D61F500320006 /* GlassmorphismView.swift in Sources */,
Expand Down Expand Up @@ -2468,7 +2480,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 2025.1004.1036;
CURRENT_PROJECT_VERSION = 2025.1014.1518;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = KX5Q77JSUF;
Expand All @@ -2487,7 +2499,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.1.2;
MARKETING_VERSION = 2.1.3;
PRODUCT_BUNDLE_IDENTIFIER = "com.ACON.ACON-iOS";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand All @@ -2511,7 +2523,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 2025.1004.1036;
CURRENT_PROJECT_VERSION = 2025.1014.1518;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = KX5Q77JSUF;
GENERATE_INFOPLIST_FILE = NO;
Expand All @@ -2529,7 +2541,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.1.2;
MARKETING_VERSION = 2.1.3;
PRODUCT_BUNDLE_IDENTIFIER = "com.ACON.ACON-iOS";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand Down
4 changes: 2 additions & 2 deletions ACON-iOS/ACON-iOS/Global/Protocols/Serviceable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ extension Serviceable {
print("❄️ 기존 서버통신 다시 성공")
retryAction()
} else {
UserDefaultsManager.removeTokens()
UserDefaultsUtils.removeTokens()
NavigationUtils.navigateToSplash()
}
}
} catch {
DispatchQueue.main.async {
UserDefaultsManager.removeTokens()
UserDefaultsUtils.removeTokens()
NavigationUtils.navigateToSplash()
}
}
Expand Down
28 changes: 16 additions & 12 deletions ACON-iOS/ACON-iOS/Global/Service/AuthManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,49 +16,52 @@ final class AuthManager {
private let refreshInterval: TimeInterval = 3 * 60 * 60

var hasToken: Bool {
return UserDefaultsManager.get(String.self, forKey: .accessToken) != nil
return UserDefaultsUtils.get(String.self, forKey: .accessToken) != nil
}

var hasVerifiedArea: Bool {
return UserDefaultsManager.get(Bool.self, forKey: .hasVerifiedArea) ?? false
return UserDefaultsUtils.get(Bool.self, forKey: .hasVerifiedArea) ?? false
}

var hasPreference: Bool {
return UserDefaultsManager.get(Bool.self, forKey: .hasPreference) ?? false
return UserDefaultsUtils.get(Bool.self, forKey: .hasPreference) ?? false
}

var hasSeenTutorial: Bool {
return UserDefaultsManager.get(Bool.self, forKey: .hasSeenTutorial) ?? false
return UserDefaultsUtils.get(Bool.self, forKey: .hasSeenTutorial) ?? false
}

var hasSeenLocalVerification: Bool {
return UserDefaultsManager.get(Bool.self, forKey: .hasSeenLocalVerification) ?? false
return UserDefaultsUtils.get(Bool.self, forKey: .hasSeenLocalVerification) ?? false
}

var hasSeenPreference: Bool {
return UserDefaultsManager.get(Bool.self, forKey: .hasSeenPreference) ?? false
return UserDefaultsUtils.get(Bool.self, forKey: .hasSeenPreference) ?? false
}

func handleTokenRefresh() async throws -> Bool {
let refreshToken = UserDefaultsManager.get(String.self, forKey: .refreshToken) ?? ""
let refreshToken = UserDefaultsUtils.get(String.self, forKey: .refreshToken) ?? ""
return try await withCheckedThrowingContinuation { continuation in
ACService.shared.authService.postReissue(PostReissueRequest(refreshToken: refreshToken)) { response in
switch response {
case .success(let data):
print("❄️ token refreshed success")
UserDefaultsManager.set(data.accessToken, forKey: .accessToken)
UserDefaultsManager.set(data.refreshToken, forKey: .refreshToken)
UserDefaultsUtils.set(data.accessToken, forKey: .accessToken)
UserDefaultsUtils.set(data.refreshToken, forKey: .refreshToken)
AuthManager.shared.updateLastTokenRefreshDate()
TokenLogger.shared.log(.refreshSucceeded(tokenPrefix: String(data.accessToken.prefix(10))))
Copy link

Copilot AI Dec 20, 2025

Choose a reason for hiding this comment

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

Logging the first 10 characters of access tokens, even in debug/TestFlight builds, could pose a security risk. While this is limited to non-production builds, consider whether this amount of token information is necessary for debugging. A shorter prefix (e.g., 4-6 characters) or a hash of the token would be safer while still allowing token changes to be tracked.

Suggested change
TokenLogger.shared.log(.refreshSucceeded(tokenPrefix: String(data.accessToken.prefix(10))))
TokenLogger.shared.log(.refreshSucceeded(tokenPrefix: String(data.accessToken.prefix(6))))

Copilot uses AI. Check for mistakes.
continuation.resume(returning: true)
case .requestErr(let error):
if error.code == 40088 {
print("❄️ remove token")
UserDefaultsManager.removeTokens()
UserDefaultsUtils.removeTokens()
TokenLogger.shared.log(.refreshFailed(error: error.localizedDescription))
continuation.resume(returning: false)
} else {
Copy link

Copilot AI Dec 20, 2025

Choose a reason for hiding this comment

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

When a token refresh fails with a requestErr case but the error code is not 40088, the failure is not logged. This makes it harder to debug auto-login issues when non-40088 errors occur. Consider adding a log entry for this case as well, similar to line 58 and 64.

Suggested change
} else {
} else {
TokenLogger.shared.log(.refreshFailed(error: error.localizedDescription))

Copilot uses AI. Check for mistakes.
continuation.resume(returning: false)
}
default:
TokenLogger.shared.log(.refreshFailed(error: "unknown"))
continuation.resume(returning: false)
}
}
Expand All @@ -67,16 +70,17 @@ final class AuthManager {

func updateLastTokenRefreshDate() {
let now = Date()
UserDefaultsManager.set(now, forKey: .lastTokenRefreshDate)
UserDefaultsUtils.set(now, forKey: .lastTokenRefreshDate)
}

// NOTE: access token이 만료되었으면 true
func needsTokenRefresh() -> Bool {
guard let lastRefresh = UserDefaultsManager.get(Date.self, forKey: .lastTokenRefreshDate) else {
guard let lastRefresh = UserDefaultsUtils.get(Date.self, forKey: .lastTokenRefreshDate) else {
Copy link

Copilot AI Dec 20, 2025

Choose a reason for hiding this comment

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

When needsTokenRefresh() returns true because lastTokenRefreshDate is nil (line 79), no token log is generated. This means the first-time login scenario after app installation won't be logged with a clear state. Consider adding TokenLogger.shared.log(.tokenExpired) or a more specific log event before returning true on line 79.

Suggested change
guard let lastRefresh = UserDefaultsUtils.get(Date.self, forKey: .lastTokenRefreshDate) else {
guard let lastRefresh = UserDefaultsUtils.get(Date.self, forKey: .lastTokenRefreshDate) else {
TokenLogger.shared.log(.tokenExpired)

Copilot uses AI. Check for mistakes.
return true
}
let elapsed = Date().timeIntervalSince(lastRefresh)
print("❄️ token elapsed: \(elapsed) / \(refreshInterval) seconds")
TokenLogger.shared.log(elapsed >= refreshInterval ? .tokenExpired : .valid)
return elapsed >= refreshInterval
}

Expand Down
18 changes: 18 additions & 0 deletions ACON-iOS/ACON-iOS/Global/Settings/Config/BuildConfig.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// BuildConfig.swift
// ACON-iOS
//
// Created by 김유림 on 10/14/25.
//

import Foundation

enum BuildConfig {

#if DEBUG
static let isDebug = true
#else
static let isDebug = false
#endif

}
4 changes: 2 additions & 2 deletions ACON-iOS/ACON-iOS/Global/Settings/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>2.1.2</string>
<string>2.1.3</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
Expand All @@ -54,7 +54,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>2025.1004.1036</string>
<string>2025.1014.1518</string>
<key>GADApplicationIdentifier</key>
<string>${GAD_APPLICATION_IDENTIFIER}</string>
<key>GAD_AD_UNIT_ID</key>
Expand Down
4 changes: 2 additions & 2 deletions ACON-iOS/ACON-iOS/Global/Utils/Enums/HeaderType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ enum HeaderType {
}

static func headerWithToken() -> [String: String] {
if let token = UserDefaultsManager.get(String.self, forKey: .accessToken) {
if let token = UserDefaultsUtils.get(String.self, forKey: .accessToken) {
return ["Content-Type" : "application/json", "Authorization" : "Bearer " + token]
} else {
return basicHeader
}
}

static func tokenOnly() -> [String:String] {
if let token = UserDefaultsManager.get(String.self, forKey: .accessToken) {
if let token = UserDefaultsUtils.get(String.self, forKey: .accessToken) {
return ["Authorization" : "Bearer " + token]
} else {
return noHeader
Expand Down
Loading