0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

NCMBのSwift SDKを使って地図検索アプリを作る(その2:位置情報をインポートする)

Last updated at Posted at 2022-10-19

NCMBでは公式SDKとしてSwift/Objective-C/Kotlin/Java/Unity/JavaScript SDKを用意しています。また、それ以外にもコミュニティSDKとして、非公式ながらFlutter/React Native/Google Apps Script/C#/Ruby/Python/PHPなど幅広い言語向けにSDKが開発されています。

今回は公式SDKの一つ、Swift SDKを使って地図検索アプリを作ってみます。前回は画面の仕様とSDKの初期化について解説しました。今回は位置情報をデータストアにインポートする流れを解説します。

完成版のコード

作成したデモアプリのコードはNCMBMania/swift_map_appにアップロードしてあります。

インポート画面について

インポート画面はContentViewのタブで読み込まれています。 ImportView がそうです。

// インポート画面
ImportView()
		.tabItem {
				VStack {
						Image(systemName: "gear")
						Text("Import")
				}
}.tag(2)

インポート画面の構築

まず ImportView のStateについてです。文字列の配列が入る logs と データをインポート(または削除)するNCMBのデータストアクラス名を入れた _className があります。

struct ImportView: View {
    @State private var logs: [String] = []
    private var _className = "Station"

UIは次のようになります。インポート実行タンをタップすると _execute が実行されます。また、logs が更新されると List を使ってログが表示されます。

var body: some View {
		VStack(spacing: 16
		) {
				Text("駅一覧を読み込みます").padding()
				// ボタンを押したら駅登録処理開始
				Button(action: _execute, label: { Text("インポート実行")})
				if logs.count > 0 {
						// ログ表示用
						Text("ログ")
						List {
								ForEach(Array(logs.enumerated()), id: \.element) { index, log in
										Text(logs[index])
								}
						}
				}
		}
}

Simulator Screen Shot - iPhone 14 - 2022-10-18 at 21.49.09.png

データのインポートについて

データをインポートする際には繰り返し処理できるように、まず既存データを削除しています。もし既存データがなかった場合でも(データストアのクラスがなくとも)エラーにはならず、空の配列が返ってくるだけです。

private func _execute() {
		// まずデータを削除する
		_removeStations()
		// JSONファイルを読み込む
		let stations = _loadStations()
		// JSONファイルの内容をデータストアに登録する
		registerDataStore(stations: stations)
}

_removeStations は以下のようになります。NCMBQueryを使ってデータを取得し、各データ(NCMBObject)のdeleteメソッドを使ってデータを削除します。

// すでにデータストアにある駅名データを削除する処理(何度も繰り返せる用)
private func _removeStations() {
		// 検索対象のデータストアのクラス(DBで言うテーブル名相当)
		var query = NCMBQuery.getQuery(className: _className)
		// 100件対象とする
		query.limit = 100
		// 検索実行
		query.findInBackground(callback: { result in
				// 処理が成功している場合
				if case let .success(ary)  = result {
						// すべてのデータを削除
						ary.forEach{ station in
								// 削除処理(非同期)
								station.deleteInBackground(callback: {_ in
										// 特に処理なし
								})
						}
				}
		})
}

データを削除したら、山手線の各駅の位置情報が入ったJSONファイルを読み込みます。

// アセットにあるJSONファイルを読み込む
private func _loadStations() -> [Station] {
		// インポートする
		guard let url = Bundle.main.url(forResource: "yamanote", withExtension: "json") else {
				fatalError("ファイルが見つからない")
		}
		// 読み込み
		guard let data = try? Data(contentsOf: url) else {
				fatalError("ファイル読み込みエラー")
		}
		// JSONデコード
		let decoder = JSONDecoder()
		guard let stations = try? decoder.decode([Station].self, from: data) else {
				fatalError("JSON読み込みエラー")
		}
		return stations
}

この時利用している Station という構造体は上記のようになっています。

struct Station: Codable {
    var name: String
    var latitude: Double
    var longitude: Double
}

読み込んだデータをデータストアに登録する registerDataStore 関数は以下のようになります。JSONファイルのパラメータを渡して、Stationクラスにデータを作成します。NCMBGeoPointを使って位置情報を保存するのがポイントです。

// 駅名の配列をデータストアに登録する
private func registerDataStore(stations: [Station]) {
		stations.forEach{ params in
				// 登録するデータストアのクラス(DBで言うテーブル名相当)
				let station = NCMBObject(className: _className)
				// 駅名をセット
				station["name"] = params.name
				// 位置情報はNCMBGeoPointを利用する
				let geo = NCMBGeoPoint(latitude: params.latitude, longitude: params.longitude)
				// 位置情報をセット
				station["geo"] = geo
				// 保存処理(非同期処理)
				station.saveInBackground(callback: { result in
						// 成功していればログに追記
						if case .success(_)  = result {
								let log = "保存しました -> \(params.name)"
								logs.append(log)
						}
				})
		}
}

Simulator Screen Shot - iPhone 14 - 2022-10-18 at 21.49.12.png

全体のコード

ImportViewの内容は次のようになります。

struct Station: Codable {
    var name: String
    var latitude: Double
    var longitude: Double
}

struct ImportView: View {
    @State private var logs: [String] = []
    private var _className = "Station"
    
