Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
5946009
refactor: Feature, Domain 곡톡 νƒ€μž… 뢄리 - [TWI-73] (#279)
jihun32 May 10, 2026
91d086e
fix: D020 컬러의 μ•ŒνŒŒκ°’μ΄ 잘λͺ»λ˜μ—ˆλ˜ 문제 μˆ˜μ • - #282
clxxrlove May 5, 2026
1a7fd96
feat: μ‹ κ·œ λ‘œλ”© λ·°/인디케이터 μΆ”κ°€ - #282
clxxrlove May 5, 2026
48ada44
feat: 인증 ν™”λ©΄μ—μ„œ κΈ°μ‘΄ λ‘œλ”© λ””μžμΈ 제거 및 μ‹ κ·œ λ‘œλ”© λ””μžμΈ 적용 - #282
clxxrlove May 5, 2026
e04fa86
feat: 전체 λ‘œλ”© 인디케이터λ₯Ό μ‹ κ·œ λ””μžμΈμœΌλ‘œ ꡐ체 - #282
clxxrlove May 5, 2026
7985245
refactor: txLoading modifier Binding 제거 및 isPresented+message μ˜€λ²„λ‘œλ“œ μ œκ±°β€¦
clxxrlove May 8, 2026
801226d
feat: GA μ…‹νŒ… - [TWI-76] (#284)
jihun32 May 12, 2026
ebb0df2
chore: PR-DDay yml μΆ”κ°€ - #293 (#294)
jihun32 May 13, 2026
ff83559
fix: Calender νƒ€μž„μ‘΄μ„ λͺ…μ‹œμ μœΌλ‘œ μˆ˜μ • - #290
clxxrlove May 12, 2026
1c429cc
fix: ν˜„μž¬ μ‹œκ°„μ΄ κ³ μ •λ˜λŠ” 문제 μˆ˜μ • - #290
clxxrlove May 12, 2026
4fd83eb
feat: Crashlytics μΆ”κ°€ 및 μ—λŸ¬ 좔적 ν™˜κ²½ κ°œμ„  (#287)
clxxrlove May 14, 2026
3a2136e
feat: 기념일 쀑볡 등둝 μ‹œ λͺ¨λ‹¬λ‘œ μ•ˆλ‚΄ν•˜λ„λ‘ λ³€κ²½ - #289
clxxrlove May 13, 2026
152db68
feat: 기념일 ν™”λ©΄μ—μ„œ μƒλŒ€ 등둝 μ™„λ£Œ 폴링 μΆ”κ°€ - #289
clxxrlove May 13, 2026
47b589b
fix: μ»€ν”Œ μ½”λ“œ μ΄ˆλŒ€μž₯ μ£Όμ†Œ λ³€κ²½ - #289
clxxrlove May 13, 2026
ef66117
fix: 기념일 μ„€μ • μ‹œ λ°μ΄νŠΈν”Όμ»€μ—μ„œ κ³Όκ±°λ₯Ό 선택 κ°€λŠ₯ν•˜κ²Œ λ³€κ²½ - #289
clxxrlove May 13, 2026
0c959c4
fix: ν™ˆ, μ„€μ • QA 반영 (#299)
clxxrlove May 14, 2026
607098a
refactor: CoreAnalytics ꡬ쑰 κ°œμ„  - #281
May 9, 2026
f0017e9
fix: ν™ˆμ—μ„œ μΉ΄λ“œ 상단탭 -> ν†΅κ³„μƒμ„Έμ—μ„œ μˆ˜μ •ν•˜κΈ° μ•ˆλ˜λŠ” 이슈 μˆ˜μ • - #285
May 10, 2026
9b2ee06
chore: stroke 얇은 icon 이미지 에셋 μΆ”κ°€ - #285
May 11, 2026
f9a82c5
fix: λͺ©ν‘œ 생성 얇은 μ•„μ΄μ½˜ μ΄λ―Έμ§€λ‘œ λ³€κ²½ - #285
May 11, 2026
5003605
fix: modal padding μˆ˜μ • 및 μ•„μ΄μ½˜ storke λŒ€μ‘ - #285
May 11, 2026
e5931d4
fix: 인증샷 상세 reacotionBar 그림자 μΆ”κ°€ - #285
May 11, 2026
6715095
fix: 찌λ₯΄κΈ° λ²„νŠΌ UI μˆ˜μ • - #285
May 11, 2026
ba44819
feat: TXButton Round illust disabled κ΅¬ν˜„ - #286
May 11, 2026
554b572
fix: μ£Όκ°„ μΊ˜λ¦°λ” μΈν„°λž™μ…˜ μš”κ΅¬μ‚¬ν•­μ— 맞게 μˆ˜μ • - #285
May 11, 2026
bbfc66c
fix: navigation popμ‹œ 흰색 ν™”λ©΄ λ³΄μ΄λŠ” 버그 μˆ˜μ • - #285
May 11, 2026
3b1fc5b
fix: AddButton 이미지 λ³€κ²½ - #285
May 11, 2026
7e4668f
fix: 인증샷 상세 λ·° λ„€λΉ„κ²Œμ΄μ…˜ λ²„νŠΌ ν„°μΉ˜ κ°„ν—μ μœΌλ‘œ μ•ˆλ˜λŠ” 버그 μˆ˜μ • - #285
May 11, 2026
37780fa
fix: ν™ˆμ—μ„œ 찌λ₯΄κΈ° μΏ¨νƒ€μž„μΌ λ•Œ disable 처리 - #285
May 12, 2026
facc813
fix: λͺ©ν‘œ νŽΈμ§‘ λ·° λ‘œλ”© ux μ €ν•˜λ˜λŠ” 이슈 μˆ˜μ • - #285
May 12, 2026
434069a
refactor: λͺ©ν‘œ μˆ˜μ • μ‹œ κΈ°μ‘΄ λͺ©ν‘œ 데이터 μ£Όμž…μ‹μœΌλ‘œ λ³€κ²½ - #285
May 13, 2026
8ff4ff0
feat: EditableGoal Entity κ΅¬ν˜„ - #285
May 14, 2026
8e55b1f
refactor: EditableGoal Entity -> MakeGoalμ—μ„œ μ‚¬μš©ν•˜λŠ” GoalForm νƒ€μž… λ³€ν™˜μ— λ”°λ₯Έ μž‘β€¦
May 14, 2026
68d9379
refactor: EditableGoal을 source of truth둜 κ°–κ²Œλ” EditGoalList μˆ˜μ • - #285
May 14, 2026
0e12c5a
refactor: StatsDetail EditableGoal μ£Όμž… - #285
May 14, 2026
adfae78
fix: 찌λ₯΄κΈ° 응닡 λ°›κΈ° μ „ λ‚ μ§œ λ³€κ²½ μ‹œ λ‹€λ₯Έ λ‚ μ§œ 데이터 μ—…λ°μ΄νŠΈ ν•  수 있던 리슀크 제거 - #285
May 14, 2026
cd4e711
fix: GitHub Actionsμ—μ„œμ˜ MacOS 버전 λ³€κ²½ - #300
clxxrlove May 14, 2026
7def1aa
chore: TCA 버전 μ—…κ·Έλ ˆμ΄λ“œ - #300
clxxrlove May 14, 2026
84b213d
chore: tuist 버전 μ—…κ·Έλ ˆμ΄λ“œ - #300
clxxrlove May 14, 2026
21ec4f3
fix: Firebase λΉŒλ“œ μ‹€νŒ¨ κ΄€λ ¨ μˆ˜μ • - #300
clxxrlove May 14, 2026
3537cc5
refactor: Firebase wrapper productType μ˜€λ²„λΌμ΄λ“œ 제거 - #300
clxxrlove May 14, 2026
8c104e9
chore: Firebase SDK 12.13.0 μ—…κ·Έλ ˆμ΄λ“œ - #300
clxxrlove May 14, 2026
4197d5e
fix: tuist binary cache λΉ„ν™œμ„±ν™”λ‘œ λΉŒλ“œ μ‹€νŒ¨ ν•΄κ²° - #300
clxxrlove May 14, 2026
0a44c1a
feat: Crashlytics dSYM μ—…λ‘œλ“œ 견고화 - #300
clxxrlove May 14, 2026
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
52 changes: 32 additions & 20 deletions .github/actions/setup-build-env/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@ inputs:
keychain-password:
description: "Keychain password"
required: true
include-pulse:
description: "Whether to include Pulse in cache (false for production builds)"
required: false
default: "true"
skip-spm-cache:
description: "Skip SPM cache for release builds to avoid simulator artifacts"
required: false
Expand All @@ -33,6 +29,32 @@ runs:
security list-keychains -d user -s "$KEYCHAIN_NAME" "$(security default-keychain | tr -d '"')"
security default-keychain -s "$KEYCHAIN_NAME"

- name: 🧰 Select Xcode
shell: bash
run: |
set -euo pipefail
XCODE_APP="/Applications/Xcode_26.4.1.app"
if [ -d "$XCODE_APP" ]; then
sudo xcode-select -s "$XCODE_APP/Contents/Developer"
else
echo "::notice::$XCODE_APP is not installed. Falling back to the runner default Xcode."
fi

- name: 🧰 Verify Xcode SDK
shell: bash
run: |
set -euo pipefail
xcodebuild -version
SDK_VERSION="$(xcrun --sdk iphoneos --show-sdk-version)"
echo "iPhoneOS SDK: $SDK_VERSION"
case "$SDK_VERSION" in
26.*|[3-9][0-9].*) ;;
*)
echo "::error::iPhoneOS SDK $SDK_VERSION is too old for App Store Connect upload. Use a runner with Xcode 26 or later."
exit 1
;;
esac

- name: πŸ’« Set up Ruby
uses: ruby/setup-ruby@v1
with:
Expand All @@ -44,13 +66,13 @@ runs:
uses: actions/cache@v4
with:
path: ~/.tuist-bin
key: tuist-binary-${{ runner.os }}-4.118.0
key: tuist-binary-${{ runner.os }}-4.194.0

- name: πŸ›  Install Tuist (manual binary)
if: steps.tuist-binary-cache.outputs.cache-hit != 'true'
shell: bash
run: |
TUIST_VERSION=4.118.0
TUIST_VERSION=4.194.0
mkdir -p ~/.tuist-bin
curl -L "https://github.com/tuist/tuist/releases/download/${TUIST_VERSION}/tuist.zip" -o tuist.zip
unzip -o tuist.zip -d ~/.tuist-bin
Expand All @@ -66,9 +88,9 @@ runs:
uses: actions/cache@v4
with:
path: Tuist/.build
key: tuist-spm-${{ runner.os }}-${{ hashFiles('Tuist/Package.resolved') }}
key: tuist-spm-xcode26-${{ runner.os }}-${{ hashFiles('Tuist/Package.resolved') }}
restore-keys: |
tuist-spm-${{ runner.os }}-
tuist-spm-xcode26-${{ runner.os }}-

- name: πŸ›  Install tuist dependencies
if: inputs.skip-spm-cache == 'true' || steps.spm-cache.outputs.cache-hit != 'true'
Expand All @@ -81,17 +103,7 @@ runs:
if [ "${{ inputs.skip-spm-cache }}" == "true" ]; then
echo "⏭️ SPM cache SKIPPED (release build)"
elif [ "${{ steps.spm-cache.outputs.cache-hit }}" == "true" ]; then
echo "βœ… SPM cache HIT - skipping tuist cache warm up"
echo "βœ… SPM cache HIT - using restored Tuist/.build"
else
echo "❌ SPM cache MISS - will build dependencies"
echo "❌ SPM cache MISS - dependencies will be compiled from source"
fi

- name: πŸ— Warm up external dependencies (with Pulse)
if: inputs.skip-spm-cache != 'true' && steps.spm-cache.outputs.cache-hit != 'true' && inputs.include-pulse == 'true'
shell: bash
run: tuist cache ComposableArchitecture Kingfisher Pulse KakaoSDKAuth KakaoSDKCommon GoogleSignIn GoogleSignInSwift

- name: πŸ— Warm up external dependencies (without Pulse)
if: inputs.skip-spm-cache != 'true' && steps.spm-cache.outputs.cache-hit != 'true' && inputs.include-pulse != 'true'
shell: bash
run: tuist cache ComposableArchitecture Kingfisher KakaoSDKAuth KakaoSDKCommon GoogleSignIn GoogleSignInSwift
2 changes: 1 addition & 1 deletion .github/workflows/cd_develop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ concurrency:

jobs:
deploy_develop:
runs-on: macos-15
runs-on: macos-26
steps:
- name: πŸ”„ Checkout source code
uses: actions/checkout@v4
Expand Down
3 changes: 1 addition & 2 deletions .github/workflows/cd_main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ concurrency:

jobs:
deploy_main:
runs-on: macos-15
runs-on: macos-26
steps:
- name: πŸ”„ Checkout source code
uses: actions/checkout@v4
Expand All @@ -22,7 +22,6 @@ jobs:
with:
keychain-name: ${{ secrets.KEYCHAIN_NAME }}
keychain-password: ${{ secrets.KEYCHAIN_PASSWORD }}
include-pulse: "false"
skip-spm-cache: "true"

- name: πŸš€ Deploy to App Store Connect (Twix)
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/ci_pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ concurrency:

jobs:
build:
runs-on: macos-15
runs-on: macos-26
steps:
- name: πŸ”„ Checkout source code
uses: actions/checkout@v4
Expand All @@ -24,9 +24,9 @@ jobs:
uses: actions/cache@v4
with:
path: ~/Library/Developer/Xcode/DerivedData
key: deriveddata-${{ runner.os }}-${{ hashFiles('Tuist/Package.resolved') }}
key: deriveddata-xcode26-${{ runner.os }}-${{ hashFiles('Tuist/Package.resolved') }}
restore-keys: |
deriveddata-${{ runner.os }}-
deriveddata-xcode26-${{ runner.os }}-

- name: πŸ§ͺ Run PR Checks (build + test)
env:
Expand Down
84 changes: 84 additions & 0 deletions .github/workflows/pr-dday-labeler.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
name: PR D-Day Labeler

on:
pull_request_target:
types: [opened, reopened, ready_for_review]
schedule:
# Runs every day at 00:15 KST.
- cron: "15 15 * * *"
workflow_dispatch:

permissions:
issues: write
pull-requests: write

jobs:
update-dday-label:
runs-on: ubuntu-latest
steps:
- name: Update PR D-Day labels
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const ddayLabels = ["D-3", "D-2", "D-1"];
const dayMs = 24 * 60 * 60 * 1000;

function targetLabelFor(pr) {
const createdAt = new Date(pr.created_at).getTime();
const elapsedDays = Math.floor((Date.now() - createdAt) / dayMs);
const day = Math.max(1, 3 - elapsedDays);
return `D-${day}`;
}

async function updatePullRequest(pr) {
if (pr.draft) {
console.log(`Skip draft PR #${pr.number}`);
return;
}

const targetLabel = targetLabelFor(pr);
const currentLabels = pr.labels.map((label) => label.name);
const labelsToRemove = currentLabels.filter(
(label) => ddayLabels.includes(label) && label !== targetLabel
);
const hasTargetLabel = currentLabels.includes(targetLabel);

for (const label of labelsToRemove) {
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
name: label,
});
console.log(`Removed ${label} from PR #${pr.number}`);
}

if (!hasTargetLabel) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
labels: [targetLabel],
});
console.log(`Added ${targetLabel} to PR #${pr.number}`);
} else {
console.log(`PR #${pr.number} already has ${targetLabel}`);
}
}

