【個人開発】支払い・サブスク管理アプリを作ってみた話


## はじめに

久しぶりに時間ができたので、個人開発をしてみました。今回手がけたのは、個人的に欲しかった **支払い・サブスク管理アプリ** です。

### 開発したアプリ

本題に入る前に、まずは開発したアプリをご紹介します。このアプリは現在、App Storeで公開中です。気に入っていただけたら、レビューしていただけると幸いです。

[App Storeのリンク](https://apps.apple.com/jp/app/%E6%94%AF%E6%89%95%E3%81%84-%E3%82%B5%E3%83%96%E3%82%B9%E3%82%AF%E7%AE%A1%E7%90%86-%E3%81%BE%E3%81%AD%E3%81%8B%E3%82%93/id6605935725)

![アプリアイコン.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/e88579a4-35a6-6eb4-caf2-c2600971ce26.png)

![AppStoreスクリーンショット.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/15fb779f-f63c-6e03-6151-4b1993cfa7de.png)

### アプリを作るきっかけ

私は多くのサブスクリプションサービスを利用していますが、支払い管理が次第に大変になってきました。今までは別のツールで支払い管理をしていましたが、操作が難しく、支出の確認に苦労することもあり、自分にとって使いやすいアプリを作ろうと思い、今回のアプリを開発しました。

## 要件定義

### 機能要件

#### 必須機能

アプリ開発に際して私が必要だと考えた機能を以下にまとめました。

- **支払い情報**（Apple Music、Amazon Primeなどの支出）を登録できる
- 支払い情報を **カテゴリ分け** できる
- **支払い方法**（クレジットカード、銀行振込など）と支払い情報を紐づけられる
- カテゴリや支払い方法ごとの支出を **グラフ** や **一覧** で確認できる
- 支払い日が近づいたら **通知** を受け取れる

#### あると便利だと思った機能

- 支払い情報を **並び替えて表示** できる
  例：「料金が高い順」「支払い日が近い順」など
- 支払い情報の **解約予定日** を登録できる
  解約忘れによる無駄な支出を防ぐため
- 支払い情報に **アイコン** を設定できる
  視覚的に情報を把握しやすいため
- **異なる通貨** で支払い情報を登録し、為替レートに基づいて換算できる
  海外サービスでは、ドルでの支払いが多いため
- **テンプレート** を用意する（携帯代、家賃、水道代など）
  支払い情報登録の手間を減らすため

### システム要件

#### フロントエンド要件

フロントエンドの要件として、「**一目で分かる**」と「**直感的に操作できる**」を重視しました。

「**一目で分かる**」ために:

- シンプルなデザイン
- 支払い日を **インジケーター** で表示
- 支払い情報に **アイコン** を表示

「**直感的に操作できる**」ために:

- 入力操作を最小限にし、可能な限り **選択式** に
- 必須項目と任意項目をセクションで分け、「必須」「任意」のバッジを付与
- 各画面で一つのアクションに絞り、不要な操作を制限
  例えば、「支払い情報の追加」画面では「カテゴリーの追加」をさせないなど

#### バックエンド要件

バックエンドの要件は、主に**管理面**を考慮して以下のように定義しました。

- **運用コストを抑える**
  個人開発では、運用コストを抑えることが重要です。特にインフラの運用・保守は負担が大きく、そこに時間を取られてアプリ開発に集中できなくなる懸念があります。そのため、バックエンドの機能はすべて **アプリケーション開発プラットフォーム** を利用し、 <font color="red"> **インフラを管理しない** </font> ようにしています。

- **コストを抑える**
  予想外のコストを避けるため、可能な限り定額制または無料で利用できるサービスを選択する。

## 基本設計／機能設計

### 開発環境

iPhoneアプリの開発には **Xcode** を使用し、ドキュメントは **Notion** で一元管理しました。画面レイアウトの作成にはAIデザインツールの **Uizard** を活用し、工数を大幅に削減できました（詳細は後述します）。

#### iPhoneアプリ開発環境

| 役割 | ツール／サービス |
| --- | --- |
| 統合開発環境 | Xcode |

#### 開発支援（Dev）

| 役割 | ツール／サービス |
| --- | --- |
| タスク管理 | Notion |
| バージョン管理 | GitHub |
| ドキュメント管理 | Notion |
| 製図 | draw.io |
| 画面レイアウト | Uizard |

### 機能一覧

#### 支払い情報の登録

アプリのメイン機能である支払い情報を登録する機能です。
支払い情報の入力では **必須項目** と **任意項目** でセクションを分けています。
必須項目には、「支払い情報名」「支払い周期」「初回支払日」「金額」といった基本項目を設定しました。

<img width="200" alt="01_支払い情報追加_必須項目" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/ccc2a459-63e6-f63c-04bd-134fd2fe9459.gif">

任意項目には「カテゴリー」「支払い方法」「アイコン」「通知日」「解約予定日」など、支払い情報をより詳細に設定できる項目を設定しました。
支払い情報を確認する際は、アイコンによる視覚的な情報を付与することが重要だと感じたため、システムアイコンとは別に、カスタムアイコンを設定できるようにしています。

<img width="200" alt="01_支払い情報追加_任意項目" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/a890f0b8-a61d-0664-9ab9-489758088ddb.gif">

#### カテゴリーの登録

支払い情報をカテゴライズするためのカテゴリーを登録する機能です。

<img width="200" alt="02_カテゴリー" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/93774158-9008-7b8b-a3c3-b95a2ab7ea32.gif">

#### 支払い方法の登録

支払い情報を支払い方法で紐づけるための、支払い方法を登録する機能です。

<img width="200" alt="03_支払い方法" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/a7c52443-a102-312b-527e-d188edc0e9f8.gif">

#### 分析

支払い情報をカテゴリーや支払い方法ごとに分析できます。支出の割合をグラフで確認できるため、どのカテゴリーにどれだけ支出しているかが一目でわかります。また、カテゴリーや支払い方法ごとの **支出一覧** も表示され、詳細な内訳を確認できます。さらに、**月間および年間の支出** を算出できるため、全体の支出を把握しやすくなっています。

<img width="200" alt="03_支払い方法" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/3c094f13-661a-5902-117a-c14bdde12068.gif">

## システム方式

### システム構成図

システム全体の構成図は以下の通りです。

![iPhoneアプリ構成図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/d4f14097-588d-e1d7-7700-94d09574367b.png)

### フロントエンド

iPhoneアプリの開発には **SwiftUI** を使用しています。SwiftUIは、UIの構築が簡単で、コードの記述量が少なくて済むため、開発効率が向上します。また、SwiftUIは **Combine** というフレームワークと組み合わせることで、状態管理が容易になります。

| カテゴリ | ツール／ライブラリ／フレームワーク |
| --- | --- |
| 言語 | Swift |
| フレームワーク | SwiftUI |
| 状態管理 | Combine |
| ルーティング | NavigationStack |
| コードフォーマッター | SwiftFormat |
| 静的解析ツール | SwiftLint |
| リソース管理 | SwiftGen |
| 単体テスト | XCTest |

### バックエンド

バックエンドには **Firebase** を採用しました。OAuth認証などの実装が容易で、個人開発に最適です。

データベースには **SwiftData** を使用しました。SwiftDataはCoreDataの後継フレームワークで、SQLを書くことなくデータを簡単に永続化できます。SwiftDataの仕様として内部ではSQLiteが使われています。

さらに、SwiftDataはCloudKitを利用してiCloudにデータを保存することもできるため、ローカルだけでなくクラウドでのデータ管理にも適しています。

#### データベース

| カテゴリ | ツール／ライブラリ／フレームワーク |
| --- | --- |
| データベース | SwiftData |
| クラウドストレージ | CloudKit |

#### アプリケーション開発プラットフォーム

| カテゴリ | サービス | 備考 |
| --- | --- | --- |
| 認証 | Firebase Authentication | OAuth または 匿名 でのログインに使用 |
| 設定管理 | Firebase Remote Config | 強制アップデート、メンテナンスモード に使用 |

#### Webサービス

お問い合わせフォームや利用規約、プライバシーポリシーのページは、レンタルサーバーを借りたり、S3などの静的ファイルホスティングを使用することも考えましたが、管理の手間を避けるために **Notion** を使用しました。Notionは無料でWeb公開ができるので、コストを抑えることができます。

| カテゴリ | サービス | 備考 |
| --- | --- | --- |
| お問い合わせフォーム | Noway Form | Notionと連携したフォーム作成サービス |
| 利用規約 | Notion | NotionのWeb公開 |
| プライバシーポリシー | Notion | NotionのWeb公開 |

#### アプリ配信サービス

| カテゴリ | サービス |
| --- | --- |
| 商用 | App Store |
| 検証| TestFlight |

#### サブスクリプション

| カテゴリ | サービス |
| --- | --- |
| サブスクリプション | StoreKit |

## 画面設計

### 画面レイアウト&画面遷移図

**Figma AI** を試してみたかったのですが、待機リストに入ったまま利用できなかったため、代わりに **Uizard** というAIデザインツールで画面設計を行いました。

[<img width=300 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/e94ee2cc-10df-b57f-fae4-3af6af813733.jpeg">](https://uizard.io/)

Uizardでは、プロンプトを入力するだけで自動的に画面レイアウトを生成してくれます。私は画面レイアウトを考えるのが得意ではないのですが、対話形式で簡単にレイアウトを調整しながら理想の画面を作成することができました。

例えば、カテゴリー管理画面のリストに「アイコンを追加」し、「左スワイプで削除ボタンを表示」する場合も、以下のようにプロンプトを入力するだけで簡単にレイアウトを作成でき、工数を大幅に削減できました。

<img width="400" alt="02_カテゴリー" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/84aa3f7d-6c54-ff7e-ae85-dafcc414c10b.gif">

Uizardは無料で利用できますが、本格的に使うなら有料プランが必要です。開発以外の作業を効率化したいエンジニアには、有料プランに加入する価値は十分にあると感じました。

## データベース設計

### ER図

Draw.ioは製図に非常に適しているツールですが、Notionで管理する場合はアップロードの手間がかかってしまうため、ER図などのシンプルな図はMermaidで作成しました。

- **PaymentEntity**
  支払い情報（Apple Music、Amazon Primeなどの支出）を管理するテーブル
- **CategoryEntity**
  カテゴリー（サブスク、生活費などの支出分類）を管理するテーブル
- **PaymentMethodEntity**
  支払い方法（クレジットカード、銀行振込などの決済方法）を管理するテーブル

![ER図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/0fe5c3ac-ff08-0385-486a-b3fc7d54ddd3.png)

## 製造

### AIツールの活用

ChatGPT 4o、Claude 3.5 Sonnet、Copilot for Xcode、UizardなどのAIツールを活用して開発を進めました。単純なコンポーネントから複雑なロジックまで、多くのベースコードはAIツールによって生成されており、おそらく全体の **約60%以上** のコードを生成してもらったと思います。

ただし、AIツールでは設計の一貫性を保つのが難しく、生成されたコードがプロジェクト全体で統一されているかは保証されないことが多いです。そのため、AIと共存しつつ、必要に応じて手直しを加えながら開発を進めていました。

### 設計手法

基本的にドメイン駆動設計を意識して設計を行いました。ドメイン駆動設計について詳しく説明すると長文になってしまうため、別の機会があれば紹介したいと思います。

### アーキテクチャ

アーキテクチャには **MVVM** を採用しています。
UIKitだと RxSwift を使うことが多いですが、SwiftUIでは @Stateや@Binding などの状態管理や **Combine** を使用してMVVMを実現しています。

開発途中で Combineの後継として **Observation** があることを知りましたので、どこかのタイミングでObservationに移行する予定です。
[Observation | Apple Developer Documentation](https://developer.apple.com/documentation/observation)

### UI/UXの設計

#### 見やすさ重視

シンプルなデザインを心がけるのはもちろんのこと、**視覚的に情報を伝える** ことが重要だと考えました。そのため、支払い情報に **アイコン** を設定し、一覧画面では支払い日が近づいている情報を **インジケーター** で表示することで、一目で支払いの状況が分かるようにしました。

<img width="200" alt="ホーム画面" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/c2009256-35b3-59cf-102c-b206a2b3d4eb.png">

#### 選択式の入力

支払い方法はある程度固定されているため、選択式で入力できるようにしました。これにより、入力の手間を減らし、ストレスない操作ができるようになりました。

<img width="200" alt="決済方法選択" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/3c1e27eb-6828-d6c3-2565-92d06e14109a.png">

<img width="200" alt="決済種類選択" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/a6e8c836-431e-cd73-bb29-064ccb2f40e5.png">

#### Swift Package Manager

SPMを活用することで、プロジェクト内のコード量を減らし、アプリのドメインに関係のないコードを削減して保守時のノイズを軽減しました。また、他のアプリへの再利用も容易になるため、今後も積極的にSPMを活用していく予定です。

今回は以下のシンプルなコンポーネントからSPMにしました。

https://github.com/sakes9/PopoverButton

https://github.com/sakes9/ColorPickerButton

https://github.com/sakes9/TipBox

ボタンやタブバー、アイコン選択のビューなどのコンポーネントなど、まだアプリ内で管理不要なコンポーネントが多いため、今後さらにSPMへの移行を進めていきたいと考えています。

### SwiftData

#### 設計

Model（アプリ内で扱うデータ）とEntity（データベースに保存するデータ）を分けて設計しています。これにより、SwiftDataなど特定の永続化フレームワークに依存しない柔軟な設計を実現しており、将来的にデータベースを変更する必要が生じた場合やテスト実行時など、DI（依存性の注入）を使って簡単に切り替えることができます。

#### 処理の実行スレッド

SwiftDataでは、デフォルトでデータベース操作が同期処理として行われますが、これをメインスレッドで実行すると、画面の操作がロックされ、ユーザー体験が低下する可能性があります。バックグラウンドスレッドで処理を実行することも可能ですが、そのためには明示的にバックグラウンドでの実行を指示するコードが必要で、結果としてコードの可読性が低下することがあります。

そこで、SwiftDataの操作にはConcurrencyを使用し、非同期でデータベース処理を行うようにしました。これにより、async/awaitを用いて非同期処理を記述でき、コードがシンプルになり、可読性も向上しました。

## 開発工数

Gitのコミットログから算出した開発工数は以下の通りです。

![Gitコミットログ_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/919a955b-3361-ab4a-fc7d-71f709b47da2.png)
![Gitコミットログ_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/d3436587-c2db-6592-3abd-a0478d9bd9d3.png)

| 日付 | 内容 |
| --- | --- |
| 2024年7月17日 | 初回コミット |
| 2024年8月9日 | App Storeに公開申請 |
| 2024年8月14日 | App Storeに公開 |

この結果、開発期間は約3週間で、全体として1ヶ月弱の期間がかかりましたが、毎日6〜8時間ほど作業を行っていたため、合計で **約150時間〜200時間** の工数がかかったと考えられます。
もともとSwiftUIやFirebaseを使用した経験があったため、そのあたりは開発期間の短縮に繋がったと考えています。

## 今後

初回リリースでは未実装となりましたが、エンジニアの方々は海外のサービスを利用する機会が多いと思います。そのため、ドルやユーロなど異なる通貨で金額を登録し、為替レートに基づいて日本円に換算する機能が欲しいと感じました。しかし、為替レートの取得にはAPIを使用する必要があり、その利用にはコストがかかるため、初回リリースでは実装を見送りました。

現時点で、アプリは必要な機能を満たしていると考えていますが、今後はパフォーマンスの改善や、ユーザーからのフィードバックに基づく機能追加や改善が主な課題になると思います。

## おわりに

今回は、**支払い・サブスクリプション管理アプリ** の開発についてのお話でした。短期間での開発でしたが、**SwiftDataによるデータの永続化** や **Swift Package Manager** を活用したパッケージ開発に関する知識を深めることができ、非常に有意義な時間を過ごせました。

開発ではAIを活用し、画面設計やコードの記述を効率化しました。正直なところ、AIツールがなければ、開発工数は <font color="red">**2倍以上（400時間程度）**</font> かかっていたと思います。AIツールの活用により、開発工数を大幅に削減し、効率的に作業を進めることができました。使い方には多少のコツが必要だと感じましたが、プロンプトエンジニアリングが注目される今、AIツールをうまく使いこなせるかどうかが、エンジニアとしての価値を左右する重要な要素になると感じました。

最後までお読みいただき、ありがとうございました。アプリは [App Store](https://apps.apple.com/jp/app/%E6%94%AF%E6%89%95%E3%81%84-%E3%82%B5%E3%83%96%E3%82%B9%E3%82%AF%E7%AE%A1%E7%90%86-%E3%81%BE%E3%81%AD%E3%81%8B%E3%82%93/id6605935725) で公開中ですので、興味がありましたらぜひダウンロードしていただけると幸いです。