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: 7 additions & 1 deletion Projects/App/Sources/Application/AppComponent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,17 @@
// Copyright ยฉ 2026 NDGL-iOS. All rights reserved.
//

import Data
import Domain
import RIBs

import RootFeature

final class AppComponent: Component<EmptyDependency>, RootDependency {

var tokenProvider: TokenProviding {
shared { TokenRepositoryFactory.makeTokenProvider() }
}

init() {
super.init(dependency: EmptyComponent())
}
Expand Down
89 changes: 89 additions & 0 deletions Projects/Core/Sources/Storage/KeychainStorage.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
//
// KeychainStorage.swift
// Core
//
// Created by NDGL on 2026-02-06.
// Copyright ยฉ 2026 NDGL-iOS. All rights reserved.
//

import Foundation
import Security

public protocol KeychainStorageProtocol {
func save(_ value: String, forKey key: String) -> Bool
func load(forKey key: String) -> String?
func delete(forKey key: String) -> Bool
func clear() -> Bool
}

public final class KeychainStorage: KeychainStorageProtocol {

private let service: String

public init(service: String = Bundle.main.bundleIdentifier ?? "com.ndgl.app") {
self.service = service
}

@discardableResult
public func save(_ value: String, forKey key: String) -> Bool {
guard let data = value.data(using: .utf8) else { return false }

// ๊ธฐ์กด ํ•ญ๋ชฉ ์‚ญ์ œ
delete(forKey: key)

let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: key,
kSecValueData as String: data,
kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
]

let status = SecItemAdd(query as CFDictionary, nil)
return status == errSecSuccess
}

public func load(forKey key: String) -> String? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: key,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne
]

var result: AnyObject?
let status = SecItemCopyMatching(query as CFDictionary, &result)

guard status == errSecSuccess,
let data = result as? Data,
let value = String(data: data, encoding: .utf8) else {
return nil
}

return value
}

@discardableResult
public func delete(forKey key: String) -> Bool {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: key
]

let status = SecItemDelete(query as CFDictionary)
return status == errSecSuccess || status == errSecItemNotFound
}

@discardableResult
public func clear() -> Bool {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service
]

let status = SecItemDelete(query as CFDictionary)
return status == errSecSuccess || status == errSecItemNotFound
}
}
23 changes: 23 additions & 0 deletions Projects/Data/Sources/Adapter/TokenProviderAdapter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// TokenProviderAdapter.swift
// Data
//
// Created by NDGL on 2026-02-06.
// Copyright ยฉ 2026 NDGL-iOS. All rights reserved.
//

import Domain
import Foundation

