Skip to content

Commit e5adf05

Browse files
authored
Небольшой редизайн (#17)
* Небольшой редизайн экрана "Ещё" В горизонтальной ориентации больше не нужно скроллить, чтобы увидеть весь контент * Добавил cursor/rules
1 parent 13d04e7 commit e5adf05

File tree

8 files changed

+184
-9
lines changed

8 files changed

+184
-9
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
description: Базовые принципы и структура проекта SwiftUI-Days
3+
globs:
4+
alwaysApply: true
5+
---
6+
7+
## Проектные принципы
8+
9+
- **Архитектура без ярлыков**: прагматичный подход; сходство с шаблонами (MVVM и др.) — случайность.
10+
- **Сервисы через Environment (общие)**: общие зависимости (например, `AppSettings`) прокидываются сверху через `Environment`.
11+
- **Локальная логика экрана**: для логики/состояния, ограниченных одной вью, используйте `@State`/`@StateObject`, не вводите публичный сервис.
12+
- **SwiftData**: храним доменные сущности; контейнер создаётся на уровне `App` и доступен через `@Environment(\.modelContext)` и `@Query`.
13+
- **Единые команды**: `make build` и `make test` обязательны для сборки/тестов.
14+
15+
## Структура папок (соглашение)
16+
17+
- `SwiftUI-Days/`
18+
- `SwiftUI_DaysApp.swift` — точка входа, DI сервисов через `.environment(...)`, инициализация SwiftData.
19+
- `EnvironmentKeys/` — пользовательские ключи окружения.
20+
- `Extensions/` — расширения и утилиты.
21+
- `Models/` — доменные модели, вспомогательные типы.
22+
- `Preview Content/` — демо-данные и превью-контейнеры.
23+
- `Screens/` — экраны и составные вью.
24+
- `Services/` — общие сервисы (например, `AppSettings`).
25+
- `SupportingFiles/` — ассеты, plist, локализации.
26+
- `SwiftUI-DaysTests/`, `SwiftUI-DaysUITests/` — тесты.
27+
- `fastlane/` — автоматизация поставки/скриншотов.
28+
- `Makefile` — команды разработки.

.cursor/rules/10-services.mdc

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
description: SwiftUI-Days - сервисы и DI (Services)
3+
globs:
4+
- "**/SwiftUI-Days/**/*"
5+
alwaysApply: false
6+
---
7+
## Сервисы через Environment
8+
9+
- Общие сервисы приложения объявляются в `SwiftUI-Days/Services`.
10+
- Прокидываются сверху в `SwiftUI_DaysApp` через `.environment(serviceInstance)`.
11+
- Доступ из вью: `@Environment(ServiceType.self)`.
12+
13+
Пример инициализации (фрагмент `SwiftUI_DaysApp.swift`):
14+
@SwiftUI-Days/SwiftUI_DaysApp.swift
15+
16+
Пример сервиса (фрагмент `Services/AppSettings.swift`):
17+
@SwiftUI-Days/Services/AppSettings.swift
18+
19+
Пример использования во вью (фрагмент `Screens/More/MoreScreen.swift`):
20+
@SwiftUI-Days/Screens/More/MoreScreen.swift
21+
22+
## Локальная логика экрана
23+
24+
- Если состояние и логика ограничены одной вью/экраном — используйте `@State`/`@StateObject`.
25+
- Не создавайте отдельный публичный сервис для частной логики одного экрана.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
description: Современные практики SwiftUI — состояние, async/await, композиция, без ViewModel-абстракций
3+
globs:
4+
- "**/*.swift"
5+
alwaysApply: false
6+
---
7+
## Современные практики SwiftUI
8+
9+
- Используйте нативный поток данных SwiftUI:
10+
- `@State` — локальное состояние экрана/вью
11+
- `@Binding` — двусторонний поток данных между родителем и дочкой
12+
- `@Observable` — разделяемое состояние в сервисах
13+
- `@Environment` — DI для общих сервисов и ключей окружения
14+
15+
- Не вводим обязательных ViewModel-слоёв. Если структура совпадает с MVVM — это совпадение, а не правило.
16+
17+
- Владение состоянием: держите состояние как можно ближе к месту использования. Выносите в общие сервисы только то, что реально используется в нескольких местах.
18+
19+
- Предпочитайте `async/await` и модификатор `.task` для жизненно-цикловых операций; обрабатывайте ошибки через `do/try/catch`.
20+
21+
- Композиция UI: небольшие и сфокусированные вью, вынос переиспользуемого в `Screens/CommonViews` и поддеревья экранов.
22+
23+
- Не вкладывайте `@Observable` объекты друг в друга (избегайте наблюдаемых полей, содержащих другие наблюдаемые объекты), чтобы не ломать систему наблюдения SwiftUI. Инициализируйте и прокидывайте зависимости на уровне вью через `Environment`.
24+
25+
### Примеры из проекта
26+
27+
- Локальное состояние экрана:
28+
@SwiftUI-Days/Screens/Detail/ItemScreen.swift
29+
30+
- Доступ к общему сервису настроек через `Environment` и реактивное обновление темы:
31+
@SwiftUI-Days/SwiftUI_DaysApp.swift
32+
@SwiftUI-Days/Screens/More/ThemeIcon/ThemeIconScreen.swift
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
description: Пользовательские ключи Environment и их назначение
3+
globs:
4+
- "**/*.swift"
5+
alwaysApply: false
6+
---
7+
8+
## Пользовательские ключи Environment
9+
10+
- Для кросс-иерархических значений, не требующих наблюдаемости, используем `EnvironmentKeys/`.
11+
- Пример: `currentDate` — источник истины для расчётов, связанных с датой.
12+
13+
Определение ключа (фрагмент `EnvironmentKeys/CurrentDateEnvironmentKey.swift`):
14+
@SwiftUI-Days/EnvironmentKeys/CurrentDateEnvironmentKey.swift
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
description: Команды сборки/тестов, fastlane и форматирование
3+
globs:
4+
alwaysApply: true
5+
---
6+
7+
## Сборка и тесты
8+
9+
- Сборка проекта (симулятор iPhone 16 Pro):
10+
11+
```
12+
make build
13+
```
14+
15+
- Запуск unit-тестов (Swift Testing):
16+
17+
```
18+
make test
19+
```
20+
21+
## Форматирование
22+
23+
```
24+
make format
25+
```
26+
27+
## Fastlane
28+
29+
- Скриншоты: `make screenshots`
30+
- TestFlight: `make testflight`
31+
- Меню fastlane: `make fastlane`
32+
- Первичная настройка окружения: `make setup`
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
description: Подход к тестам — Swift Testing (@Test, #expect, #require), покрытие бизнес-логики
3+
globs:
4+
- "**/SwiftUI-DaysTests/**/*"
5+
- "**/SwiftUI-DaysUITests/**/*"
6+
alwaysApply: false
7+
---
8+
## Тестирование
9+
10+
- Для модульных тестов используем Swift Testing (`import Testing`).
11+
- Для тестовых функций — `@Test`. Если используются операции, которые могут выбросить ошибку, помечайте тест `throws` и разворачивайте опционалы через `try #require`.
12+
- Для проверок — `#expect`, избегайте сравнений `Bool` через `==`; вместо этого используйте `#expect(isTrue)`/`#expect(isFalse)`.
13+
- Логику, связанную с моделями и преобразованием данных, тестируем в `SwiftUI-DaysTests/`.
14+
15+
### Примеры из проекта
16+
17+
- Тесты моделей и форматирования данных:
18+
@SwiftUI-Days/SwiftUI-DaysTests/ItemTests.swift
19+
@SwiftUI-Days/SwiftUI-DaysTests/DisplayOptionTests.swift
20+
@SwiftUI-Days/SwiftUI-DaysTests/IconVariantTests.swift
21+
22+
<!-- Запуск тестов описан в 40-build-and-tests.mdc -->

SwiftUI-Days.xcodeproj/project.pbxproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,7 @@
534534
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
535535
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
536536
MACOSX_DEPLOYMENT_TARGET = 14.0;
537-
MARKETING_VERSION = 1.8;
537+
MARKETING_VERSION = 1.9;
538538
PRODUCT_BUNDLE_IDENTIFIER = "com.oleg991.SwiftUI-Days";
539539
PRODUCT_NAME = "$(TARGET_NAME)";
540540
SDKROOT = auto;
@@ -582,7 +582,7 @@
582582
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
583583
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
584584
MACOSX_DEPLOYMENT_TARGET = 14.0;
585-
MARKETING_VERSION = 1.8;
585+
MARKETING_VERSION = 1.9;
586586
PRODUCT_BUNDLE_IDENTIFIER = "com.oleg991.SwiftUI-Days";
587587
PRODUCT_NAME = "$(TARGET_NAME)";
588588
SDKROOT = auto;

SwiftUI-Days/Screens/More/MoreScreen.swift

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,9 @@ struct MoreScreen: View {
1111
ZStack {
1212
Spacer().containerRelativeFrame([.vertical])
1313
VStack(spacing: 16) {
14-
Group {
15-
appThemeIconButton
16-
appDataButton
17-
feedbackButton
18-
rateAppButton
19-
shareAppButton
20-
githubButton
14+
ViewThatFits(in: .horizontal) {
15+
horizontalLayout
16+
verticalLayout
2117
}
2218
.buttonStyle(.borderedProminent)
2319
.foregroundStyle(.buttonTint)
@@ -30,6 +26,32 @@ struct MoreScreen: View {
3026
}
3127
}
3228

29+
private var horizontalLayout: some View {
30+
HStack(spacing: 24) {
31+
VStack(alignment: .trailing, spacing: 16) {
32+
appThemeIconButton
33+
appDataButton
34+
feedbackButton
35+
}
36+
VStack(alignment: .leading, spacing: 16) {
37+
rateAppButton
38+
shareAppButton
39+
githubButton
40+
}
41+
}
42+
}
43+
44+
private var verticalLayout: some View {
45+
VStack(spacing: 16) {
46+
appThemeIconButton
47+
appDataButton
48+
feedbackButton
49+
rateAppButton
50+
shareAppButton
51+
githubButton
52+
}
53+
}
54+
3355
private var appThemeIconButton: some View {
3456
NavigationLink("App theme and Icon", destination: ThemeIconScreen())
3557
.accessibilityIdentifier("appThemeIconButton")

0 commit comments

Comments
 (0)