Files
obsidian-vault/personal/projects/psychologist-app/skip-tools-poc.md
T

30 KiB
Raw Blame History

title, aliases, created, updated, tags, related
title aliases created updated tags related
Skip.tools PoC — оценка зрелости
Skip PoC
Skip Tools evaluation
skip-tools-poc
Skip.tools
2026-05-16 2026-05-31
project
app
psychology
dev
skip
android
personal/projects/psychologist-app/overview
personal/projects/psychologist-app/dev-setup

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 (20252026)

Жив ли проект?

Однозначно да. Метрики по состоянию на май 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, @AppStorage
  • AsyncImage, Button, HStack, VStack, ZStack, ScrollView, List, Form
  • LinearGradient, RadialGradient, Circle, Rectangle, RoundedRectangle, Capsule
  • NavigationStack, TabView, Sheet, Alert
  • Text, TextField, SecureField, Toggle, Slider, Picker
  • withAnimation (есть известный баг — см. ниже)

Частичная поддержка / проблемы:

  • Canvasстатус неясен (не упоминается в матрице явно, вероятно не поддерживается)
  • TimelineView — аналогично, не подтверждён
  • @GestureState — только Skip Fuse
  • AppStorage с 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

Ответить на три вопроса:

  1. Работает ли WebSocket / Whisper API через Skip на Android?
  2. Реализуем ли анимированный персонаж (Lottie или Rive workaround)?
  3. Работает ли запись голоса через 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 прямо сейчас

Лучшая альтернатива для этого конкретного кейса:

  1. iOS MVP (нативный SwiftUI) — 4-6 недель, полный Rive, AVFoundation, Realtime API
  2. Android через 6+ месяцев:
    • Re-evaluate Skip (к тому времени Canvas поддержка появится)
    • Или React Native (Rive official runtime, mature ecosystem)

Не рекомендуется:

  • Flutter — из-за iOS 26 UI regression и неопределённости команды
  • Ionic/Capacitor — для анимированного персонажа и голоса WebView недостаточен

Приложения: Ссылки и источники

Skip официальная документация

GitHub Issues (ключевые)

  • #559 — WebSocket не работал в libcurl (починен в 6.3)
  • #402 — withAnimation() глобальный state баг
  • #687 — NavigationLink state bug (Fuse)

Skip-пакеты для нашего кейса

Rive

OpenAI Realtime API


Документ создан: 16 мая 2026. Актуален для Skip v1.8.14 и Swift 6.3.


Связанные заметки