Проверка iOS App Intents на облачном Mac: параметры и Команды

AI-автоматизация ·~5 мин чтения

Проверка iOS App Intents на облачном Mac: параметры и Команды

Команда «Записать прочитанное» находится на машине разработчика, но не появляется в симуляторе на удалённой сборочной машине. Не спешите менять код приложения: корректность параметров, выполнение Intent и обнаружение действия системой — три разные проверки. Облачный Mac помогает закрепить тестовую среду для первых двух; последнюю всё равно нужно проверить в симуляторе с установленным приложением.

Сначала определите границы автоматического тестирования

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

Приёмка состоит из трёх уровней: чистые тесты Swift проверяют пустой ввод и недопустимые символы; XCTest вызывает perform() и проверяет результат и ошибки; при ручной проверке в симуляторе убеждаются, что действие видно, параметр можно заполнить, а результат — прочитать. Сбои на первых двух уровнях должны блокировать слияние изменений. Третий уровень зависит от системной индексации, поэтому его лучше включить в проверку перед выпуском версии, а не писать тест с фиксированным временем ожидания.

Не удаляйте и не устанавливайте приложение заново раз за разом в автоматическом тесте только ради появления команды. Сначала подтвердите, что контракт кода соблюдён, а затем проверяйте установку, запуск приложения и обнаружение действия системой.

Вынесите правила ввода в тип, который можно тестировать напрямую

Правила ниже допускают от 1 до 32 символов: латинские буквы ASCII, цифры, подчёркивания и дефисы. Пробельные символы допустимы только в начале и конце ввода; при других символах возвращается явная ошибка. В примере perform() лишь возвращает идентификатор и не сохраняет данные.

import AppIntents
import Foundation

enum ReadingIDError: LocalizedError {
    case invalid

    var errorDescription: String? {
        "编号须为 1–32 位字母、数字、下划线或连字符"
    }
}

enum ReadingID {
    static func normalize(_ raw: String) throws -> String {
        let value = raw.trimmingCharacters(in: .whitespacesAndNewlines)
        let pattern = #"^[A-Za-z0-9_-]{1,32}$"#
        guard value.range(of: pattern, options: .regularExpression) != nil else {
            throw ReadingIDError.invalid
        }
        return value
    }
}

struct CheckReadingIDIntent: AppIntent {
    static var title: LocalizedStringResource = "检查书籍编号"
    static var openAppWhenRun = false

    @Parameter(title: "书籍编号")
    var bookID: String

    func perform() async throws -> some IntentResult & ReturnsValue<String> {
        let normalized = try ReadingID.normalize(bookID)
        return .result(value: normalized)
    }
}

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

Проверяйте граничные случаи, а не только корректный ввод

Как минимум проверьте, что " book_7 " превращается в "book_7", а пустая строка, пробелы внутри идентификатора и идентификаторы длиннее 32 символов отклоняются. В ветке ошибки проверяйте её тип, а не просто факт возникновения исключения: иначе тест может пройти и при неожиданной ошибке нижележащей зависимости. При тестировании perform() также убедитесь, что результат соответствует правилам нормализации. Разделяйте тесты чистой логики и тесты вызова Intent, чтобы при сбое понимать, изменились правила ввода или обёртка системного интерфейса.

Зарегистрируйте действие и проверьте его в симуляторе

Чтобы система могла показывать предлагаемое действие, добавьте AppShortcutsProvider в цель приложения. В фразе сохраните заполнитель имени приложения: он помогает системе связать действие с текущим приложением. Это не строка для shell.

import AppIntents

struct ReadingShortcuts: AppShortcutsProvider {
    static var appShortcuts: [AppShortcut] {
        AppShortcut(
            intent: CheckReadingIDIntent(),
            phrases: ["在 \(.applicationName) 检查书籍编号"],
            shortTitle: "检查书籍编号",
            systemImageName: "book"
        )
    }
}

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

Зафиксируйте тестовое устройство и каталог результатов

На облачном Mac от ZSHMac сначала выполните xcrun simctl list devices available, чтобы увидеть установленные доступные симуляторы, выберите UDID устройства и задайте SIM_UDID. Команда ниже предполагает, что в проекте есть общая схема Reader; при запуске используйте схему своего проекта. Каждый раз указывайте новый путь для пакета результатов, иначе существующий файл может привести к немедленному сбою команды тестирования.

mkdir -p artifacts
test -n "${SIM_UDID:-}" || { printf '%s\n' 'SIM_UDID is required' >&2; exit 1; }
result="$PWD/artifacts/app-intents-$(date +%Y%m%d-%H%M%S)-$$.xcresult"
xcodebuild test \
  -scheme Reader \
  -destination "platform=iOS Simulator,id=$SIM_UDID" \
  -resultBundlePath "$result"

В CI сохраняйте код завершения команды и пакет результатов: код определяет, прошла ли задача, а пакет помогает найти тест, вызвавший сбой. Если схема не является общей или нужный симулятор недоступен, сначала исправьте условия запуска, а не относите проблему к логике Intent. При ручной приёмке записывайте, появилось ли действие, корректна ли подсказка для ввода и как оно ведёт себя с допустимым и недопустимым параметром.

При сбое проверяйте каждый уровень отдельно

Симптом Сначала проверьте Не начинайте с
Не проходит тест чистой логики Диапазон символов, длину и обработку пробелов по краям Переустановки симулятора
Результат perform() не соответствует ожидаемому Передачу параметра и тип возвращаемого значения Ожидания системной индексации
Команда тестирования не находит устройство Список доступных устройств и UDID Изменения правил Swift
Действия нет в «Командах» Принадлежность файла цели приложения, установку и первый запуск Добавления фиксированной задержки в тест
Действие есть, но при выполнении возникает ошибка Подсказку для параметра, тип ошибки и версию целевой системы Подавления всех ошибок

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

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

Почему действия нет в Командах, если XCTest прошёл?

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

Где разместить проверку параметров?

Вынесите повторно используемые правила в отдельный тип Swift и вызывайте его из perform(). Тогда модульные тесты не зависят от системной индексации.

Достаточно ли CI без графического интерфейса?

Нет. CI проверяет входные данные и результат, а видимость действия и запрос параметров нужно проверить в симуляторе или на устройстве с целевой версией системы.

Нужен выделенный Mac в облаке?

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

Посмотреть конфигурации и заказать