Как проверить обмен данными при перетаскивании в iOS

DevOps и CI/CD ·~5 мин чтения

Как проверить обмен данными при перетаскивании в iOS

Перед выпуском команда перетащила номер задачи из списка в поле редактирования. Предварительный просмотр выглядел правильно, после отпускания ошибки не было — но сохранилась пустая строка. Дело не в анимации: интерфейс может создать ложное впечатление успеха, если не совпадают тип данных, объявленный источником, тип, принимаемый целью, или момент завершения асинхронного чтения. При удалённой работе на облачном Mac сначала можно проверить контракт данных с помощью XCTest, а затем открыть симулятор и проверить сам жест. Одно не заменяет другого.

Сначала оформите протокол перетаскивания как проверяемый контракт

Для каждого вида перетаскиваемого содержимого укажите три вещи: объявленный поставщиком UTType, типы, разрешённые на принимающей стороне, и содержимое, которое должно получиться после успешного чтения. Например, если номер задачи нужно лишь вставить в текстовое поле, обе стороны могут договориться использовать UTType.utf8PlainText. Не стоит регистрировать на отправляющей стороне обычный текст, запрашивать на принимающей только изображения, а затем считать срабатывание события отпускания признаком успеха.

Опишите и пути обработки ошибок: цель не должна подсвечиваться как доступная для неподдерживаемого типа; при ошибке чтения нельзя вставлять пустую задачу; отмена перетаскивания не должна менять исходные данные. Если нужно сохранить поля помимо номера задачи, определите собственный формат данных и обеспечьте его разбор на обеих сторонах, а не прячьте структурированные данные в отображаемом тексте.

Завершение перетаскивания — событие интерфейса, а не запись данных. Обновлять состояние приложения можно лишь после проверки совместимости типов, успешного асинхронного чтения и валидации содержимого.

Проверьте тип и фактическое содержимое с помощью XCTest

Добавьте тестовый файл в тестовый target приложения. Поставщик в примере ниже регистрирует текст UTF-8. Тест сначала проверяет тип, затем дожидается обратного вызова чтения и сверяет полученные байты. Мышь и графический рабочий стол ему не нужны, поэтому такой тест удобно запускать как быструю проверку в сборочном конвейере.

import XCTest
import UniformTypeIdentifiers

final class DragPayloadTests: XCTestCase {
    func testPlainTextPayload() {
        let provider = NSItemProvider()
        provider.registerDataRepresentation(
            forTypeIdentifier: UTType.utf8PlainText.identifier,
            visibility: .all
        ) { completion in
            completion(Data("ticket-42".utf8), nil)
            return nil
        }

        XCTAssertTrue(provider.hasItemConformingToTypeIdentifier(
            UTType.plainText.identifier
        ))
        XCTAssertFalse(provider.hasItemConformingToTypeIdentifier(
            UTType.image.identifier
        ))

        let loaded = expectation(description: "Load drag payload")
        provider.loadDataRepresentation(
            forTypeIdentifier: UTType.utf8PlainText.identifier
        ) { data, error in
            XCTAssertNil(error)
            XCTAssertEqual(data.flatMap { String(data: $0, encoding: .utf8) },
                           "ticket-42")
            loaded.fulfill()
        }
        wait(for: [loaded], timeout: 5)
    }
}

Здесь plainText служит для проверки совместимого типа, а utf8PlainText — для чтения конкретного зарегистрированного представления. После успешного теста используйте в рабочем коде ту же проверку типа и логику декодирования: иначе тест может принимать один тип, а интерфейс — пользоваться другим списком разрешённых типов. В обратном вызове рабочий код также должен обрабатывать error, пустые данные и ошибки декодирования UTF-8. Одного результата hasItemConformingToTypeIdentifier недостаточно, чтобы менять интерфейс.

Зафиксируйте целевой симулятор и запустите тест

Сначала проверьте доступные конфигурации в консоли VMCommit. После предоставления Mac подключитесь к нему по SSH и посмотрите список установленных симуляторов. Замените проект, Scheme и идентификатор устройства ниже своими значениями. Идентификатор берите из вывода первой команды: строка в примере не обозначает реальное устройство.

xcrun simctl list devices available
xcodebuild -list -project App.xcodeproj
xcodebuild test \
  -project App.xcodeproj \
  -scheme App \
  -destination 'platform=iOS Simulator,id=YOUR_DEVICE_ID' \
  -only-testing:AppTests/DragPayloadTests

