Всех приветствую.
Совсем недавно поступила задача заменить стандартные карты от Apple на Яндекс карты. Ниже немного о Yandex MapKit.
Yandex MapKit — это кроссплатформенная библиотека, которая позволяет использовать возможности Яндекс.Карт в мобильных приложениях для iOS и Android.
Основная проблема заключалась в том, что библиотека Yandex MapKit создана для UIKit, но наша цель поставить все это дело на SwiftUI. Поэтому прибегаем к старой доброй пикче:

Будем считать, что вы получили ключ у Яндекса и установили библиотеку в проект, для ознакомления с установкой прикрепляю ссылку.
Первым делом создаем класс AppDelegate, подробнее как реализовать этот класс в SwiftUI проекте здесь.
Затем идем в таргет проекта -> Info и прописываем Privacy

В классе AppDelegate импортируем библиотеку Яндекса и устанавливаем ваш API ключ из кабинета разработчика Яндекса
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { YMKMapKit.setApiKey("Ваш API-ключ") YMKMapKit.sharedInstance() }
Создадим класс LocationManager, в который импортируем следующее:
import Foundation import CoreLocation import YandexMapsMobile import Combine
LocationManager будет отвечать за всю логику взаимодействия с картами, объявим в нем саму карту и приватную переменную manager, которая наследуется от класса CLLocationManager, для работы с местоположением пользователя
class LocationManager: NSObject{ lazy var map : YMKMap = { return mapView.mapWindow.map }() private let manager = CLLocationManager() let mapView = YMKMapView(frame: CGRect.zero) override init(){ super.init() } }
Унаследуем класс от протокола CLLocationManagerDelegate и в init() подпишем делегат объявленного менеджера и вызовем у него функции нахождения геопозиции.
class LocationManager: NSObject, CLLocationManagerDelegate
override init(){ super.init() manager.delegate = self manager.startUpdatingLocation() }
Реализуем в классе LocationManager функцию делегата для проверки статуса и начала использования геопозиции
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) { if status == .authorizedWhenInUse { self.manager.startUpdatingLocation() } }
Затем создадим переменную в которую будем записывать последнее местоположение пользователя:
@Published var lastUserLocation: CLLocation? = nil
Реализуем функцию протокола для того, чтобы слушать изменение локации пользователя и записывать в переменную:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { self.lastUserLocation = locations.last }
Приступим написанию UI части. Создадим SwiftUIView с названием MapView и обернем в ZStack. Объявим экземпляр класса LocationManager
@ObservedObject var locationManager = LocationManager()
Так как Yandex MapKit работает с UIKit, создадим структуру YandexMapView и наследуемся от протокола UIViewRepresentable.
Объявим EnviromentObject переменную от класса LocationManager и реализуем функцию makeUIView(), в котором вернем как View ранее созданные mapView в классе LocationManager
import SwiftUI import YandexMapsMobile import Combine struct YandexMapView: UIViewRepresentable { @EnvironmentObject var locationManager : YaLocationManager func makeUIView(context: Context) -> YMKMapView { return locationManager.mapView } func updateUIView(_ mapView: YMKMapView, context: Context) {} }
Вернемся в LocationManager и создадим приватную функцию centerMapLocation, которая на вход будет принимать параметр target, в который будем подавать долготу и широту, и параметр map, который принимает Яндекс карту.
func centerMapLocation(target location: YMKPoint?, map: YMKMapView) { guard let location = location else { print("Failed to get user location"); return } map.mapWindow.map.move( with: YMKCameraPosition(target: location, zoom: 18, azimuth: 0, tilt: 0), animationType: YMKAnimation(type: YMKAnimationType.smooth, duration: 0.5) ) }
И теперь реализуем функцию для показа текущей геопозиции пользователя, она будет брать lastUserLocation и передавать в метод centerMapLocation, описанный выше.
func currentUserLocation(){ if let myLocation = lastKnownLocation { centerMapLocation(target: YMKPoint(latitude: myLocation.coordinate.latitude, longitude: myLocation.coordinate.longitude), map: mapView) } }
Объявим в ZStack YandexMapView и передадим ей объявленный класс
ZStack{ YandexMapView() .edgesIgnoringSafeArea(.all) .environmentObject(locationManager) }
Осталось лишь в OnAppear вызвать метод currentUserLocation
.onAppear{ locationManager.currentUserLocation() }
В результате получаем, работающую Яндекс Карту в вашем SwiftUI проекте
Результат

Ниже прикрепляю код всех классов:
LocationManager
class LocationManager: NSObject, CLLocationManagerDelegate, ObservableObject{ private let manager = CLLocationManager() let mapView = YMKMapView(frame: CGRect.zero) @Published var lastUserLocation: CLLocation? = nil lazy var map : YMKMap = { return mapView.mapWindow.map }() override init() { super.init() self.manager.delegate = self } func currentUserLocation(){ if let myLocation = lastUserLocation { centerMapLocation(target: YMKPoint(latitude: myLocation.coordinate.latitude, longitude: myLocation.coordinate.longitude), map: mapView ) } } func centerMapLocation(target location: YMKPoint?, map: YMKMapView) { guard let location = location else { print("Failed to get user location"); return } map.mapWindow.map.move( with: YMKCameraPosition(target: location, zoom: 18, azimuth: 0, tilt: 0), animationType: YMKAnimation(type: YMKAnimationType.smooth, duration: 0.5) ) } func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) { if status == .authorizedWhenInUse { self.manager.startUpdatingLocation() } } func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { // Notify listeners that the user has a new location self.lastUserLocation = locations.last } }
MapView
struct MapView: View { @ObservedObject var locationManager = LocationManager() var body: some View { ZStack{ YandexMapsView().edgesIgnoringSafeArea(.all).environmentObject(locationManager) }.onAppear{ locationManager.currentUserLocation() } } }
YandexMapView
struct YandexMapView: UIViewRepresentable { @EnvironmentObject var locationManager : YaLocationManager func makeUIView(context: Context) -> YMKMapView { return locationManager.mapView } func updateUIView(_ mapView: YMKMapView, context: Context) {} }
В следующей части будет реализован поиск по адресу, позиция на карте, выбранного адреса и обновление адреса по координатам из центра камеры.
Всем спасибо за внимание.
