もぐもぐトレード

Swift

GitHub

Firestore

嫌いな給食はトレード!完食ポイントで激アツデザート争奪バトル!

am2525nyan

あぎゃ

推しアイデア

これで居残り給食はこのアプリでもうおしまい!食べれないものは週に一回トレード可能! 完食してポイントゲット!ポイントを賭けて好きな給食を手に入れよう!

作った背景

まず給食を残さないこと 給食を楽しめる 自分の好きな給食メニューをじゃんけんで決めるのは時間がかかるので、ベット方式で一瞬で決められるように 本当に食べれないものを好きな時に気軽に交換できるようにしたい

推し技術

頑張るぞーー

プロジェクト詳細

アプリ概要

「もぐもぐトレード」は、給食の時間を少し楽しくしながら、好き嫌いの克服やフードロス削減につなげるマッチングアプリです。 苦手な食材をただ残すのではなく、「誰かのほしいもの」と交換したり、完食したらポイントがたまったり、余った人気メニューをポイントで入札したりできます。給食を“がまんの時間”ではなく、“ちょっと楽しみな時間”に変えることを目指して作りました。

ターゲット

給食のある小中学生 特に、苦手な食べ物がある子、給食の時間に少し気まずさを感じる子、完食するモチベーションがほしい子を想定しています。先生にとっても、残食を減らしたり、クラス内の給食体験を前向きにする補助ツールです。

ストーリー

なぜこのプロダクトを作ろうと思ったか

給食では、苦手な食べ物を前にして困ってしまう子がいます。一方で、同じ食べ物を「もっと食べたい」と思っている子もいます。 そこで、ただ残す・無理に食べるのではなく、クラスの中で楽しく助け合える仕組みにできないかと考えました。食べることをゲームのように楽しめたら、給食の時間がもっと明るくなると思います。

誰のどんな課題を解決したいか・どんな楽しさを届けたいか

苦手な食材がある子には、「残す」以外の選択肢を届けたいです。完食をがんばった子には、ポイントがたまる達成感を届けたいです。 また、余った人気メニューをめぐるブラインドオークションによって、「今日は何ポイント使おうかな?」というワクワクも生まれます。給食を、ただ食べるだけでなく、考える・選ぶ・協力する時間に変えたいです。

🌟 健康とトレードのバランス(ルール設計のこだわり)

ただし、このアプリは「嫌いなものを何でも横流ししていいツール」ではありません。給食は子どもたちの健康な体づくりのために、原則は自分で栄養を摂ることが大切です。

そのため、たとえば「牛乳が嫌いだから」といって毎日連続して出品することはできないよう、ポイントに明確な制限を設けました。 具体的には、給食を1回「完食」すると貯まるポイントは【1pt】であるのに対し、苦手なメニューを「出品」するために必要なポイントは【5pt】に設定しています。 この「5回完食して、ようやく1回助けてもらえる」という具体的なハードルを設けることで、子どもたちが「明日はがんばって食べてみよう」と思えるメリハリが生まれます。ただ嫌いなものを避けるためではなく、「がんばる日」があるからこそ「どうしても苦手な日に助け合える」という、子どもの偏食改善と健康的な食生活をシステム側からも優しくサポートする設計にこだわりました。

技術構成・アーキテクチャ図など

1. ディレクトリ(ファイル)構成

プロジェクトは機能ごとにモジュール化され、依存性を排除した見通しの良い構成になっています。

mogumogu-trade:アプリ本体ターゲット App/:アプリ起動後の画面分岐、およびメインのタブバー制御 Features/:画面単位の機能実装 Onboarding/:新規ユーザーのクラス参加フロー Trade/:給食メニューの「出品」および「みんなの出品一覧」 QRCheck/:完食マイレージ用のQRコード表示・スキャンリーダー Allergy/:児童のアレルギー登録および確認画面 Teacher/:先生モードの骨組みUI BlindBet/:オークション形式のベッティングUI Models/:データ構造の定義(StudentProfile, TradeOffer, PointLedgerEntry, QRPayload, Allergen など) Dependencies/:Firestore、UserDefaults、QR生成、各種インフラ側ロジックのクライアント(Client)化 docs/:完食ポイント台帳における Firestore の設計メモ・ドキュメント

2. アプリの起動フローと画面遷移

アプリ起動からメイン機能へは、ユーザーのログイン(登録)状態に応じて自動で分岐します。

  1. 初期化mogumogu_tradeApp.swift 内で FirebaseApp.configure() を実行し、バックエンドと接続。
  2. ルート判定AppRootViewProfileStorageClient を経由して、ローカル(UserDefaults)に保存済みのプロフィール情報を読み込みます。

データなしClassJoinView(クラス参加画面)へ遷移。 ・データありMainTabView(メイン画面)へ自動遷移。

  1. メイン画面(MainTabView):画面下部に4つのタブを配置し、各機能へアクセスします。

「出品」(自メニューの登録) ・「みんな」(トレード相手の募集一覧) ・「マイレージ」(完食ポイント・QRスキャン) ・「オークション」(ベッティング機能)

