無料でプロトタイプのiOS動画再生アプリを作る話(2021)

## 概要

iOSアプリ（iPhone等）の開発についていろいろ調べたところ、仲間内の配布であれ、プロトタイプであれ、Appleさん(AppStore)経由でないとまず無理な状況となり、導入障壁となっていると感じております。

開発テストができる`TestFlight`を使っても、テスト向け配布にはAppleさんの審査が必要だったり、iOSアプリ開発に慣れていなければ開発者プログラムの加入や審査手続き等で時間とお金を持っていかれます。また、サイドローディング等も耳にしますが、真っ当なソフトハウスであれば、ちゃんとお金を払って開発する事になります。

そんな状況の中、iOSで手軽にプロトタイプを配布する事は難しいのか、HelloWorldから一歩踏み込んで模索してみました。

:::note warn
私自身アプリ開発の専門家ではないので、間違いはご指摘いただければ幸いです。
また、私がWEB系開発者である事もあり、基本的にJavaScript(NodeJS)ベースの説明は省略されていますので前提知識がないと難しいかもしれません。
:::

## 開発・配布手法の比較

ざっと探してみた結果、目についた手法を一覧にまとめました。

|手法|制約・問題点|
|-|-|
|**Objective-C, Swift, React Nativeによるパッケージの配布**|・**<font color="red">開発者プログラム加入＆Apple審査必須</font>**<br>・開発者はMac必須<br>・テスタはテスト申請用のAppleID必須|
|**Expoによる配布**|・テスタはExpoアプリ必須<br>・テスタはExpoアカウント必須<br>・**<font color="red">プログラムを公開したアカウントと、招待したアカウントでしか動作できない</font>**（※iOSの場合）<br>・機能制約多し|
|**Snackによる配布**|・テスタはExpoアプリ必須<br>・ソースがオープン（URLを知っている人はソース参照・コピーできてしまう）<br>・機能制約多し<br>・ソースのメンテナンス性悪し（fiddle系なので…）|

となっています。**<font color="red">赤の太文字</font>**が障壁となるトピックです。
ざっと見る限り、オープンソースであれば**「Snackによる配布」**が良さげな感じがします。

いきなり単語が五月雨に出てきたので、軽く説明をします。

## Objective-C, Swift とは

iOS開発では言わずと知れた基本的な開発言語です。それ単体では他のプラットフォーム(Android)との互換性はありません。

## ReactNative とは

上記Objective-C,Swiftを使わず、JavaScript,TypeScript(NodeJS) でiOS／Androidのネイティブアプリが作れるWeb系エンジニアにとっては導入効率の良いフレームワーク。プラットフォームの壁を越えつつ、OS依存する作りこみもできる良いとこ取りの開発手法です。

## Expo とは

ReactNative をより使いやすくしたSDK
ReactNativeよりもできることは少ないですが、Macの開発環境（Xcode）を必要としません。
ReactNativeの機能も一部使えますが、OS依存する機能はExpo側が対応していない限り使えません。
Expoでも他の手法と同様でパッケージとしてビルド可能です。

## Snack とは

Expo をWEB上でサンドボックス化できるサービス。JSFiddle等をイメージして頂くとわかりやすいです。

例を見ていただいたほうが手っ取り早いかと思い、以下のアプリを公開しました。
JavaScriptやReactを扱える人はこれを見て全てを把握されると思います。

:::note info
otoge-player
一見・素朴な普通の動画プレイヤーですが、動画プレイヤー自身のタップ操作を無効にすることで、リズムゲーム等のタップ・スワイプ操作練習ができるというある筋では重宝するアプリです。
:::

https://snack.expo.dev/@rawseq/a1d5f4

あくまでお試しだった為、作りが雑で見づらい点もありますが
触って動かせるので、位置や動作を変えてみたり、いろいろ試すことで分かってきます。
なんといっても実機で試せるのはいいですね。

上記Snackは以下のExpoベースの開発を丸ごとSnackに移植したものです。

https://github.com/RAWSEQ/otoge-player

中身をざっくり説明すると、以下の事をやってます。

- iOSの写真にアクセス（許可を取ったり）
- iOSのカメラロールからビデオを呼び出し再生。ビデオプレイヤー自体に付属する制御を無効化し、外部ボタンから制御
- SVG図形表現
- CSSによる動的デザイン制御

SVG図形とCSSが使えたら大体のモックアップは作れると思いますが
その他使えるか使えないかの判断は本家APIリファレンスをご参照ください

https://docs.expo.dev/versions/latest/

## Snack (Expo) の導入ポイント

### ファイル構成と機能

上記Snackを参考にしつつ、説明します。
主に大切なのは以下の2ファイルのみです。

- **package.json** Node.jsのnpmと同様、拡張モジュールを管理
- **App.js** プログラム本体


### **package.json**

