ClaudeExpo2025/10/28 13:15

Faster, more reliable video uploads with Expo Modules

要点だけを先に読めるように短く再構成したセクションです。

元記事

Quick Digest

要約

要点だけを先に読めるように短く再構成したセクションです。

claudejamodel: claude-sonnet-4-20250514

Expo Modulesを使用した高速で信頼性の高い動画アップロード機能の実装

Key Points

  • Expo Modulesでネイティブバックグラウンドアップロードを実装
  • AWS S3マルチパートアップロードで20%高速化
  • SharedObjectによる長時間実行タスクで信頼性向上

Summary

Boomアプリが動画アップロード機能を改善するため、Expo Modulesを使用してネイティブバックグラウンドアップロード機能を実装した事例です。従来のJavaScriptベースのアップローダーから、AWS S3マルチパートアップロードとSharedObjectを活用したネイティブソリューションに移行しました。

Key Points

  • 従来の課題: JavaScriptアップローダーはアプリのライフサイクルに依存し、バックグラウンド実行やネットワーク中断への対応が困難
  • 技術的解決策: Expo ModulesのSharedObjectを使用した長時間実行可能なアップロードタスクを実装
  • AWS S3マルチパート対応: 大容量動画ファイルを並列アップロードで高速化
  • パフォーマンス向上: 100-300MBの動画クリップで約20%のアップロード時間短縮を実現
  • 信頼性の向上: テスト環境でアップロード停止の問題が解消
  • 開発効率: TurboModulesと比較してボイラープレートが少なく、メンテナンスコストを削減

Full Translation

翻訳

原文の流れを保ったまま読める翻訳セクションです。

claudejamodel: claude-sonnet-4-20250514

Expo Modulesによる高速で信頼性の高い動画アップロード

Expo Modulesによる高速で信頼性の高い動画アップロード

Users • React Native • Development • October 28, 2025 • 3分で読める

Petr Chalupa
ゲスト著者

BoomがExpo Modulesを使用してネイティブバックグラウンドアップロードとAWS S3マルチパートサポートにより、より高速で信頼性の高いモバイル動画アップロードパイプラインを構築した方法を学びましょう。

これは、SRTVのソフトウェアエンジニアでReact Pragueの主催者であるPetr Chalupaからのゲスト投稿です。

...

Boom([App Store](App Store)、[Google Play](Google Play))は動画ファーストの競技プラットフォームです。クリエイターは11のカテゴリーにわたる月次コンテストにクリップを投稿し、ユーザーがお気に入りに投票します。その後、審査員が各カテゴリーの勝者を選出します。各勝者は多額の賞金を受け取ります。

Boomの核心は競技であり、単なるソーシャル共有ではありません。動画のアップロードはサイド機能ではなく、クリエイターがコンテストに参加する方法なのです。そのため、信頼性があり、ストレスフリーなアップロードフローが体験にとって重要です。

課題:モバイルでの大容量動画アップロード

初期実装は単純なJavaScriptアップローダーでした。MVPには機能しましたが、それ以上にはスケールしませんでした。いくつかの重要な欠陥がありました:

  • バックグラウンド実行の欠如:JavaScript実行はアプリのライフサイクルに紐づいています。アプリがバックグラウンドになると、OSによってアップロードが中断されます。
  • ネットワーク中断の処理:単一のネットワーク障害で全体のアップロードが失敗し、ユーザーは最初からやり直しを強いられます。

目標:高速で信頼性の高いモバイル動画アップロード

私たちの目標は、以下のようなアップロード体験を作ることでした:

  • 高速:ユーザーの携帯電話から私たちのサーバーに可能な限り迅速に動画を送信する。
  • 信頼性:アップロードはアプリの切り替え、ネットワークの問題、その他の中断を乗り越える必要がある。

解決策:Expo Modulesによるネイティブバックグラウンドアップロード

私たちは[Expo Modules](Expo Modules)を使用して全く新しいアップロードパイプラインを構築しました。SharedObjectにより、長時間存続するステートフルなアップロードタスクを作成しました。AWS S3マルチパートアップロードに切り替えることで、大容量動画がより高速かつ信頼性高くアップロードされるようになりました。

