マッチングアプリは、現代で最も需要の高いアプリカテゴリーのひとつです。「いいね」「スキップ」のスワイプ操作、マッチング通知、そしてリアルタイムチャット——これらを個人開発で実現するのは難しそうに感じるかもしれません。しかし、Rork を使えば、コーディング経験が少なくてもマッチングアプリの核となる機能を構築できます。
このチュートリアルで作るもの
- ユーザープロフィールカードのスワイプUI(右スワイプ=いいね、左スワイプ=スキップ)
- 相互にいいねした際のマッチング判定
- マッチング相手とのリアルタイムチャット(Supabase Realtime)
- プロフィール画像のアップロード(Supabase Storage)
前提知識と環境準備
このチュートリアルを進めるには、以下が必要です。
- Rork アカウント(無料プランでも開始可能)
- Supabase アカウント(無料枠あり)
- React Native の基本的な理解(必須ではないが、あると理解が深まります)
Rork の基本的な使い方がまだわからない方は、まず Rork 入門:ゼロからアプリを作る方法 を先にご覧ください。
Supabase プロジェクトの初期設定
Supabase でデータベースを構築します。マッチングアプリに必要なテーブルは次の3つです。
- profiles:ユーザープロフィール(名前、年齢、自己紹介、画像URL)
- swipes:スワイプの記録(誰が誰を「いいね」or「スキップ」したか)
- matches:マッチしたペアの記録
- messages:チャットメッセージ
以下の SQL を Supabase の SQL Editor で実行してください。
-- プロフィールテーブル
create table profiles (
id uuid references auth.users primary key,
name text not null,
age integer,
bio text,
avatar_url text,
created_at timestamptz default now()
);
-- スワイプ記録テーブル
create table swipes (
id uuid default gen_random_uuid() primary key,
swiper_id uuid references profiles(id),
swiped_id uuid references profiles(id),
direction text check (direction in ('like', 'pass')),
created_at timestamptz default now(),
unique(swiper_id, swiped_id)
);
-- マッチテーブル
create table matches (
id uuid default gen_random_uuid() primary key,
user1_id uuid references profiles(id),
user2_id uuid references profiles(id),
created_at timestamptz default now()
);
-- メッセージテーブル
create table messages (
id uuid default gen_random_uuid() primary key,
match_id uuid references matches(id),
sender_id uuid references profiles(id),
body text not null,
created_at timestamptz default now()
);
-- Realtime を有効にする
alter publication supabase_realtime add table messages;実行後、Supabase ダッシュボードの「Authentication」→「Policies」でそれぞれのテーブルに適切な Row Level Security(RLS)ポリシーを設定します。
スワイプUIの実装
Rork にプロジェクトを作成したら、スワイプUIのプロンプトを入力します。Rork はプロンプトから React Native のコードを生成してくれます。
Rork へのプロンプト例
マッチングアプリのスワイプ画面を作ってください。
- 画面中央にプロフィールカード(写真、名前、年齢、自己紹介)を表示
- 右スワイプで「いいね」、左スワイプで「スキップ」
- React Native Gesture Handler を使ったスムーズなアニメーション
- カード下部に「❌ スキップ」「❤️ いいね」ボタンも配置
- Supabase から profiles テーブルのデータを取得して表示
Rork が生成するコードの骨格はこのようになります。
// screens/SwipeScreen.tsx(Rork 生成コードの例)
import { PanGestureHandler } from 'react-native-gesture-handler';
import Animated, {
useAnimatedGestureHandler,
useAnimatedStyle,
useSharedValue,
withSpring,
runOnJS,
} from 'react-native-reanimated';
export function SwipeScreen() {
const translateX = useSharedValue(0);
const translateY = useSharedValue(0);
const gestureHandler = useAnimatedGestureHandler({
onActive: (event) => {
translateX.value = event.translationX;
translateY.value = event.translationY;
},
onEnd: (event) => {
if (event.translationX > 100) {
// 右スワイプ:いいね
runOnJS(handleLike)();
} else if (event.translationX < -100) {
// 左スワイプ:スキップ
runOnJS(handlePass)();
}
translateX.value = withSpring(0);
translateY.value = withSpring(0);
},
});
return (
<PanGestureHandler onGestureEvent={gestureHandler}>
<Animated.View style={[styles.card, animatedStyle]}>
{/* プロフィールカードの内容 */}
</Animated.View>
</PanGestureHandler>
);
}マッチング判定ロジック
スワイプ後、Supabase に記録して相互いいねを確認します。
// utils/swipe.ts
import { supabase } from './supabase';
export async function handleSwipe(
swiperId: string,
swipedId: string,
direction: 'like' | 'pass'
) {
// スワイプを記録
await supabase.from('swipes').insert({
swiper_id: swiperId,
swiped_id: swipedId,
direction,
});
if (direction === 'like') {
// 相手からのいいねが既にあるか確認
const { data } = await supabase
.from('swipes')
.select('id')
.eq('swiper_id', swipedId)
.eq('swiped_id', swiperId)
.eq('direction', 'like')
.single();
if (data) {
// 相互いいね → マッチング成立
await supabase.from('matches').insert({
user1_id: swiperId,
user2_id: swipedId,
});
return { matched: true };
}
}
return { matched: false };
}マッチング成立時は「マッチしました!」のモーダルを表示し、チャット画面へ誘導します。
リアルタイムチャットの実装
マッチングが成立したら、相手とのチャットが解放されます。Supabase Realtime を使うことで、メッセージがリアルタイムに双方の画面に届く仕組みを実現できます。
// screens/ChatScreen.tsx
import { useEffect, useState } from 'react';
import { supabase } from '../utils/supabase';
export function ChatScreen({ matchId }: { matchId: string }) {
const [messages, setMessages] = useState([]);
useEffect(() => {
// 既存メッセージを取得
fetchMessages();
// Realtime サブスクリプション
const channel = supabase
.channel(`match:${matchId}`)
.on(
'postgres_changes',
{
event: 'INSERT',
schema: 'public',
table: 'messages',
filter: `match_id=eq.${matchId}`,
},
(payload) => {
// 新メッセージをリアルタイム反映
setMessages((prev) => [...prev, payload.new]);
}
)
.subscribe();
return () => supabase.removeChannel(channel);
}, [matchId]);
const sendMessage = async (body: string) => {
await supabase.from('messages').insert({
match_id: matchId,
sender_id: currentUserId,
body,
});
// Realtime で自動的に相手側に届く
};
return (
// チャット UI
);
}チャット機能をさらに深掘りしたい方は、Rork でリアルタイムチャットアプリを構築する完全ガイド も参考にしてください。
プロフィール画像のアップロード
ユーザーが自分の写真を登録できる機能も重要です。Supabase Storage を使います。
// utils/uploadAvatar.ts
import * as ImagePicker from 'expo-image-picker';
import { supabase } from './supabase';
import { decode } from 'base64-arraybuffer';
export async function uploadAvatar(userId: string): Promise<string | null> {
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [1, 1], // 正方形にトリミング
quality: 0.8,
base64: true,
});
if (result.canceled || !result.assets[0].base64) return null;
const fileName = `avatars/${userId}-${Date.now()}.jpg`;
const { error } = await supabase.storage
.from('profiles')
.upload(fileName, decode(result.assets[0].base64), {
contentType: 'image/jpeg',
upsert: true,
});
if (error) throw error;
const { data } = supabase.storage.from('profiles').getPublicUrl(fileName);
return data.publicUrl;
}よくあるエラーと対処法
マッチングアプリ開発で遭遇しやすいエラーとその対処法を紹介します。
RLS ポリシーエラー(403 Forbidden)
Supabase で「permission denied for table swipes」などのエラーが出た場合は、RLS ポリシーが未設定か、設定が不正確です。Supabase ダッシュボードの「Authentication → Policies」で、ログイン中のユーザーが自分のデータを読み書きできるポリシーを追加してください。
スワイプアニメーションがカクつく
react-native-reanimated の runOnUI / runOnJS の呼び分けを確認してください。UI スレッドで実行すべき処理を JS スレッドで行うとカクつきの原因になります。
Supabase Realtime が機能しない
alter publication supabase_realtime add table messages; の SQL を実行しているか確認してください。これを忘れると Realtime のイベントが発火しません。
全体を振り返って
Rork を使えば、スワイプUI・マッチング判定・リアルタイムチャットという、マッチングアプリの核心機能を比較的短期間で構築できます。まずは Supabase でデータ設計を固め、Rork にプロンプトを入力しながらUIを組み上げていくのが最も効率的なアプローチです。
マッチングアプリはプッシュ通知・位置情報・プレミアム課金など、拡張要素も豊富です。MVP(最小限のプロダクト)から始めて、ユーザーの反応を見ながら機能を追加していきましょう。
個人開発でアプリをリリースして収益化する方法については、React Native × Expo アーキテクチャ完全ガイド も参考にしてください。