Closed30

クロスプラットフォーム使って動画プレイヤーをつくろう

蔀

やること

  • React Native / Flutterで簡単な動画プレイヤーつくって比較してみる
  • React NativeはExpoを使う

https://github.com/0si43/video-player-sample

仕様

  • ユーザーは端末の動画をアプリ上にアップロードできる
  • アップロードした画像は一覧表示されて、タップしたら再生
  • ローカルストレージに保存(サンプルなので)
  • 動画プレイヤー画面にはシークバーがあって、巻き戻しボタン・再生ボタン・早送りボタンがある
蔀

React Native Expo

npx create-expo-app react-native-app
npm run android
npm run ios
npm run web
蔀

Expo Application Servicesはクラウド型のfastlaneみたいなサービス
CI/CD向けにビルドしたりストア提出したりが可能

蔀

出力したiOSプロジェクトが見たかったが、フォルダがなかった。
Expoのデフォルトだと、Expo Managed Workflowになっているらしく、iOS/Androidの出力は吐き出されない設定。
このコマンドで出力できた。

npx expo prebuild
// revert
npx expo prebuild --clear
蔀

clearコマンドは効かなかった……

# ネイティブフォルダを削除
rm -rf ios android

# キャッシュもクリア
rm -rf node_modules
npm install
蔀

React NativeまだCocoaPodsなんだ

蔀
  • AppDelegateでRCTReactNativeFactoryを呼び出して、あとはライブラリ任せ
    • 中身はこのObjective-C++コード

https://github.com/facebook/react-native/blob/main/packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.mm

@UIApplicationMain
public class AppDelegate: ExpoAppDelegate {
  var window: UIWindow?

  var reactNativeDelegate: ExpoReactNativeFactoryDelegate?
  var reactNativeFactory: RCTReactNativeFactory?

  public override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
  ) -> Bool {
    let delegate = ReactNativeDelegate()
    let factory = ExpoReactNativeFactory(delegate: delegate)
    delegate.dependencyProvider = RCTAppDependencyProvider()

    reactNativeDelegate = delegate
    reactNativeFactory = factory
    bindReactNativeFactory(factory)

#if os(iOS) || os(tvOS)
    window = UIWindow(frame: UIScreen.main.bounds)
    factory.startReactNative(
      withModuleName: "main",
      in: window,
      launchOptions: launchOptions)
#endif

    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
蔀
  • Yoga Layout Engineというのが大事らしい
    • Flexbox記法 → Yoga → iOS/Android座標 → 画面描画
  • またNext.jsとReact Nativeを併用するフレームワークもあるっぽい
蔀

こんな対応関係らしい。わかりやすい

React Native UI Component Android View iOS View Web Analog Description
<View> <ViewGroup> <UIView> A non-scrolling <div> A container that supports layout with flexbox, style, some touch handling, and accessibility controls
<Text> <TextView> <UITextView> <p> Displays, styles, and nests strings of text and even handles touch events
<Image> <ImageView> <UIImageView> <img> Displays different types of images
<ScrollView> <ScrollView> <UIScrollView> <div> A generic scrolling container that can contain multiple components and views
<TextInput> <EditText> <UITextField> <input type="text"> Allows the user to enter text

https://reactnative.dev/docs/intro-react-native-components

蔀

App StoreへのリリースもExpoで便利になっているらしい
CLIツール入れて、App Store向けバイナリつくってな雰囲気
Expo Application Servicesというサービスで、無料ユーザーは30回/月までビルド可能
CDをまるっとやってくれる

https://docs.expo.dev/build/setup/

蔀

Expo Goを使うとアプリのビルドなしで試せる
(WebViewかと思ったら一応アプリとして動作している模様)

蔀

Webだと動画データの保存に失敗するなと思ってたら、ローカルストレージにそんなに大きなデータ入れられないかららしい
(Google Chromeだと10MB上限)
逆にモバイル環境ってその縛りないんだな

蔀

MapKitを使いたいときはreact-native-mapsを使う
この手のライブラリが各SDK向けに存在する模様

蔀

状態管理とテストライブラリがあんまりわかってない

  • ZustandかJotaiがいいらしいけど、標準のReact hooksではダメなんだろうか
    • グローバルな状態管理、useContextでもいけるので、それだと何がまずいのかわからない
  • テストはjestというのが組み込まれているらしいけど、どう使うんだろ
  • ↑組み込まれてはいなかった……jest-expoというのがあるらしい
蔀

よく見たら動画保存のコードめちゃくちゃ汚いな

蔀

公式ドキュメント見ながら動くテストコードをとりあえず書けた
あまり意味のないテストだけど、npm run test で動く
CIに組み込むとかはまあやればできるでしょう
testing-library/react-nativeのインストールがReactのバージョンが合わなくてできなかったので、
npm install --save-dev @testing-library/react-native --legacy-peer-deps で無理やり入れた

https://github.com/0si43/video-player-sample/pull/2

蔀

React Nativeやったこと

  • React Native Expoの環境構築
  • 動画プレイヤーの作成
    • ダブルタップで巻き戻し・早送り
  • Webブラウザ / iOSシミュレーターでの動作確認
  • API callの手法調べた
  • 状態管理ライブラリの選択肢調べた
  • iOS/AndroidのSDK使う方法調べた
  • テストコードの作成

やってないこと

  • iOS/AndroidのSDK使って動作を調整する(あんまり細かいこと出来なさそう)
  • CI構築
  • デリバリー(Expo使ってもなおめんどくさそう)
  • 状態管理ライブラリ何がいいか
  • 可読性高いコードにする
蔀

確かにiOS向けビルド遅いかもしれない

蔀

ターミナルでやるなら

flutter create video_player_app

以前試したときはVSCodeのコマンドパレットからやっていて、こっちの方が楽かも

  • Flutterのextensionインストール
  • Flutter: New Project
  • SDKを落とす
  • パスを通す(一応コマンド書いとく)
    • vim ~/.zshrc
    • パスを追加
      • export PATH=$HOME/development/flutter/bin:$PATH
    • source ~/.zshrc
    • which flutter
    • flutter --version

https://docs.flutter.dev/install/with-vs-code

蔀

VSCodeでのビルド、iOSがデフォになってたけど、VSCode右下で環境を変えられた
.vscode/launch.json を書いてもいいらしい

蔀

info.plistの許可書かないでも初回起動時は写真にアクセスできる気がする
React Nativeもそうだった気がするけど、なんでなんだろう

蔀

モバイル環境は再生できるようになったけども、Web環境のローカルストレージ化がなかなか大変なので、Webは捨てる

蔀

昨日iOSシミュレーターでビルドできなくなってClaudeに聞いても解決できなくて苦しんだんだけど、revertしたらなんとかなった
pubspecに追加したdependencyで、何かビルドのときに引っかかるものがあったのだと思われる

蔀

Flutterもできたので閉じ

このスクラップは1ヶ月前にクローズされました