3. 技術的なアプローチ・実装のポイント

堅牢なアーキテクチャ(依存性注入)

  • Point-Free製のライブラリ swift-dependencies を全面的に採用。
  • Firestore通信、UserDefaults(ローカル保存)、QRコード生成、UUID生成、現在時刻の取得など、すべてのサイドエフェクト(副作用)を Client(クライアント)層 として抽象化しています。これにより、プレビュー表示の高速化やユニットテストの容易性を担保しています。

Firebase / データベース設計

  • トレード機能 (TradeClient): *Firestore の classes/{classId}/trades パスをリアルタイム購読(Snapshot Listener)。
  • トレード成立時は、単なる上書きではなく Firestore Transaction(トランザクション) を使用。相手の出品ステータスを確実に matched(成立済)へと変更した上で、自分側の反転出品データを生成するアトミックな書き込み操作を行っています。

完食マイレージとQR不正防止

ポイント管理 (MileageClient):ユーザーの保有ポイントは直接数値を書き換えるのではなく、classes/{classId}/pointLedger に対する「台帳への追記型(履歴データ)」として実装。現在の残高は、これまでの amount(増減値)の合計値を算出することで安全に導き出します。

QRコード認証 (QRCodeClient / QRCheckViewModel):セキュリティ担保のため、JSON形式のペイロードを暗号化・QRコード化。スキャン時には「同じクラス内であるか」「自分のQRを自分で読んでいないか(自己承認の禁止)」「生成から5分以内であるか(期限検証)」の3重チェックをViewModel側で厳密に検証します。

安全安心のアレルギーチェック

  • アレルギー判定ロジックは、副作用のない純粋関数として AllergyChecker に分離。
  • TradeViewModel での出品時や交換リクエスト時にこのチェッカーが走り、対象のアレルゲンが含まれているメニューのトレードをシステム側で事前に強力にブロックします。

遊びごころポイント

ターゲットに刺さる機能

トレード機能では、苦手な食材やほしい条件を選んで、クラス内でマッチングできます。自由記述ではなく選択式にすることで、小中学生でも使いやすく、安全にやり取りできるようにしています。 完食マイレージでは、完食した本人がQRコードを表示し、友だちが読み取ることでポイントが入ります。友だちに確認してもらう体験が、ちょっとしたイベントになります。 ブラインドオークションでは、余ったプリンやゼリーなどをポイントで入札できます。締め切りまでは他の人の入札額が見えないため、ドキドキ感があります。

技術的遊び

QRコードを使った完食承認を入れているところが技術的な遊びポイントです。単にボタンを押すだけでなく、「見せる」「読み取る」という現実の動作をアプリ体験に組み込んでいます。 また、ポイントは単なる数値ではなく Firestore の pointLedger に履歴として保存します。完食で増えたポイント、将来のオークションで使ったポイントなどを後から追える設計にしている点もこだわりです。

アプリ画面(スクショや動画など)

1. 出品画面 自分の給食メニュー(揚げパン、プリン、ゼリーなど)を選んで、トレードに出品するための画面です。アレルギーチェック機能とも連動しており、安全なトレードの起点となります。 image

2. クラスの人たちの出品一覧 同じクラスの友達が今どんなメニューを出品しているかを、リアルタイムで一覧確認できる画面です。ここから欲しいメニューを選んでトレードの申請を行います。 image

3. QRコード(自分自身のQRコード) 完食マイレージ機能の「みせる側」の画面です。自分が完食したことを証明するために、自分専用のQRコードをアプリ内で自動生成して表示します。 image

4. QRコード(QRコードを読み込む画面) 完食マイレージ機能の「よむ側」の画面です。スマホのカメラが起動し、友達がみせてくれたQRコードをその場でスキャンします。読み込みに成功すると、賑やかな紙吹雪のアニメーションとともにお祝い演出が走り、完食ポイントが加算されます。 image

挑戦・成長したところ

小中学生向けのアプリなので、機能を増やすだけでなく「迷わず使えるか」を考えるのが難しかったです。説明文を長くするのではなく、ボタン名や画面構成だけで自然に使えるように意識しました。 また、Firestore のリアルタイム同期やトランザクション、QRコードによる承認、ポイント台帳設計など、見た目の楽しさの裏側にあるデータ整合性にも挑戦しました。特にポイントは直接残高を書き換えず、履歴から合計する設計にしたことで、後から確認しやすい仕組みにできました。

チームワーク・チーム開発で工夫したところ

機能ごとに責務を分け、View、ViewModel、Dependency Client、Model を分離するようにしました。これにより、画面担当とデータ連携担当が並行して作業しやすくなります。 また、MVPの範囲を「生徒向け中心」に絞ったことで、やることを広げすぎず、まず体験として成立する部分を優先できました。先生モードやアレルギー対応など、安全に関わる部分は将来拡張も見据えながら、必要最低限の導線を用意しています。

am2525nyan

@saki