Skip to content

Commit 8bbf50e

Browse files
authored
Merge branch 'develop' into docs/#211-readme
2 parents 652951c + e7beeab commit 8bbf50e

86 files changed

Lines changed: 2712 additions & 982 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gemini/styleguide.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,9 @@
1-
# 지침사항
2-
당신은 iOS 수석 개발자 입니다. 당신은 한국인 이므로 코드 리뷰 및 요약을 한국어로 해야 합니다.
1+
## Review Guidelines
2+
3+
- Write all review comments in Korean.
4+
- Keep review comments concise and high-signal.
5+
- Prioritize findings about bugs, performance, and readability.
6+
- Do not explain obvious, trivial, or low-signal issues.
7+
- When useful, begin the review with a short summary of the main changes.
8+
- Focus on actionable feedback rather than broad commentary.
9+

.github/workflows/build.yml

Lines changed: 3 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -16,36 +16,7 @@ permissions:
1616
checks: write
1717

1818
jobs:
19-
detect_qa_tag:
20-
runs-on: macos-latest
21-
outputs:
22-
has_qa_tag: ${{ steps.detect.outputs.has_qa_tag }}
23-
steps:
24-
- uses: actions/checkout@v5
25-
with:
26-
fetch-depth: 0
27-
fetch-tags: true
28-
29-
- name: Detect QA Tag on PR Head
30-
id: detect
31-
shell: bash
32-
run: |
33-
set -euo pipefail
34-
PR_HEAD_SHA="${{ github.event.pull_request.head.sha }}"
35-
MATCHED_TAG="$(git tag --points-at "$PR_HEAD_SHA" | grep -E '^qa(-local)?-' | head -n 1 || true)"
36-
37-
if [ -n "$MATCHED_TAG" ]; then
38-
echo "Found QA tag on PR head: $MATCHED_TAG"
39-
echo "has_qa_tag=true" >> "$GITHUB_OUTPUT"
40-
exit 0
41-
fi
42-
43-
echo "No QA tag found on PR head"
44-
echo "has_qa_tag=false" >> "$GITHUB_OUTPUT"
45-
4619
build:
47-
needs: detect_qa_tag
48-
if: needs.detect_qa_tag.outputs.has_qa_tag != 'true'
4920
runs-on: macos-latest
5021
timeout-minutes: 30
5122
steps:
@@ -206,15 +177,6 @@ jobs:
206177
XC_STATUS=${PIPESTATUS[0]}
207178
set -e
208179
209-
if [ -f build.log ]; then
210-
echo "== error: lines =="
211-
if grep -i "error:" build.log; then
212-
if [ "$XC_STATUS" -eq 0 ]; then
213-
XC_STATUS=1
214-
fi
215-
fi
216-
fi
217-
218180
exit $XC_STATUS
219181
220182
- name: Comment build failure on PR
@@ -228,9 +190,9 @@ jobs:
228190
if (fs.existsSync(path)) {
229191
const log = fs.readFileSync(path, 'utf8');
230192
const lines = log.split(/\r?\n/);
231-
const errorLines = lines.filter((line) => /error:/i.test(line));
193+
const errorLines = lines.filter((line) => /^(.*?):(\d+):(\d+):\s+error:/i.test(line));
232194
if (errorLines.length > 0) {
233-
body += "Lines containing 'error:':\n\n```\n" + errorLines.join('\n') + '\n```\n';
195+
body += "Compiler error lines:\n\n```\n" + errorLines.join('\n') + '\n```\n';
234196
235197
const repoRoot = process.env.GITHUB_WORKSPACE || process.cwd();
236198
const pathMod = require('path');
@@ -258,7 +220,7 @@ jobs:
258220
body += "\nCode excerpts:\n\n```\n" + snippets.join('\n\n') + "\n```\n";
259221
}
260222
} else {
261-
body += "No lines containing 'error:' were found in build.log.";
223+
body += "No compiler-style error diagnostics were found in build.log.";
262224
}
263225
} else {
264226
body += 'build.log not found.';

.github/workflows/testflight.yml

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
name: iOS TestFlight
22

33
on:
4-
push:
5-
tags:
6-
- "qa-*"
7-
workflow_dispatch:
4+
pull_request:
5+
types:
6+
- closed
7+
branches:
8+
- develop
89

910
env:
1011
SCHEME: DevLog
@@ -24,12 +25,15 @@ permissions:
2425

2526
jobs:
2627
testflight:
28+
if: github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'develop' && (contains(github.event.pull_request.labels.*.name, 'qa') || contains(github.event.pull_request.labels.*.name, 'qa-local'))
2729
runs-on: macos-latest
2830
timeout-minutes: 45
2931

3032
steps:
31-
- name: Checkout
33+
- name: Checkout merge commit
3234
uses: actions/checkout@v5
35+
with:
36+
ref: ${{ github.event.pull_request.merge_commit_sha }}
3337

3438
- name: Install private config files
3539
uses: ./.github/actions/install-private-config
@@ -74,10 +78,10 @@ jobs:
7478
- name: Build for TestFlight
7579
run: bundle exec fastlane testflight_build_only
7680

77-
- name: Skip TestFlight Upload for Local QA Tag
78-
if: github.event_name == 'push' && startsWith(github.ref_name, 'qa-local-')
79-
run: echo "Skipping TestFlight upload for local QA tag ${GITHUB_REF_NAME}"
81+
- name: Skip TestFlight Upload for Local QA Label
82+
if: contains(github.event.pull_request.labels.*.name, 'qa-local')
83+
run: echo "Skipping TestFlight upload for PR labeled qa-local"
8084

8185
- name: Upload to TestFlight
82-
if: github.event_name != 'push' || !startsWith(github.ref_name, 'qa-local-')
86+
if: contains(github.event.pull_request.labels.*.name, 'qa') && !contains(github.event.pull_request.labels.*.name, 'qa-local')
8387
run: bundle exec fastlane upload_testflight_build

DevLog.xcodeproj/project.pbxproj

Lines changed: 0 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -15,28 +15,13 @@
1515
DFABA3B02E23526500FEFBDB /* FirebaseFirestore in Frameworks */ = {isa = PBXBuildFile; productRef = DFABA3AF2E23526500FEFBDB /* FirebaseFirestore */; };
1616
DFABA3B22E23526500FEFBDB /* FirebaseFunctions in Frameworks */ = {isa = PBXBuildFile; productRef = DFABA3B12E23526500FEFBDB /* FirebaseFunctions */; };
1717
DFABA3B42E23526500FEFBDB /* FirebaseMessaging in Frameworks */ = {isa = PBXBuildFile; productRef = DFABA3B32E23526500FEFBDB /* FirebaseMessaging */; };
18-
DFD643A72EC787AB0073E133 /* firebase.json in Resources */ = {isa = PBXBuildFile; fileRef = DFD643A42EC787AB0073E133 /* firebase.json */; };
19-
DFD643A92EC787AB0073E133 /* package.json in Resources */ = {isa = PBXBuildFile; fileRef = DFD643A02EC787AB0073E133 /* package.json */; };
20-
DFD643AC2EC787AB0073E133 /* tsconfig.json in Resources */ = {isa = PBXBuildFile; fileRef = DFD643A22EC787AB0073E133 /* tsconfig.json */; };
21-
DFD643AE2EC787AB0073E133 /* package-lock.json in Resources */ = {isa = PBXBuildFile; fileRef = DFD643A12EC787AB0073E133 /* package-lock.json */; };
2218
DFD645402EC827A10073E133 /* .gitignore in Resources */ = {isa = PBXBuildFile; fileRef = DFD6453F2EC827A10073E133 /* .gitignore */; };
2319
DFD74E2F2E423EA700613803 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = DFD74E2E2E423EA700613803 /* README.md */; };
2420
DFF2DACE2EDC02AD00778738 /* OrderedCollections in Frameworks */ = {isa = PBXBuildFile; productRef = DFF2DACD2EDC02AD00778738 /* OrderedCollections */; };
2521
/* End PBXBuildFile section */
2622