    // すでにデータストアにある駅名データを削除する処理(何度も繰り返せる用)
    private func _removeStations() {
        // 検索対象のデータストアのクラス(DBで言うテーブル名相当)
        var query = NCMBQuery.getQuery(className: _className)
        // 100件対象とする
        query.limit = 100
        // 検索実行
        query.findInBackground(callback: { result in
            // 処理が成功している場合
            if case let .success(ary)  = result {
                // すべてのデータを削除
                ary.forEach{ station in
                    // 削除処理(非同期)
                    station.deleteInBackground(callback: {_ in
                        // 特に処理なし
                    })
                }
            }
        })
    }
    
    // アセットにあるJSONファイルを読み込む
    private func _loadStations() -> [Station] {
        // インポートする
        guard let url = Bundle.main.url(forResource: "yamanote", withExtension: "json") else {
            fatalError("ファイルが見つからない")
        }
        // 読み込み
        guard let data = try? Data(contentsOf: url) else {
            fatalError("ファイル読み込みエラー")
        }
        // JSONデコード
        let decoder = JSONDecoder()
        guard let stations = try? decoder.decode([Station].self, from: data) else {
            fatalError("JSON読み込みエラー")
        }
        return stations
    }
    
    // 駅名の配列をデータストアに登録する
    private func registerDataStore(stations: [Station]) {
        stations.forEach{ params in
            // 登録するデータストアのクラス(DBで言うテーブル名相当)
            let station = NCMBObject(className: _className)
            // 駅名をセット
            station["name"] = params.name
            // 位置情報はNCMBGeoPointを利用する
            let geo = NCMBGeoPoint(latitude: params.latitude, longitude: params.longitude)
            // 位置情報をセット
            station["geo"] = geo
            // 保存処理(非同期処理)
            station.saveInBackground(callback: { result in
                // 成功していればログに追記
                if case .success(_)  = result {
                    let log = "保存しました -> \(params.name)"
                    logs.append(log)
                }
            })
        }
    }
    
    private func _execute() {
        // まずデータを削除する
        _removeStations()
        // JSONファイルを読み込む
        let stations = _loadStations()
        // JSONファイルの内容をデータストアに登録する
        registerDataStore(stations: stations)
    }
    
    var body: some View {
        VStack(spacing: 16
        ) {
            Text("駅一覧を読み込みます").padding()
            // ボタンを押したら駅登録処理開始
            Button(action: _execute, label: { Text("インポート実行")})
            if logs.count > 0 {
                // ログ表示用
                Text("ログ")
                List {
                    ForEach(Array(logs.enumerated()), id: \.element) { index, log in
                        Text(logs[index])
                    }
                }
            }
        }
    }
}

まとめ

今回はJSONファイルをインポートし、それを使ってNCMBObjectを作成しました。次回は地図コンポーネントと合わせて、位置情報検索を実装します。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?