From 7a135fdfa3e88831a78670d76ae803571a155b6a Mon Sep 17 00:00:00 2001 From: kangddong Date: Wed, 18 Feb 2026 18:46:12 +0900 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20=ED=94=BC=EC=B2=98=20=EA=B0=84=20?= =?UTF-8?q?=EA=B3=B5=EC=9C=A0=20=EB=90=98=EB=8A=94=20=EA=B0=9D=EC=B2=B4?= =?UTF-8?q?=EB=93=A4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Extensions/Date+Extensions.swift | 96 +++++++++++++++++++ .../Sources/Extensions/Int+Extensions.swift | 27 ++++++ .../DoriCore/Sources/Models/EventType.swift | 19 ++++ .../Sources/Models/Relationship.swift | 17 ++++ .../Sources/Models/TransactionType.swift | 15 +++ .../Sources/AmountLabel.swift | 21 ++++ .../Sources/FloatingActionButton.swift | 55 +++++++++++ .../Sources/PageIndicator.swift | 42 ++++++++ .../Sources/Typography/TypoToken.swift | 78 +++++++++++++++ .../Onboarding/Sources/IntroView.swift | 35 ------- Projects/Feature/Project.swift | 2 +- 11 files changed, 371 insertions(+), 36 deletions(-) create mode 100644 Projects/Core/DoriCore/Sources/Extensions/Date+Extensions.swift create mode 100644 Projects/Core/DoriCore/Sources/Extensions/Int+Extensions.swift create mode 100644 Projects/Core/DoriCore/Sources/Models/EventType.swift create mode 100644 Projects/Core/DoriCore/Sources/Models/Relationship.swift create mode 100644 Projects/Core/DoriCore/Sources/Models/TransactionType.swift create mode 100644 Projects/Core/DoriDesignSystem/Sources/AmountLabel.swift create mode 100644 Projects/Core/DoriDesignSystem/Sources/FloatingActionButton.swift create mode 100644 Projects/Core/DoriDesignSystem/Sources/PageIndicator.swift create mode 100644 Projects/Core/DoriDesignSystem/Sources/Typography/TypoToken.swift diff --git a/Projects/Core/DoriCore/Sources/Extensions/Date+Extensions.swift b/Projects/Core/DoriCore/Sources/Extensions/Date+Extensions.swift new file mode 100644 index 0000000..b9d5cb0 --- /dev/null +++ b/Projects/Core/DoriCore/Sources/Extensions/Date+Extensions.swift @@ -0,0 +1,96 @@ +// +// Date+Extensions.swift +// Dori-iOS +// +// Created by 강동영 on 2/11/26. +// + +import Foundation + +public extension Date { + private static let koreanLocale = Locale(identifier: "ko_KR") + private static let koreanCalendar: Calendar = { + var calendar = Calendar(identifier: .gregorian) + calendar.locale = koreanLocale + return calendar + }() + + // "12월 12일(금)" 형식 + var koreanDateWithWeekday: String { + let formatter = DateFormatter() + formatter.locale = Self.koreanLocale + formatter.dateFormat = "M월 d일(E)" + return formatter.string(from: self) + } + + // "2024년 12월" 형식 + var koreanYearMonth: String { + let formatter = DateFormatter() + formatter.locale = Self.koreanLocale + formatter.dateFormat = "yyyy년 M월" + return formatter.string(from: self) + } + + // "12월" 형식 + var koreanMonth: String { + let formatter = DateFormatter() + formatter.locale = Self.koreanLocale + formatter.dateFormat = "M월" + return formatter.string(from: self) + } + + // 해당 월의 첫째 날 + var startOfMonth: Date { + Self.koreanCalendar.date(from: Self.koreanCalendar.dateComponents([.year, .month], from: self)) ?? self + } + + // 해당 월의 마지막 날 + var endOfMonth: Date { + Self.koreanCalendar.date(byAdding: DateComponents(month: 1, day: -1), to: startOfMonth) ?? self + } + + // 해당 월의 일 수 + var daysInMonth: Int { + Self.koreanCalendar.range(of: .day, in: .month, for: self)?.count ?? 30 + } + + // 해당 월 1일의 요일 (일요일 = 1) + var firstWeekdayOfMonth: Int { + Self.koreanCalendar.component(.weekday, from: startOfMonth) + } + + // 해당 날짜의 일(day) + var day: Int { + Self.koreanCalendar.component(.day, from: self) + } + + // 해당 날짜의 월(month) + var month: Int { + Self.koreanCalendar.component(.month, from: self) + } + + // 해당 날짜의 연(year) + var year: Int { + Self.koreanCalendar.component(.year, from: self) + } + + // 이전 달 + var previousMonth: Date { + Self.koreanCalendar.date(byAdding: .month, value: -1, to: self) ?? self + } + + // 다음 달 + var nextMonth: Date { + Self.koreanCalendar.date(byAdding: .month, value: 1, to: self) ?? self + } + + // 같은 날인지 확인 + func isSameDay(as other: Date) -> Bool { + Self.koreanCalendar.isDate(self, inSameDayAs: other) + } + + // 같은 달인지 확인 + func isSameMonth(as other: Date) -> Bool { + Self.koreanCalendar.isDate(self, equalTo: other, toGranularity: .month) + } +} diff --git a/Projects/Core/DoriCore/Sources/Extensions/Int+Extensions.swift b/Projects/Core/DoriCore/Sources/Extensions/Int+Extensions.swift new file mode 100644 index 0000000..207aead --- /dev/null +++ b/Projects/Core/DoriCore/Sources/Extensions/Int+Extensions.swift @@ -0,0 +1,27 @@ +// +// Int+Extensions.swift +// Dori-iOS +// +// Created by 강동영 on 2/11/26. +// + +import Foundation + +public extension Int { + // "100,000원" 형식 + var wonFormatted: String { + let formatter = NumberFormatter() + formatter.numberStyle = .decimal + formatter.locale = Locale(identifier: "ko_KR") + let formatted = formatter.string(from: NSNumber(value: self)) ?? "\(self)" + return "\(formatted)원" + } + + // "100,000" 형식 (원 없이) + var decimalFormatted: String { + let formatter = NumberFormatter() + formatter.numberStyle = .decimal + formatter.locale = Locale(identifier: "ko_KR") + return formatter.string(from: NSNumber(value: self)) ?? "\(self)" + } +} diff --git a/Projects/Core/DoriCore/Sources/Models/EventType.swift b/Projects/Core/DoriCore/Sources/Models/EventType.swift new file mode 100644 index 0000000..90b71af --- /dev/null +++ b/Projects/Core/DoriCore/Sources/Models/EventType.swift @@ -0,0 +1,19 @@ +// +// EventType.swift +// Dori-iOS +// +// Created by 강동영 on 2/15/26. +// + +import Foundation + +public enum EventType: String, CaseIterable, Codable, Equatable, Sendable, Identifiable { + case wedding = "결혼식" + case funeral = "장례식" + case firstBirthday = "돌잔치" + case housewarming = "집들이" + case birthday = "생일" + case other = "기타" + + public var id: String { rawValue } +} diff --git a/Projects/Core/DoriCore/Sources/Models/Relationship.swift b/Projects/Core/DoriCore/Sources/Models/Relationship.swift new file mode 100644 index 0000000..98be58f --- /dev/null +++ b/Projects/Core/DoriCore/Sources/Models/Relationship.swift @@ -0,0 +1,17 @@ +// +// Relationship.swift +// Dori-iOS +// +// Created by 강동영 on 2/15/26. +// + +import Foundation + +public enum Relationship: String, CaseIterable, Codable, Equatable, Sendable, Hashable, Identifiable { + case friend = "친구" + case family = "가족" + case company = "회사" + case other = "기타" + + public var id: String { rawValue } +} diff --git a/Projects/Core/DoriCore/Sources/Models/TransactionType.swift b/Projects/Core/DoriCore/Sources/Models/TransactionType.swift new file mode 100644 index 0000000..8588d33 --- /dev/null +++ b/Projects/Core/DoriCore/Sources/Models/TransactionType.swift @@ -0,0 +1,15 @@ +// +// TransactionType.swift +// Dori-iOS +// +// Created by 강동영 on 2/15/26. +// + +import Foundation + +public enum TransactionType: String, CaseIterable, Codable, Equatable, Sendable, Hashable, Identifiable { + case given = "주도리" + case received = "받도리" + + public var id: String { rawValue } +} diff --git a/Projects/Core/DoriDesignSystem/Sources/AmountLabel.swift b/Projects/Core/DoriDesignSystem/Sources/AmountLabel.swift new file mode 100644 index 0000000..2d6c37a --- /dev/null +++ b/Projects/Core/DoriDesignSystem/Sources/AmountLabel.swift @@ -0,0 +1,21 @@ +// +// AmountLabel.swift +// Dori-iOS +// +// Created by 강동영 on 2/18/26. +// + +import SwiftUI +import DoriCore + +public struct AmountLabel: View { + let amount: Int + + public init(_ amount: Int) { + self.amount = amount + } + + public var body: some View { + Text(amount.wonFormatted) + } +} diff --git a/Projects/Core/DoriDesignSystem/Sources/FloatingActionButton.swift b/Projects/Core/DoriDesignSystem/Sources/FloatingActionButton.swift new file mode 100644 index 0000000..93ef7ef --- /dev/null +++ b/Projects/Core/DoriDesignSystem/Sources/FloatingActionButton.swift @@ -0,0 +1,55 @@ +// +// FloatingActionButton.swift +// Dori-iOS +// +// Created by 강동영 on 2/15/26. +// + +import SwiftUI + +public struct FloatingActionButton: View { + let action: () -> Void + + public init(action: @escaping () -> Void) { + self.action = action + } + + public var body: some View { + Button(action: action) { + Image(systemName: "plus") + .font(.title2) + .fontWeight(.semibold) + .foregroundColor(.white) + .frame( + width: 56, + height: 56 + ) + .background(DoriColors.main.color) + .clipShape(Circle()) + .shadow( + color: .black.opacity(0.3), + radius: 4, + x: 0, + y: 2 + ) + } + } +} + +#Preview { + ZStack { + Color.gray.opacity(0.2) + .ignoresSafeArea() + + VStack { + Spacer() + HStack { + Spacer() + FloatingActionButton { + print("FAB tapped") + } + .padding() + } + } + } +} diff --git a/Projects/Core/DoriDesignSystem/Sources/PageIndicator.swift b/Projects/Core/DoriDesignSystem/Sources/PageIndicator.swift new file mode 100644 index 0000000..d3d3cc7 --- /dev/null +++ b/Projects/Core/DoriDesignSystem/Sources/PageIndicator.swift @@ -0,0 +1,42 @@ +// +// PageIndicator.swift +// Dori-iOS +// +// Created by 강동영 on 2/15/26. +// + +import SwiftUI + +public struct PageIndicator: View { + let count: Int + @Binding var currentIndex: Int? + + public init( + count: Int, + currentIndex: Binding + ) { + self.count = count + if currentIndex.wrappedValue == nil { + self._currentIndex = .constant(0) + } else { + self._currentIndex = currentIndex + } + } + + public var body: some View { + HStack { + ForEach(0.. FontStyle { + let spec = styleSpec + let fontName = spec.weight.getFontName(from: provider) + return FontStyle(.custom(fontName), size: spec.size) + } +} + +public extension TypoStyle { + enum Heading: Int, CaseIterable { + case h11 = 11, h13 = 13, h14 = 14, h15 = 15, h16 = 16, h20 = 20, h30 = 30 + + var size: CGFloat { CGFloat(rawValue) } + + // 등차수열 적용 + var lineHeight: CGFloat { 2 * size - 6 } + } + + enum SubTitle: Int, CaseIterable { + case t12 = 12, t14 = 14, t15 = 15, t16 = 16, t20 = 20 + + var size: CGFloat { CGFloat(rawValue) } + + // 등차수열 적용 + var lineHeight: CGFloat { 2 * size - 6 } + } + + enum Body: Int, CaseIterable { + case b11 = 11, b12, b13, b14, b15, b16 + + var size: CGFloat { CGFloat(rawValue) } + + // 등차수열 적용 + var lineHeight: CGFloat { 2 * size - 6 } + } + + enum Caption: Int, CaseIterable { + case c11 = 11, c12, c13, c14, c15, c16, c18 = 18, c20 = 20 + + var size: CGFloat { CGFloat(rawValue) } + + // 등차수열 적용 + var lineHeight: CGFloat { 2 * size - 6 } + } +} diff --git a/Projects/Feature/Onboarding/Sources/IntroView.swift b/Projects/Feature/Onboarding/Sources/IntroView.swift index 3cb5b64..299cd8f 100644 --- a/Projects/Feature/Onboarding/Sources/IntroView.swift +++ b/Projects/Feature/Onboarding/Sources/IntroView.swift @@ -170,41 +170,6 @@ public struct IntroView: View { } } -struct PageIndicator: View { - let count: Int - @Binding var currentIndex: Int? - - init( - count: Int, - currentIndex: Binding - ) { - self.count = count - if currentIndex.wrappedValue == nil { - self._currentIndex = .constant(0) - } else { - self._currentIndex = currentIndex - } - - } - - var body: some View { - HStack { - ForEach(0.. Date: Sat, 14 Feb 2026 20:29:34 +0900 Subject: [PATCH 2/4] =?UTF-8?q?chore(tuist):=20tuist=20=ED=97=AC=ED=8D=BC?= =?UTF-8?q?=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Projects/App/Project.swift | 9 +- Projects/Feature/Project.swift | 10 +- Projects/Infra/Project.swift | 2 +- Projects/Platform/Project.swift | 8 +- .../DoriManifest.swift | 38 ------- .../DoriTargets.swift | 101 +----------------- .../Environment.swift | 69 ++++++++++++ .../InfoPlist+Extension.swift | 32 ++++++ .../Project+Extension.swift | 26 +++++ .../Settings+Extension.swift | 96 +++++++++++++++++ .../Target+Extension.swift | 68 ++++++++++++ .../TargetDependency+Extension.swift | 19 ++++ 12 files changed, 329 insertions(+), 149 deletions(-) delete mode 100644 Tuist/ProjectDescriptionHelpers/DoriManifest.swift create mode 100644 Tuist/ProjectDescriptionHelpers/Environment.swift create mode 100644 Tuist/ProjectDescriptionHelpers/InfoPlist+Extension.swift create mode 100644 Tuist/ProjectDescriptionHelpers/Project+Extension.swift create mode 100644 Tuist/ProjectDescriptionHelpers/Settings+Extension.swift create mode 100644 Tuist/ProjectDescriptionHelpers/Target+Extension.swift create mode 100644 Tuist/ProjectDescriptionHelpers/TargetDependency+Extension.swift diff --git a/Projects/App/Project.swift b/Projects/App/Project.swift index 591b129..ed7267a 100644 --- a/Projects/App/Project.swift +++ b/Projects/App/Project.swift @@ -10,7 +10,10 @@ import ProjectDescriptionHelpers let project = Project.dori( targets: [ - DoriAppTarget.make( + .app( + name: "DoriApp", + bundleId: Environment.App.baseBundleId, + resources: [.glob(pattern: "Resources/**", excluding: ["Resources/info.plist"])], dependencies: [ DoriModules.onboarding.module.projectDependency, DoriModules.calendar.module.projectDependency, @@ -22,8 +25,8 @@ let project = Project.dori( DoriModules.keychain.module.projectDependency, DoriModules.designSystem.module.projectDependency, DoriModules.core.module.projectDependency, - DoriDependency.composableArchitecture, - ] + .external(.composableArchitecture) + ], ), ] ) diff --git a/Projects/Feature/Project.swift b/Projects/Feature/Project.swift index 285cf0e..a494dd2 100644 --- a/Projects/Feature/Project.swift +++ b/Projects/Feature/Project.swift @@ -23,15 +23,19 @@ let project = Project.dori( ), .doriFramework( DoriModules.calendar.module, - dependencies: [DoriDependency.composableArchitecture] + dependencies: [.external(.composableArchitecture)] ), .doriFramework( DoriModules.history.module, - dependencies: [DoriDependency.composableArchitecture] + dependencies: [.external(.composableArchitecture)] ), .doriFramework( DoriModules.myPage.module, - dependencies: [DoriDependency.composableArchitecture] + dependencies: [ + DoriModules.designSystem.module.projectDependency, + DoriModules.network.module.projectDependency, + .external(.composableArchitecture) + ] ), ] ) diff --git a/Projects/Infra/Project.swift b/Projects/Infra/Project.swift index b8e90f8..05fcf93 100644 --- a/Projects/Infra/Project.swift +++ b/Projects/Infra/Project.swift @@ -17,7 +17,7 @@ let project = Project.dori( DoriModules.networkImpl.module, dependencies: [ DoriModules.network.module.targetDependency, - DoriDependency.alamofire, + .external(.alamofire), ] ), ] diff --git a/Projects/Platform/Project.swift b/Projects/Platform/Project.swift index c47d7e9..d758a23 100644 --- a/Projects/Platform/Project.swift +++ b/Projects/Platform/Project.swift @@ -14,10 +14,10 @@ let project = Project.dori( .doriFramework( DoriModules.kakaoAuth.module, dependencies: [ - DoriDependency.composableArchitecture, - DoriDependency.kakaoSDKCommon, - DoriDependency.kakaoSDKAuth, - DoriDependency.kakaoSDKUser, + .external(.composableArchitecture), + .external(.kakaoSDKCommon), + .external(.kakaoSDKAuth), + .external(.kakaoSDKUser) ] ), .doriFramework( diff --git a/Tuist/ProjectDescriptionHelpers/DoriManifest.swift b/Tuist/ProjectDescriptionHelpers/DoriManifest.swift deleted file mode 100644 index 96b1c41..0000000 --- a/Tuist/ProjectDescriptionHelpers/DoriManifest.swift +++ /dev/null @@ -1,38 +0,0 @@ -import ProjectDescription - -public enum DoriManifest { - public static let projectName = "Dori-iOS" - public static let organizationName = "com.arex" - public static let bundleIDPrefix = "com.arex.dori" - public static var deploymentTarget: DeploymentTargets { - .iOS("17.6") - } - - public static var commonSettings: SettingsDictionary { - [ - "SWIFT_VERSION": "6.0", - ] - } -} - -public enum DoriDependency { - public static var alamofire: TargetDependency { - .external(name: "Alamofire") - } - - public static var composableArchitecture: TargetDependency { - .external(name: "ComposableArchitecture") - } - - public static var kakaoSDKCommon: TargetDependency { - .external(name: "KakaoSDKCommon") - } - - public static var kakaoSDKAuth: TargetDependency { - .external(name: "KakaoSDKAuth") - } - - public static var kakaoSDKUser: TargetDependency { - .external(name: "KakaoSDKUser") - } -} diff --git a/Tuist/ProjectDescriptionHelpers/DoriTargets.swift b/Tuist/ProjectDescriptionHelpers/DoriTargets.swift index 1b2658a..fb76973 100644 --- a/Tuist/ProjectDescriptionHelpers/DoriTargets.swift +++ b/Tuist/ProjectDescriptionHelpers/DoriTargets.swift @@ -15,7 +15,7 @@ public enum DoriLayer: String, CaseIterable, Sendable { /// Per-layer Project.swift의 project name public var projectName: String { switch self { - case .app: DoriManifest.projectName + case .app: Environment.projectName default: "Dori\(rawValue)" } } @@ -93,102 +93,3 @@ public enum DoriModules: CaseIterable, Sendable { } } } - -public enum DoriAppTarget { - public static let name = "DoriApp" - - private static let path = "." - private static let rootPath = "Projects/App" - private static let xcconfigPath = "\(rootPath)/Resources/Common.xcconfig" - - public static func make(dependencies: [TargetDependency]) -> Target { - .target( - name: name, - destinations: .iOS, - product: .app, - bundleId: DoriManifest.bundleIDPrefix, - deploymentTargets: DoriManifest.deploymentTarget, - infoPlist: .extendingDefault(with: [ - "UILaunchScreen": .dictionary([:]), - "BASE_URL": "$(BASE_URL)", - "KAKAO_NATIVE_APP_KEY": "$(KAKAO_NATIVE_APP_KEY)", - "Appearance": "Light", - "CFBundleURLTypes": [ - [ - "CFBundleTypeRole": "Editor", - "CFBundleURLName": Plist.Value.string(DoriManifest.bundleIDPrefix), - "CFBundleURLSchemes": ["$(KAKAO_CAllBACK)"], - ], - ], - "LSApplicationQueriesSchemes": [ - "kakaokompassauth", - "kakaolink", - ], - ]), - sources: ["Sources/**"], - resources: [.glob(pattern: "Resources/**", excluding: ["Resources/info.plist"])], - dependencies: dependencies, - settings: .settings( - base: DoriManifest.commonSettings, - configurations: [ - .debug(name: "Debug", xcconfig: .relativeToRoot(xcconfigPath)), - .release(name: "Release", xcconfig: .relativeToRoot(xcconfigPath)), - ] - ) - ) - } -} - -public extension Target { - static func doriFramework( - _ module: DoriModule, - dependencies: [TargetDependency] = [], - hasResources: Bool = false - ) -> Target { - .target( - name: module.name, - destinations: .iOS, - product: .framework, - bundleId: "\(DoriManifest.bundleIDPrefix).\(module.name)", - deploymentTargets: DoriManifest.deploymentTarget, - sources: ["\(module.localPath)/Sources/**"], - resources: hasResources ? ["\(module.localPath)/Resources/**"] : nil, - dependencies: dependencies, - settings: .settings(base: DoriManifest.commonSettings) - ) - } - - static func doriUnitTests( - _ module: DoriModule, - dependencies: [TargetDependency] = [] - ) -> Target { - .target( - name: "\(module.name)Tests", - destinations: .iOS, - product: .unitTests, - bundleId: "\(DoriManifest.bundleIDPrefix).\(module.name)Tests", - deploymentTargets: DoriManifest.deploymentTarget, - sources: ["\(module.localPath)/Tests/**"], - dependencies: [.target(name: module.name)] + dependencies, - settings: .settings(base: DoriManifest.commonSettings) - ) - } -} - -public extension Project { - static func dori( - name: String = DoriManifest.projectName, - packages: [Package] = [], - targets: [Target], - resourceSynthesizers: [ResourceSynthesizer] = [] - ) -> Project { - Project( - name: name, - organizationName: DoriManifest.organizationName, - packages: packages, - settings: .settings(base: DoriManifest.commonSettings), - targets: targets, - resourceSynthesizers: resourceSynthesizers - ) - } -} diff --git a/Tuist/ProjectDescriptionHelpers/Environment.swift b/Tuist/ProjectDescriptionHelpers/Environment.swift new file mode 100644 index 0000000..db881b3 --- /dev/null +++ b/Tuist/ProjectDescriptionHelpers/Environment.swift @@ -0,0 +1,69 @@ +// +// Environment.swift +// ProjectDescriptionHelpers +// +// Created by 강동영 on 2/12/26. +// + +import Foundation + +import ProjectDescription + +// MARK: - Build Configuration +public enum BuildConfiguration: String, CaseIterable { + case debug = "Debug" + case release = "Release" + + public var bundleIdSuffix: String { + switch self { + case .debug: return "" + case .release: return "" + } + } + + public var appName: String { + switch self { + case .debug: return "Dori-Debug" + case .release: return "Dori" + } + } +} + +// MARK: - Environment +public struct Environment { + public static let deploymentTarget = "17.6" + public static let teamID = "T5D2PB4P5T" + public static let organizationName = "com.arex" + public static let defaultRegion = "ko" + public static let projectName = "Dori-iOS" + + public struct App { + public static let baseBundleId = "\(organizationName).dori" + public static let displayName = "도리" + public static let version = "1.0.0" + public static let buildNumber = "1" + + public static func bundleId(for configuration: BuildConfiguration = .release) -> String { + baseBundleId + configuration.bundleIdSuffix + } + } + + public static func bundleId(for module: String, configuration: BuildConfiguration = .release) -> String { + "\(organizationName).\(module.lowercased())\(configuration.bundleIdSuffix)" + } + + public static func bundleId(category: ModuleCategory, module: String, configuration: BuildConfiguration = .release) -> String { + "\(organizationName).\(category.rawValue).\(module.lowercased())\(configuration.bundleIdSuffix)" + } +} + +// MARK: - Module Categories +public enum ModuleCategory: String, CaseIterable { + case app = "app" + case core = "core" + case feature = "feature" + case domain = "domain" + case data = "data" + case shared = "shared" + case plugin = "plugin" +} diff --git a/Tuist/ProjectDescriptionHelpers/InfoPlist+Extension.swift b/Tuist/ProjectDescriptionHelpers/InfoPlist+Extension.swift new file mode 100644 index 0000000..1df6cef --- /dev/null +++ b/Tuist/ProjectDescriptionHelpers/InfoPlist+Extension.swift @@ -0,0 +1,32 @@ +// +// InfoPlist+Extension.swift +// Manifests +// +// Created by 강동영 on 2/13/26. +// + +import ProjectDescription + +extension InfoPlist { + static let commonDictionary: [String: Plist.Value] = [ + "UILaunchScreen": .dictionary([:]), + "BASE_URL": "$(BASE_URL)", + "KAKAO_NATIVE_APP_KEY": "$(KAKAO_NATIVE_APP_KEY)", + "Appearance": "Light", + "CFBundleURLTypes": [ + [ + "CFBundleTypeRole": "Editor", + "CFBundleURLName": Plist.Value.string(Environment.App.baseBundleId), + "CFBundleURLSchemes": ["$(KAKAO_CAllBACK)"], + ], + ], + "LSApplicationQueriesSchemes": [ + "kakaokompassauth", + "kakaolink", + ], + ] + + public static func baseInfoPlist() -> InfoPlist { + return .extendingDefault(with: commonDictionary) + } +} diff --git a/Tuist/ProjectDescriptionHelpers/Project+Extension.swift b/Tuist/ProjectDescriptionHelpers/Project+Extension.swift new file mode 100644 index 0000000..f504eba --- /dev/null +++ b/Tuist/ProjectDescriptionHelpers/Project+Extension.swift @@ -0,0 +1,26 @@ +// +// Project+Extension.swift +// ProjectDescriptionHelpers +// +// Created by 강동영 on 2/13/26. +// + +import ProjectDescription + +public extension Project { + static func dori( + name: String = Environment.projectName, + packages: [Package] = [], + targets: [Target], + resourceSynthesizers: [ResourceSynthesizer] = [] + ) -> Project { + Project( + name: name, + organizationName: Environment.organizationName, + packages: packages, + settings: .settings(), + targets: targets, + resourceSynthesizers: resourceSynthesizers + ) + } +} diff --git a/Tuist/ProjectDescriptionHelpers/Settings+Extension.swift b/Tuist/ProjectDescriptionHelpers/Settings+Extension.swift new file mode 100644 index 0000000..6c08689 --- /dev/null +++ b/Tuist/ProjectDescriptionHelpers/Settings+Extension.swift @@ -0,0 +1,96 @@ +// +// Settings+Extension.swift +// ProjectDescriptionHelpers +// +// Created by 강동영 on 2/12/26. +// + +import ProjectDescription + +public extension Settings { + /// 프레임워크용 기본 설정 + static let frameworkSettings: Settings = .settings( + base: [ + "SKIP_INSTALL": "YES", + "DEFINES_MODULE": "YES", + "ENABLE_BITCODE": "NO", + "IPHONEOS_DEPLOYMENT_TARGET": .string(Environment.deploymentTarget), + "SWIFT_VERSION": "6.0", + "CLANG_ENABLE_MODULES": "YES" + ] + ) + + /// 테스트용 기본 설정 + static let testSettings: Settings = .settings( + base: [ + "ENABLE_TESTING_SEARCH_PATHS": "YES", + "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES": "YES", + "ENABLE_TESTABILITY": "YES", + "IPHONEOS_DEPLOYMENT_TARGET": .string(Environment.deploymentTarget), + "SWIFT_VERSION": "6.0" + ] + ) + + /// 앱용 설정 + static func appSettings( + teamID: String = Environment.teamID + ) -> Settings { + let rootPath = "Projects/App" + let xcconfigPath = "\(rootPath)/Resources/Common.xcconfig" + + let baseSettings: [String: SettingValue] = [ + "APP_NAME": .string(Environment.App.displayName), + "CODE_SIGN_STYLE": "Automatic", + "DEVELOPMENT_TEAM": .string(teamID), + "MARKETING_VERSION": .string(Environment.App.version), + "CURRENT_PROJECT_VERSION": .string(Environment.App.buildNumber), + "ENABLE_BITCODE": "NO", + "IPHONEOS_DEPLOYMENT_TARGET": .string(Environment.deploymentTarget), + "SWIFT_VERSION": "6.0", + ] + + let debugSettings: [String: SettingValue] = [ + "PRODUCT_NAME": .string(BuildConfiguration.debug.appName), + "ENABLE_TESTABILITY": "YES", + "GCC_OPTIMIZATION_LEVEL": "0", + "SWIFT_OPTIMIZATION_LEVEL": "-Onone", + "DEBUG_INFORMATION_FORMAT": "dwarf", + "GCC_PREPROCESSOR_DEFINITIONS": .array(["DEBUG=1"]) + ] + + let releaseSettings: [String: SettingValue] = [ + "PRODUCT_NAME": .string(BuildConfiguration.release.appName), + "SWIFT_OPTIMIZATION_LEVEL": "-O", + "ENABLE_TESTABILITY": "NO", + "DEBUG_INFORMATION_FORMAT": "dwarf-with-dsym", + "SWIFT_COMPILATION_MODE": "wholemodule" + ] + + return .settings( + base: baseSettings, + configurations: [ + .debug( + name: .debug, + settings: debugSettings, + xcconfig: .relativeToRoot(xcconfigPath) + ), + .release( + name: .release, + settings: releaseSettings, + xcconfig: .relativeToRoot(xcconfigPath) + ) + ] + ) + } + + /// 데모 앱용 설정 + static let demoAppSettings: Settings = .settings( + base: [ + "CODE_SIGN_STYLE": "Automatic", + "DEVELOPMENT_TEAM": .string(Environment.teamID), + "IPHONEOS_DEPLOYMENT_TARGET": .string(Environment.deploymentTarget), + "SWIFT_VERSION": "6.0", + "ENABLE_TESTABILITY": "YES" + ] + ) +} diff --git a/Tuist/ProjectDescriptionHelpers/Target+Extension.swift b/Tuist/ProjectDescriptionHelpers/Target+Extension.swift new file mode 100644 index 0000000..a392906 --- /dev/null +++ b/Tuist/ProjectDescriptionHelpers/Target+Extension.swift @@ -0,0 +1,68 @@ +// +// Target+Extension.swift +// ProjectDescriptionHelpers +// +// Created by 강동영 on 2/13/26. +// + +import ProjectDescription + +public extension Target { + static func doriFramework( + _ module: DoriModule, + dependencies: [TargetDependency] = [], + hasResources: Bool = false + ) -> Target { + .target( + name: module.name, + destinations: .iOS, + product: .framework, + bundleId: "\(Environment.App.baseBundleId).\(module.name)", + deploymentTargets: .iOS(Environment.deploymentTarget), + sources: ["\(module.localPath)/Sources/**"], + resources: hasResources ? ["\(module.localPath)/Resources/**"] : nil, + dependencies: dependencies, + settings: .frameworkSettings + ) + } + + static func doriUnitTests( + _ module: DoriModule, + dependencies: [TargetDependency] = [] + ) -> Target { + .target( + name: "\(module.name)Tests", + destinations: .iOS, + product: .unitTests, + bundleId: "\(Environment.App.baseBundleId).\(module.name)Tests", + deploymentTargets: .iOS(Environment.deploymentTarget), + sources: ["\(module.localPath)/Tests/**"], + dependencies: [.target(name: module.name)] + dependencies, + settings: .testSettings + ) + } + + static func app( + name: String, + bundleId: String, + infoPlist: InfoPlist? = .baseInfoPlist(), + sources: SourceFilesList = ["Sources/**"], + resources: ResourceFileElements = ["Resources/**"], + dependencies: [TargetDependency] = [], + settings: Settings? = .appSettings(), + entitlements: Entitlements? = nil + ) -> Target { + .target( + name: name, + destinations: .iOS, + product: .app, + bundleId: bundleId, + deploymentTargets: .iOS(Environment.deploymentTarget), + infoPlist: infoPlist, + sources: sources, + resources: resources, + dependencies: dependencies, + settings: settings + ) + } +} diff --git a/Tuist/ProjectDescriptionHelpers/TargetDependency+Extension.swift b/Tuist/ProjectDescriptionHelpers/TargetDependency+Extension.swift new file mode 100644 index 0000000..b57e567 --- /dev/null +++ b/Tuist/ProjectDescriptionHelpers/TargetDependency+Extension.swift @@ -0,0 +1,19 @@ +import ProjectDescription + +extension TargetDependency { + public static func external(_ dependency: DoriDependency) -> TargetDependency { + .external(name: dependency.name) + } +} + +public enum DoriDependency: String { + case alamofire + case composableArchitecture + case kakaoSDKCommon + case kakaoSDKAuth + case kakaoSDKUser + + var name: String { + rawValue + } +} From d1cca1a67e2a4acd9b3f42cc3cc649c80c8429ad Mon Sep 17 00:00:00 2001 From: kangddong Date: Sat, 14 Feb 2026 20:33:32 +0900 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20TypoStyle=20->=20TypoToken=20/TypoS?= =?UTF-8?q?emantic=EC=9C=BC=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 현재 디자인이 Semantic으로 100% 전환되지않은 케이스가 있어 TypoToken / TypoSemantic 두가지를 병행하여 사용 - .lineSpacing()에 Line height 적용하며 버그 발생, linespacing, lineheight 개념 숙지 필요.. ! # Conflicts: # Projects/Core/DoriDesignSystem/Sources/Typography/TypoToken.swift --- .../Typography/DesignTokenDemoView.swift | 18 +- .../Sources/Typography/FontStyle.swift | 45 ++-- .../Sources/Typography/TypoStyle.swift | 192 ++++++++++++++---- .../Sources/Typography/View+.swift | 18 +- .../Onboarding/Sources/IntroView.swift | 8 +- .../Onboarding/Sources/SplashView.swift | 2 +- 6 files changed, 208 insertions(+), 75 deletions(-) diff --git a/Projects/Core/DoriDesignSystem/Sources/Typography/DesignTokenDemoView.swift b/Projects/Core/DoriDesignSystem/Sources/Typography/DesignTokenDemoView.swift index f8de5ed..feec905 100644 --- a/Projects/Core/DoriDesignSystem/Sources/Typography/DesignTokenDemoView.swift +++ b/Projects/Core/DoriDesignSystem/Sources/Typography/DesignTokenDemoView.swift @@ -47,13 +47,13 @@ fileprivate struct DesignTokenDemoView: View { .frame(height: 50) .foregroundStyle(.grey100) Text("도리 화이팅") - .pretendard(.heading(heading)) + .pretendard(.headline(heading)) } } } header: { Text("헤딩") .padding() - .pretendard(.heading(.h15)) + .pretendard(.bold(.b15)) .foregroundStyle(.doriWhite) .frame(maxWidth: .infinity) .background(.secondary) @@ -73,7 +73,7 @@ fileprivate struct DesignTokenDemoView: View { } header: { Text("서브 타이틀") .padding() - .pretendard(.heading(.h15)) + .pretendard(.bold(.b15)) .foregroundStyle(.doriWhite) .frame(maxWidth: .infinity) .background(.secondary) @@ -93,7 +93,7 @@ fileprivate struct DesignTokenDemoView: View { } header: { Text("바디") .padding() - .pretendard(.heading(.h15)) + .pretendard(.bold(.b15)) .foregroundStyle(.doriWhite) .frame(maxWidth: .infinity) .background(.main) @@ -113,7 +113,7 @@ fileprivate struct DesignTokenDemoView: View { } header: { Text("캡션") .padding() - .pretendard(.heading(.h15)) + .pretendard(.bold(.b15)) .foregroundStyle(.doriWhite) .frame(maxWidth: .infinity) .background(.main) @@ -142,10 +142,10 @@ extension DesignTokenDemoView { DoriColors.doriBlack.color ] - private static let typoHeadings: [TypoStyle.Heading] = TypoStyle.Heading.allCases - private static let typoSubTitles: [TypoStyle.SubTitle] = TypoStyle.SubTitle.allCases - private static let typoBodys: [TypoStyle.Body] = TypoStyle.Body.allCases - private static let typoCaptions: [TypoStyle.Caption] = TypoStyle.Caption.allCases + private static let typoHeadings: [TypoSemantic.Heading] = TypoSemantic.Heading.allCases + private static let typoSubTitles: [TypoSemantic.SubTitle] = TypoSemantic.SubTitle.allCases + private static let typoBodys: [TypoSemantic.Body] = TypoSemantic.Body.allCases + private static let typoCaptions: [TypoSemantic.Caption] = TypoSemantic.Caption.allCases } #Preview { diff --git a/Projects/Core/DoriDesignSystem/Sources/Typography/FontStyle.swift b/Projects/Core/DoriDesignSystem/Sources/Typography/FontStyle.swift index a039303..ce4b224 100644 --- a/Projects/Core/DoriDesignSystem/Sources/Typography/FontStyle.swift +++ b/Projects/Core/DoriDesignSystem/Sources/Typography/FontStyle.swift @@ -9,33 +9,34 @@ import SwiftUI // MARK: - Font 타입 속성을 정의하는 구조체 public enum FontType: Equatable { - case system(Font.Weight) - case custom(String) + case system(Font.Weight) + case custom(String) } // MARK: - TypoStyle 혹은 직접 활용도 가능하끔 구조 작성 @MainActor public struct FontStyle { - public let font: Font - public let fontSize: CGFloat - public let lineHeight: CGFloat + public let font: Font + public let fontSize: CGFloat + public let lineHeight: CGFloat + public let lineSpacing: CGFloat + + public init( + _ family: FontType = .system(.regular), + size: CGFloat = 16 + ) { + self.fontSize = size - public init( - _ family: FontType = .system(.regular), - size: CGFloat = 16, - lineHeight: CGFloat = 1.5, - ) { - self.fontSize = size - - switch family { - case .system(let weight): - self.font = .system(size: size, weight: weight) - case .custom(let fontName): - // Bundle.module에서 폰트 등록 후 사용 - FontManager.registerFontIfNeeded(fontName) - self.font = .custom(fontName, size: size) - } - - self.lineHeight = size * lineHeight - size + switch family { + case .system(let weight): + self.font = .system(size: size, weight: weight) + case .custom(let fontName): + // Bundle.module에서 폰트 등록 후 사용 + FontManager.registerFontIfNeeded(fontName) + self.font = .custom(fontName, size: size) } + // 등차수열 적용 + self.lineHeight = 2 * size - 6 + self.lineSpacing = lineHeight - fontSize + } } diff --git a/Projects/Core/DoriDesignSystem/Sources/Typography/TypoStyle.swift b/Projects/Core/DoriDesignSystem/Sources/Typography/TypoStyle.swift index cbea099..a499ad7 100644 --- a/Projects/Core/DoriDesignSystem/Sources/Typography/TypoStyle.swift +++ b/Projects/Core/DoriDesignSystem/Sources/Typography/TypoStyle.swift @@ -8,26 +8,32 @@ import Foundation @MainActor -public enum TypoStyle { - case heading(TypoStyle.Heading) - case subtitle(TypoStyle.SubTitle) - case body(TypoStyle.Body) - case caption(TypoStyle.Caption) +public enum TypoSemantic { + case headline(TypoSemantic.Heading) + case title(TypoSemantic.Title) + case subtitle(TypoSemantic.SubTitle) + case body(TypoSemantic.Body) + case caption(TypoSemantic.Caption) // MARK: - 폰트에 의존하지 않는 스타일 정의 - private var styleSpec: (weight: FontWeight, size: CGFloat, lineHeight: CGFloat) { + private var styleSpec: FontSpec { + switch self { - case .heading(let heading): - return (.bold, heading.size, heading.lineHeight) - + case .headline(let headline): + let spec = headline.token.getFontSpec() + return .init(spec.weight, spec.size) + case .title(let title): + let spec = title.token.getFontSpec() + return .init(spec.weight, spec.size) case .subtitle(let subtitle): - return (.semiBold, subtitle.size, subtitle.lineHeight) - + let spec = subtitle.token.getFontSpec() + return .init(spec.weight, spec.size) case .body(let body): - return (.medium, body.size, body.lineHeight) - + let spec = body.token.getFontSpec() + return .init(spec.weight, spec.size) case .caption(let caption): - return (.regular, caption.size, caption.lineHeight) + let spec = caption.token.getFontSpec() + return .init(spec.weight, spec.size) } } @@ -35,44 +41,162 @@ public enum TypoStyle { public func getFontStyle(with provider: FontProvider) -> FontStyle { let spec = styleSpec let fontName = spec.weight.getFontName(from: provider) - return FontStyle(.custom(fontName), size: spec.size, lineHeight: spec.lineHeight) + return FontStyle(.custom(fontName), size: spec.size) } } -public extension TypoStyle { - enum Heading: Int, CaseIterable { - case h11 = 11, h13 = 13, h14 = 14, h15 = 15, h16 = 16, h20 = 20, h30 = 30 +public extension TypoSemantic { + enum Heading: CaseIterable { + case h1 - var size: CGFloat { CGFloat(rawValue) } + var token: TypoToken { TypoToken.bold(.b16) } + } + + enum Title: CaseIterable { + case t1 - // 등차수열 적용 - var lineHeight: CGFloat { 2 * size - 6 } + var token: TypoToken { TypoToken.medium(.m16) } } - enum SubTitle: Int, CaseIterable { - case t12 = 12, t14 = 14, t15 = 15, t16 = 16, t20 = 20 + enum SubTitle: CaseIterable { + case sb1, sb2, m2 - var size: CGFloat { CGFloat(rawValue) } + var token: TypoToken { + switch self { + case .sb1: + .semiBold(.sb20) + case .sb2: + .semiBold(.sb14) + case .m2: + .medium(.m14) + } + } + } + + enum Body: CaseIterable { + case b1, b4 + case sb2, sb3, sb6 + case m3, m5 + case r2, r3, r4, r6 - // 등차수열 적용 - var lineHeight: CGFloat { 2 * size - 6 } + var token: TypoToken { + switch self { + case .b1: + .bold(.b30) + case .b4: + .bold(.b14) + case .sb2: + .semiBold(.sb16) + case .sb3: + .semiBold(.sb15) + case .sb6: + .semiBold(.sb12) + case .m3: + .medium(.m15) + case .m5: + .medium(.m13) + case .r2: + .regular(.r16) + case .r3: + .regular(.r15) + case .r4: + .regular(.r14) + case .r6: + .regular(.r12) + } + } + } + + enum Caption: CaseIterable { + case b1, b2 + case m2 + case r1, r2 + + var token: TypoToken { + switch self { + case .b1: + .bold(.b13) + case .b2: + .bold(.b11) + case .m2: + .medium(.m11) + case .r1: + .regular(.r13) + case .r2: + .regular(.r11) + } + } + } +} + +@MainActor +public enum TypoToken { + case bold(TypoToken.Bold) + case semiBold(TypoToken.SemiBold) + case medium(TypoToken.Medium) + case regular(TypoToken.Regular) + + // MARK: - 폰트에 의존하지 않는 스타일 정의 + private var styleSpec: FontSpec { + switch self { + case .bold(let heading): + return .init(.bold, heading.size) + + case .semiBold(let subtitle): + return .init(.semiBold, subtitle.size) + + case .medium(let body): + return .init(.medium, body.size) + + case .regular(let caption): + return .init(.regular, caption.size) + } + } + + // MARK: - FontProvider를 받아서 FontStyle 생성 + public func getFontStyle(with provider: FontProvider) -> FontStyle { + let spec = styleSpec + let fontName = spec.weight.getFontName(from: provider) + return FontStyle(.custom(fontName), size: spec.size) } - enum Body: Int, CaseIterable { - case b11 = 11, b12, b13, b14, b15, b16 + public func getFontSpec() -> FontSpec { + return styleSpec + } +} + +public struct FontSpec { + let weight: FontWeight + let size: CGFloat + + init(_ weight: FontWeight, _ size: CGFloat) { + self.weight = weight + self.size = size + } +} + +public extension TypoToken { + enum Bold: Int, CaseIterable { + case b11 = 11, b13 = 13, b14 = 14, b15 = 15, b16 = 16, b20 = 20, b30 = 30 var size: CGFloat { CGFloat(rawValue) } + } + + enum SemiBold: Int, CaseIterable { + case sb12 = 12, sb14 = 14, sb15 = 15, sb16 = 16, sb20 = 20 - // 등차수열 적용 - var lineHeight: CGFloat { 2 * size - 6 } + var size: CGFloat { CGFloat(rawValue) } } - enum Caption: Int, CaseIterable { - case c11 = 11, c12, c13, c14, c15, c16, c18 = 18, c20 = 20 + enum Medium: Int, CaseIterable { + case m11 = 11, m12, m13, m14, m15, m16 var size: CGFloat { CGFloat(rawValue) } + } + + enum Regular: Int, CaseIterable { + case r11 = 11, r12, r13, r14, r15, r16, r18 = 18, r20 = 20 - // 등차수열 적용 - var lineHeight: CGFloat { 2 * size - 6 } + var size: CGFloat { CGFloat(rawValue) } } } diff --git a/Projects/Core/DoriDesignSystem/Sources/Typography/View+.swift b/Projects/Core/DoriDesignSystem/Sources/Typography/View+.swift index dec186e..2e503de 100644 --- a/Projects/Core/DoriDesignSystem/Sources/Typography/View+.swift +++ b/Projects/Core/DoriDesignSystem/Sources/Typography/View+.swift @@ -9,12 +9,20 @@ import SwiftUI public extension View { // MARK: - font, linespacing 적용되어있음 (기본값: Pretendard) - func pretendard(_ style: TypoStyle) -> some View { + func pretendard(_ semantic: TypoSemantic) -> some View { let pretendardProvider = PretendardProvider() - let fontStyle = style.getFontStyle(with: pretendardProvider) + let fontStyle = semantic.getFontStyle(with: pretendardProvider) return self .font(fontStyle.font) - .lineSpacing(fontStyle.lineHeight) + .lineSpacing(fontStyle.lineSpacing) + } + + func pretendard(_ token: TypoToken) -> some View { + let pretendardProvider = PretendardProvider() + let fontStyle = token.getFontStyle(with: pretendardProvider) + return self + .font(fontStyle.font) + .lineSpacing(fontStyle.lineSpacing) } func hopangche( @@ -22,9 +30,9 @@ public extension View { lineHeight: CGFloat = 55 ) -> some View { let fontName = SamlipHopangProvider.FontName.basic.name - let fontStyle = FontStyle(.custom(fontName), size: size, lineHeight: lineHeight) + let fontStyle = FontStyle(.custom(fontName), size: size) return self .font(fontStyle.font) - .lineSpacing(fontStyle.lineHeight) + .lineSpacing(fontStyle.lineSpacing) } } diff --git a/Projects/Feature/Onboarding/Sources/IntroView.swift b/Projects/Feature/Onboarding/Sources/IntroView.swift index 299cd8f..5408e72 100644 --- a/Projects/Feature/Onboarding/Sources/IntroView.swift +++ b/Projects/Feature/Onboarding/Sources/IntroView.swift @@ -105,14 +105,14 @@ public struct IntroView: View { .hopangche(size: 55) .foregroundStyle(.main) Text(prop.subtitle) - .pretendard(.caption(.c18)) + .pretendard(.regular(.r18)) .foregroundStyle(.main) } else { Text(prop.title) - .pretendard(.subtitle(.t20)) + .pretendard(.subtitle(.sb1)) .foregroundStyle(.main) Text(prop.subtitle) - .pretendard(.subtitle(.t20)) + .pretendard(.subtitle(.sb1)) .foregroundStyle(.main) } @@ -142,7 +142,7 @@ public struct IntroView: View { store.send(.kakaoLoginButtonTapped) } label: { Text("카카오로 시작하기") - .pretendard(.subtitle(.t15)) + .pretendard(.semiBold(.sb15)) .foregroundStyle(DoriColors.doriBlack.color) .frame(maxWidth: .infinity) .frame(height: 46) diff --git a/Projects/Feature/Onboarding/Sources/SplashView.swift b/Projects/Feature/Onboarding/Sources/SplashView.swift index 84b0363..418369e 100644 --- a/Projects/Feature/Onboarding/Sources/SplashView.swift +++ b/Projects/Feature/Onboarding/Sources/SplashView.swift @@ -31,7 +31,7 @@ public struct SplashView: View { .hopangche(size: 55) .foregroundStyle(.main) Text(prop.subtitle) - .pretendard(.caption(.c18)) + .pretendard(.regular(.r18)) .foregroundStyle(.main) } From 1e52a744f89e25fa7f0a4c5d1276654af156c650 Mon Sep 17 00:00:00 2001 From: kangddong Date: Wed, 18 Feb 2026 19:01:28 +0900 Subject: [PATCH 4/4] =?UTF-8?q?fix:=20Tuist=20=ED=97=AC=ED=8D=BC=20?= =?UTF-8?q?=EC=9E=91=EC=97=85=20=EA=B0=84=20=EB=88=84=EB=9D=BD=20=EB=90=9C?= =?UTF-8?q?=20Workspace=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Workspace.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Workspace.swift b/Workspace.swift index d486dc8..218d486 100644 --- a/Workspace.swift +++ b/Workspace.swift @@ -9,6 +9,6 @@ import ProjectDescription import ProjectDescriptionHelpers let workspace = Workspace( - name: DoriManifest.projectName, + name: Environment.projectName, projects: DoriLayer.allCases.map(\.projectPath) )