以下は私たちのUploadTaskのTypeScriptインターフェースです:

export type UploadTaskEvents = {
  onProgress: (params: { progress: number }) => void
}

export declare class UploadTask extends SharedObject<UploadTaskEvents> {
  constructor(clipPath: string, coverPath: string)
  readonly parts: URL[]
  clipUrls?: string[]
  completionUrl?: string
  coverUrl?: string
  preProcess(): void
  upload(): Promise<void>
}

UploadTaskクラスをJavaScript側に公開するiOSネイティブモジュール:

import ExpoModulesCore

public class BackgroundUploadModule: Module {
  public func definition() -> ModuleDefinition {
    Name("BackgroundUpload")
    
    Class(UploadTask.self) {
      Constructor { (clip: URL, cover: URL) -> UploadTask in
        return UploadTask(clip: clip, cover: cover)
      }
      
      Property("parts") { uploadTask in
        uploadTask.clip.parts
      }
      
      Property("clipUrls") { uploadTask in
        uploadTask.clip.uploadUrls
      }.set { (uploadTask: UploadTask, uploadUrls: [URL]) in
        uploadTask.clip.uploadUrls = uploadUrls
      }
      
      Property("completionUrl") { uploadTask in
        uploadTask.clip.completionUrl
      }.set { (uploadTask: UploadTask, completionUrl: URL) in
        uploadTask.clip.completionUrl = completionUrl
      }
      
      Property("coverUrl") { uploadTask in
        uploadTask.cover.uploadUrl
      }.set { (uploadTask: UploadTask, uploadUrl: URL) in
        uploadTask.cover.uploadUrl = uploadUrl
      }
      
      Function("preProcess") { uploadTask in
        try uploadTask.preProcessAssets()
      }
      
      AsyncFunction("upload") { uploadTask in
        try await uploadTask.upload()
      }
    }
  }
}

React Native統合例

以下はReact Nativeでの統合の簡略版です。ネイティブモジュールがチャンク化、並行処理、リトライ、バックグラウンド実行を処理します。

// パーツ、進捗、URLを所有するUploadTaskをインスタンス化
const uploadTask = new BackgroundUpload.UploadTask(videoUri, thumbnailUri)

// 進捗をリッスン
const progressListener = uploadTask.addListener('onProgress', ({ progress }) => {
  updateProgress({ uploadProgress: progress })
})

// ファイルを独立したパーツに分割
uploadTask.preProcess()

// 署名付きURLをバックエンドにリクエスト
const { data: uploadInfo } = await createUploadUrls({
  variables: {
    input: {
      partsCount: uploadTask.parts.length
    }
  },
})

// タスクを設定
uploadTask.clipUrls = uploadInfo.clip.uploadUrls.map(({ uploadUrl }) => uploadUrl)
uploadTask.completionUrl = uploadInfo.clip.completionUrl
uploadTask.coverUrl = uploadInfo.cover.uploadUrl

// リトライ付きで並列にパーツのアップロードを開始
try {
  await uploadTask.upload()
} catch (error) {
  logError(error)
} finally {
  progressListener.remove()
}

結果:より高速で小さく、信頼性の高いアップロード

  • 速度:100-300MBのクリップで、エンドツーエンドのアップロード時間の中央値が約20%改善。
  • 信頼性:最新のテスト実行で「スタック」したアップロードは観測されず。
  • 測定:エンドツーエンド = 「同意して投稿」をタップ → バックエンドが完了を確認。

結論:Expo Modulesがネイティブアップロードを追加する最良の方法である理由

大容量メディアアップロードなどの高性能で堅牢な機能において、JavaScriptとネイティブコードを組み合わせたハイブリッドアプローチがしばしば最良の解決策です。

Expo Modulesは、この種の機能を構築するのに最適です。TurboModulesと比較して、保守が容易で、ボイラープレートが少なく、Expoプロジェクトとスムーズに統合できます。これにより、重要な部分でネイティブパフォーマンスを提供しながら、長期的な保守コストを削減できます。

この投資により、よりスムーズなユーザー体験と、より堅牢で信頼性の高いアプリを実現しました。

Expo Modulesを使った、より高速で信頼性の高い動画アップロード | Expo | DocsDigest