Можете ли Вы Получить Широту и Долготу Из Базы данных для MapKit в SwiftUI

#swiftui #mapkit

Вопрос:

Я работаю над проектом SwiftUI и хочу разместить карту в представлении, использующем координаты, хранящиеся в Firestore. Пример Apple для MapKit в SwiftUI использует статические параметры широты и долготы в свойстве @State, а затем привязывает свойство к представлению Map ().

 struct BusinessMapView: View {
    @State private var region: MKCoordinateRegion = {
        var mapCoordinates = CLLocationCoordinate2D(latitude: 44.621754, longitude: -66.475873)
        var mapZoomLevel = MKCoordinateSpan(latitudeDelta: 5.00, longitudeDelta: 5.00)
        var mapRegion = MKCoordinateRegion(center: mapCoordinates, span: mapZoomLevel)
        return mapRegion
    }()
            
    var body: some View {
        Map(coordinateRegion: $region)
    }
}
 

Я хочу сделать следующее, но очевидно, что это недопустимо, так как вы не можете получить доступ к другим свойствам в другом свойстве.

 struct BusinessMapView: View {
    @ObservedObject var businessAddressRowViewModel: BusinessAddressRowViewModel

    @State private var region: MKCoordinateRegion = {
        var mapCoordinates = CLLocationCoordinate2D(latitude: businessAddressRowViewModel.businessAddress.latitude, longitude: businessAddressRowViewModel.businessAddress.longitude)
        var mapZoomLevel = MKCoordinateSpan(latitudeDelta: 5.00, longitudeDelta: 5.00)
        var mapRegion = MKCoordinateRegion(center: mapCoordinates, span: mapZoomLevel)
        return mapRegion
    }()
            
    var body: some View {
        Map(coordinateRegion: $region)
    }
}
 

Итак, мой вопрос в том, есть ли способ задать координаты из базы данных для карты() в SwiftUI или это единственный вариант использовать статические значения широты и долготы?

ПРАВКА ДОБАВЛЕНА ДЛЯ ПОЛУЧЕНИЯ ДОПОЛНИТЕЛЬНОЙ ИНФОРМАЦИИ

 class BusinessAddressRowViewModel: ObservableObject, Identifiable {
    
    // Properties
    var id: String = ""
    
    public static let shared = BusinessAddressRowViewModel()
    
    // Published Properties
    @Published var businessAddress: BusinessAddress
    
    // Combine Cancellable
    private var cancellables = Set<AnyCancellable>()
    
    
    // Initializer
    init(businessAddress: BusinessAddress) {
        self.businessAddress = businessAddress
        self.startCombine()
    }
    
    // Starting Combine
    func startCombine() {
        // Get Bank Account
        $businessAddress
            .receive(on: RunLoop.main)
            .compactMap { businessAddress in
                businessAddress.id
            }
            .assign(to: .id, on: self)
            .store(in: amp;cancellables)
    }
}
 

The shared property gives an error stating the parameter businessAddress is missing.

The data is coming from Firebase Firestore here.

 class BusinessAddressRepository: ObservableObject {
    let db = Firestore.firestore()
    private var snapshotListener: ListenerRegistration?
    
    @Published var businessAddresses = [BusinessAddress]()
    
    init() {
        startSnapshotListener()
    }
        
    func startSnapshotListener() {
        // Get the currentUserUid
        guard let currentUserId = Auth.auth().currentUser else {
            return
        }
        
        if snapshotListener == nil {
            // Add a SnapshotListener to the BusinessAddress Collection.
            self.snapshotListener = db.collection(FirestoreCollection.users).document(currentUserId.uid).collection(FirestoreCollection.businessAddresses).addSnapshotListener { (querySnapshot, error) in
                // Check to see if an error occured and print it. IMPLEMENT ERROR HANDLING LATER
                if let error = error {
                    print("Error getting documents: (error)")
                } else {
                    print("BusinessAddressRepository - snapshotListener called")
                    
                    // Check to make sure the Collection contains Documents
                    guard let documents = querySnapshot?.documents else {
                        print("No Business Addresses.")
                        return
                    }
                    
                    // Documents exist.
                    self.businessAddresses = documents.compactMap { businessAddress in
                        do {
                            return try businessAddress.data(as: BusinessAddress.self)
                        } catch {
                            print(error)
                        }
                        return nil
                    }
                }
            }
        }
    }

    func stopSnapshotListener() {
        if snapshotListener != nil {
            snapshotListener?.remove()
            snapshotListener = nil
        }
    }
}
 