public final class TokenProviderAdapter: TokenProviding, @unchecked Sendable {

private let tokenRepository: TokenRepositoryProtocol

public init(tokenRepository: TokenRepositoryProtocol) {
self.tokenRepository = tokenRepository
}

public func accessToken() -> String? {
tokenRepository.get(.accessToken)
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// AuthRepositoryFactory.swift
// AuthServiceFactory.swift
// Data
//
// Created by kimnahun on 1/21/26.
Expand All @@ -13,7 +13,3 @@ import Networks
public func makeAuthService() -> AuthServiceProtocol {
AuthService()
}

public func makeAuthRepository(authService: AuthServiceProtocol) -> AuthRepositoryProtocol {
AuthRepository(authService: authService)
}
19 changes: 0 additions & 19 deletions Projects/Data/Sources/DI/FollowRepositoryFactory.swift

This file was deleted.

15 changes: 15 additions & 0 deletions Projects/Data/Sources/DI/FollowServiceFactory.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//
// FollowServiceFactory.swift
// Data
//
// Created by NDGL on 2026-02-06.
// Copyright ยฉ 2026 NDGL-iOS. All rights reserved.
//

import Domain
import Foundation
import Networks

public func makeFollowService() -> FollowServiceProtocol {
FollowService()
}
28 changes: 28 additions & 0 deletions Projects/Data/Sources/DI/TokenRepositoryFactory.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// TokenRepositoryFactory.swift
// Data
//
// Created by NDGL on 2026-02-06.
// Copyright ยฉ 2026 NDGL-iOS. All rights reserved.
//

import Core
import Domain
import Foundation

public enum TokenRepositoryFactory {

public static func make() -> TokenRepositoryProtocol {
let keychainStorage = KeychainStorage()
return TokenRepository(keychainStorage: keychainStorage)
}

public static func makeTokenProvider() -> TokenProviding {
let tokenRepository = make()
return TokenProviderAdapter(tokenRepository: tokenRepository)
}

public static func makeTokenProvider(with repository: TokenRepositoryProtocol) -> TokenProviding {
TokenProviderAdapter(tokenRepository: repository)
}
}
19 changes: 19 additions & 0 deletions Projects/Data/Sources/DI/TravelServiceFactory.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// TravelServiceFactory.swift
// Data
//
// Created by NDGL on 2026-02-06.
// Copyright ยฉ 2026 NDGL-iOS. All rights reserved.
//

import Domain
import Foundation
import Moya
import Networks

public func makeTravelService(tokenProvider: TokenProviding) -> TravelServiceProtocol {
let provider: MoyaProvider<TravelAPI> = NetworkProviderFactory.makeAuthenticatedProvider(
tokenProvider: tokenProvider
)
return TravelService(provider: provider)
}
40 changes: 0 additions & 40 deletions Projects/Data/Sources/Repository/Auth/AuthRepository.swift

This file was deleted.

38 changes: 38 additions & 0 deletions Projects/Data/Sources/Repository/Auth/TokenRepository.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// TokenRepository.swift
// Data
//
// Created by NDGL on 2026-02-06.
// Copyright ยฉ 2026 NDGL-iOS. All rights reserved.
//

import Core
import Domain
import Foundation

public final class TokenRepository: TokenRepositoryProtocol {

// MARK: - Properties

private let keychainStorage: KeychainStorageProtocol

// MARK: - Initialization

public init(keychainStorage: KeychainStorageProtocol) {
self.keychainStorage = keychainStorage
}

// MARK: - TokenRepositoryProtocol

public func save(_ value: String, for type: TokenType) {
keychainStorage.save(value, forKey: type.rawValue)
}

public func get(_ type: TokenType) -> String? {
keychainStorage.load(forKey: type.rawValue)
}

public func delete(_ type: TokenType) {
keychainStorage.delete(forKey: type.rawValue)
}
Comment on lines +27 to +37

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

โš ๏ธ Potential issue | ๐ŸŸก Minor

save์™€ delete ๋ฉ”์„œ๋“œ์—์„œ ๋ฐ˜ํ™˜๊ฐ’์ด ๋ฌด์‹œ๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

keychainStorage.save()์™€ keychainStorage.delete()์˜ Bool ๋ฐ˜ํ™˜๊ฐ’์ด ๋ฌด์‹œ๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ €์žฅ/์‚ญ์ œ ์‹คํŒจ ์‹œ ํ˜ธ์ถœ์ž์—๊ฒŒ ์•Œ๋ ค์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๋ฅผ ๊ณ ๋ คํ•ด ๋ณด์„ธ์š”.

๐Ÿ’ก ๋ฐ˜ํ™˜๊ฐ’ ์ „ํŒŒ ๊ณ ๋ ค

ํ˜„์žฌ ๊ตฌํ˜„๋„ ๋ฌธ์ œ์—†์ด ๋™์ž‘ํ•˜์ง€๋งŒ, ์‹คํŒจ ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ๋ฅผ ์œ„ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

-    public func save(_ value: String, for type: TokenType) {
-        keychainStorage.save(value, forKey: type.rawValue)
+    `@discardableResult`
+    public func save(_ value: String, for type: TokenType) -> Bool {
+        keychainStorage.save(value, forKey: type.rawValue)
     }

๋˜๋Š” ํ˜„์žฌ ํ”„๋กœํ† ์ฝœ ์„ค๊ณ„๊ฐ€ void ๋ฐ˜ํ™˜์„ ์˜๋„ํ•œ ๊ฒƒ์ด๋ผ๋ฉด ๋ฌด์‹œํ•ด๋„ ๋ฉ๋‹ˆ๋‹ค.

๐Ÿ“ Committable suggestion

โ€ผ๏ธ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public func save(_ value: String, for type: TokenType) {
keychainStorage.save(value, forKey: type.rawValue)
}
public func get(_ type: TokenType) -> String? {
keychainStorage.load(forKey: type.rawValue)
}
public func delete(_ type: TokenType) {
keychainStorage.delete(forKey: type.rawValue)
}
`@discardableResult`
public func save(_ value: String, for type: TokenType) -> Bool {
keychainStorage.save(value, forKey: type.rawValue)
}
public func get(_ type: TokenType) -> String? {
keychainStorage.load(forKey: type.rawValue)
}
public func delete(_ type: TokenType) {
keychainStorage.delete(forKey: type.rawValue)
}
๐Ÿค– Prompt for AI Agents
In `@Projects/Data/Sources/Repository/Auth/TokenRepository.swift` around lines 27
- 37, ํ˜„์žฌ save(_:for:)์™€ delete(_:)๊ฐ€ keychainStorage.save(...)์™€
keychainStorage.delete(...)์˜ Bool ๋ฐ˜ํ™˜๊ฐ’์„ ๋ฌด์‹œํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ ์‹คํŒจ๋ฅผ ํ˜ธ์ถœ์ž์—๊ฒŒ ์ „๋‹ฌํ•˜๋„๋ก ๋ณ€๊ฒฝํ•˜์„ธ์š”: update
the public func save(_ value: String, for type: TokenType) and public func
delete(_ type: TokenType) to either return Bool (propagate the result of
keychainStorage.save(...) / keychainStorage.delete(...)) or throw an error based
on the Bool, keeping public func get(_:) unchanged; reference
keychainStorage.save, keychainStorage.delete, save(_:for:) and delete(_:) when
making the change.

}
41 changes: 0 additions & 41 deletions Projects/Data/Sources/Repository/Follow/FollowRepository.swift

This file was deleted.

27 changes: 0 additions & 27 deletions Projects/Data/Sources/Transform/Auth/AuthTransform.swift

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
//
// AuthRepositoryProtocol.swift
// AuthServiceProtocol.swift
// Domain
//
// Created by kimnahun on 1/21/26.
// Created by NDGL on 2026-02-06.
// Copyright ยฉ 2026 NDGL-iOS. All rights reserved.
//

import Foundation

public protocol AuthRepositoryProtocol: Sendable {
public protocol AuthServiceProtocol: Sendable {
func signup(info: SignupInfo) async -> Result<SignupResult, SignupError>
}
Loading