1
2

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 3 years have passed since last update.

【SwiftUI】Mapkitから取得した情報をFirebaseに保存する

Last updated at Posted at 2020-11-17

はじめに

SwiftUIでMapkitとFirebaseを使用し、位置情報をデータベースに保存することを目的とする。
前回記事までにSwiftUIで取得した現在地から逆ジオコーディングし、都道府県名と市区町村名を取得するところまでを記載した。
今回は取得した情報をFirebaseに保存するところを記載する。
Mapkitを使った逆コーデイングについては前回記事を参考にしてください。

参考記事
【SwiftUI】Mapkitを使った位置情報の取得と逆ジオコーデイング

開発環境

OSX 10.15.7 (Catalina)
Xcode 12.2.0
CocoaPods 1.10.0

本日の記事内容まとめ

  1. Mapkitを使用して取得した位置情報をfirebaseに保存する。
  2. ボタンを押したときにデータベースを更新できるようにする
  3. 異なるView間でのデータのやり取りをできるようにする

今回記事で実装・更新したところ

今回記事で更新、追加修正した箇所は主にmapViewContentview内になります。
それぞれ実装した内容については以下に記載します。

MapView内

異なるView間のデータのやり取りを行う場合は、取得元は@binding、取得先は@stateで変数を定義しなくてはいけません。そこが理解し切れていなかったため、何度もエラーが出て、実装にとても時間がかかりました。

以下の記事がとても参考になりました。
【SwiftUI】@Stateとか@Bindingて何

新たに実装した箇所
・ContentViewへのデータを送信のために@Bindingの定義
・latitudeとlongitudeを定義

ContentView.swift
// 以下の行を追加
// MKMapViewの設定
struct mapView : UIViewRepresentable {
    typealias UIViewType = MKMapView

    // 以下の行を追加
    @Binding var manager : CLLocationManager
    @Binding var alert : Bool
    // ここまで
    
    @Binding var latitude : Double
    @Binding var longitude : Double

......

    class Coordinator: NSObject, CLLocationManagerDelegate {
        
        var parent : mapView
        
        init(parent1 : mapView) {
            
            parent = parent1
        }
        
        func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
            
            if status == .denied{
                
                parent.alert.toggle()
                print("denied")
            }
        }
        
        func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
            
            let location = locations.last
            
            // ここから追加
            // ContentView内で呼び出すために、緯度と軽度をそれぞれ取得
            self.parent.latitude = (location?.coordinate.latitude)!
            self.parent.longitude = (location?.coordinate.longitude)!
            // ここまで
            
            let point = MKPointAnnotation()
            
            let georeader = CLGeocoder()

ContentView内

・MapViewからのデータの受け取り
@Stateで定義。
・Firebaseの更新にはaddDocumentを使用する。
・ボタンを設定し、クリック時にデータベースを更新

ContentView.swift
struct ContentView: View {
    
    @State var title = ""
    @State var subtitle = ""
    
    // ここから追加
    @State var latitude : Double
    @State var longitude : Double
    // ここまで
    
    @State var manager = CLLocationManager()
    @State var alert = false
    
    var body: some View {
        
        // ContentViewに地図を表示
        ZStack(alignment: .bottom, content: {
            // latitudeとlongitudeを追加
            mapView(manager: $manager, alert: $alert, latitude: $latitude, longitude: $longitude, title: $title, subtitle: $subtitle).alert(isPresented: $alert) {
                
                Alert(title: Text("Please Enable Location Access In Setting Panel!!!"))
            }
            
            // 地名を取得した場合に表示
            if self.title != "" {
                HStack(spacing: 12) {
                    Image(systemName: "info.circle.fill").font(.largeTitle).foregroundColor(.black)
                    VStack(alignment: .leading, spacing: 5){
                        Text(self.title).font(.body).foregroundColor(.black)
                        Text(self.subtitle).font(.caption).foregroundColor(.gray)
                    }
                    Spacer()

                    // ここから追加
                    Button(action: {

                        // firebaseの定義
                        let db = Firestore.firestore()
                        db.collection("locations").addDocument(data: [ "administrativeArea" : self.title, "locality" : self.subtitle, "Geopoint" : GeoPoint(latitude: self.latitude, longitude: self.longitude), "date": Timestamp(date: Date()),]) { (err) in
                            
                            if err != nil{
                                
                                print((err?.localizedDescription)!)
                                return
                            }
                            print("success")
                        }
                        
                    }) {
                        Image(systemName: "tray.and.arrow.up").font(.largeTitle).foregroundColor(.black)
                    }
                    // ここまで
                Spacer()
                }
                .padding()
                // "Color"はAssets.xcassetsで設定
                .background(Color("Color"))
                .cornerRadius(15)
                .offset(y: -30)
            .padding()
            }
        })
    }
}


ContentView_PreviewsとSceneDelegateのContentView部分に追記

それぞれにlatitudeとlongitudeを記載しました。
因数としてTokyoの緯度・経度を記載していますが、位置情報が取得できた場合は更新され、現在地を表示します。

ContentView.swift
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        // "latitude"と"longitude"を追記
        // 場所はTokyoで設定。現在地を取得した場合に更新される。
        ContentView(latitude: 35.6804, longitude: 139.7690)
    }
}
SceneDelegate.swift
class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?


    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
        // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
        // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).

        // Create the SwiftUI view that provides the window contents.

        // "latitude"と"longitude"を追記
        // 場所はTokyoで設定。現在地を取得した場合に更新される。
        let contentView = ContentView(latitude: 35.6804, longitude: 139.7690)

        // Use a UIHostingController as window root view controller.
        if let windowScene = scene as? UIWindowScene {

アプリのフロー

  1. アプリ立ち上げ
  2. Mapkitを用いて位置情報の取得→逆ジオコーディングを行い、都道府県名・市町村名を取得
  3. ContentViewに地図とインフォセクションの表示
  4. インフォセクション内のボタンを押し、Firebaseのデータベースを更新

iPhone_11_–_14_2.png

Simulatorを起動し、インフォセクション内のボタンを押すと、Firebaseのデータベースが更新されます。
実機で確認したところ、実機を使用した場合も現在地を取得し、逆ジオコーディングし、データベースを更新できました。

Location-Sharing_-Cloud_Firestore-_Firebase_コンソール.png

今後実装予定の部分

・Firebaseからのデータの受け取り
・受け取ったデータを表示するViewの実装
→listViewで表示。RowViewも作成する
・TabViewを実装
→地図表示と保存したデータを出力するViewを作成

以上です。

1
2
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
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?