30 KiB
title, aliases, created, updated, tags, related
| title | aliases | created | updated | tags | related | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Skip.tools PoC — оценка зрелости |
|
2026-05-16 | 2026-05-31 |
|
|
Skip.tools: Оценка зрелости для AI-приложения с анимированным персонажем и голосом
Дата исследования: 16 мая 2026
Версия Skip: 1.8.14 (актуальная)
Контекст: AI-приложение рефлексии на SwiftUI с анимированным blob/orb персонажем (Rive) и голосовым вводом/выводом (OpenAI Realtime API или Whisper+TTS)
Вердикт TL;DR
⚠️ УСЛОВНО ГОТОВ — с существенными оговорками
Skip в 2026 году — это уже не эксперимент. Фреймворк активен, свободен, компилирует настоящий Jetpack Compose. Но для нашего конкретного кейса есть два критических барьера: Rive не поддерживается официально и требует платформо-специфичного workaround, а WebSocket был сломан до Skip 1.8/Swift 6.3 (сейчас починен). Рекомендация: iOS-first MVP + Skip на неделю PoC, принять решение по результатам.
1. Текущий статус Skip.tools (2025–2026)
Жив ли проект?
Однозначно да. Метрики по состоянию на май 2026:
| Метрика | Значение |
|---|---|
| GitHub Stars | ~3,000 ⭐ |
| Forks | 101 |
| Последний релиз | v1.8.14 (8 мая 2026) |
| Лицензия | MPL-2.0 (полностью open source с v1.7) |
| Язык | Swift 100% |
| Режимы работы | Skip Fuse (нативный) / Skip Lite (транспайл) |
Важный поворот: в феврале 2025 Skip стал соучредителем Swift on Android Working Group, а Swift 6.3 официально шипнул Swift SDK для Android. Skip 1.8 использует этот официальный SDK — больше нет зависимости от кастомного форка.
Два режима: что выбрать?
| Skip Fuse (нативный) | Skip Lite (транспайл) | |
|---|---|---|
| Механизм | Swift компилируется нативно для Android | Swift → Kotlin (исходный код) |
| Плюсы | Полный Swift, все сторонние пакеты, детерминированная память | Прямой доступ к Kotlin/Java API, читаемый Kotlin-вывод, меньший размер |
| Минусы | +60 МБ к APK, медленная сборка | Не весь Swift транслируется, меньше пакетов |
| Рекомендация для нас | ✅ Лучший выбор — полный Swift, rich animations | — |
Что поддерживается из SwiftUI?
Уровень покрытия (из официальной матрицы):
Полная поддержка (✅):
@State,@Binding,@Environment,@EnvironmentObject,@ObservedObject,@StateObject,@AppStorageAsyncImage,Button,HStack,VStack,ZStack,ScrollView,List,FormLinearGradient,RadialGradient,Circle,Rectangle,RoundedRectangle,CapsuleNavigationStack,TabView,Sheet,AlertText,TextField,SecureField,Toggle,Slider,PickerwithAnimation(есть известный баг — см. ниже)
Частичная поддержка / проблемы:
Canvas— статус неясен (не упоминается в матрице явно, вероятно не поддерживается)TimelineView— аналогично, не подтверждён@GestureState— только Skip FuseAppStorageс optional values — не работает
Известные баги (актуальные issues):
| Issue | Проблема | Дата |
|---|---|---|
| #402 | withAnimation() утекает в глобальное состояние на Android |
Апр 2026 |
| #687 | State не обновляется под NavigationLink (Fuse) | Май 2026 |
| #376 | NavigationLink throttling (задержка навигации) | Мар 2026 |
| #339 | .frame(maxWidth:) игнорирует границы родителя |
Фев 2026 |
| #331 | Text с % и Markdown рендерится неправильно |
Фев 2026 |
Критически важно для нас: баг с
withAnimation()(#402) напрямую влияет на анимированный персонаж.
Приложения в production
Официально подтверждённые:
- Skip Showcase — каталог SwiftUI компонентов, опубликован в App Store и Google Play
- Skip Bookings — тревел-букинг с картами (пример)
- Skip Weather — погода с async networking
- В v2.3.6 Showcase добавили целую игру Block Blast на ~1000 строк чистого Swift
Третьесторонних production-приложений в публичном доступе не найдено — Skip пока используется преимущественно инди-разработчиками.
2. Rive + Skip: совместимость
Официальная поддержка
❌ Нет официального пакета skip-rive. Список официальных Skip-модулей:
skip-motion (Lottie), skip-av (AVFoundation), skip-firebase, skip-revenue (RevenueCat)...
Rive в нём отсутствует.
Как Rive работает на iOS
Rive iOS runtime (rive-app/rive-ios, 780 ⭐, v6.20.3, MIT License) использует:
- Objective-C++ (~32% кода) для C++ рантайма Rive
- Интеграция в SwiftUI через
RiveViewModel+RiveView
// Типичный код на iOS
import RiveRuntime
struct CharacterView: View {
@StateObject var riveVM = RiveViewModel(fileName: "blob_character")
var body: some View {
riveVM.view()
.frame(width: 300, height: 300)
}
}
Проблема для Skip: Rive iOS package содержит нативный C++ runtime и Objective-C++ bindings. Skip не может транспилировать Objective-C++ код — это за пределами его возможностей.
Workaround: Platform-specific через ComposeView
Skip предоставляет мощный механизм для именно такого случая:
struct CharacterView: View {
let characterState: CharacterState
var body: some View {
#if os(Android)
// Android: используем Rive Android SDK через Compose
ComposeView { RiveAndroidComposer(state: characterState) }
#else
// iOS: нативный RiveRuntime
RiveNativeView(state: characterState)
#endif
}
}
// iOS-only код (полный Swift, без ограничений)
#if !SKIP
import RiveRuntime
struct RiveNativeView: View {
let state: CharacterState
@StateObject var riveVM = RiveViewModel(fileName: "blob_character")
var body: some View {
riveVM.view()
.onAppear { riveVM.triggerInput("setState", value: state.rawValue) }
}
}
#endif
// Android Kotlin через #if SKIP блок
#if SKIP
import app.rive.runtime.kotlin.compose.RiveAnimation
struct RiveAndroidComposer: ContentComposer {
let state: CharacterState
@Composable func Compose(context: ComposeContext) {
RiveAnimation(
resId = R.raw.blob_character,
modifier = context.modifier,
stateMachineName = "State Machine",
onInit = { controller in
controller.setBooleanState("State Machine", "isListening", state == .listening)
}
)
}
}
#endif
Оценка workaround:
- ✅ Технически реализуемо
- ✅ iOS остаётся чистым нативным RiveRuntime
- ⚠️ Нужно поддерживать две реализации
- ⚠️ Требует добавить зависимость
rive-androidв Gradle вручную черезskip.yml - ⏱️ Оценка трудозатрат: 2-3 дня дополнительно
Lottie как альтернатива Rive
skip-motion (официальный пакет) — Lottie поддерживается!
import SkipMotion
struct CharacterView: View {
var body: some View {
MotionView(lottie: lottieAnimationData)
.frame(width: 300, height: 300)
}
}
| Параметр | Статус |
|---|---|
| Версия | v0.7.2 (Jan 2026) |
| iOS backend | lottie-ios v4.6.0 |
| Android backend | lottie-android v6.7.1 |
| Размер | +~1 МБ к ipa/apk |
| Управление воспроизведением | ⚠️ Раннее состояние, ограниченные контролы |
| State machine | ❌ Нет (в отличие от Rive) |
Ключевое ограничение Lottie: у него нет State Machine как у Rive. Для персонажа с динамическими состояниями (listening, thinking, speaking) это критично — нужно либо делать несколько анимаций, либо переключать между файлами. В Rive это нативно.
SwiftUI-native анимированный персонаж (без Rive/Lottie)
Возможный подход: написать blob/orb через SwiftUI shapes + withAnimation:
struct BlobCharacterView: View {
@State private var animationPhase: Double = 0
var body: some View {
TimelineView(.animation) { timeline in
Canvas { context, size in
// Custom blob morphing via Bezier paths
let path = blobPath(for: timeline.date.timeIntervalSince1970)
context.fill(path, with: .color(.purple.opacity(0.8)))
}
}
.frame(width: 200, height: 200)
}
}
Проблема: Canvas и TimelineView — не подтверждены в матрице совместимости Skip. Вероятно, не поддерживаются на Android (не найдены в списке поддерживаемых Views). Плюс баг withAnimation() (#402).
Вывод по анимации: Rive через workaround — самый мощный вариант, но требует дополнительных усилий. Lottie безопаснее для Skip, но функционально беднее. Pure SwiftUI blob — рискованно из-за Canvas/TimelineView.
3. Голосовая интеграция + Skip
skip-av: статус AVFoundation
Официальный пакет skip-av (v0.7.1, 15 мая 2026, 12 ⭐):
| API | Android-поддержка | Что работает |
|---|---|---|
AVAudioRecorder |
🟢 High | record(), stop(), pause(), isRecording, peakPower, averagePower |
AVAudioPlayer |
🟢 High | play(), pause(), stop(), volume, rate, duration |
AVPlayer |
🟠 Low | Только базовый init/play/pause |
VideoPlayer |
🟡 Medium | init(player:) |
Бэкенд на Android: MediaRecorder + MediaPlayer (не ExoPlayer для записи).
// Запись голоса — работает на обеих платформах через skip-av
let settings: [String: Any] = [
AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
AVSampleRateKey: 16000,
AVNumberOfChannelsKey: 1,
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
]
// Platform-specific путь к файлу
let recordingURL: URL = {
#if SKIP
let context = ProcessInfo.processInfo.androidContext
let file = java.io.File(context.filesDir, "voice_input.m4a")
return URL(fileURLWithPath: file.absolutePath)
#else
return FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)
.first!.appendingPathComponent("voice_input.m4a")
#endif
}()
audioRecorder = try AVAudioRecorder(url: recordingURL, settings: settings)
audioRecorder?.record()
Нужно добавить в AndroidManifest.xml:
<uses-permission android:name="android.permission.RECORD_AUDIO" />
WebSocket для OpenAI Realtime API
История баг-фикса:
| Событие | Дата |
|---|---|
| Проблема #559 открыта: libcurl без WebSocket | Дек 2025 |
| Фикс смерджен в swift-docker | 23 Дек 2025 |
| Swift 6.3 официально релизнут с WebSocket | Мар 2026 |
| Skip 1.8 обновлён на официальный Swift 6.3 SDK | Май 2026 |
Текущий статус (Skip 1.8.14): WebSocket должен работать — Skip 1.8 использует Swift 6.3, где проблема решена. Но статус issue #559 остаётся "Open" — рекомендуется проверить в PoC.
# Проверить активный SDK
skip android sdk list
# Должен показать swift-6.3-..._android
skip android build --version
Альтернативный подход (более безопасный): Whisper + TTS вместо Realtime API
// Более Skip-friendly архитектура:
// 1. Запись голоса → AVAudioRecorder (skip-av)
// 2. Отправка файла на Whisper API → URLSession (HTTP POST, не WebSocket)
// 3. Ответ через GPT-4 → URLSession
// 4. TTS → URLSession, воспроизведение → AVAudioPlayer (skip-av)
HTTP через URLSession полностью поддерживается Skip — нет никаких проблем. Whisper+TTS архитектура безопаснее для MVP.
WebRTC (skip-livekit)
Интересно: в официальных модулях есть skip-livekit — WebRTC через LiveKit. Но для OpenAI Realtime API WebRTC нужен другой конкретный эфемерный ключ, не LiveKit. Это не подходит напрямую.
Механизм platform-specific кода
Skip предоставляет три директивы препроцессора:
// Код только для Android (в Fuse: нативный Swift; в Lite: транспилируется)
#if os(Android)
// ...
#endif
// Код транспилируется в Kotlin (можно вызывать Kotlin/Java API напрямую)
#if SKIP
import android.content.pm.PackageManager
let hasPermission = context.checkSelfPermission(
android.Manifest.permission.RECORD_AUDIO
) == PackageManager.PERMISSION_GRANTED
#endif
// Только iOS
#if !os(Android)
import AVFoundation
AVAudioSession.sharedInstance().requestRecordPermission { granted in ... }
#endif
Это мощный escape hatch — любая iOS-only функциональность доступна без ограничений в #if !SKIP блоках.
4. Альтернативы если Skip не готов
iOS-first MVP без Android
Плюсы:
- ✅ Нет компромиссов — полный RiveRuntime, полный AVFoundation, WebRTC
- ✅ iOS = 5× выше ARPU ($140 vs $69 в год на пользователя)
- ✅ Скорость разработки — нет overhead на кросс-платформу
- ✅ App Store первичен для платных подписок
Минусы:
- ❌ Упускаем ~3.7B Android пользователей
- ❌ Если позже добавлять Android — переписывать
Коммерческая оценка: Для подписочного приложения iOS-first — стандартная практика. Большинство AI-приложений (Calm, Headspace, Replika) стартовали с iOS. Android добавляли позже.
React Native 2025
| Критерий | Оценка |
|---|---|
| Голос (микрофон, TTS) | ✅ expo-av, хорошая поддержка |
| Rive анимация | ✅ rive-react-native — официальный runtime! |
| OpenAI Realtime API | ✅ нет проблем с WebSocket |
| Подписки | ✅ react-native-purchases (RevenueCat) |
| Производительность анимаций | ⚠️ JS bridge может давать jank |
| Язык для iOS-разработчика | ❌ TypeScript/JS — новый стек |
| New Architecture migration | ⚠️ Legacy arch заморожена с июня 2025 |
Итог: Функционально React Native покрывает все наши нужды. Но требует знания JS/TS экосистемы. Rive Runtime для React Native — официальный и зрелый.
Flutter 2025
| Критерий | Оценка |
|---|---|
| Голос | ✅ flutter_sound, speech_to_text |
| Rive анимация | ✅ официальный rive пакет на pub.dev |
| OpenAI Realtime API | ✅ WebSocket нативный |
| Подписки | ✅ purchases_flutter (RevenueCat) |
| Производительность | ⚠️ +40 МБ overhead, GC паузы |
| iOS 26 "Liquid Glass" UI | ❌ Flutter команда отказалась адаптировать Cupertino widgets |
| Язык | ❌ Dart — новый стек |
Критическое замечание: GitHub issues flutter #170310 — Flutter команда подтвердила, что не будет имплементировать новый Apple '26 UI дизайн в Cupertino библиотеке в ближайшее время. Для AI-приложения это может не критично, но iOS UI выглядит устаревшим.
Kotlin Multiplatform + Compose Multiplatform
| Критерий | Оценка |
|---|---|
| UI фреймворк | Compose Multiplatform (нативный Android, mimicked iOS) |
| Rive | ✅ Rive-CMP (community, v0.4.0, 105 ⭐) |
| Голос | ✅ kotlin-native доступ к platform APIs |
| Для Swift-разработчика | ❌ Нужен Kotlin — совсем новый стек |
| iOS UI | ⚠️ Не SwiftUI — кастомный рендеринг Skia |
Для Swift-разработчика KMP — самый далёкий вариант. Хотя технически сильный.
Capacitor/Ionic
Не рекомендуется для нашего кейса. WebView-рендеринг, анимации через CSS/JS будут уступать нативным, особенно для персонажа и голоса.
5. Конкретный план PoC на 1 неделю
Цель PoC
Ответить на три вопроса:
- Работает ли WebSocket / Whisper API через Skip на Android?
- Реализуем ли анимированный персонаж (Lottie или Rive workaround)?
- Работает ли запись голоса через skip-av?
День 1: Базовый проект
brew tap skiptools/skip && brew install skip
skip checkup
# Создаём проект в режиме Fuse
skip android sdk install # убедиться что 6.3+
skip init --fuse-app --appid=com.yourapp.reflection reflection-app ReflectionApp
# Проверяем SDK версию
skip android sdk list
Критерий успеха: Базовое SwiftUI приложение запускается параллельно на iOS Simulator и Android Emulator.
День 2-3: Голос + сеть
// Тест 1: Запись через AVAudioRecorder
// Тест 2: HTTP POST на Whisper API (URLSession)
// Тест 3: WebSocket соединение (если нужен Realtime API)
// Проверка WebSocket
import Foundation
class WebSocketTest: ObservableObject {
var task: URLSessionWebSocketTask?
func test() {
let url = URL(string: "wss://echo.websocket.org")!
task = URLSession.shared.webSocketTask(with: url)
task?.resume()
task?.send(.string("Hello")) { error in
print(error == nil ? "✅ WebSocket работает!" : "❌ \(error!)")
}
}
}
Критерий успеха: Запись работает на Android, HTTP к OpenAI проходит, WebSocket не выдаёт ошибку libcurl.
День 4-5: Анимированный персонаж
Вариант A (рекомендуется для PoC): Lottie через skip-motion
// Package.swift: добавить skip-motion
.package(url: "https://source.skip.dev/skip-motion.git", "0.0.0"..<"2.0.0")
import SkipMotion
struct CharacterView: View {
var isListening: Bool
var body: some View {
MotionView(lottie: isListening ? listeningAnimation : idleAnimation)
.frame(width: 250, height: 250)
}
}
Вариант B (для Rive): Реализовать RiveCharacterView с #if SKIP/iOS разделением (как показано выше). Добавить rive-android в skip.yml:
# Android/app/src/main/skip.yml
dependencies:
- app.rive:rive-android:9.12.0
Критерий успеха: Персонаж анимируется на обеих платформах, состояния переключаются.
День 6-7: Интеграция и оценка
- Собрать мини end-to-end: нажал кнопку → запись → Whisper → ответ → TTS → воспроизведение → персонаж реагирует
- Замерить производительность анимации на Android (fps)
- Оценить размер APK
Критерии успеха/провала PoC
| Критерий | Success | Fail |
|---|---|---|
| Голосовая запись | Работает на Android API 28+ | Крэшит или нет звука |
| HTTP к OpenAI | Запросы проходят | SSL/network ошибки |
| WebSocket | Соединение устанавливается | libcurl ошибка |
| Анимация (Lottie) | Плавная, состояния переключаются | Дёргается, не отображается |
| Rive workaround (опц.) | Загружается .riv файл | Compile error, не интегрируется |
| Общая стабильность | Нет крэшей при 10-мин сессии | Крэши, ANR на Android |
Fallback план если PoC провалился
Уровень 1 (мягкий фейл — одна проблема):
- WebSocket не работает → используем Whisper+TTS по HTTP
- Rive не интегрируется → используем Lottie или SwiftUI анимации
- Lottie не устраивает → делаем чистую iOS-версию с Rive, Android откладываем
Уровень 2 (полный фейл Skip): Переходим на iOS-first MVP (натив SwiftUI):
- 4-6 недель до первой версии
- Полный Rive, полный AVFoundation, OpenAI Realtime API
- Android: через 3-6 месяцев либо через React Native (Rive официально поддерживается), либо через Skip если к тому времени созреет
6. Итоговая рекомендация
Рекомендованная стратегия: "Skip с Lottie + Whisper-first"
Неделя 1-2: PoC на Skip (план выше)
Неделя 3-6: MVP на Skip если PoC ✅
↓ если PoC ❌
iOS-first MVP (нативный SwiftUI + Rive)
Рекомендованный стек для MVP на Skip:
| Компонент | Решение |
|---|---|
| UI фреймворк | SwiftUI + Skip Fuse |
| Анимация персонажа | Lottie через skip-motion (безопасно) или Rive с platform-specific workaround |
| Голосовой ввод | AVAudioRecorder через skip-av |
| STT | Whisper API по HTTP (не WebSocket) — максимально безопасно |
| TTS | OpenAI TTS API по HTTP + AVAudioPlayer |
| AI диалог | GPT-4o через HTTP/URLSession |
| Подписки | skip-revenue (RevenueCat официально поддержан) |
| Аналитика | skip-posthog (официально) |
| Auth | skip-firebase (Firebase Auth) |
Почему Whisper+TTS вместо Realtime API:
- WebSocket технически починен в Skip 1.8 (Swift 6.3), но нужно верифицировать в PoC
- Whisper+TTS архитектура — надёжнее и дешевле для рефлексии (нет потоковой передачи)
- Можно добавить Realtime API позже как premium-фичу
Почему Lottie вместо Rive:
- Официальный skip-motion пакет, нет platform-specific кода
- Для MVP не нужна полная State Machine Rive
- Rive можно добавить после MVP когда сделаете полноценный workaround
Ограничения и риски при использовании Skip
| Риск | Уровень | Митигация |
|---|---|---|
Баг withAnimation() (#402) утечка в глобальное состояние |
🔴 Высокий | Тестировать в PoC, использовать .animation() modifier вместо withAnimation |
| Canvas/TimelineView не поддерживаются | 🔴 Высокий | Использовать Lottie/Rive, не делать чистый SwiftUI blob |
| WebSocket не верифицирован в production | 🟡 Средний | Whisper+TTS как план A |
| Rive workaround требует два кодабейса | 🟡 Средний | Lottie для MVP, Rive потом |
| +60 МБ к размеру APK (Skip Fuse) | 🟢 Низкий | Приемлемо для B2C приложения |
| Мало примеров third-party production apps | 🟡 Средний | Активное сообщество, официальные примеры |
Если НЕ использовать Skip прямо сейчас
Лучшая альтернатива для этого конкретного кейса:
- iOS MVP (нативный SwiftUI) — 4-6 недель, полный Rive, AVFoundation, Realtime API
- Android через 6+ месяцев:
- Re-evaluate Skip (к тому времени Canvas поддержка появится)
- Или React Native (Rive official runtime, mature ecosystem)
Не рекомендуется:
- Flutter — из-за iOS 26 UI regression и неопределённости команды
- Ionic/Capacitor — для анимированного персонажа и голоса WebView недостаточен
Приложения: Ссылки и источники
Skip официальная документация
- skip.tools/docs — главная документация
- skip.tools/docs/modules/skip-ui/ — матрица совместимости SwiftUI
- skip.tools/docs/modules/skip-av/ — AVFoundation поддержка
- skip.tools/docs/platformcustomization/ — #if SKIP механизм
- skip.tools/compare/ — сравнение с Flutter/React Native
GitHub Issues (ключевые)
- #559 — WebSocket не работал в libcurl (починен в 6.3)
- #402 — withAnimation() глобальный state баг
- #687 — NavigationLink state bug (Fuse)
Skip-пакеты для нашего кейса
- skip-motion — Lottie анимации
- skip-av — AVFoundation / аудио
- skip-revenue — RevenueCat подписки
- skip-firebase — Firebase Auth/Firestore
Rive
- rive-app/rive-ios — iOS runtime (v6.20.3, 780 ⭐)
- rive-app/rive-android — Android runtime (Compose API beta)
- Rive-CMP — Community Compose Multiplatform wrapper
OpenAI Realtime API
- swift-realtime-openai — Swift SDK (431 ⭐, поддержка WebRTC+WebSocket)
Документ создан: 16 мая 2026. Актуален для Skip v1.8.14 и Swift 6.3.