if (context.payload.pull_request) {
await updatePullRequest(context.payload.pull_request);
return;
}

const pulls = await github.paginate(github.rest.pulls.list, {
owner: context.repo.owner,
repo: context.repo.repo,
state: "open",
per_page: 100,
});

for (const pr of pulls) {
await updatePullRequest(pr);
}
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ init:
mise exec node@24 -- node -v
mise use --global node@24 go@1
mise install tuist
mise use tuist@4.115.1
mise use tuist@4.194.0

clean:
tuist clean
Expand All @@ -15,7 +15,7 @@ clean:

generate:
tuist install
tuist generate
tuist generate --cache-profile none

module:
swift Scripts/GenerateModule.swift
Expand Down
13 changes: 8 additions & 5 deletions Projects/App/Project.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ private let commonInfoPlist: [String: Plist.Value] = Project.Environment.InfoPli
"DEEPLINK_HOST": "$(DEEPLINK_HOST)",
"API_BASE_URL": "$(API_BASE_URL)",
"NSCameraUsageDescription": "UseCamera",
"CFBundleShortVersionString": "1.1.1"
"CFBundleShortVersionString": "1.1.2"
], uniquingKeysWith: { current, _ in current })

private let commonDependencies: [TargetDependency] = [
Expand All @@ -45,7 +45,8 @@ private let commonDependencies: [TargetDependency] = [
.external(dependency: .GoogleSignIn),
.external(dependency: .FirebaseCore),
.external(dependency: .FirebaseMessaging),
.external(dependency: .FirebaseRemoteConfig)
.external(dependency: .FirebaseRemoteConfig),
.core(implements: .crashlytics)
]

private let commonBuildSettings: SettingsDictionary = [
Expand All @@ -60,7 +61,7 @@ private let commonBuildSettings: SettingsDictionary = [
"KAKAO_APP_KEY": "fb2997e54bfe080cc5c1d9706d1251f4",
"GOOGLE_CLIENT_ID": "48737424560-adiebqu29lsflj85v9vrd4e4a3cp6sa3.apps.googleusercontent.com",
"GOOGLE_REVERSED_CLIENT_ID": "com.googleusercontent.apps.48737424560-adiebqu29lsflj85v9vrd4e4a3cp6sa3",
"DEEPLINK_HOST": "keepiluv.jiyong.xyz",
"DEEPLINK_HOST": "keepiluv.teamtwix.com",
"API_BASE_URL": "https://api.dev.teamtwix.com"
]

Expand All @@ -75,8 +76,10 @@ let project = Project(
config: .init(
infoPlist: .extendingDefault(with: commonInfoPlist),
entitlements: .file(path: "Support/Twix.entitlements"),
scripts: [.swiftLint],
dependencies: commonDependencies,
scripts: [.swiftLint, .crashlyticsUploadSymbols],
dependencies: commonDependencies + [
.core(implements: .analytics)
],
settings: .settings(
base: commonBuildSettings.merging([
"PROVISIONING_PROFILE_SPECIFIER": "match Development \(Project.Environment.BundleId.bundlePrefix)"
Expand Down
7 changes: 7 additions & 0 deletions Projects/App/Sources/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import CoreLogging
import FirebaseCore
import FirebaseCrashlytics
import FirebaseMessaging
import UIKit
import UserNotifications
Expand All @@ -21,6 +22,12 @@ final class AppDelegate: NSObject, UIApplicationDelegate {
// Firebase μ΄ˆκΈ°ν™”
FirebaseApp.configure()

#if DEBUG
Crashlytics.crashlytics().setCrashlyticsCollectionEnabled(false)
#else
Crashlytics.crashlytics().setCrashlyticsCollectionEnabled(true)
#endif

// ν‘Έμ‹œ μ•Œλ¦Ό delegate μ„€μ •
UNUserNotificationCenter.current().delegate = self
Messaging.messaging().delegate = self
Expand Down
33 changes: 33 additions & 0 deletions Projects/App/Sources/Crashlytics/AppCrashlyticsEvent.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// AppCrashlyticsEvent.swift
// Twix
//

import CoreCrashlyticsInterface
import Foundation

enum AppCrashlyticsLogEvent: CrashlyticsLogEvent {
case sessionExpiredAtOnboardingStatusCheck

var message: String {
switch self {
case .sessionExpiredAtOnboardingStatusCheck:
"session expired at onboarding status check"
}
}
}

enum AppCrashlyticsRecordEvent: CrashlyticsRecordEvent {
case appStartupFailed
case onboardingStatusCheckFailed

var customKeys: [String: String] {
switch self {
case .appStartupFailed:
[CrashlyticsKey.screen: "app_startup"]

case .onboardingStatusCheckFailed:
[CrashlyticsKey.screen: "startup_onboarding_check"]
}
}
}
Loading
Loading