2723
/* Begin PBXFileReference section */
2824
DFD48B002DC4D6E2005905C5 /* DevLog.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DevLog.app; sourceTree = BUILT_PRODUCTS_DIR; };
29-
DFD643952EC787AB0073E133 /* apple.ts */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.typescript; path = apple.ts; sourceTree = "<group>"; };
30-
DFD643962EC787AB0073E133 /* github.ts */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.typescript; path = github.ts; sourceTree = "<group>"; };
31-
DFD643972EC787AB0073E133 /* google.ts */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.typescript; path = google.ts; sourceTree = "<group>"; };
32-
DFD643992EC787AB0073E133 /* notification.ts */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.typescript; path = notification.ts; sourceTree = "<group>"; };
33-
DFD6439A2EC787AB0073E133 /* schedule.ts */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.typescript; path = schedule.ts; sourceTree = "<group>"; };
34-
DFD6439C2EC787AB0073E133 /* delete.ts */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.typescript; path = delete.ts; sourceTree = "<group>"; };
35-
DFD6439E2EC787AB0073E133 /* index.ts */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.typescript; path = index.ts; sourceTree = "<group>"; };
36-
DFD643A02EC787AB0073E133 /* package.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = package.json; sourceTree = "<group>"; };
37-
DFD643A12EC787AB0073E133 /* package-lock.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "package-lock.json"; sourceTree = "<group>"; };
38-
DFD643A22EC787AB0073E133 /* tsconfig.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = tsconfig.json; sourceTree = "<group>"; };
39-
DFD643A42EC787AB0073E133 /* firebase.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = firebase.json; sourceTree = "<group>"; };
4025
DFD6453F2EC827A10073E133 /* .gitignore */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitignore; sourceTree = "<group>"; };
4126
DFD74E2E2E423EA700613803 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
4227
/* End PBXFileReference section */
@@ -91,7 +76,6 @@
9176
DFD74E2E2E423EA700613803 /* README.md */,
9277
DFE28EB62DCCF26300B28FE5 /* Frameworks */,
9378
DFD48B012DC4D6E2005905C5 /* Products */,
94-
DFD643A52EC787AB0073E133 /* Firebase */,
9579
);
9680
sourceTree = "<group>";
9781
};
@@ -103,64 +87,6 @@
10387
name = Products;
10488
sourceTree = "<group>";
10589
};
106-
DFD643982EC787AB0073E133 /* auth */ = {
107-
isa = PBXGroup;
108-
children = (
109-
DFD643952EC787AB0073E133 /* apple.ts */,
110-
DFD643962EC787AB0073E133 /* github.ts */,
111-
DFD643972EC787AB0073E133 /* google.ts */,
112-
);
113-
path = auth;
114-
sourceTree = "<group>";
115-
};
116-
DFD6439B2EC787AB0073E133 /* fcm */ = {
117-
isa = PBXGroup;
118-
children = (
119-
DFD643992EC787AB0073E133 /* notification.ts */,
120-
DFD6439A2EC787AB0073E133 /* schedule.ts */,
121-
);
122-
path = fcm;
123-
sourceTree = "<group>";
124-
};
125-
DFD6439D2EC787AB0073E133 /* user */ = {
126-
isa = PBXGroup;
127-
children = (
128-
DFD6439C2EC787AB0073E133 /* delete.ts */,
129-
);
130-
path = user;
131-
sourceTree = "<group>";
132-
};
133-
DFD6439F2EC787AB0073E133 /* src */ = {
134-
isa = PBXGroup;
135-
children = (
136-
DFD643982EC787AB0073E133 /* auth */,
137-
DFD6439B2EC787AB0073E133 /* fcm */,
138-
DFD6439D2EC787AB0073E133 /* user */,
139-
DFD6439E2EC787AB0073E133 /* index.ts */,
140-
);
141-
path = src;
142-
sourceTree = "<group>";
143-
};
144-
DFD643A32EC787AB0073E133 /* functions */ = {
145-
isa = PBXGroup;
146-
children = (
147-
DFD6439F2EC787AB0073E133 /* src */,
148-
DFD643A02EC787AB0073E133 /* package.json */,
149-
DFD643A12EC787AB0073E133 /* package-lock.json */,
150-
DFD643A22EC787AB0073E133 /* tsconfig.json */,
151-
);
152-
path = functions;
153-
sourceTree = "<group>";
154-
};
155-
DFD643A52EC787AB0073E133 /* Firebase */ = {
156-
isa = PBXGroup;
157-
children = (
158-
DFD643A32EC787AB0073E133 /* functions */,
159-
DFD643A42EC787AB0073E133 /* firebase.json */,
160-
);
161-
path = Firebase;
162-
sourceTree = "<group>";
163-
};
16490
DFE28EB62DCCF26300B28FE5 /* Frameworks */ = {
16591
isa = PBXGroup;
16692
children = (
@@ -250,10 +176,6 @@
250176
files = (
251177
DFD645402EC827A10073E133 /* .gitignore in Resources */,
252178
DFD74E2F2E423EA700613803 /* README.md in Resources */,
253-
DFD643A72EC787AB0073E133 /* firebase.json in Resources */,
254-
DFD643A92EC787AB0073E133 /* package.json in Resources */,
255-
DFD643AC2EC787AB0073E133 /* tsconfig.json in Resources */,
256-
DFD643AE2EC787AB0073E133 /* package-lock.json in Resources */,
257179
);
258180
runOnlyForDeploymentPostprocessing = 0;
259181
};
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//
2+
// AppLayerAssembler.swift
3+
// DevLog
4+
//
5+
// Created by opfic on 3/19/26.
6+
//
7+
8+
final class AppLayerAssembler: Assembler {
9+
func assemble(_ container: any DIContainer) {
10+
container.register(FCMTokenSyncHandler.self) {
11+
FCMTokenSyncHandler(
12+
repository: container.resolve(UserDataRepository.self)
13+
)
14+
}
15+
container.register(UserTimeZoneSyncHandler.self) {
16+
UserTimeZoneSyncHandler(
17+
repository: container.resolve(UserDataRepository.self)
18+
)
19+
}
20+
}
21+
}

DevLog/App/Assembler/Assembler.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@ protocol Assembler {
1111

1212
final class AppAssembler: Assembler {
1313
private let assemblers: [Assembler] = [
14+
PersistenceAssembler(),
1415
InfraAssembler(),
1516
DataAssembler(),
16-
DomainAssembler()
17+
DomainAssembler(),
18+
AppLayerAssembler()
1719
]
1820

1921
func assemble(_ container: any DIContainer) {

DevLog/App/Assembler/DataAssembler.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,7 @@ final class DataAssembler: Assembler {
3434

3535
container.register(AuthSessionRepository.self) {
3636
AuthSessionRepositoryImpl(
37-
authService: container.resolve(AuthService.self),
38-
userDefaultsStore: container.resolve(UserDefaultsStore.self)
37+
authService: container.resolve(AuthService.self)
3938
)
4039
}
4140

DevLog/App/Assembler/DomainAssembler.swift

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ private extension DomainAssembler {
6666
container.register(DeleteTodoUseCase.self) {
6767
DeleteTodoUseCaseImpl(container.resolve(TodoRepository.self))
6868
}
69+
70+
container.register(UndoDeleteTodoUseCase.self) {
71+
UndoDeleteTodoUseCaseImpl(container.resolve(TodoRepository.self))
72+
}
6973
}
7074

7175
func registerUserDataUseCases(_ container: DIContainer) {
@@ -91,10 +95,18 @@ private extension DomainAssembler {
9195
DeletePushNotificationUseCaseImpl(container.resolve(PushNotificationRepository.self))
9296
}
9397

98+
container.register(UndoDeletePushNotificationUseCase.self) {
99+
UndoDeletePushNotificationUseCaseImpl(container.resolve(PushNotificationRepository.self))
100+
}
101+
94102
container.register(FetchPushNotificationsUseCase.self) {
95103
FetchPushNotificationsUseCaseImpl(container.resolve(PushNotificationRepository.self))
96104
}
97105

106+
container.register(ObserveUnreadPushCountUseCase.self) {
107+
ObserveUnreadPushCountUseCaseImpl(container.resolve(PushNotificationRepository.self))
108+
}
109+
98110
container.register(TogglePushNotificationReadUseCase.self) {
99111
TogglePushNotificationReadUseCaseImpl(container.resolve(PushNotificationRepository.self))
100112
}
@@ -112,6 +124,10 @@ private extension DomainAssembler {
112124
container.register(DeleteWebPageUseCase.self) {
113125
DeleteWebPageUseCaseImpl(container.resolve(WebPageRepository.self))
114126
}
127+
128+
container.register(UndoDeleteWebPageUseCase.self) {
129+
UndoDeleteWebPageUseCaseImpl(container.resolve(WebPageRepository.self))
130+
}
115131
}
116132

117133
func registerUserPreferencesUseCases(_ container: DIContainer) {
@@ -123,14 +139,6 @@ private extension DomainAssembler {
123139
UpdateSystemThemeUseCaseImpl(container.resolve(UserPreferencesRepository.self))
124140
}
125141

126-
container.register(FetchFirstLaunchUseCase.self) {
127-
FetchFirstLaunchUseCaseImpl(container.resolve(UserPreferencesRepository.self))
128-
}
129-
130-
container.register(UpdateFirstLaunchUseCase.self) {
131-
UpdateFirstLaunchUseCaseImpl(container.resolve(UserPreferencesRepository.self))
132-
}
133-
134142
container.register(FetchRecentSearchQueriesUseCase.self) {
135143
FetchRecentSearchQueriesUseCaseImpl(container.resolve(UserPreferencesRepository.self))
136144
}

DevLog/App/Assembler/InfraAssembler.swift

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,5 @@ final class InfraAssembler: Assembler {
5252
WebPageMetadataService()
5353
}
5454

55-
container.register(UserDefaultsStore.self) {
56-
UserDefaultsStore()
57-
}
58-
59-
container.register(ThemeStore.self) {
60-
ThemeStore()
61-
}
6255
}
6356
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//
2+
// PersistenceAssembler.swift
3+
// DevLog
4+
//
5+
// Created by opfic on 3/15/26.
6+
//
7+
8+
final class PersistenceAssembler: Assembler {
9+
func assemble(_ container: any DIContainer) {
10+
container.register(UserDefaultsStore.self) {
11+
UserDefaultsStore()
12+
}
13+
14+
container.register(ThemeStore.self) {
15+
ThemeStore()
16+
}
17+
}
18+
}

0 commit comments

Comments
 (0)