概要
テーマ購入・ジャンルアンロックに使用するアプリ内通貨。日次ログイン・動画視聴・プレミアム月次配布で獲得。
AppGroup UserDefaults に残高を保存し、Widget からも参照可能。
関連画面
獲得手段
| イベント | 付与量 | 制約 |
| デイリーログインボーナス | +1コイン/日 | 1日1回(lastLoginDateで制限) |
| プレミアム月次配布 | +100コイン | 毎月1日、プレミアムユーザーのみ |
| 動画視聴ボーナス | +3コイン | 1日最大5回 |
消費手段
| 項目 | 消費量 |
| 有料テーマ購入 | 200コイン |
| ジャンルアンロック | 100コイン × 13ジャンル = 1300コイン |
収支シミュレーション
1ヶ月(30日)の最大獲得量:
ログイン: 30 × 1 = 30コイン
動画ボーナス: 30 × 5 × 3 = 450コイン
プレミアム: +100コイン(プレミアムのみ)
合計: 480〜580コイン/月
全ジャンル解放(1300)+ テーマ複数(200×N)に必要なため、自然な課金導線。
API
CoinService {
func getBalance() -> Int
func recordDailyLogin() -> CoinResult
func recordMonthlyPremiumBonus() -> CoinResult
func recordVideoBonus() -> CoinResult
func spendCoins(_ amount: Int) -> Bool
func getLoginStreak() -> Int
func getRemainingVideoBonusCount() -> Int // 当日の残り回数
}
struct CoinResult {
let earned: Int
let streak: Int
let balance: Int
let alreadyReceived: Bool
}
ストリーク計算ロジック
// 前日ログイン → 継続 (+1)、そうでなければ 1日目リセット
streak = (lastLogin == yesterday) ? streak + 1 : 1
ビジネスルール
- ログインボーナスは
lastLoginDate を AppGroup に保存して二重付与防止
- 動画ボーナスは
video_bonus_count_* で当日の取得回数を記録
- プレミアム月次は毎月1日のみ付与(month文字列で判定)
- 残高 0 でも動画視聴で獲得可能(プレッシャーフリー設計)
外部連携
| 連携先 | 用途 |
| AppGroup UserDefaults | 残高・履歴の保存 |
| RewardedAdManager | 動画ボーナス時の広告表示 |
| PremiumService | 月次ボーナス対象判定 |
エラー処理
| 発生条件 | 対応 |
| 残高不足 | 「コインが足りません」アラート |
| 動画ボーナス上限 | 「本日の上限に達しました」 |
| AppGroup nil | 標準UserDefaultsへフォールバック |
実装メモ
- CoinService は
@unchecked Sendable
- UserDefaults キー:
coin_balance, last_login_date, login_streak, video_bonus_count_*
- CoinResult.alreadyReceived で「すでに本日受取済み」を表現
変更履歴
| バージョン | 日付 | 変更内容 |
| 1.0 | 2026-05-09 | 初版作成 |