```json:package.json
{
  "dependencies": {
    "expo-av": "~10.1.3",
    "expo-constants": "~12.1.3",
    "expo-status-bar": "~1.1.0",
    "react-native-svg": "12.1.1",
    "expo-image-picker": "~11.0.3",
    "@expo/vector-icons": "^12.0.0",
    "react-native-paper": "4.9.2",
    "expo-screen-orientation": "~4.0.3"
  }
}
```

見ての通りnpmベースの拡張モジュールが記載されています。
拡張モジュールはpackage.jsonに**記載するだけ**で導入できます。
`expo-***` というのがExpo向けの拡張機能
`react-native-***` というのがExpoで使えるReactNative向け拡張機能が使えます

どんなモジュールがあるのかは上記URLから本家をご参照ください。
導入できるかどうかや、互換性のあるバージョンかどうかは記載した時に示してくれます。

### **App.js**

プログラム本体です。名前の通りReactベースですが、JavaScriptが読めて書ければなんとかなるレベルです。

React要素としては、ステート管理をスポットで行っているところぐらいですね。

```js
  // 定義　（splashHideというステート変数とsetSplashHideというセッターを定義。この場合、デフォルトはfalse
  const [splashHide, setSplashHide] = React.useState(false);

  // 参照　（splashHideの定義は変数の様に参照可能）
  <View style={[styles.splash,(splashHide)?styles.buttonSvgHide:{}]}>

  // セッター （セットすると、上記「参照」の値が動的に作用します。setSplashHide関数は非同期であることに注意。セットした直後に変数を参照しても反映されていない）
  setSplashHide(true);

```

## Expoとの互換性

今回 Expoで開発し、それをSnackに移行したのですが、Webのエディタ上に上記2ファイルを普通にコピーアンドペーストで簡単にSnack上に再現できます。
確認した範疇であれば、相違なく動く様です。
Expo/ReactNative以外の拡張モジュールを使えるかは詳細に確認していません。

## Snack(Expo)開発のポイント

今回、動画プレイヤーアプリを作ったのですが、開発して分かった事を共有させていただきます。

### ・Expoのドキュメントに記載されている事以外は出来ないと考えたほうが良い

Expoで提供されている事以外は実現できないケースが多かったので、ドキュメントの記載以外の事はできないと考えたほうがいいです。逆に開発手法選定の基準となると思います。

https://docs.expo.dev/versions/latest/

### ・SVG図形はファイルとして扱えない

画像はファイルとして扱えますが、SVGはファイルとして扱えません。`react-native-svg` という拡張モジュールを導入して、以下の様なSVGファイルをReactNativeのコードに変換するWEBツールを使って変換したものを導入しています。

https://react-svgr.com/playground/?native=true&typescript=true

### ・iPhoneのホームバーを隠すことが出来ない

iPhoneのホームバー（Home Indicator）を隠すにはApple特有の定義の導入(CocoaPods：pod_install)が必要です。
ReactNativeの場合はnpmの拡張モジュールとしてインストールできるのですが、Expoの場合、Expo自身のSDKが影響してインストールできません。その代わりにExpoSDKで何とかしてもらう形になりますが、ざっと見た感じホームバーの操作については残念ながら対応できていない感じでした。
（今回の動画プレイヤーはそれが重要要素でしたので、縦横画面をひん曲げて無理やり解決しています。）
Expoで対応していない機能で「課金機能」等はよく聞くのですが、実際に導入をしていると、ちょっとした機能が対応しておらず、実はそれが意外と重要な要素である事に気づきます。

また、今しれっと重要な事が流されたのですが、Expoは基本的にはReactNativeベースなのですが、Expo自身のSDKが影響してReactNativeの拡張が利用できない場合があります。だからと言ってExpoからReactNativeに移行する場合にはExpoSDKを元に組み上げられたパッケージ構成をReactNative向けにに作り替えないといけません。その為、Expoの実装はあくまでプロトタイプと割り切る必要があります。

### ・iPad(iPadOS)の縦横ロックが出来ない

Expoにはアプリで画面の縦横を指定して固定する制御ができます。もちろんiPhoneだと出来るのですが、何故か手持ちのiPadでは、それが効きませんでした。
何故かを確認すると、iPadOSにバージョンアップしてから、画面の制御系統が変わって対応できなくなったそうです。こうした、OSのバージョンアップによって以前までは使えていた機能が使えなくなったりする場合があり、その場合の対応はExpoSDK任せとなってしまうところが欠点となります。

## まとめ

SnackはJSfiddleの様な、スニペット的に「やって見せる」様な用途で使われるものと思いますが、カメラロールへのアクセスやAVコントロール等、結構具体的な事までできるので、プロトタイプの提示として利用するのも良いかもしれません。

**参考にさせていただいた記事：**

https://qiita.com/Annoske/items/b86aee75b6975e7e285d

https://qiita.com/yassun-youtube/items/83c40dfb787058d5150d