Conversation
| } | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
Ниже — только предметные замечания по диффу.
-
Проверьте, что SwiftDataTransferrableScene реально прокидывает modelContainer в окружение контента (через .modelContainer(...) или environment(.modelContext, ...)). Раньше это гарантировалось явным .modelContainer(sharedModelContainer). Если обёртка этого не делает, MainView может остаться без modelContext и упасть/вести себя некорректно.
-
API: вместо String для exportedUTType лучше принимать UTType. Это снизит риск опечаток и упростит переиспользование. Минимум — вынесите идентификатор в константу/extension:
- extension UTType { static let persistentModelID = UTType(exportedAs: "com.amikhaylin.persistentModelID") }
- Тогда параметр exportedUTType можно сделать UTType.
-
Убедитесь, что тип с идентификатором "com.amikhaylin.persistentModelID" объявлен (UTExportedTypeDeclarations в Info.plist) если предполагается межприложечный обмен/Drag&Drop/ShareSheet. Без декларации другие приложения не будут его «знать».
-
Название модуля/символов: «Transferrable» vs «Transferable». В индустрии и в SwiftUI принято «Transferable» (одна r). Если библиотека своя — лучше исправить орфографию в названии модуля и API до того, как оно «застынет».
-
Проверьте сигнатуру SwiftDataTransferrableScene: контентный замыкатель должно быть помечено как @SceneBuilder. Иначе .commands и композиция нескольких сцен могут работать некорректно.
-
Расположение .commands: сейчас модификатор прикреплён к WindowGroup внутри обёртки. Это ок, если обёртка не добавляет свои сцены, для которых команды тоже должны применяться. Если обёртка создаёт дополнительные сцены, продумайте, куда должны применяться команды (возможно, их стоит поднимать на уровень самой обёртки или дать способ пробросить команды наружу).
-
InspectorCommands — доступность по платформам. Он macOS‑специфичный. Если таргет — iOS/iPadOS, оберните в #if os(macOS) || targetEnvironment(macCatalyst), чтобы избежать проблем со сборкой/SDK.
-
Уберите закомментированный .modelContainer — если он больше не нужен, мёртвый код только шумит.
-
sharedModelContainer лучше сделать let, если он неизменяемый, чтобы явно зафиксировать семантику.
-
Поведенческая эквивалентность: раньше вы передавали modelContext: sharedModelContainer.mainContext в .swiftDataTransferrable. Теперь отдаёте весь контейнер. Проверьте, что внутри обёртки используется корректный контекст (обычно mainContext) и что нет лишнего создания/подмены контекстов.
-
Если планируется несколько окон/сцен (iPadOS/macOS), убедитесь, что один и тот же ModelContainer шарится корректно между WindowGroup экземплярами внутри обёртки, иначе получите рассинхронизацию состояния.
Это основные точки риска и места для улучшения.
| minimumVersion = 3.0.0; | ||
| }; | ||
| }; | ||
| 4DC43AA02F1502A7009A5C73 /* XCRemoteSwiftPackageReference "CloudStorage" */ = { |
There was a problem hiding this comment.
Наблюдения по диффу и конкретные рекомендации:
Что изменено по сути
- CURRENT_PROJECT_VERSION во всех конфигурациях поднят с 119 до 120.
- Минимальная версия SwiftPM-пакета “swiftdata-transferrable” повышена с 2.0.0 до 3.0.0 (upToNextMajorVersion).
Риски и улучшения
- Дублирование и рассинхронизация CURRENT_PROJECT_VERSION
- Сейчас значение продублировано в массе таргетов/конфигураций — это создает высокий риск пропустить где-то инкремент при следующем релизе.
- Рекомендации:
- Вынести CURRENT_PROJECT_VERSION в общий .xcconfig (Base) и удалить таргетные переопределения, чтобы таргеты наследовали значение.
- Либо задать его на уровне проекта (Project) и убрать из таргетов.
- Автоматизировать инкремент в CI (agvtool new-version -all 120, fastlane increment_build_number и т.п.).
- Добавить скрипт-проверку на CI, который валидирует, что CFBundleVersion совпадает у всех апп-бандлов/экстеншенов релизной сборки (это требование App Store Connect).
- Обновление swiftdata-transferrable до 3.0.0
- Major-бамп потенциально содержит breaking changes.
- Рекомендации:
- Убедиться в совместимости по минимальным платформах и toolchain (версия Swift/Xcode). Многие библиотеки с переходом на 3.x требуют Swift 6 и iOS 18+/macOS 15+.
- Прогнать миграции API, собрать все таргеты, где зависимость реально подключена.
- Обновить/закоммитить Package.resolved для репродуцируемых сборок.
- Если вы хотите ограничиться 3.0.x до валидации стабильности — рассмотрите upToNextMinorVersion(3.0.0) либо точную фиксацию на конкретной 3.0.x.
- Проверьте таргет-скоуп зависимости: не тянется ли пакет в экстеншены/Watch, где SwiftData/Transferable могут быть недоступны или нежелательны. Лучше явно подключать пакет только к тем таргетам, которым он нужен.
- Поддерживаемые платформы и деплой-таргеты
- В проекте встречаются:
- IPHONEOS_DEPLOYMENT_TARGET = 18.1 (не изменялось, но имейте в виду, что повышение зависимости до 3.x может “подтянуть” требования к платформе).
- MACOSX_DEPLOYMENT_TARGET = 26.0 — это выглядит как ошибка. Никакого “macOS 26.0” не существует, и такой таргет может привести к непредсказуемым последствиям при линковке/валидации.
- Если это про Mac Catalyst, корректно настраивать:
- MACOSX_DEPLOYMENT_TARGET на реальную версию macOS (например, 14.0/15.0).
- IPHONEOS_DEPLOYMENT_TARGET[sdk=macabi*] для iOS-части Catalyst.
- Если это просто опечатка или исторический артефакт — приведите к валидному значению. Повышение пакета может усилить проверку платформ и “всплыть” это место.
- Если это про Mac Catalyst, корректно настраивать:
- Проверьте, что новый пакет 3.x совместим с таргетами для Watch/Widgets, либо исключите их при необходимости.
- Info.plist и GENERATE_INFOPLIST_FILE
- В ряде таргетов одновременно указаны GENERATE_INFOPLIST_FILE = YES и INFOPLIST_FILE = ... Это порождает путаницу, какой plist реально используется, и откуда берется CFBundleVersion.
- Рекомендации:
- Либо используйте сгенерированный plist (убрав явный INFOPLIST_FILE), либо явный файл (выключив GENERATE_INFOPLIST_FILE).
- Гарантируйте, что CFBundleVersion берется из CURRENT_PROJECT_VERSION для всех апп-бандлов/экстеншенов. Это важно для синхронности при загрузке в App Store.
- Процессный момент
- Смешение “билд-бампа” и major-апдейта зависимости в одном PR повышает риск и усложняет откат. В будущем лучше разделять:
- PR-1: обновление зависимости + миграции.
- PR-2: инкремент билд-номера перед релизом.
Итого
- Инкремент билд-номера выглядит консистентным, но его стоит централизовать/автоматизировать.
- Самое критичное, что стоит поправить/проверить немедленно:
- Валидность MACOSX_DEPLOYMENT_TARGET (26.0 — почти наверняка ошибка).
- Совместимость и область применения swiftdata-transferrable 3.x (платформы, Swift, таргеты).
- Единый источник правды для Info.plist/CFBundleVersion, чтобы избежать расхождений при релизной сборке.
| "version" : "3.0.2" | ||
| } | ||
| } | ||
| ], |
There was a problem hiding this comment.
Коротко и по делу:
-
Это мажорное обновление (2.x → 3.x). Проверьте release notes/CHANGELOG у Whiffer/swiftdata-transferrable на наличие breaking changes и требований к минимальным версиям платформ/инструментов (Swift/Xcode/iOS). Без этого велика вероятность неожиданных билд/рантайм-ошибок.
-
Просто поменять Package.resolved — недостаточно. Убедитесь, что в Package.swift (или настройках Xcode) изменено ограничение версии:
- Если раньше было .upToNextMajor(from: "2.0.0") или точное "2.0.0", нужно разрешить 3.x (например, .upToNextMajor(from: "3.0.0") или .exact("3.0.2"), если хотите жёсткий пин).
- Иначе резолвер проигнорирует этот Package.resolved и вернётся к 2.x.
-
Не редактируйте Package.resolved вручную. Обновляйте через SPM/Xcode (swift package update / Xcode > Package Dependencies > Update) — это снизит риск рассинхронизации и «плавающих» сборок.
-
Проверьте соответствие ревизии и тега: хеш 62bac0… действительно должен соответствовать тегу 3.0.2. Лучше полагаться на семантические версии, а не на «сырые» ревизии.
-
Если это библиотека (а не конечное приложение), оценивайте целесообразность коммитить Package.resolved: для библиотек его часто не коммитят, чтобы не навязывать зависимостям точные версии потребителям. Для приложений — наоборот, фиксируют для воспроизводимых сборок.
-
Синхронизация в Xcode: убедитесь, что обновлён именно верхнеуровневый Package.resolved в xcshareddata/swiftpm, если у вас workspace/несколько проектов — чтобы CI и локальная сборка использовали один и тот же lockfile.
-
После апгрейда:
- Запустите полную сборку/тесты на всех целевых платформах.
- Проверьте импорты и точки использования API пакета на предмет переименований/аннотаций @available.
- Если пакет требует более новый Swift tools version или поднятия iOS/macOS target — обновите их явно в Package.swift и настройках проекта.
-
Если нужна предсказуемость выпуска, зафиксируйте версию диапазоном (например, .upToNextMinor(from: "3.0.0")), чтобы получать патчи 3.0.x, но избежать непреднамеренного скачка на 4.x в будущем.
| import SwiftDataTransferrable | ||
| import CloudStorage | ||
|
|
||
| struct ContentView: View { |
There was a problem hiding this comment.
Наблюдения и рекомендации по диффу:
-
Удаление import SwiftDataTransferrable
- Проверьте, не опирались ли вы на автогенерацию/расширения для Transferable (drag&drop, ShareLink, копирование) для моделей SwiftData. Если да — сборка/функционал сломаются.
- Если пакет обеспечивал автоматическую конформность @model к Transferable, предложенная замена:
- Ввести отдельный легковесный DTO (Codable + Transferable) и маппить из/в модель SwiftData.
- Или описать Transferable вручную через CodableRepresentation/ProxyRepresentation. Это надёжнее и прозрачнее, чем зависеть от магии стороннего пакета.
- Если пакет больше не нужен фактически — хорошо, но убедитесь, что:
- В проекте нет «призрачных» импортов этого модуля в других файлах.
- Удалён пакет из зависимостей (Package.swift/SwiftPM), чтобы не тянуть лишнее.
-
Импорт CloudStorage
- Проверьте доступность по платформам/минимальным версиям (iOS 17+/macOS 14+). Если таргеты шире:
- Оберните import CloudStorage и использование @cloudstorage в #if canImport(CloudStorage) и/или добавьте @available.
- Не храните в @cloudstorage большие данные/часто меняющиеся сущности — только настройки/флаги/ключи. Для остального используйте SwiftData/CloudKit.
- Обработайте сценарии отсутствия iCloud (пользователь не залогинен/выключил iCloud Drive): дефолтные значения, деградация функционала без крэшей.
- Синхронизация: если @cloudstorage влияет на выборку SwiftData, не делайте последующие фильтры вьюшкой — используйте @query с predicate/sortDescriptors, чтобы избежать лишних перерисовок и несогласованности.
- Проверьте доступность по платформам/минимальным версиям (iOS 17+/macOS 14+). Если таргеты шире:
-
Архитектура с SwiftData
- В View:
- Используйте @Environment(.modelContext) и @query с предикатами вместо ручной фильтрации в теле View.
- Для тяжёлых операций создайте фоновый ModelContext из ModelContainer, чтобы не блокировать UI.
- Потоки: помните, что контекст во вью — @mainactor. Любые фоновые изменения синхронизируйте транзакциями/merge-политиками.
- В View:
-
Кроссплатформенность и сборка
- Если проект мультиплатформенный, согласуйте импорты:
- #if os(iOS) || os(macOS) import CloudStorage #endif
- Убедитесь, что тестовые таргеты не ломаются из‑за отсутствия CloudStorage.
- Если проект мультиплатформенный, согласуйте импорты:
-
Качество и поддерживаемость
- Если вы отказались от SwiftDataTransferrable из‑за «магии», зафиксируйте в коде явные конформности (Transferable/Codable) и добавьте маленькие snapshot/юнит‑тесты на конвертацию модели ↔ DTO, чтобы не словить регресс при будущих изменениях модели.
Если дадите фрагмент, где раньше использовались типы/макросы из SwiftDataTransferrable (drag&drop, ShareLink, Pasteboard), предложу точную замену с минимальным вмешательством.
| } | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Вот что стоит поправить/перепроверить по диффу — только по делу.
Архитектура сцены/контейнера данных
- Вы закомментировали .modelContainer(sharedModelContainer) у WindowGroup и обернули всё в SwiftDataTransferrableScene(modelContainer: …). Убедитесь, что SwiftDataTransferrableScene действительно применяет .modelContainer к вложенным сценам. Если нет — получите крэш/пустой @Query/@Environment(.modelContext). Я бы:
- Либо оставил .modelContainer(sharedModelContainer) на WindowGroup,
- Либо добавил тест/ассерты в Debug (assert(Environment(.modelContext) != nil в ContentView)).
- Вы используете sharedModelContainer.mainContext напрямую в командах. Это ок, если контейнер общий и привязан к сцене. Но в многооконной конфигурации лучше использовать модельный контекст из окружения активного окна (или передавать его вниз как Environment). Сейчас может рассинхронизироваться состояние окна и контекста.
SwiftData/CloudKit
- Явно указали cloudKitDatabase: .none. Если раньше рассчитывали на CloudKit-синхронизацию — вы её отключили. Если такого намерения не было — это регрессия в поведении.
SwiftDataTransferrable
- Перенесли .swiftDataTransferrable(...) с вью на уровень сцены через SwiftDataTransferrableScene. Проверьте, что:
- Экспортируемый UTI ("com.amikhaylin.persistentModelID") действительно объявлен в Info.plist (UTType Exported) и уникален.
- Поведение drag&drop/копирования работает во всех окнах, где нужно. Если в будущем добавятся другие сцены — их тоже надо будет оборачивать.
- Доступность: SwiftDataTransferrableScene помечен @available под ваши целевые версии macOS. Иначе — сборка/рантайм-падения на старых системах.
Команды и шорткаты
- Конфликт горячих клавиш:
- "Add to Calendar → for Due Date" — .keyboardShortcut("c", [.command, .option])
- "Duplicate task" — .keyboardShortcut("c", [.command, .option])
Это реальный конфликт. Предложение: дубликат на ⌘D, а календарь оставить на ⌥⌘C (и ⌥⇧⌘C для Today).
- Кнопка удаления в меню: вместо Image(...).foregroundStyle(.red) используйте Button(role: .destructive) — корректная семантика, правильная окраска в меню и поддержка доступности.
Мутации и устойчивость к изменениям коллекций
- В командах часто итерируете selectedTasks.tasks и одновременно модифицируете модель (complete, delete и т. п.). Безопаснее итерироваться по снэпшоту:
- let snapshot = Array(selectedTasks.tasks)
- for task in snapshot { … }
Так вы избежите потенциальных сайд-эффектов при изменении selection во время итерации.
- Массовые правки (complete, setDueDate, nextWeek, delete) лучше объединять в одну транзакцию контекста, чтобы не триггерить лишние сохранения/нотификации:
- let ctx = sharedModelContainer.mainContext
- try? ctx.transaction { … } или ctx.perform { …; try? ctx.save() }
- Частое refresher.refresh.toggle(): если используете его как «костыль» против проблем с обновлением @query — лучше устранить корень (наблюдаемая модель/предикаты/сорта/собственные Publisher). Если оставить — хотя бы не трогать там, где SwiftData сам корректно оповещает.
UI/меню
- .tag(priority as Int) в Menu "Priority" не используется (не Picker). Уберите — шум.
- Иконки/цвета в меню: для «Delete task» лучше полностью положиться на роль .destructive; явные цвета в меню macOS часто игнорируются системно.
- Вызовы CalendarManager.addToCalendar(...) из команд: если есть запросы разрешений — подумайте о фидбэке/ошибках (NSAlert/анонс статуса) и об исполнении на главном потоке.
Прочее
- Печать пути хранилища print("(fileURL.absoluteString)") — оберните в #if DEBUG или используйте os.Logger. В релизе лишний шум.
- Инициализация ContentView через .id(refresher.refresh) жестко пересобирает вью. Это может сбрасывать внутреннее состояние сабвью. Если это «молоток» для обновления запросов — попробуйте уйти на Observable-модель/фильтры, чтобы не перегенерировать всю иерархию.
- Проверить доступность шорткатов в подменю: они работают, но убедитесь, что нет дубликатов на верхнем уровне.
Резюме минимальных правок, которые я бы внёс сразу:
- Устранить конфликт шорткатов (Duplicate → ⌘D).
- Удаление: Button(role: .destructive).
- Итерации по выборке — через снэпшот массива.
- Вернуть .modelContainer(sharedModelContainer) на WindowGroup или подтвердить, что SwiftDataTransferrableScene гарантированно его применяет.
- Обернуть print в #if DEBUG.
- Убрать .tag из меню приоритета.
- По возможности сгруппировать массовые мутации в транзакции контекста и сократить зависимость от refresher.refresh.
| .draggable(status) | ||
| .listRowSeparator(.hidden) | ||
| .dropDestination(for: Todo.self) { tasks, _ in | ||
| for task in tasks { |
There was a problem hiding this comment.
Наблюдения и предложения по улучшению:
-
Потенциальная путаница типов и UX: вы сделали строку статуса перетаскиваемой (.draggable(status)), но drop-приёмник настроен на Todo (.dropDestination(for: Todo.self)). Если цель — перетаскивать задачи между статусами, .draggable нужно вешать на представления задач, а не на контейнер статуса. Если же хотите перетаскивать сами статусы (реорганизация колонок/секций), добавьте отдельный dropDestination для Status и соответствующую логику.
- Вариант для задач:
- На ячейке задачи: .draggable(task)
- На контейнере статуса: .dropDestination(for: Todo.self) { tasks, _ in … }
- Вариант для статусов:
- На заголовке статуса: .draggable(status)
- Там же добавьте: .dropDestination(for: Status.self) { statuses, _ in … }
- Вариант для задач:
-
Конфликт жестов: .draggable на корневом элементе строки списка часто конфликтует с кнопками, контекстным меню и жестами списка (скролл, свайпы). Лучше ограничить область начала drag-жеста отдельным “хэндлом” (например, иконка) или хотя бы заголовком статуса, а не всей строкой:
- Пример: Text(status.title).draggable(status)
- Если оставляете drop на всей строке, задайте .contentShape(Rectangle()), чтобы таргет был предсказуем.
-
Transferable и доступность API: .draggable(value:) и .dropDestination(for:) требуют iOS 16+/macOS 13+. Убедитесь, что:
- Типы (Todo, Status) действительно conform к Transferable с корректными UTType (или используйте альтернативы для более старых ОС).
- Есть @available-ограничения или условная компоновка, если поддерживаете более ранние версии.
-
Возврат значения из drop-замыкания: убедитесь, что вы возвращаете корректный Bool. Возвращайте false, если ни одна из задач не была обработана (например, уже в нужном статусе), чтобы система правильно понимала исход операции.
-
Валидация и мульти-дроп: при переносе нескольких задач отфильтруйте неподходящие/дубликаты, применяйте изменения батчем и возвращайте true только если реально что-то изменили. Оберните изменения модели в withAnimation для плавности.
-
Визуальная обратная связь: используйте вариант с isTargeted, чтобы подсвечивать статус-цель при наведении. Это сильно помогает UX при DnD:
- .dropDestination(for: Todo.self, isTargeted: $isTargeted) { tasks, _ in … }
-
Превью перетаскивания: по умолчанию превью — снимок всей view, что для строки статуса может быть тяжёлым и громоздким. Задайте компактный кастомный preview у .draggable, чтобы избежать мерцаний и избыточной нагрузки.
-
Граница ответственности: совмещать на одной и той же view источники drag разных типов (Status и Todo) и один drop-приёмник нередко ведёт к неочевидным конфликтам. Разнесите drag/drop по разным подвидам в иерархии (например, заголовок секции — про статусы, область списка задач — про задачи).
Итого: сейчас добавление .draggable(status) в том же месте, где drop ожидает Todo, выглядит как логическая ошибка/недопонимание намерений. Чётко определите, что именно пользователь должен уметь перетаскивать (задачи или статусы), и расположите соответствующие .draggable/.dropDestination на тех view, которые этому соответствуют, с учётом конфликтов жестов, обратной связи и совместимости API.
| #endif | ||
| } | ||
| .listStyle(SidebarListStyle()) | ||
| } else { |
There was a problem hiding this comment.
Ниже — только существенные замечания по диффу.
-
dropDestination: неверное понимание второго параметра и местами отсутствует возврат Bool
- В сигнатуре dropDestination(for:action:) второй параметр — это точка (CGPoint) позиции дропа, а не индекс. Вы используете имя offset и присваиваете его в order (project.order = offset / group.order = offset), что как минимум приводит к ошибке типов, а концептуально — к некорректной логике упорядочивания.
- В нескольких macOS-блоках вы не возвращаете Bool из замыкания dropDestination — это либо не скомпилируется, либо drop не будет считаться принятым. Пример проблемных мест:
- В секции “ungrouped” проектов (macOS) — нет return true.
- В секции проектов внутри группы (macOS) — нет return true.
- В секции групп (macOS) — нет return true.
- Рекомендация:
- Явно возвращайте true после обработки;
- Не используйте второй аргумент dropDestination как индекс. Если вам нужен индекс вставки, используйте onInsert(of:perform:) на ForEach/List (там приходит index), либо DropDelegate и самостоятельно вычисляйте индекс по координате через привязки геометрии. После вычисления целевого индекса переформируйте order так же, как вы делаете это в onMove: пересоберите список, вставьте элементы в нужное место и последовательно пронумеруйте order.
-
Несогласованность алгоритмов reorder между iOS и macOS
- На iOS вы корректно пересортировываете и переназначаете order всем элементам после .onMove.
- На macOS вы пытаетесь писать “order = offset” (см. пункт выше). В итоге либо не скомпилируется, либо приведёт к дублям и «дырам» в порядке.
- Рекомендация: унифицируйте подход — после дропа на macOS также пересобирайте порядок целиком (как в iOS), вместо попытки присвоить «псевдо-индекс».
-
Две конкурирующие drop-цели для проектов внутри группы
- У вас есть dropDestination на уровне группы (привязка проекта к группе) и ещё один dropDestination на уровне списка проектов внутри группы (реордер/перемещение внутрь). На практике это может конфликтовать при хит-тесте и приводить к неоднозначности, куда именно «попал» дроп.
- Рекомендация: чётко разделите области дропа:
- Дроп на заголовок DisclosureGroup — назначение группы;
- Дроп внутрь списка — реордер. Убедитесь, что только одна dropDestination перехватывает событие для каждой области и обе возвращают корректный Bool.
-
Присвоение группы без корректировки порядка
- В .dropDestination(for: Project.self) на уровне группы вы теперь убрали фильтр where project.group == nil || project.group != group и просто всегда проект перепривязываете к этой же группе. Это в целом безвредно, но:
- Нет выставления порядка при переносе в группу (проект может «потеряться» в сортировке).
- Рекомендация: при добавлении в группу присваивайте order последним (например, lastOrder + 1), либо пересортируйте последовательность группы целиком.
- В .dropDestination(for: Project.self) на уровне группы вы теперь убрали фильтр where project.group == nil || project.group != group и просто всегда проект перепривязываете к этой же группе. Это в целом безвредно, но:
-
.draggable(project as Project) и .tag(project as Project)
- Приведение к Project здесь выглядит избыточным, если переменная уже типа Project. Если это сделано для разрешения перегрузки .draggable, лучше зафиксировать это комментарием.
- Отдельная рекомендация по переносам Core Data-объектов: вместо передачи целого NSManagedObject в drag/drop лучше передавать лёгкий Transferable-пэйлоад (например, objectID URI или UUID), и уже по нему искать объект в контексте. Это надёжнее (особенно при межпроцессном/межсценовом переносе) и снимает риски с сериализацией/жизненным циклом управляемых объектов.
-
Иконка "folder.badge.minus"
- Проверьте доступность символа для минимальных целевых версий macOS/iOS в проекте. Если поддерживаются более старые ОС, потребуется @available или запасной символ.
-
Дублирование логики сортировок/переназначения order
- Одинаковый код встречается в нескольких местах (.onMove в двух списках, дроп в macOS-блоках).
- Рекомендация: вынесите общую логику в переиспользуемые функции: reorder(list:moving:to:), appendToGroup(:), moveToGroup(:at:), normalizeOrder(in:), чтобы не расходились реализации между платформами и местами вызова.
-
Совместимость API
- .draggable/.dropDestination требуют iOS 16+/macOS 13+. Если поддерживаете более ранние, нужны @available-гварды или альтернативы (.onDrop с NSItemProvider / DropDelegate).
Итого, самые критичные правки для мержа:
- Исправить сигнатуры dropDestination: вернуть Bool и не трактовать второй параметр как индекс; пересобирать order по аналогии с iOS .onMove.
- Устранить конфликтующие drop-цели и гарантировать ожидаемое поведение при дропе на заголовок группы vs внутрь списка.
- При переносе проекта в группу назначать валидный order.
- По возможности перейти на лёгкие Transferable-пэйлоады вместо передачи NSManagedObject.
No description provided.