Данные передаются в BusinessAddressRowViewModel из модели BusinessAddressViewModel. BusinessAddressView содержит список, в котором создаются все строки.

 class BusinessAddressViewModel: ObservableObject {
    var businessAddressRepository: BusinessAddressRepository

    // Published Properties
    @Published var businessAddressRowViewModels = [BusinessAddressRowViewModel]()
    
    // Combine Cancellable
    private var cancellables = Set<AnyCancellable>()
        
    // Intitalizer
    init(businessAddressRepository: BusinessAddressRepository) {
        self.businessAddressRepository = businessAddressRepository
        self.startCombine()
    }
    
    // Starting Combine - Filter results for business addresses created by the current user only.
    func startCombine() {
        businessAddressRepository
            .$businessAddresses
            .receive(on: RunLoop.main)
            .map { businessAddress in
                businessAddress
                    .map { businessAddress in
                        BusinessAddressRowViewModel(businessAddress: businessAddress)
                    }
            }
            .assign(to: .businessAddressRowViewModels, on: self)
            .store(in: amp;cancellables)
    }
}
 

Комментарии:

1. Приведенный выше код во втором блоке не компилируется. Это выдает ошибку. Поскольку регион ссылается на бизнес-координаты, это запрещено. Мой вопрос в том, как вы можете использовать регион с данными из базы данных, в которой вы не можете ссылаться на другие свойства, такие как businessCoordinates.

Ответ №1:

У вас здесь проблема с инициализацией, не имеющая ничего общего с картой(). Вы пытаетесь использовать businessCoordinates созданную ObservedObject переменную в инициализаторе и, я уверен, получаете Cannot use instance member 'businessCoordinates' within property initializer; property initializers run before 'self' is available ошибку.

Если вам не нужны «бизнес-координаторы» в любом месте представления, кроме данных, я бы рекомендовал следующее:

 class BusinessCoordinates: ObservableObject {
    
    public static let shared = BusinessCoordinates()
...
}
 

Это даст вам Singleton то, что вы можете использовать по своему желанию. Тогда ты используешь его вот так:

 struct BusinessMapView: View {
    @State private var region: MKCoordinateRegion

    init() {
        let mapCoordinates = CLLocationCoordinate2D(latitude: BusinessCoordinates.shared.latitude, longitude: BusinessCoordinates.shared.longitude)
        var mapZoomLevel = MKCoordinateSpan(latitudeDelta: 5.00, longitudeDelta: 5.00)
        _region = State(initialValue: MKCoordinateRegion(center: mapCoordinates, span: mapZoomLevel))

    }
            
    var body: some View {
        Map(coordinateRegion: $region)
    }
}
 

Комментарии:

1. Хотя мне нравится этот подход, я не уверен, что он будет работать для моего приложения. Я обновил вопрос выше, чтобы показать текущий файл BusinessCoordinates. Поскольку файл является моделью представления для представления строк, я передаю виртуальной машине бизнес-адрес. Это означает, что я должен инициировать совместное свойство с деловым адресом. Я не знаю, как это было бы возможно.

2. Вы вставляете его BusinessAddressRowViewModel , но в MapView то, что вы используете в BusinessCoordinates качестве ObservedObject . Откуда это взялось?

3. Прошу прощения за путаницу. Я переименовал файл в исходном сообщении, пытаясь упростить ситуацию. Я изменил имя объекта ObservedObject, чтобы оно соответствовало имени файла.

4. Откуда поступают данные? Оставляя в стороне ваш вид карты, как вы вводите данные BusinessAddressRowViewModel ? И что хранит эти данные до того, как вы создадите экземпляр BusinessAddressRowViewModel ?

5. Я добавил файл репозитория, который извлекает данные из Firebase Firestore, и модель businessaddressviewмодель, которая создает модели BusinessAddressRowViewМодели.