Путь в -only-testing должен соответствовать тестовому target и имени класса в проекте. Если команда сообщает, что Scheme не существует, сначала проверьте, открыт ли к ней общий доступ. Если целевое устройство недоступно, заново получите список на этом Mac, а не используйте идентификатор с другой машины. При сбое теста сохраните исходный вывод, чтобы отличить ошибку запуска симулятора от провала проверки содержимого.

Проверьте фактическое отпускание в графическом сеансе

Контрактный тест обходит обработку жестов и не доказывает, что UIDragInteraction или UIDropInteraction подключены правильно. Откройте симулятор на графическом рабочем столе, установите тестируемое приложение и выполните полный сценарий в следующем порядке:

  1. Нажмите и удерживайте допустимый исходный элемент, затем начните перетаскивание. Убедитесь, что предварительный просмотр соответствует выбранному элементу.
  2. Переместите элемент к разрешённой цели и проверьте, появляется ли индикация возможности отпускания. Затем переместите его в область, не принимающую этот тип, и убедитесь, что индикация исчезает.
  3. Отпустите элемент, дождитесь завершения чтения и проверьте, что в поле редактирования появился правильный номер задачи — ровно один раз.
  4. Начните ещё одно перетаскивание и отмените его до отпускания. Убедитесь, что ни список, ни поле редактирования не изменились.

Если содержимое велико, выделите «идёт чтение» в отдельное состояние: запретите повторную отправку и обновляйте содержимое только после завершения чтения. При ошибке верните возможность работать с интерфейсом и покажите понятное сообщение. Не показывайте «Импортировано» до возврата асинхронного вызова лишь ради более плавной анимации. Графическая проверка оценивает видимые пользователю переходы состояний, а XCTest — данные на нижнем уровне. Результаты следует фиксировать отдельно.

При сбое проследите путь данных

Если «цель не подсвечивается», начните с типов: выведите в среде разработки идентификаторы, зарегистрированные источником и разрешённые принимающей стороной. Убедитесь, что проверяется совместимость типов, а не только совпадение отображаемых названий. Если «цель подсвечивается, но содержимое не появляется», проверьте, выполняется ли обратный вызов загрузки, затем изучите ошибку, длину данных и результат декодирования. Не записывайте всё содержимое в общие журналы: задача может включать рабочие данные, а для определения проблемного этапа обычно хватает типа и длины.

Если содержимое «иногда вставляется дважды», проверьте, не обновляется ли модель одновременно в обработчике отпускания и в обратном вызове завершения загрузки и не запускается ли чтение одного поставщика повторно. Назначайте каждой операции отпускания отдельный идентификатор и фиксируйте результат один раз — только после успешной проверки. При отмене или ошибке отбрасывайте результат, ожидающий фиксации. При переходе к изображениям и другим видам содержимого пишите отдельные тесты для каждого принимаемого типа: успешный тест обычного текста не подтверждает работу всех сценариев перетаскивания.

Перед выпуском сверьте два контрольных списка

Список автоматических проверок должен охватывать совместимость типов, отклонение неподдерживаемых типов, совпадение байтового содержимого, обработку пустого значения и ошибок чтения, а также провал теста при превышении времени ожидания. Список графических проверок должен охватывать правильный предварительный просмотр источника, правильную индикацию цели, однократное обновление после успеха, отсутствие изменений после отмены и сообщение об ошибке, которое не выдаёт сбой за успех. Первый список подходит для каждого коммита; ко второму стоит особенно внимательно возвращаться после изменений в обработке взаимодействия или версии системы.

В завершение повторите сценарий на чистом симуляторе и убедитесь, что результат не зависит от содержимого поля редактирования, оставшегося после предыдущего запуска. Сохраните сведения о типах, которые приложение действительно принимает, вместе с обоими контрольными списками. При следующем изменении формата перетаскивания это поможет определить, где возникла проблема: в контракте данных, асинхронном чтении или отклике на жест.

Часто задаваемые вопросы

Достаточно ли теста NSItemProvider для проверки интерфейса?

Нет. Он проверяет тип и чтение данных; область сброса, предпросмотр и отмену нужно проверить в графическом сеансе.

Когда принимающая сторона должна читать данные?

Сначала проверьте совместимость UTType, затем загрузите представление асинхронно. Не считайте импорт завершённым до ответа обработчика.

Выберите срок аренды

Проверьте следующий шаг на выделенном физическом Mac mini

VMCommit предлагает выделенные физические Mac mini с чипом M4 в аренду на день, неделю, месяц или квартал. Выберите модель и локацию, а затем проверьте доступность в реальном времени в панели управления.

Арендовать Mac mini