Flutterで地図アプリ『SketchMap』を2週間で開発・リリース～課題解決と審査突破の全記録～

Flutterを使って地図アプリ**SketchMap**を2週間で開発し、リリースした体験を共有します。**SketchMap**は、地図上に自由に描画できる機能を持つアプリで、旅行の計画や日常会話、フィールドワークなどでの利用を想定しています。

本記事では、短期間で開発を終えることができた理由や、開発中に直面した課題、審査対応、そしてリリース後の振り返りを共有します。

## この記事のポイント
* なぜ2週間でリリースできたのか
* 開発の背景と解決したかった課題
* 開発プロセスと技術選定の工夫
* Apple StoreとGoogle Playの審査対応の詳細
* リリース後の振り返りと今後の展望

## 目次
- [アプリ概要](#アプリ概要)
- [なぜ2週間でリリースできたのか](#なぜ2週間でリリースできたのか)
- [開発の背景と課題](#開発の背景と課題)
  - [背景](#背景)
  - [課題](#課題)
- [技術選定と実装の工夫](#技術選定と実装の工夫)
  - [地図APIの選定](#地図apiの選定)
  - [描画機能の実装](#描画機能の実装)
  - [現在地の取得](#現在地の取得)
  - [住所検索機能の実装](#住所検索機能の実装)
  - [利用規約とマーケティング対応](#利用規約とマーケティング対応)
- [Apple StoreとGoogle Playの審査対応](#apple-storeとgoogle-playの審査対応)
  - [Apple Storeの審査](#apple-storeの審査)
  - [Google Playの審査](#google-playの審査)
- [リリース後の振り返り](#リリース後の振り返り)
  - [学び](#学び)
  - [課題](#課題-1)
- [今後の展望](#今後の展望)
- [さいごに](#さいごに)

## アプリ概要
**SketchMap**は、地図上に手描きで描画できるFlutterアプリです。ユーザーが自由に地図を活用できることを目指して開発しました。

### 主な機能
* **地図表示**：OpenStreetMapを使用した地図機能
* **手描き描画**：地図上に自由に線や文字を描画可能
* **現在地表示**：GPSで取得した位置を地図上にリアルタイムで表示
* **住所検索**：Nominatim APIを使用して地図上で住所を検索可能
* **広告収益化**：Google AdMobを利用して広告を表示し収益化

![Apple iPhone 11 Pro Max Presentation.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3953880/315ff3e1-0257-7da3-7220-2c23aa138ec0.png)

アプリは以下からダウンロードできます：
[App StoreでSketchMapをダウンロード](https://apps.apple.com/jp/app/sketchmap/id6738701125)
[Google PlayでSketchMapをダウンロード](https://play.google.com/store/apps/details?id=com.sketch_map&hl=ja)

## なぜ2週間でリリースできたのか
本アプリを2週間でリリースできた理由は、次の要素にあります：

### シンプルな機能構成に絞ったこと
開発初期から「まずは動くものをリリースする」ことを目標とし、記録機能などの複雑な機能は後回しにしました。そのため、以下の機能に注力しました：

* **地図表示と描画機能**
* **現在地の取得**
* **住所検索**

### Flutterの開発効率の高さ
Flutterは、クロスプラットフォーム対応を短期間で実現するための強力なフレームワークです。ホットリロード機能や豊富なライブラリが、効率的な開発を支えてくれました。

### 明確なタスク管理
Notionを活用して優先順位を整理し、**必須機能**と**追加機能**に分けました。たとえば、描画した情報を記録する機能は**追加機能**に分類し、リリース後のアップデートで検討する予定です。

## 開発の背景と課題
### 背景
私は旅行や友人との会話の中で、「地図上に直接メモを取れるツールがあれば便利だ」と感じることがありました。例えば：
* 会話中に場所を特定しながらアイデアや情報を記録したい
* フィールドワークや日常生活での発見を共有したい
* リアルタイムで地図メモに記録を残したい

既存のアプリでも地図にメモを取る機能を持つものはありますが、自分が必要とする「会話や情報共有をリアルタイムに進められる使いやすいツール」とは少し違っていました。そこで、「自分の理想を形にしよう」と考えたのが、**SketchMap**を作り始めたきっかけです。

### 課題
開発を進める上で、以下の課題に直面しました：
1. **地図APIの選定とコスト管理**：Google Maps APIは従量課金制でコストが高いため、無料で利用可能な代替APIを探す必要があったこと
1. **UI設計**：描画機能と地図操作が干渉しないUIを実現すること
1. **審査対応**：Apple StoreとGoogle Play、それぞれの審査基準を満たすこと
1. **短期間での効率的な開発**：限られた時間で必要な機能を優先順位付けし、完成まで到達すること

## 技術選定と実装の工夫
### 地図APIの選定
#### OpenStreetMapの採用
最初はGoogle Maps APIを検討しましたが、無料枠の制限が厳しく、利用量が増えるとコストが高額になる懸念がありました。そのため、オープンソースで無料利用可能なOpenStreetMapを採用しました。
**使用ライブラリ**：[flutter_map](https://pub.dev/packages/flutter_map)

### 描画機能の実装
#### 描画モードと地図操作モードの切り替え
描画機能では、地図操作（ズーム・パン）と描画が競合しないようにするUI設計が課題でした。これを解決するため、モード切替ボタンを設置しました。

**使用ライブラリ**：[flutter_drawing_board](https://pub.dev/packages/flutter_drawing_board)

### 現在地の取得
#### GPSデータの取得と地図への反映
GPSデータを取得し、地図上に現在地を表示する機能を実装しました。現在地取得機能の導入により、ユーザーが自身の位置を簡単に確認できる環境を提供しました。
**使用ライブラリ**：[geolocator](https://pub.dev/packages/geolocator)

### 住所検索機能の実装
#### Nominatim APIを用いた住所検索機能
**SketchMap**では、地図上で特定の住所を検索できる機能を実装しています。Nominatim APIを使用することで、軽量かつ無料で住所検索が可能です。以下にそのコード例を示します。
**使用API**：[Nominatim API](https://nominatim.org/)

```dart
Future<void> searchAddress(String query) async {
  final response = await http.get(
    Uri.parse('https://nominatim.openstreetmap.org/search?q=$query&format=json'),
  );
  if (response.statusCode == 200) {
    final data = jsonDecode(response.body);
    if (data.isNotEmpty) {
      final lat = double.parse(data[0]['lat']);
      final lon = double.parse(data[0]['lon']);
      // 地図を指定した場所に移動
      mapController.move(LatLng(lat, lon), 15.0);
    }
  }
}
```

### 利用規約とマーケティング対応
#### GitHub Pagesでの利用規約ホスティング
[利用規約](https://tatsumi6314.github.io/sketch_map/terms.html)はGitHub Copilotを活用して迅速に作成し、**GitHub Pages**でホスティングしました。アプリ内からリンクで参照可能にしています。
#### ストア表示用の画像作成
ストアに掲載するスクリーンショットやプロモーション用の画像は、[AppMockUp Studio](https://app-mockup.com/)を活用して作成しました。
このツールを利用することで、アプリの画面を見やすくデザインし、ユーザーにとって魅力的なプレゼンテーションを短時間で仕上げることができました。



## Apple StoreとGoogle Playの審査対応
Apple StoreとGoogle Playの審査では、それぞれに特有の課題がありました。
### Apple Storeの審査
Apple Storeの審査では、特にトラッキング関連の基準が厳しく、以下の指摘を受けました。最終的には、4回目の提出で承認されました。

#### 1. プライバシーポリシーが不十分 (Guideline 5.1.1)
**指摘内容**：
位置情報や診断データの収集目的が不十分で、`NSLocationWhenInUseUsageDescription`などの目的文字列に具体例が含まれていないと指摘されました。

**対応**：

利用目的を具体的に記載した目的文字列に修正：
```xml
<key>NSLocationAlwaysUsageDescription</key>
<string>位置情報を使用して現在地周辺に移動し、地図上にスケッチできます。</string>
<key>NSLocationUsageDescription</key>
<string>位置情報を使用して現在地周辺に移動し、地図上にスケッチできます。</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>位置情報を使用して現在地周辺に移動し、地図上にスケッチできます。</string>
```

#### 2. トラッキング許可の設定不足 (Guideline 5.1.2)
**指摘内容**：
`AppTrackingTransparency`フレームワークの実装が不足しており、トラッキング許可リクエストが表示されないため、ユーザーのトラッキング許可を得ていないと判断されました。

**対応**：
`AppTrackingTransparency`フレームワークを導入し、初回起動時にトラッキング許可をリクエストするコードを追加：
```dart
import 'package:app_tracking_transparency/app_tracking_transparency.dart';

Future<void> requestTrackingPermission() async {
  final status = await AppTrackingTransparency.trackingAuthorizationStatus;
  if (status == TrackingStatus.notDetermined) {
    await AppTrackingTransparency.requestTrackingAuthorization();
  }
}

@override
void initState() {
  super.initState();
  requestTrackingPermission();
}
```

#### 3. 不足していたトラッキング許可リクエスト (Guideline 2.1)
**指摘内容**：
`AppTrackingTransparency`フレームワークがコード内に実装されているが、審査端末（iPadOS 18.1.1）でトラッキング許可リクエストが表示されないと指摘されました。

調査の結果、レビューワーの端末で既にトラッキング許可が`determined`の状態だったことが原因であると判明しました。この状態では、フレームワークの仕様によりリクエストが再表示されません。

**対応**：
1. **レビューワーに以下の点を伝えるメッセージを送付**：
   * `AppTrackingTransparency`フレームワークが実装されており、初回起動時に適切に動作すること
   * 一度許可設定が確定した場合は、仕様上再表示されないこと
1. **証拠動画の提出**
トラッキング許可リクエストが正しく動作していることを示す動画を撮影し、以下の内容を示しました：
   * アプリ初回起動時にトラッキング許可リクエストが表示される様子
   * 設定済みの場合にリクエストが再表示されない仕様
   * ダイアログを承認・拒否した際の挙動
   * メッセージ内で、レビューワーが設定済みの許可をリセットする手順の説明

### Google Playの審査
Google Playの審査は比較的スムーズで、提出から1日で承認されました。ただし、Google AdMob広告の配置がポリシーに準拠しているか確認することが重要でした。特に、**ボタンや重要なUI要素の近くに広告を配置しない**ように注意しました。

## リリース後の振り返り
**SketchMap**をリリースしてから、多くの学びと改善の課題が見えてきました。以下に、具体的な振り返りをまとめます。

### 学び
1. **Flutterの開発効率の高さ**
Flutterを採用したことで、AndroidとiOSのクロスプラットフォーム対応を実現しつつ、短期間でのリリースが可能になりました。特にホットリロード機能や豊富なライブラリ（`flutter_map`や`geolocator`など）は、効率的な開発を大いに支えてくれました。
1. **OpenStreetMapの有用性**
Google Maps APIに比べ、無料で高い柔軟性を持つOpenStreetMapを選んだことは正解でした。OSSコミュニティも活発で、`flutter_map`ライブラリを利用したカスタマイズが簡単に行えました。
1. **収益化の基礎を学べた**
Google AdMobを使った広告収益化を実践する中で、広告配置や収益モデルについて学ぶことができました。ただし、最適化の余地がまだ多く残っていると感じています。

### 課題
1. **Apple Storeの審査対応**
Apple Storeの審査では、プライバシー保護に関する厳しい基準への対応が求められました。特に、利用規約やトラッキング許可ダイアログの実装について、細かい修正を繰り返し行い、4回目の提出でようやく承認されました。これを通じて、審査基準への深い理解と迅速な対応の重要性を学びました。
1. **広告収益の最適化**
現在、広告の収益が想定よりも低い状況です。これは、広告の配置や種類が最適でないことが原因だと考えています。インタースティシャル広告や報酬型広告の導入を含め、収益モデルの再検討が必要です。
1. **ユーザーフィードバックへの迅速な対応**
リリース直後のため、現時点では重大なバグは報告されていません。しかし、今後ユーザーからのフィードバックを迅速に収集し、必要に応じて機能の改善やバグ対応を行う仕組みを強化する必要があります。また、長期的には、テストプロセスをより強化して品質を高めていく計画です。

### 今後の展望
**SketchMap**をさらに便利で多くの人に使われるアプリに成長させるため、以下の改善と新機能の追加を計画しています。
1. **描画履歴の保存と復元機能**
ユーザーが地図上に描いた内容を保存し、後で復元できる機能を追加します。この機能により、作業を中断しても続きから再開することが可能になります。特に、長時間の作業や旅行の記録などに役立つことを期待しています。
1. **地図のテーマや現在地アイコンのカスタマイズ**
ユーザーが地図のテーマ（ダークモード、モノクロなど）や現在地のアイコンを自分好みにカスタマイズできる機能を追加予定です。これにより、アプリの見た目や使い勝手が個人の好みに合わせて柔軟に調整できるようになります。
1. **多言語対応**
現在は日本語のみの対応ですが、英語や他言語にも対応し、海外ユーザーの利用を拡大します。

## さいごに
**SketchMap**は、「地図を使いながらその場でメモが取れたら便利だな」という日常のアイデアから生まれました。短期間でリリースできたのは、Flutterの効率的な開発環境と、必要な機能に絞り込んだシンプルな設計のおかげです。

これからも、ユーザーのみなさんの声を参考にしながら、アプリをもっと便利で楽しいものにしていきたいと考えています。もし**SketchMap**を使ってみて、「こういう機能があればいいな」と思うことがあれば、ぜひ教えてください。みなさんのフィードバックが、このアプリをより良いものにしてくれます。

最後まで読んでいただき、ありがとうございました！アプリをダウンロードして、ぜひあなただけのアイデアや発見を地図上に描いてみてください。