【個人開発】SwiftUIでiOSアプリを公開！地図上にメモができるアプリのリリース過程

Swiftを学習し始めて約3ヶ月
ついに**自作のiOSアプリをリリース**することができました！

:::note info
自作アプリ：地図上にメモができるアプリ-mapping
:::

今回はSwift初心者がアプリ開発できるまでの過程をまとめていきます。

◇この記事を読んで欲しい人
+ iOSアプリ開発の流れ
+ 個人開発の過程

# Swift学習を始めたきっかけ
◇学習前の状況
+ アプリ開発未経験
+ Webサイト自作経験有り
+ プログラミングは趣味でやってるだけ

私がSwift学習を始めたきっかけは「**iPhoneを使ってるなら自分でアプリ作れたら便利じゃん**」と浅はかなものでした。

Swiftの知識は全くなかったのでとりあえず参考書を一冊購入し学習を始めました。使用した本のレビュー記事も載せておきます。

[SwiftUI学習におすすめの参考書！詳細！SwiftUI iPhoneアプリ開発 入門ノート[2021]　iOS 15+Xcode 13対応](https://tech.amefure.com/swift-beginner-book)

# iOSアプリを公開するために
iOSアプリを公開するために必要なものは以下の通りでした。

+ Xcode(統合開発環境)のインストール
+ Swiftの学習
+ Apple Developer Programの登録(有料)
+ Apple ID
+ Mac

◇Xcode
![FDCFFC33-6F2B-4B77-BEE6-C5048C2948E6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2264957/505fd554-c82b-caed-523d-a161c3663438.png)

Xcodeは無料でインストールできますが容量が非常に重いのでインストール完了までに数時間かかることもあります。


◇Apple Developer Program
![8291DA59-2CBC-44B0-B853-606D33483E73.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2264957/65cac21e-c421-32c4-c21d-a59c7fba6c7d.png)

アプリを公開しないのであればApple Developer Programへの登録は不要ですが、公開したい場合は**年額$99**(**日本円にして11000〜15000円**)の費用がかかってしまいます。

# アプリを作るにあたって
先ほどもチラッと書きましたが初めて私が作成したアプリは**地図上にメモができるアプリ**です。

![A92A4340-D313-429A-917C-5BC244B85F82.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2264957/b683fdfd-ee19-a832-e68a-553e13529f64.jpeg)

アプリを作るためにはしっかりした要件定義と設計が必要だなと思います。初心者ながらにそれっぽくまとめただけですが一応載せておきます。

◇アプリ開発の流れ
1. 要件定義
1. 設計
1. 制作
1. テスト
1. 公開


# 要件定義

◇ターゲット
+ 色々出歩くのが好きな人
+ グルメな人
+ 使用して良かった駐車場の位置を記録したい人
+ 安いガソリンスタンドをメモしときたい人
+ 現在地の住所を簡単に知りたい人

◇アプリの概要
+ 地図上の好きな位置にピンを立てれる
+ そのピンに対する名称やメモを記述
+ 現在地からそのピンまでのルートと所要時間などを表示
+ 現在地の住所を表示

◇実装するために
+ 地図上にピンを立てるために住所な名称、メモを入力してもらう
+ それらの情報はJSONファイルで管理
+ 地図はMapsと連携できそう

## 設計
SwiftではMVCアーキテクチャに倣ってアプリ構造を設計していきます。

+ Model(モデル)：データそのもの
+ View(ビュー)：表示される画面
+ Controller(コントローラ)：MとVを操作する

Swift UIでのMVCアーキテクチャの紐付けがまだよく分かっていないですができる限り切り分けて行きたいと思います。

### モデル(データ設計)
今回のアプリで使うデータは「**ユーザが登録する位置情報**」と「**地図表示のためのデータ**」です。

◇ユーザーが登録する位置情報
位置情報のフォーマットはLocation構造体として定義し**地図表示のための緯度経度と座標**を持たせています。JSONファイル内では**Location構造体を配列の要素として格納**していくことにしました。
```javascript:JSONファイル内
[{"address":"東京都千代田区千代田１−１","longitude":2,"id":"43358E09-F193-4183-99A2-9AC33195A5DC","memo":"良い場所","latitude":2,"spot":"施設","name":"東京スカイツリー"}]
```
```swift:LocationModels.swift
struct Location:  Codable , Identifiable ,Equatable{
    
    var id = UUID()     // 識別子
    var address:String  // 住所
    var name:String     // 名称
    var memo:String     // メモ
    var spot:Spot       // スポットカテゴリ
    
    // アノテーション用-------------------
    var latitude: Double  // 緯度
    var longitude: Double // 経度
//    // 座標
    var coordinate:CLLocationCoordinate2D {
       CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
    }
    // アノテーション用-------------------   
}
```
◇地図表示のためのデータ
こちらはMapKitフレームワークを導入して地図表示に必要な機能を提供するLocationManagerクラスとして定義します。

```swift:MapModels.swift
import MapKit
import SwiftUI

// 現在地を取得するためのクラス
class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate {
    // CLLocationManagerをインスタンス化
    let manager = CLLocationManager()
    let geocoder = CLGeocoder()
    // 領域の更新をパブリッシュする
    @Published var region = MKCoordinateRegion()
    
    @Published var address:String? = ""
    
    override init() {
        super.init() // スーパクラスのイニシャライザを実行
        manager.delegate = self // 自身をデリゲートプロパティに設定
        manager.requestWhenInUseAuthorization() // 位置情報を利用許可をリクエスト
        manager.desiredAccuracy = kCLLocationAccuracyBest // 最高精度の位置情報を要求
        manager.distanceFilter = 3.0 // 更新距離(m)
        manager.startUpdatingLocation()
        self.reloadRegion()
    }
    
    func reloadRegion (){
        
        if let location = manager.location {
     geocoder.reverseGeocodeLocation( location, completionHandler: { ( placemarks, error ) in
                
                if let placemark = placemarks?.first {
                    //住所
                    let administrativeArea = placemark.administrativeArea == nil ? "" : placemark.administrativeArea!
                    let locality = placemark.locality == nil ? "" : placemark.locality!
                    let subLocality = placemark.subLocality == nil ? "" : placemark.subLocality!
                    let thoroughfare = placemark.thoroughfare == nil ? "" : placemark.thoroughfare!
                    let subThoroughfare = placemark.subThoroughfare == nil ? "" : placemark.subThoroughfare!
                    let placeName = !thoroughfare.contains( subLocality ) ? subLocality : thoroughfare
                    self.address = administrativeArea + locality + placeName + subThoroughfare
                }
            })
            
            let center = CLLocationCoordinate2D(
                latitude: location.coordinate.latitude,
                longitude: location.coordinate.longitude)
            
            region = MKCoordinateRegion(
                center: center,
                latitudinalMeters: 1000.0,
                longitudinalMeters: 1000.0
            )
        }
    }
    // 位置情報が拒否された場合に初期表示位置を構築：東京スカイツリーの場所
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager){
        let guarded = manager.authorizationStatus.rawValue
        if guarded == 2 {
            let center = CLLocationCoordinate2D(
                latitude: 35.709152712026265,
                longitude: 139.80771829999996)
            
            region = MKCoordinateRegion(
                center: center,
                latitudinalMeters: 1000.0,
                longitudinalMeters: 1000.0
            )
        }
    }
    // ジオコーディング　住所 → 座標 InputViewの住所チェック
    func geocode(addressKey:String,completionHandler: @escaping (CLLocationCoordinate2D?) -> Void){
        
        geocoder.geocodeAddressString(addressKey) { (placemarks, error) in
            guard let unwrapPlacemark = placemarks else {
                // ジオコーディングできない文字列の場合
                DispatchQueue.main.async {
                    completionHandler(nil)
                }
                return
            }
            // ジオコーディングできた文字列の場合
            let location  = unwrapPlacemark.first!.location!
            // 非同期処理で実行
            DispatchQueue.main.async {
                completionHandler(location.coordinate)
            }
        }
    }
}
```

### ビュー(デザイン)
アプリのビュー、いわゆる UI部分やデザインなどを決めておきます。

+ テーマカラー：ピンク
+ ベースカラー：黒色
+ アプリアイコン：フリーアイコンを使用
+ アプリ名：mapping
+ キャッチコピー：「自分だけの地図を作ろう」


![1862A7CC-A3A5-4AA2-A5BF-E0897E7FAE4B.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2264957/4a9dc201-c8e1-cf61-8466-543163272498.png)


### コントローラー(画面設計)
画面設計では必要となる画面枚数と画面遷移の導線をなんとなくイメージしてみました。

◇必要となる画面
+ 地図表示ビュー
+ 現在地の住所取得ビュー
+ 位置情報のリスト表示ビュー
+ 設定ビュー
+ 入力フォームビュー

◇画面遷移
今回は主となる4つのビューをタブビューで切り替えられるようにし、そのどこからでも「入力フォームビュー」にアクセスできるようにします。

## 制作
アプリ制作には約2ヶ月ほどかかりました。実装した機能は以下の通りです。

◇実装した機能
+ タブビュー
+ リストビュー
+ リストのフィルタリング機能
+ アラート機能
+ 位置情報取得
+ アノテーション設置
+ 入力フォーム
+ JSONファイルへの読み書き
+ ジオコーディング
+ 経路探索表示
+ シェア機能

## テスト
Swift UIでは**シミュレーターを簡単に起動できる**ので都度テストしながらの開発ができました。

あとは色々な入力値や位置情報の許可が貰えない時の対応などをエラーを起こしそうな可能性のある事象を試して行きました。


## 公開
アプリ制作が完了したらいよいよ公開する時です。公開までの詳細な手順はここでは記載しませんので以下を参考にしてください。

[【Swift】iOSアプリをAppStoreで公開する方法！前準備とXcodeのビルドのやり方](https://tech.amefure.com/swift-appstore-public)

◇公開までの流れ
1. Xcodeで開発
1. Apple Developer Programへの登録
1. AppStore Connectを使って審査申請
1. 審査に通過
1. アプリ配信開始

# 個人開発の難しいところ

## デザイン
**一番難しかったのは「デザイン」**といっても過言ではないかも知れません。配色や部品の位置、全体のレイアウトや文字色、文字サイズ、フォントなど細かい違いですが見た目に大きく関わってきます。

なんとか自分でそれっぽいデザインを作り上げることが出来たものの今でも「なんかダサいなぁ〜」と思いつつ眺めています。

色々なアプリを見ながらこれは学んでいくしかないかも知れません。

## 機能の実装とエラー解決
私の場合、エンジニアの知り合いなどいないので**完全な個人開発**となってしまいました。
なので機能を1つ実装するにも時にはドキュメントを読み込み、時には質問サイトに投稿したりと四苦八苦したのを覚えています。

ですがそれのおかげで**アプリ開発前よりは成長した実感**を感じています。やはりiOSアプリ開発学習の一番な近道は実践的な制作活動かもしれません。

## アプリの宣伝
デザインの次に難しいのが「**アプリの宣伝**」です。
正直個人で作ったアプリなので検索にもなかなか出て来ず、そもそもインストールされることが全くありません笑

今の所使ってるのは自分だけなんじゃないかレベルです。。

でもこれは**自分の作ったアプリが需要を満たせてないだけの可能性も高い**ので仕方ないことですが。。

# iOSアプリ開発の感想

今回のアプリ開発を経験して学べたことはとても多かったと思います。

アプリ制作から公開までの1連の流れを理解でき、Swiftを扱う上での知識もそれなりについた気がします。

あと何よりも**アプリ制作についてあれこれ悩んでいる時やコードを書いている時がとても楽しかった**です。

# 最後にアプリの紹介
私が作ったアプリを改めて紹介させてください。

:::note info
自作アプリ：地図上にメモができるアプリ-mapping
:::

◇概要
このアプリは**自分のお気に入りのショップやレストラン、スポットなどを登録**できる地図アプリです。
その場所の住所を入力すると登録でき、名称やメモは自由に記述することができます。

mappingを使って「**自分だけの地図を作りませんか？**」

インストールはこちら
[mapping](https://apps.apple.com/jp/app/mapping/id1639823172])

またこのアプリはGitHub上にコードを全て公開しています。「ここはこんなコードになっているんだ〜」みたいに確認してみてください。

[GitHub-mapping](https://github.com/amefure/mapping)

まだまだ勉強中ですのでご指定やアドバイス、レビューなどがいただけると泣いて喜びます。

ご覧いただきありがとうございました。